|
@@ -22,8 +22,6 @@
|
|
|
#include "etnaviv_gpu.h"
|
|
|
#include "etnaviv_gem.h"
|
|
|
#include "etnaviv_mmu.h"
|
|
|
-#include "etnaviv_iommu.h"
|
|
|
-#include "etnaviv_iommu_v2.h"
|
|
|
#include "common.xml.h"
|
|
|
#include "state.xml.h"
|
|
|
#include "state_hi.xml.h"
|
|
@@ -329,6 +327,18 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu)
|
|
|
gpu->identity.revision = 0x1051;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /*
|
|
|
+ * NXP likes to call the GPU on the i.MX6QP GC2000+, but in
|
|
|
+ * reality it's just a re-branded GC3000. We can identify this
|
|
|
+ * core by the upper half of the revision register being all 1.
|
|
|
+ * Fix model/rev here, so all other places can refer to this
|
|
|
+ * core by its real identity.
|
|
|
+ */
|
|
|
+ if (etnaviv_is_model_rev(gpu, GC2000, 0xffff5450)) {
|
|
|
+ gpu->identity.model = chipModel_GC3000;
|
|
|
+ gpu->identity.revision &= 0xffff;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
dev_info(gpu->dev, "model: GC%x, revision: %x\n",
|
|
@@ -528,6 +538,14 @@ static void etnaviv_gpu_enable_mlcg(struct etnaviv_gpu *gpu)
|
|
|
gpu_write(gpu, VIVS_PM_MODULE_CONTROLS, pmc);
|
|
|
}
|
|
|
|
|
|
+void etnaviv_gpu_start_fe(struct etnaviv_gpu *gpu, u32 address, u16 prefetch)
|
|
|
+{
|
|
|
+ gpu_write(gpu, VIVS_FE_COMMAND_ADDRESS, address);
|
|
|
+ gpu_write(gpu, VIVS_FE_COMMAND_CONTROL,
|
|
|
+ VIVS_FE_COMMAND_CONTROL_ENABLE |
|
|
|
+ VIVS_FE_COMMAND_CONTROL_PREFETCH(prefetch));
|
|
|
+}
|
|
|
+
|
|
|
static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu)
|
|
|
{
|
|
|
u16 prefetch;
|
|
@@ -568,33 +586,20 @@ static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu)
|
|
|
gpu_write(gpu, VIVS_MC_BUS_CONFIG, bus_config);
|
|
|
}
|
|
|
|
|
|
- /* set base addresses */
|
|
|
- gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_RA, gpu->memory_base);
|
|
|
- gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_FE, gpu->memory_base);
|
|
|
- gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_TX, gpu->memory_base);
|
|
|
- gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PEZ, gpu->memory_base);
|
|
|
- gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PE, gpu->memory_base);
|
|
|
-
|
|
|
- /* setup the MMU page table pointers */
|
|
|
- etnaviv_iommu_domain_restore(gpu, gpu->mmu->domain);
|
|
|
+ /* setup the MMU */
|
|
|
+ etnaviv_iommu_restore(gpu);
|
|
|
|
|
|
/* Start command processor */
|
|
|
prefetch = etnaviv_buffer_init(gpu);
|
|
|
|
|
|
gpu_write(gpu, VIVS_HI_INTR_ENBL, ~0U);
|
|
|
- gpu_write(gpu, VIVS_FE_COMMAND_ADDRESS,
|
|
|
- gpu->buffer->paddr - gpu->memory_base);
|
|
|
- gpu_write(gpu, VIVS_FE_COMMAND_CONTROL,
|
|
|
- VIVS_FE_COMMAND_CONTROL_ENABLE |
|
|
|
- VIVS_FE_COMMAND_CONTROL_PREFETCH(prefetch));
|
|
|
+ etnaviv_gpu_start_fe(gpu, etnaviv_iommu_get_cmdbuf_va(gpu, gpu->buffer),
|
|
|
+ prefetch);
|
|
|
}
|
|
|
|
|
|
int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
|
|
|
{
|
|
|
int ret, i;
|
|
|
- struct iommu_domain *iommu;
|
|
|
- enum etnaviv_iommu_version version;
|
|
|
- bool mmuv2;
|
|
|
|
|
|
ret = pm_runtime_get_sync(gpu->dev);
|
|
|
if (ret < 0) {
|
|
@@ -642,32 +647,10 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
|
|
|
goto fail;
|
|
|
}
|
|
|
|
|
|
- /* Setup IOMMU.. eventually we will (I think) do this once per context
|
|
|
- * and have separate page tables per context. For now, to keep things
|
|
|
- * simple and to get something working, just use a single address space:
|
|
|
- */
|
|
|
- mmuv2 = gpu->identity.minor_features1 & chipMinorFeatures1_MMU_VERSION;
|
|
|
- dev_dbg(gpu->dev, "mmuv2: %d\n", mmuv2);
|
|
|
-
|
|
|
- if (!mmuv2) {
|
|
|
- iommu = etnaviv_iommu_domain_alloc(gpu);
|
|
|
- version = ETNAVIV_IOMMU_V1;
|
|
|
- } else {
|
|
|
- iommu = etnaviv_iommu_v2_domain_alloc(gpu);
|
|
|
- version = ETNAVIV_IOMMU_V2;
|
|
|
- }
|
|
|
-
|
|
|
- if (!iommu) {
|
|
|
- dev_err(gpu->dev, "Failed to allocate GPU IOMMU domain\n");
|
|
|
- ret = -ENOMEM;
|
|
|
- goto fail;
|
|
|
- }
|
|
|
-
|
|
|
- gpu->mmu = etnaviv_iommu_new(gpu, iommu, version);
|
|
|
- if (!gpu->mmu) {
|
|
|
+ gpu->mmu = etnaviv_iommu_new(gpu);
|
|
|
+ if (IS_ERR(gpu->mmu)) {
|
|
|
dev_err(gpu->dev, "Failed to instantiate GPU IOMMU\n");
|
|
|
- iommu_domain_free(iommu);
|
|
|
- ret = -ENOMEM;
|
|
|
+ ret = PTR_ERR(gpu->mmu);
|
|
|
goto fail;
|
|
|
}
|
|
|
|
|
@@ -678,7 +661,9 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
|
|
|
dev_err(gpu->dev, "could not create command buffer\n");
|
|
|
goto destroy_iommu;
|
|
|
}
|
|
|
- if (gpu->buffer->paddr - gpu->memory_base > 0x80000000) {
|
|
|
+
|
|
|
+ if (gpu->mmu->version == ETNAVIV_IOMMU_V1 &&
|
|
|
+ gpu->buffer->paddr - gpu->memory_base > 0x80000000) {
|
|
|
ret = -EINVAL;
|
|
|
dev_err(gpu->dev,
|
|
|
"command buffer outside valid memory window\n");
|
|
@@ -867,45 +852,6 @@ int etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m)
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
-/*
|
|
|
- * Power Management:
|
|
|
- */
|
|
|
-static int enable_clk(struct etnaviv_gpu *gpu)
|
|
|
-{
|
|
|
- if (gpu->clk_core)
|
|
|
- clk_prepare_enable(gpu->clk_core);
|
|
|
- if (gpu->clk_shader)
|
|
|
- clk_prepare_enable(gpu->clk_shader);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static int disable_clk(struct etnaviv_gpu *gpu)
|
|
|
-{
|
|
|
- if (gpu->clk_core)
|
|
|
- clk_disable_unprepare(gpu->clk_core);
|
|
|
- if (gpu->clk_shader)
|
|
|
- clk_disable_unprepare(gpu->clk_shader);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static int enable_axi(struct etnaviv_gpu *gpu)
|
|
|
-{
|
|
|
- if (gpu->clk_bus)
|
|
|
- clk_prepare_enable(gpu->clk_bus);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static int disable_axi(struct etnaviv_gpu *gpu)
|
|
|
-{
|
|
|
- if (gpu->clk_bus)
|
|
|
- clk_disable_unprepare(gpu->clk_bus);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
/*
|
|
|
* Hangcheck detection for locked gpu:
|
|
|
*/
|
|
@@ -945,7 +891,7 @@ static void recover_worker(struct work_struct *work)
|
|
|
gpu->completed_fence = gpu->active_fence;
|
|
|
|
|
|
etnaviv_gpu_hw_init(gpu);
|
|
|
- gpu->switch_context = true;
|
|
|
+ gpu->lastctx = NULL;
|
|
|
gpu->exec_state = -1;
|
|
|
|
|
|
mutex_unlock(&gpu->lock);
|
|
@@ -1178,6 +1124,9 @@ struct etnaviv_cmdbuf *etnaviv_gpu_cmdbuf_new(struct etnaviv_gpu *gpu, u32 size,
|
|
|
if (!cmdbuf)
|
|
|
return NULL;
|
|
|
|
|
|
+ if (gpu->mmu->version == ETNAVIV_IOMMU_V2)
|
|
|
+ size = ALIGN(size, SZ_4K);
|
|
|
+
|
|
|
cmdbuf->vaddr = dma_alloc_wc(gpu->dev, size, &cmdbuf->paddr,
|
|
|
GFP_KERNEL);
|
|
|
if (!cmdbuf->vaddr) {
|
|
@@ -1193,6 +1142,7 @@ struct etnaviv_cmdbuf *etnaviv_gpu_cmdbuf_new(struct etnaviv_gpu *gpu, u32 size,
|
|
|
|
|
|
void etnaviv_gpu_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf)
|
|
|
{
|
|
|
+ etnaviv_iommu_put_cmdbuf_va(cmdbuf->gpu, cmdbuf);
|
|
|
dma_free_wc(cmdbuf->gpu->dev, cmdbuf->size, cmdbuf->vaddr,
|
|
|
cmdbuf->paddr);
|
|
|
kfree(cmdbuf);
|
|
@@ -1333,8 +1283,6 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
- mutex_lock(&gpu->lock);
|
|
|
-
|
|
|
/*
|
|
|
* TODO
|
|
|
*
|
|
@@ -1348,16 +1296,18 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
|
|
|
if (unlikely(event == ~0U)) {
|
|
|
DRM_ERROR("no free event\n");
|
|
|
ret = -EBUSY;
|
|
|
- goto out_unlock;
|
|
|
+ goto out_pm_put;
|
|
|
}
|
|
|
|
|
|
fence = etnaviv_gpu_fence_alloc(gpu);
|
|
|
if (!fence) {
|
|
|
event_free(gpu, event);
|
|
|
ret = -ENOMEM;
|
|
|
- goto out_unlock;
|
|
|
+ goto out_pm_put;
|
|
|
}
|
|
|
|
|
|
+ mutex_lock(&gpu->lock);
|
|
|
+
|
|
|
gpu->event[event].fence = fence;
|
|
|
submit->fence = fence->seqno;
|
|
|
gpu->active_fence = submit->fence;
|
|
@@ -1395,9 +1345,9 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
|
|
|
hangcheck_timer_reset(gpu);
|
|
|
ret = 0;
|
|
|
|
|
|
-out_unlock:
|
|
|
mutex_unlock(&gpu->lock);
|
|
|
|
|
|
+out_pm_put:
|
|
|
etnaviv_gpu_pm_put(gpu);
|
|
|
|
|
|
return ret;
|
|
@@ -1425,6 +1375,21 @@ static irqreturn_t irq_handler(int irq, void *data)
|
|
|
intr &= ~VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR;
|
|
|
}
|
|
|
|
|
|
+ if (intr & VIVS_HI_INTR_ACKNOWLEDGE_MMU_EXCEPTION) {
|
|
|
+ int i;
|
|
|
+
|
|
|
+ dev_err_ratelimited(gpu->dev,
|
|
|
+ "MMU fault status 0x%08x\n",
|
|
|
+ gpu_read(gpu, VIVS_MMUv2_STATUS));
|
|
|
+ for (i = 0; i < 4; i++) {
|
|
|
+ dev_err_ratelimited(gpu->dev,
|
|
|
+ "MMU %d fault addr 0x%08x\n",
|
|
|
+ i, gpu_read(gpu,
|
|
|
+ VIVS_MMUv2_EXCEPTION_ADDR(i)));
|
|
|
+ }
|
|
|
+ intr &= ~VIVS_HI_INTR_ACKNOWLEDGE_MMU_EXCEPTION;
|
|
|
+ }
|
|
|
+
|
|
|
while ((event = ffs(intr)) != 0) {
|
|
|
struct fence *fence;
|
|
|
|
|
@@ -1466,39 +1431,72 @@ static int etnaviv_gpu_clk_enable(struct etnaviv_gpu *gpu)
|
|
|
{
|
|
|
int ret;
|
|
|
|
|
|
- ret = enable_clk(gpu);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
+ if (gpu->clk_bus) {
|
|
|
+ ret = clk_prepare_enable(gpu->clk_bus);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
|
|
|
- ret = enable_axi(gpu);
|
|
|
- if (ret) {
|
|
|
- disable_clk(gpu);
|
|
|
- return ret;
|
|
|
+ if (gpu->clk_core) {
|
|
|
+ ret = clk_prepare_enable(gpu->clk_core);
|
|
|
+ if (ret)
|
|
|
+ goto disable_clk_bus;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (gpu->clk_shader) {
|
|
|
+ ret = clk_prepare_enable(gpu->clk_shader);
|
|
|
+ if (ret)
|
|
|
+ goto disable_clk_core;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
+
|
|
|
+disable_clk_core:
|
|
|
+ if (gpu->clk_core)
|
|
|
+ clk_disable_unprepare(gpu->clk_core);
|
|
|
+disable_clk_bus:
|
|
|
+ if (gpu->clk_bus)
|
|
|
+ clk_disable_unprepare(gpu->clk_bus);
|
|
|
+
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
static int etnaviv_gpu_clk_disable(struct etnaviv_gpu *gpu)
|
|
|
{
|
|
|
- int ret;
|
|
|
+ if (gpu->clk_shader)
|
|
|
+ clk_disable_unprepare(gpu->clk_shader);
|
|
|
+ if (gpu->clk_core)
|
|
|
+ clk_disable_unprepare(gpu->clk_core);
|
|
|
+ if (gpu->clk_bus)
|
|
|
+ clk_disable_unprepare(gpu->clk_bus);
|
|
|
|
|
|
- ret = disable_axi(gpu);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- ret = disable_clk(gpu);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
+int etnaviv_gpu_wait_idle(struct etnaviv_gpu *gpu, unsigned int timeout_ms)
|
|
|
+{
|
|
|
+ unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
|
|
|
|
|
|
- return 0;
|
|
|
+ do {
|
|
|
+ u32 idle = gpu_read(gpu, VIVS_HI_IDLE_STATE);
|
|
|
+
|
|
|
+ if ((idle & gpu->idle_mask) == gpu->idle_mask)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (time_is_before_jiffies(timeout)) {
|
|
|
+ dev_warn(gpu->dev,
|
|
|
+ "timed out waiting for idle: idle=0x%x\n",
|
|
|
+ idle);
|
|
|
+ return -ETIMEDOUT;
|
|
|
+ }
|
|
|
+
|
|
|
+ udelay(5);
|
|
|
+ } while (1);
|
|
|
}
|
|
|
|
|
|
static int etnaviv_gpu_hw_suspend(struct etnaviv_gpu *gpu)
|
|
|
{
|
|
|
if (gpu->buffer) {
|
|
|
- unsigned long timeout;
|
|
|
-
|
|
|
/* Replace the last WAIT with END */
|
|
|
etnaviv_buffer_end(gpu);
|
|
|
|
|
@@ -1507,22 +1505,7 @@ static int etnaviv_gpu_hw_suspend(struct etnaviv_gpu *gpu)
|
|
|
* happen quickly (as the WAIT is only 200 cycles). If
|
|
|
* we fail, just warn and continue.
|
|
|
*/
|
|
|
- timeout = jiffies + msecs_to_jiffies(100);
|
|
|
- do {
|
|
|
- u32 idle = gpu_read(gpu, VIVS_HI_IDLE_STATE);
|
|
|
-
|
|
|
- if ((idle & gpu->idle_mask) == gpu->idle_mask)
|
|
|
- break;
|
|
|
-
|
|
|
- if (time_is_before_jiffies(timeout)) {
|
|
|
- dev_warn(gpu->dev,
|
|
|
- "timed out waiting for idle: idle=0x%x\n",
|
|
|
- idle);
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- udelay(5);
|
|
|
- } while (1);
|
|
|
+ etnaviv_gpu_wait_idle(gpu, 100);
|
|
|
}
|
|
|
|
|
|
return etnaviv_gpu_clk_disable(gpu);
|
|
@@ -1634,7 +1617,7 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
|
|
|
{
|
|
|
struct device *dev = &pdev->dev;
|
|
|
struct etnaviv_gpu *gpu;
|
|
|
- int err = 0;
|
|
|
+ int err;
|
|
|
|
|
|
gpu = devm_kzalloc(dev, sizeof(*gpu), GFP_KERNEL);
|
|
|
if (!gpu)
|
|
@@ -1651,16 +1634,15 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
|
|
|
/* Get Interrupt: */
|
|
|
gpu->irq = platform_get_irq(pdev, 0);
|
|
|
if (gpu->irq < 0) {
|
|
|
- err = gpu->irq;
|
|
|
- dev_err(dev, "failed to get irq: %d\n", err);
|
|
|
- goto fail;
|
|
|
+ dev_err(dev, "failed to get irq: %d\n", gpu->irq);
|
|
|
+ return gpu->irq;
|
|
|
}
|
|
|
|
|
|
err = devm_request_irq(&pdev->dev, gpu->irq, irq_handler, 0,
|
|
|
dev_name(gpu->dev), gpu);
|
|
|
if (err) {
|
|
|
dev_err(dev, "failed to request IRQ%u: %d\n", gpu->irq, err);
|
|
|
- goto fail;
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
/* Get Clocks: */
|
|
@@ -1694,13 +1676,10 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
|
|
|
err = component_add(&pdev->dev, &gpu_ops);
|
|
|
if (err < 0) {
|
|
|
dev_err(&pdev->dev, "failed to register component: %d\n", err);
|
|
|
- goto fail;
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
-
|
|
|
-fail:
|
|
|
- return err;
|
|
|
}
|
|
|
|
|
|
static int etnaviv_gpu_platform_remove(struct platform_device *pdev)
|