diff options
Diffstat (limited to 'drivers/gpu/drm/msm/adreno/adreno_gpu.c')
-rw-r--r-- | drivers/gpu/drm/msm/adreno/adreno_gpu.c | 184 |
1 files changed, 117 insertions, 67 deletions
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 57586c794b84..6934cee07d42 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -8,7 +8,7 @@ #include <linux/ascii85.h> #include <linux/interconnect.h> -#include <linux/qcom_scm.h> +#include <linux/firmware/qcom/qcom_scm.h> #include <linux/kernel.h> #include <linux/of_address.h> #include <linux/pm_opp.h> @@ -208,7 +208,7 @@ adreno_iommu_create_address_space(struct msm_gpu *gpu, struct msm_gem_address_space *aspace; u64 start, size; - mmu = msm_iommu_new(&pdev->dev, quirks); + mmu = msm_iommu_gpu_new(&pdev->dev, gpu, quirks); if (IS_ERR_OR_NULL(mmu)) return ERR_CAST(mmu); @@ -246,6 +246,66 @@ u64 adreno_private_address_space_size(struct msm_gpu *gpu) return SZ_4G; } +#define ARM_SMMU_FSR_TF BIT(1) +#define ARM_SMMU_FSR_PF BIT(3) +#define ARM_SMMU_FSR_EF BIT(4) + +int adreno_fault_handler(struct msm_gpu *gpu, unsigned long iova, int flags, + struct adreno_smmu_fault_info *info, const char *block, + u32 scratch[4]) +{ + const char *type = "UNKNOWN"; + bool do_devcoredump = info && !READ_ONCE(gpu->crashstate); + + /* + * If we aren't going to be resuming later from fault_worker, then do + * it now. + */ + if (!do_devcoredump) { + gpu->aspace->mmu->funcs->resume_translation(gpu->aspace->mmu); + } + + /* + * Print a default message if we couldn't get the data from the + * adreno-smmu-priv + */ + if (!info) { + pr_warn_ratelimited("*** gpu fault: iova=%.16lx flags=%d (%u,%u,%u,%u)\n", + iova, flags, + scratch[0], scratch[1], scratch[2], scratch[3]); + + return 0; + } + + if (info->fsr & ARM_SMMU_FSR_TF) + type = "TRANSLATION"; + else if (info->fsr & ARM_SMMU_FSR_PF) + type = "PERMISSION"; + else if (info->fsr & ARM_SMMU_FSR_EF) + type = "EXTERNAL"; + + pr_warn_ratelimited("*** gpu fault: ttbr0=%.16llx iova=%.16lx dir=%s type=%s source=%s (%u,%u,%u,%u)\n", + info->ttbr0, iova, + flags & IOMMU_FAULT_WRITE ? "WRITE" : "READ", + type, block, + scratch[0], scratch[1], scratch[2], scratch[3]); + + if (do_devcoredump) { + /* Turn off the hangcheck timer to keep it from bothering us */ + del_timer(&gpu->hangcheck_timer); + + gpu->fault_info.ttbr0 = info->ttbr0; + gpu->fault_info.iova = iova; + gpu->fault_info.flags = flags; + gpu->fault_info.type = type; + gpu->fault_info.block = block; + + kthread_queue_work(gpu->worker, &gpu->fault_work); + } + + return 0; +} + int adreno_get_param(struct msm_gpu *gpu, struct msm_file_private *ctx, uint32_t param, uint64_t *value, uint32_t *len) { @@ -352,6 +412,8 @@ int adreno_set_param(struct msm_gpu *gpu, struct msm_file_private *ctx, /* Ensure string is null terminated: */ str[len] = '\0'; + mutex_lock(&gpu->lock); + if (param == MSM_PARAM_COMM) { paramp = &ctx->comm; } else { @@ -361,6 +423,8 @@ int adreno_set_param(struct msm_gpu *gpu, struct msm_file_private *ctx, kfree(*paramp); *paramp = str; + mutex_unlock(&gpu->lock); + return 0; } case MSM_PARAM_SYSPROF: @@ -499,16 +563,9 @@ struct drm_gem_object *adreno_fw_create_bo(struct msm_gpu *gpu, int adreno_hw_init(struct msm_gpu *gpu) { - struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); - int ret, i; - VERB("%s", gpu->name); - ret = adreno_load_fw(adreno_gpu); - if (ret) - return ret; - - for (i = 0; i < gpu->nr_rings; i++) { + for (int i = 0; i < gpu->nr_rings; i++) { struct msm_ringbuffer *ring = gpu->rb[i]; if (!ring) @@ -918,73 +975,46 @@ void adreno_wait_ring(struct msm_ringbuffer *ring, uint32_t ndwords) ring->id); } -/* Get legacy powerlevels from qcom,gpu-pwrlevels and populate the opp table */ -static int adreno_get_legacy_pwrlevels(struct device *dev) -{ - struct device_node *child, *node; - int ret; - - node = of_get_compatible_child(dev->of_node, "qcom,gpu-pwrlevels"); - if (!node) { - DRM_DEV_DEBUG(dev, "Could not find the GPU powerlevels\n"); - return -ENXIO; - } - - for_each_child_of_node(node, child) { - unsigned int val; - - ret = of_property_read_u32(child, "qcom,gpu-freq", &val); - if (ret) - continue; - - /* - * Skip the intentionally bogus clock value found at the bottom - * of most legacy frequency tables - */ - if (val != 27000000) - dev_pm_opp_add(dev, val, 0); - } - - of_node_put(node); - - return 0; -} - -static void adreno_get_pwrlevels(struct device *dev, +static int adreno_get_pwrlevels(struct device *dev, struct msm_gpu *gpu) { + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); unsigned long freq = ULONG_MAX; struct dev_pm_opp *opp; int ret; gpu->fast_rate = 0; - /* You down with OPP? */ - if (!of_find_property(dev->of_node, "operating-points-v2", NULL)) - ret = adreno_get_legacy_pwrlevels(dev); - else { - ret = devm_pm_opp_of_add_table(dev); - if (ret) - DRM_DEV_ERROR(dev, "Unable to set the OPP table\n"); - } - - if (!ret) { - /* Find the fastest defined rate */ - opp = dev_pm_opp_find_freq_floor(dev, &freq); - if (!IS_ERR(opp)) { - gpu->fast_rate = freq; - dev_pm_opp_put(opp); + /* devm_pm_opp_of_add_table may error out but will still create an OPP table */ + ret = devm_pm_opp_of_add_table(dev); + if (ret == -ENODEV) { + /* Special cases for ancient hw with ancient DT bindings */ + if (adreno_is_a2xx(adreno_gpu)) { + dev_warn(dev, "Unable to find the OPP table. Falling back to 200 MHz.\n"); + dev_pm_opp_add(dev, 200000000, 0); + } else if (adreno_is_a320(adreno_gpu)) { + dev_warn(dev, "Unable to find the OPP table. Falling back to 450 MHz.\n"); + dev_pm_opp_add(dev, 450000000, 0); + } else { + DRM_DEV_ERROR(dev, "Unable to find the OPP table\n"); + return -ENODEV; } + } else if (ret) { + DRM_DEV_ERROR(dev, "Unable to set the OPP table\n"); + return ret; } - if (!gpu->fast_rate) { - dev_warn(dev, - "Could not find a clock rate. Using a reasonable default\n"); - /* Pick a suitably safe clock speed for any target */ - gpu->fast_rate = 200000000; - } + /* Find the fastest defined rate */ + opp = dev_pm_opp_find_freq_floor(dev, &freq); + if (IS_ERR(opp)) + return PTR_ERR(opp); + + gpu->fast_rate = freq; + dev_pm_opp_put(opp); DBG("fast_rate=%u, slow_rate=27000000", gpu->fast_rate); + + return 0; } int adreno_gpu_ocmem_init(struct device *dev, struct adreno_gpu *adreno_gpu, @@ -1042,6 +1072,24 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, struct adreno_rev *rev = &config->rev; const char *gpu_name; u32 speedbin; + int ret; + + /* Only handle the core clock when GMU is not in use */ + if (config->rev.core < 6) { + /* + * This can only be done before devm_pm_opp_of_add_table(), or + * dev_pm_opp_set_config() will WARN_ON() + */ + if (IS_ERR(devm_clk_get(dev, "core"))) { + /* + * If "core" is absent, go for the legacy clock name. + * If we got this far in probing, it's a given one of + * them exists. + */ + devm_pm_opp_set_clkname(dev, "core_clk"); + } else + devm_pm_opp_set_clkname(dev, "core"); + } adreno_gpu->funcs = funcs; adreno_gpu->info = adreno_info(config->rev); @@ -1066,7 +1114,9 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, adreno_gpu_config.nr_rings = nr_rings; - adreno_get_pwrlevels(dev, gpu); + ret = adreno_get_pwrlevels(dev, gpu); + if (ret) + return ret; pm_runtime_set_autosuspend_delay(dev, adreno_gpu->info->inactive_period); @@ -1079,13 +1129,13 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, void adreno_gpu_cleanup(struct adreno_gpu *adreno_gpu) { struct msm_gpu *gpu = &adreno_gpu->base; - struct msm_drm_private *priv = gpu->dev->dev_private; + struct msm_drm_private *priv = gpu->dev ? gpu->dev->dev_private : NULL; unsigned int i; for (i = 0; i < ARRAY_SIZE(adreno_gpu->info->fw); i++) release_firmware(adreno_gpu->fw[i]); - if (pm_runtime_enabled(&priv->gpu_pdev->dev)) + if (priv && pm_runtime_enabled(&priv->gpu_pdev->dev)) pm_runtime_disable(&priv->gpu_pdev->dev); msm_gpu_cleanup(&adreno_gpu->base); |