Merge tag 'mmc-v4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
[cascardo/linux.git] / drivers / gpu / drm / etnaviv / etnaviv_gpu.c
index b382cf5..b1254f8 100644 (file)
@@ -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);
@@ -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)