// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2013 Red Hat * Author: Rob Clark <robdclark@gmail.com> * * Copyright (c) 2014 The Linux Foundation. All rights reserved.
*/
ret = of_reserved_mem_region_to_resource(np, 0, &r); if (ret) {
zap_available = false; return ret;
}
mem_phys = r.start;
/* * Check for a firmware-name property. This is the new scheme * to handle firmware that may be signed with device specific * keys, allowing us to have a different zap fw path for different * devices. * * If the firmware-name property is found, we bypass the * adreno_request_fw() mechanism, because we don't need to handle * the /lib/firmware/qcom/... vs /lib/firmware/... case. * * If the firmware-name property is not found, for backwards * compatibility we fall back to the fwname from the gpulist * table.
*/
of_property_read_string_index(np, "firmware-name", 0, &signed_fwname); if (signed_fwname) {
fwname = signed_fwname;
ret = request_firmware_direct(&fw, fwname, gpu->dev->dev); if (ret)
fw = ERR_PTR(ret);
} elseif (fwname) { /* Request the MDT file from the default location: */
fw = adreno_request_fw(to_adreno_gpu(gpu), fwname);
} else { /* * For new targets, we require the firmware-name property, * if a zap-shader is required, rather than falling back * to a firmware name specified in gpulist. * * Because the firmware is signed with a (potentially) * device specific key, having the name come from gpulist * was a bad idea, and is only provided for backwards * compatibility for older targets.
*/ return -ENOENT;
}
if (IS_ERR(fw)) {
DRM_DEV_ERROR(dev, "Unable to load %s\n", fwname); return PTR_ERR(fw);
}
/* Figure out how much memory we need */
mem_size = qcom_mdt_get_size(fw); if (mem_size < 0) {
ret = mem_size; goto out;
}
if (mem_size > resource_size(&r)) {
DRM_DEV_ERROR(dev, "memory region is too small to load the MDT\n");
ret = -E2BIG; goto out;
}
/* Allocate memory for the firmware image */
mem_region = memremap(mem_phys, mem_size, MEMREMAP_WC); if (!mem_region) {
ret = -ENOMEM; goto out;
}
/* * Load the rest of the MDT * * Note that we could be dealing with two different paths, since * with upstream linux-firmware it would be in a qcom/ subdir.. * adreno_request_fw() handles this, but qcom_mdt_load() does * not. But since we've already gotten through adreno_request_fw() * we know which of the two cases it is:
*/ if (signed_fwname || (to_adreno_gpu(gpu)->fwloc == FW_LOCATION_LEGACY)) {
ret = qcom_mdt_load(dev, fw, fwname, pasid,
mem_region, mem_phys, mem_size, NULL);
} else { char *newname;
ret = qcom_mdt_load(dev, fw, newname, pasid,
mem_region, mem_phys, mem_size, NULL);
kfree(newname);
} if (ret) goto out;
/* Send the image to the secure world */
ret = qcom_scm_pas_auth_and_reset(pasid);
/* * If the scm call returns -EOPNOTSUPP we assume that this target * doesn't need/support the zap shader so quietly fail
*/ if (ret == -EOPNOTSUPP)
zap_available = false; elseif (ret)
DRM_DEV_ERROR(dev, "Unable to authorize the image\n");
/* Short cut if we determine the zap shader isn't available/needed */ if (!zap_available) return -ENODEV;
/* We need SCM to be able to load the firmware */ if (!qcom_scm_is_available()) {
DRM_DEV_ERROR(&pdev->dev, "SCM is not available\n"); return -EPROBE_DEFER;
}
geometry = msm_iommu_get_geometry(mmu); if (IS_ERR(geometry)) return ERR_CAST(geometry);
/* * Use the aperture start or SZ_16M, whichever is greater. This will * ensure that we align with the allocated pagetable range while still * allowing room in the lower 32 bits for GMEM and whatnot
*/
start = max_t(u64, SZ_16M, geometry->aperture_start);
size = geometry->aperture_end - start + 1;
/* * Userspace VM is actually using TTBR0, but both are the same size, * with b48 (sign bit) selecting which TTBRn to use. So if IAS is * 48, the total (kernel+user) address space size is effectively * 49 bits. But what userspace is control of is the lower 48.
*/ return BIT(ttbr1_cfg->ias) - ADRENO_VM_START;
}
/* * Wait until the cooldown period has passed and we would actually * collect a crashdump to re-enable stall-on-fault.
*/
spin_lock_irqsave(&priv->fault_stall_lock, flags); if (!priv->stall_enabled &&
ktime_after(ktime_get(), priv->stall_reenable_time) &&
!READ_ONCE(gpu->crashstate)) { struct msm_mmu *mmu = to_msm_vm(gpu->vm)->mmu;
/* * In case there is a subsequent storm of pagefaults, disable * stall-on-fault for at least half a second.
*/
spin_lock_irqsave(&priv->fault_stall_lock, irq_flags); if (priv->stall_enabled) {
priv->stall_enabled = false;
/* * 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"; elseif (info->fsr & ARM_SMMU_FSR_PF)
type = "PERMISSION"; elseif (info->fsr & ARM_SMMU_FSR_EF)
type = "EXTERNAL";
switch (param) { case MSM_PARAM_COMM: case MSM_PARAM_CMDLINE: /* kstrdup_quotable_cmdline() limits to PAGE_SIZE, so * that should be a reasonable upper bound
*/ if (len > PAGE_SIZE) return UERR(EINVAL, drm, "invalid len"); break; default: if (len != 0) return UERR(EINVAL, drm, "invalid len");
}
switch (param) { case MSM_PARAM_COMM: case MSM_PARAM_CMDLINE: { char *str, **paramp;
str = memdup_user_nul(u64_to_user_ptr(value), len); if (IS_ERR(str)) return PTR_ERR(str);
return 0;
} case MSM_PARAM_SYSPROF: if (!capable(CAP_SYS_ADMIN)) return UERR(EPERM, drm, "invalid permissions"); return msm_context_set_sysprof(ctx, gpu, value); case MSM_PARAM_EN_VM_BIND: /* We can only support VM_BIND with per-process pgtables: */ if (ctx->vm == gpu->vm) return UERR(EINVAL, drm, "requires per-process pgtables");
/* * We can only swtich to VM_BIND mode if the VM has not yet * been created:
*/ if (ctx->vm) return UERR(EBUSY, drm, "VM already created");
newname = kasprintf(GFP_KERNEL, "qcom/%s", fwname); if (!newname) return ERR_PTR(-ENOMEM);
/* * Try first to load from qcom/$fwfile using a direct load (to avoid * a potential timeout waiting for usermode helper)
*/ if ((adreno_gpu->fwloc == FW_LOCATION_UNKNOWN) ||
(adreno_gpu->fwloc == FW_LOCATION_NEW)) {
ret = request_firmware_direct(&fw, newname, drm->dev); if (!ret) {
DRM_DEV_INFO(drm->dev, "loaded %s from new location\n",
newname);
adreno_gpu->fwloc = FW_LOCATION_NEW; goto out;
} elseif (adreno_gpu->fwloc != FW_LOCATION_UNKNOWN) {
DRM_DEV_ERROR(drm->dev, "failed to load %s: %d\n",
newname, ret);
fw = ERR_PTR(ret); goto out;
}
}
/* * Then try the legacy location without qcom/ prefix
*/ if ((adreno_gpu->fwloc == FW_LOCATION_UNKNOWN) ||
(adreno_gpu->fwloc == FW_LOCATION_LEGACY)) {
/* * Finally fall back to request_firmware() for cases where the * usermode helper is needed (I think mainly android)
*/ if ((adreno_gpu->fwloc == FW_LOCATION_UNKNOWN) ||
(adreno_gpu->fwloc == FW_LOCATION_HELPER)) {
ret = request_firmware(&fw, newname, drm->dev); if (!ret) {
DRM_DEV_INFO(drm->dev, "loaded %s with helper\n",
newname);
adreno_gpu->fwloc = FW_LOCATION_HELPER; goto out;
} elseif (adreno_gpu->fwloc != FW_LOCATION_UNKNOWN) {
DRM_DEV_ERROR(drm->dev, "failed to load %s: %d\n",
newname, ret);
fw = ERR_PTR(ret); goto out;
}
}
int adreno_hw_init(struct msm_gpu *gpu)
{ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); int ret;
VERB("%s", gpu->name);
if (adreno_gpu->info->family >= ADRENO_6XX_GEN1 &&
qcom_scm_set_gpu_smmu_aperture_is_available()) { /* We currently always use context bank 0, so hard code this */
ret = qcom_scm_set_gpu_smmu_aperture(0); if (ret)
DRM_DEV_ERROR(gpu->dev->dev, "unable to set SMMU aperture: %d\n", ret);
}
for (int i = 0; i < gpu->nr_rings; i++) { struct msm_ringbuffer *ring = gpu->rb[i];
/* Detect and clean up an impossible fence, ie. if GPU managed * to scribble something invalid, we don't want that to confuse * us into mistakingly believing that submits have completed.
*/ if (fence_before(ring->fctx->last_fence, ring->memptrs->fence)) {
ring->memptrs->fence = ring->fctx->last_fence;
}
}
return 0;
}
/* Use this helper to read rptr, since a430 doesn't update rptr in memory */ static uint32_t get_rptr(struct adreno_gpu *adreno_gpu, struct msm_ringbuffer *ring)
{ struct msm_gpu *gpu = &adreno_gpu->base;
/* Copy the shadow to the actual register */
ring->cur = ring->next;
/* * Mask wptr value that we calculate to fit in the HW range. This is * to account for the possibility that the last command fit exactly into * the ringbuffer and rb->next hasn't wrapped to zero yet
*/
wptr = get_wptr(ring);
/* ensure writes to ringbuffer have hit system memory: */
mb();
/* wait for CP to drain ringbuffer: */ if (!spin_until(get_rptr(adreno_gpu, ring) == wptr)) returntrue;
/* TODO maybe we need to reset GPU here to recover from hang? */
DRM_ERROR("%s: timeout waiting to drain ringbuffer %d rptr/wptr = %X/%X\n",
gpu->name, ring->id, get_rptr(adreno_gpu, ring), wptr);
returnfalse;
}
int adreno_gpu_state_get(struct msm_gpu *gpu, struct msm_gpu_state *state)
{ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); int i, count = 0;
WARN_ON(!mutex_is_locked(&gpu->lock));
kref_init(&state->ref);
ktime_get_real_ts64(&state->time);
for (i = 0; i < gpu->nr_rings; i++) { int size = 0, j;
/* Copy at least 'wptr' dwords of the data */
size = state->ring[i].wptr;
/* After wptr find the last non zero dword to save space */ for (j = state->ring[i].wptr; j < MSM_GPU_RINGBUFFER_SZ >> 2; j++) if (gpu->rb[i]->start[j])
size = j + 1;
if (size) {
state->ring[i].data = kvmemdup(gpu->rb[i]->start, size << 2, GFP_KERNEL); if (state->ring[i].data)
state->ring[i].data_size = size << 2;
}
}
/* Some targets prefer to collect their own registers */ if (!adreno_gpu->registers) return 0;
/* Count the number of registers */ for (i = 0; adreno_gpu->registers[i] != ~0; i += 2)
count += adreno_gpu->registers[i + 1] -
adreno_gpu->registers[i] + 1;
state->registers = kcalloc(count * 2, sizeof(u32), GFP_KERNEL); if (state->registers) { int pos = 0;
for (i = 0; adreno_gpu->registers[i] != ~0; i += 2) {
u32 start = adreno_gpu->registers[i];
u32 end = adreno_gpu->registers[i + 1];
u32 addr;
staticchar *adreno_gpu_ascii85_encode(u32 *src, size_t len)
{ void *buf;
size_t buf_itr = 0, buffer_size; char out[ASCII85_BUFSZ]; long l; int i;
if (!src || !len) return NULL;
l = ascii85_encode_len(len);
/* * Ascii85 outputs either a 5 byte string or a 1 byte string. So we * account for the worst case of 5 bytes per dword plus the 1 for '\0'
*/
buffer_size = (l * 5) + 1;
buf = kvmalloc(buffer_size, GFP_KERNEL); if (!buf) return NULL;
for (i = 0; i < l; i++)
buf_itr += scnprintf(buf + buf_itr, buffer_size - buf_itr, "%s",
ascii85_encode(src[i], out));
return buf;
}
/* len is expected to be in bytes * * WARNING: *ptr should be allocated with kvmalloc or friends. It can be free'd * with kvfree() and replaced with a newly kvmalloc'd buffer on the first call * when the unencoded raw data is encoded
*/ void adreno_show_object(struct drm_printer *p, void **ptr, int len, bool *encoded)
{ if (!*ptr || !len) return;
if (!*encoded) { long datalen, i;
u32 *buf = *ptr;
/* * Only dump the non-zero part of the buffer - rarely will * any data completely fill the entire allocated size of * the buffer.
*/ for (datalen = 0, i = 0; i < len >> 2; i++) if (buf[i])
datalen = ((i + 1) << 2);
/* * If we reach here, then the originally captured binary buffer * will be replaced with the ascii85 encoded string
*/
*ptr = adreno_gpu_ascii85_encode(buf, datalen);
drm_printf(p, "revision: %u (%"ADRENO_CHIPID_FMT")\n",
adreno_gpu->info->revn,
ADRENO_CHIPID_ARGS(adreno_gpu->chip_id)); /* * If this is state collected due to iova fault, so fault related info * * TTBR0 would not be zero, so this is a good way to distinguish
*/ if (state->fault_info.ttbr0) { conststruct msm_gpu_fault_info *info = &state->fault_info;
/* Information extracted from what we think are the current * pgtables. Hopefully the TTBR0 matches what we've extracted * from the SMMU registers in smmu_info!
*/
drm_puts(p, "pgtable-fault-info:\n");
drm_printf(p, " - ttbr0: %.16llx\n", (u64)info->pgtbl_ttbr0);
drm_printf(p, " - asid: %d\n", info->asid);
drm_printf(p, " - ptes: %.16llx %.16llx %.16llx %.16llx\n",
info->ptes[0], info->ptes[1], info->ptes[2], info->ptes[3]);
}
if (state->vm_logs) {
drm_puts(p, "vm-log:\n"); for (i = 0; i < state->nr_vm_logs; i++) { struct msm_gem_vm_log_entry *e = &state->vm_logs[i];
drm_printf(p, " - %s:%d: 0x%016llx-0x%016llx\n",
e->op, e->queue_id, e->iova,
e->iova + e->range);
}
}
/* Dump common gpu status and scratch registers on any hang, to make * the hangcheck logs more useful. The scratch registers seem always * safe to read when GPU has hung (unlike some other regs, depending * on how the GPU hung), and they are useful to match up to cmdstream * dumps when debugging hangs:
*/ void adreno_dump_info(struct msm_gpu *gpu)
{ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); int i;
/* would be nice to not have to duplicate the _show() stuff with printk(): */ void adreno_dump(struct msm_gpu *gpu)
{ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); int i;
if (!adreno_gpu->registers) return;
/* dump these out in a form that can be parsed by demsm: */
printk("IO:region %s 00000000 00020000\n", gpu->name); for (i = 0; adreno_gpu->registers[i] != ~0; i += 2) {
uint32_t start = adreno_gpu->registers[i];
uint32_t end = adreno_gpu->registers[i+1];
uint32_t addr;
/* 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);
} elseif (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;
}
} elseif (ret) {
DRM_DEV_ERROR(dev, "Unable to set the OPP table\n"); return ret;
}
/* Find the fastest defined rate */
opp = dev_pm_opp_find_freq_floor(dev, &freq); if (IS_ERR(opp)) return PTR_ERR(opp);
ocmem = of_get_ocmem(dev); if (IS_ERR(ocmem)) { if (PTR_ERR(ocmem) == -ENODEV) { /* * Return success since either the ocmem property was * not specified in device tree, or ocmem support is * not compiled into the kernel.
*/ return 0;
}
return PTR_ERR(ocmem);
}
ocmem_hdl = ocmem_allocate(ocmem, OCMEM_GRAPHICS, adreno_gpu->info->gmem); if (IS_ERR(ocmem_hdl)) return PTR_ERR(ocmem_hdl);
/* Only handle the core clock when GMU is not in use (or is absent). */ if (adreno_has_gmu_wrapper(adreno_gpu) ||
adreno_gpu->info->family < ADRENO_6XX_GEN1) { /* * 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");
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.