for (perf_index = 0; perf_index < gmu->nr_gpu_freqs - 1; perf_index++) if (gpu_freq == gmu->gpu_freqs[perf_index]) break;
/* If enabled, find the corresponding DDR bandwidth index */ if (info->bcms && gmu->nr_gpu_bws > 1) { unsignedint bw = dev_pm_opp_get_bw(opp, true, 0);
for (bw_index = 0; bw_index < gmu->nr_gpu_bws - 1; bw_index++) { if (bw == gmu->gpu_bw_table[bw_index]) break;
}
/* Vote AB as a fraction of the max bandwidth, starting from A750 */ if (bw && adreno_is_a750_family(adreno_gpu)) {
u64 tmp;
/* For now, vote for 25% of the bandwidth */
tmp = bw * 25;
do_div(tmp, 100);
/* * The AB vote consists of a 16 bit wide quantized level * against the maximum supported bandwidth. * Quantization can be calculated as below: * vote = (bandwidth * 2^16) / max bandwidth
*/
tmp *= MAX_AB_VOTE;
do_div(tmp, gmu->gpu_bw_table[gmu->nr_gpu_bws - 1]);
/* * This can get called from devfreq while the hardware is idle. Don't * bring up the power if it isn't already active. All we're doing here * is updating the frequency so that when we come back online we're at * the right rate.
*/ if (suspended) return;
if (!gmu->legacy) {
a6xx_hfi_set_freq(gmu, perf_index, bw_index); /* With Bandwidth voting, we now vote for all resources, so skip OPP set */ if (!bw_index)
dev_pm_opp_set_opp(&gpu->pdev->dev, opp); return;
}
/* * Send an invalid index as a vote for the bus bandwidth and let the * firmware decide on the right vote
*/
gmu_write(gmu, REG_A6XX_GMU_DCVS_BW_SETTING, 0xff);
/* Set and clear the OOB for DCVS to trigger the GMU */
a6xx_gmu_set_oob(gmu, GMU_OOB_DCVS_SET);
a6xx_gmu_clear_oob(gmu, GMU_OOB_DCVS_SET);
ret = gmu_read(gmu, REG_A6XX_GMU_DCVS_RETURN); if (ret)
dev_err(gmu->dev, "GMU set GPU frequency error: %d\n", ret);
staticbool a6xx_gmu_check_idle_level(struct a6xx_gmu *gmu)
{
u32 val; int local = gmu->idle_level;
/* SPTP and IFPC both report as IFPC */ if (gmu->idle_level == GMU_IDLE_STATE_SPTP)
local = GMU_IDLE_STATE_IFPC;
val = gmu_read(gmu, REG_A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE);
if (val == local) { if (gmu->idle_level != GMU_IDLE_STATE_IFPC ||
!a6xx_gmu_gx_is_on(gmu)) returntrue;
}
returnfalse;
}
/* Wait for the GMU to get to its most idle state */ int a6xx_gmu_wait_for_idle(struct a6xx_gmu *gmu)
{ return spin_until(a6xx_gmu_check_idle_level(gmu));
}
/* Set the log wptr index * note: downstream saves the value in poweroff and restores it here
*/ if (adreno_is_a7xx(adreno_gpu))
gmu_write(gmu, REG_A7XX_GMU_GENERAL_9, 0); else
gmu_write(gmu, REG_A6XX_GPU_GMU_CX_GMU_PWR_COL_CP_RESP, 0);
if (ret)
DRM_DEV_ERROR(gmu->dev, "GMU firmware initialization timed out\n");
set_bit(GMU_STATUS_FW_START, &gmu->status);
return ret;
}
staticint a6xx_gmu_hfi_start(struct a6xx_gmu *gmu)
{
u32 val; int ret;
gmu_write(gmu, REG_A6XX_GMU_HFI_CTRL_INIT, 1);
ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_HFI_CTRL_STATUS, val,
val & 1, 100, 10000); if (ret)
DRM_DEV_ERROR(gmu->dev, "Unable to start the HFI queues\n");
/* Trigger a OOB (out of band) request to the GMU */ int a6xx_gmu_set_oob(struct a6xx_gmu *gmu, enum a6xx_gmu_oob_state state)
{ int ret;
u32 val; int request, ack;
WARN_ON_ONCE(!mutex_is_locked(&gmu->lock));
if (state >= ARRAY_SIZE(a6xx_gmu_oob_bits)) return -EINVAL;
/* Wait for the acknowledge interrupt */
ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_GMU2HOST_INTR_INFO, val,
val & (1 << ack), 100, 10000);
if (ret)
DRM_DEV_ERROR(gmu->dev, "Timeout waiting for GMU OOB set %s: 0x%x\n",
a6xx_gmu_oob_bits[state].name,
gmu_read(gmu, REG_A6XX_GMU_GMU2HOST_INTR_INFO));
/* * GEMNoC can power collapse whilst the GPU is being powered down, resulting * in the power down sequence not being fully executed. That in turn can * prevent CX_GDSC from collapsing. Assert Qactive to avoid this.
*/ if (adreno_is_a621(adreno_gpu) || adreno_is_7c3(adreno_gpu))
gmu_write(gmu, REG_A6XX_GMU_AO_AHB_FENCE_CTRL, BIT(0));
}
/* Let the GMU know that we are about to go into slumber */ staticint a6xx_gmu_notify_slumber(struct a6xx_gmu *gmu)
{ int ret;
/* Disable the power counter so the GMU isn't busy */
gmu_write(gmu, REG_A6XX_GMU_CX_GMU_POWER_COUNTER_ENABLE, 0);
/* Disable SPTP_PC if the CPU is responsible for it */ if (gmu->idle_level < GMU_IDLE_STATE_SPTP)
a6xx_sptprac_disable(gmu);
if (!gmu->legacy) {
ret = a6xx_hfi_send_prep_slumber(gmu); goto out;
}
/* Tell the GMU to get ready to slumber */
gmu_write(gmu, REG_A6XX_GMU_BOOT_SLUMBER_OPTION, 1);
ret = a6xx_gmu_set_oob(gmu, GMU_OOB_BOOT_SLUMBER);
a6xx_gmu_clear_oob(gmu, GMU_OOB_BOOT_SLUMBER);
if (!ret) { /* Check to see if the GMU really did slumber */ if (gmu_read(gmu, REG_A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE)
!= 0x0f) {
DRM_DEV_ERROR(gmu->dev, "The GMU did not go into slumber\n");
ret = -ETIMEDOUT;
}
}
out:
a6xx_gemnoc_workaround(gmu);
/* Put fence into allow mode */
gmu_write(gmu, REG_A6XX_GMU_AO_AHB_FENCE_CTRL, 0); return ret;
}
staticint a6xx_rpmh_start(struct a6xx_gmu *gmu)
{ int ret;
u32 val;
if (!test_and_clear_bit(GMU_STATUS_PDC_SLEEP, &gmu->status)) return 0;
ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_RSCC_CONTROL_ACK, val,
val & (1 << 1), 100, 10000); if (ret) {
DRM_DEV_ERROR(gmu->dev, "Unable to power on the GPU RSC\n"); return ret;
}
ret = gmu_poll_timeout_rscc(gmu, REG_A6XX_RSCC_SEQ_BUSY_DRV0, val,
!val, 100, 10000);
if (ret) {
DRM_DEV_ERROR(gmu->dev, "GPU RSC sequence stuck while waking up the GPU\n"); return ret;
}
gmu_write(gmu, REG_A6XX_GMU_RSCC_CONTROL_REQ, 0);
return 0;
}
staticvoid a6xx_rpmh_stop(struct a6xx_gmu *gmu)
{ int ret;
u32 val;
if (test_and_clear_bit(GMU_STATUS_FW_START, &gmu->status)) return;
gmu_write(gmu, REG_A6XX_GMU_RSCC_CONTROL_REQ, 1);
ret = gmu_poll_timeout_rscc(gmu, REG_A6XX_GPU_RSCC_RSC_STATUS0_DRV0,
val, val & (1 << 16), 100, 10000); if (ret)
DRM_DEV_ERROR(gmu->dev, "Unable to power off the GPU RSC\n");
/* The second spin of A7xx GPUs messed with some register offsets.. */ if (adreno_is_a740_family(adreno_gpu))
seqmem0_drv0_reg = REG_A7XX_RSCC_SEQ_MEM_0_DRV0_A740;
/* ensure no writes happen before the uCode is fully written */
wmb();
err: if (!IS_ERR_OR_NULL(pdcptr))
iounmap(pdcptr); if (!IS_ERR_OR_NULL(seqptr))
iounmap(seqptr);
}
/* * The lowest 16 bits of this value are the number of XO clock cycles for main * hysteresis which is set at 0x1680 cycles (300 us). The higher 16 bits are * for the shorter hysteresis that happens after main - this is 0xa (.5 us)
*/
#define GMU_PWR_COL_HYST 0x000a1680
/* Set up the idle state for the GMU */ staticvoid a6xx_gmu_power_config(struct a6xx_gmu *gmu)
{ struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
if (adreno_is_a650_family(adreno_gpu) || adreno_is_a7xx(adreno_gpu))
dtcm_base = 0x10004000;
if (gmu->legacy) { /* Sanity check the size of the firmware that was loaded */ if (fw_image->size > 0x8000) {
DRM_DEV_ERROR(gmu->dev, "GMU firmware is bigger than the available region\n"); return -EINVAL;
}
/* Vote veto for FAL10 */ if (adreno_is_a650_family(adreno_gpu) || adreno_is_a7xx(adreno_gpu)) {
gmu_write(gmu, REG_A6XX_GPU_GMU_CX_GMU_CX_FALNEXT_INTF, 1);
gmu_write(gmu, REG_A6XX_GPU_GMU_CX_GMU_CX_FAL_INTF, 1);
}
/* Turn on TCM (Tightly Coupled Memory) retention */ if (adreno_is_a7xx(adreno_gpu))
a6xx_llc_write(a6xx_gpu, REG_A7XX_CX_MISC_TCM_RET_CNTL, 1); else
gmu_write(gmu, REG_A6XX_GMU_GENERAL_7, 1);
ret = a6xx_rpmh_start(gmu); if (ret) return ret;
if (state == GMU_COLD_BOOT) { if (WARN(!adreno_gpu->fw[ADRENO_FW_GMU], "GMU firmware is not loaded\n")) return -ENOENT;
ret = a6xx_gmu_fw_load(gmu); if (ret) return ret;
}
/* Clear init result to make sure we are getting a fresh value */
gmu_write(gmu, REG_A6XX_GMU_CM3_FW_INIT_RESULT, 0);
gmu_write(gmu, REG_A6XX_GMU_CM3_BOOT_CONFIG, 0x02);
/* Write the iova of the HFI table */
gmu_write(gmu, REG_A6XX_GMU_HFI_QTBL_ADDR, gmu->hfi.iova);
gmu_write(gmu, REG_A6XX_GMU_HFI_QTBL_INFO, 1);
/* * Snapshots toggle the NMI bit which will result in a jump to the NMI * handler instead of __main. Set the M3 config value to avoid that.
*/
gmu_write(gmu, REG_A6XX_GMU_CM3_CFG, 0x4052);
if (a6xx_info->gmu_chipid) {
chipid = a6xx_info->gmu_chipid;
} else { /* * Note that the GMU has a slightly different layout for * chip_id, for whatever reason, so a bit of massaging * is needed. The upper 16b are the same, but minor and * patchid are packed in four bits each with the lower * 8b unused:
*/
chipid = adreno_gpu->chip_id & 0xffff0000;
chipid |= (adreno_gpu->chip_id << 4) & 0xf000; /* minor */
chipid |= (adreno_gpu->chip_id << 8) & 0x0f00; /* patchid */
}
/* Set up the lowest idle level on the GMU */
a6xx_gmu_power_config(gmu);
ret = a6xx_gmu_start(gmu); if (ret) return ret;
if (gmu->legacy) {
ret = a6xx_gmu_gfx_rail_on(gmu); if (ret) return ret;
}
/* Enable SPTP_PC if the CPU is responsible for it */ if (gmu->idle_level < GMU_IDLE_STATE_SPTP) {
ret = a6xx_sptprac_enable(gmu); if (ret) return ret;
}
ret = a6xx_gmu_hfi_start(gmu); if (ret) return ret;
/* Force the GMU off in case it isn't responsive */ staticvoid a6xx_gmu_force_off(struct a6xx_gmu *gmu)
{ struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; struct msm_gpu *gpu = &adreno_gpu->base;
/* * Turn off keep alive that might have been enabled by the hang * interrupt
*/
gmu_write(&a6xx_gpu->gmu, REG_A6XX_GMU_GMU_PWR_COL_KEEPALIVE, 0);
/* Flush all the queues */
a6xx_hfi_stop(gmu);
/* Stop the interrupts */
a6xx_gmu_irq_disable(gmu);
/* Force off SPTP in case the GMU is managing it */
a6xx_sptprac_disable(gmu);
a6xx_gemnoc_workaround(gmu);
/* Make sure there are no outstanding RPMh votes */
a6xx_gmu_rpmh_off(gmu);
/* Clear the WRITEDROPPED fields and put fence into allow mode */
gmu_write(gmu, REG_A6XX_GMU_AHB_FENCE_STATUS_CLR, 0x7);
gmu_write(gmu, REG_A6XX_GMU_AO_AHB_FENCE_CTRL, 0);
/* Make sure the above writes go through */
wmb();
/* Halt the gmu cm3 core */
gmu_write(gmu, REG_A6XX_GMU_CM3_SYSRESET, 1);
if (WARN(!gmu->initialized, "The GMU is not set up yet\n")) return -EINVAL;
gmu->hung = false;
/* Turn on the resources */
pm_runtime_get_sync(gmu->dev);
/* * "enable" the GX power domain which won't actually do anything but it * will make sure that the refcounting is correct in case we need to * bring down the GX after a GMU failure
*/ if (!IS_ERR_OR_NULL(gmu->gxpd))
pm_runtime_get_sync(gmu->gxpd);
/* Use a known rate to bring up the GMU */
clk_set_rate(gmu->core_clk, 200000000);
clk_set_rate(gmu->hub_clk, adreno_is_a740_family(adreno_gpu) ?
200000000 : 150000000);
ret = clk_bulk_prepare_enable(gmu->nr_clocks, gmu->clocks); if (ret) {
pm_runtime_put(gmu->gxpd);
pm_runtime_put(gmu->dev); return ret;
}
/* Set the bus quota to a reasonable value for boot */
a6xx_gmu_set_initial_bw(gpu, gmu);
/* Check to see if we are doing a cold or warm boot */ if (adreno_is_a7xx(adreno_gpu)) {
status = a6xx_llc_read(a6xx_gpu, REG_A7XX_CX_MISC_TCM_RET_CNTL) == 1 ?
GMU_WARM_BOOT : GMU_COLD_BOOT;
} elseif (gmu->legacy) {
status = gmu_read(gmu, REG_A6XX_GMU_GENERAL_7) == 1 ?
GMU_WARM_BOOT : GMU_COLD_BOOT;
} else { /* * Warm boot path does not work on newer A6xx GPUs * Presumably this is because icache/dcache regions must be restored
*/
status = GMU_COLD_BOOT;
}
ret = a6xx_gmu_fw_start(gmu, status); if (ret) goto out;
ret = a6xx_hfi_start(gmu, status); if (ret) goto out;
/* * Turn on the GMU firmware fault interrupt after we know the boot * sequence is successful
*/
gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_CLR, ~0);
gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_MASK, ~A6XX_HFI_IRQ_MASK);
enable_irq(gmu->hfi_irq);
/* Set the GPU to the current freq */
a6xx_gmu_set_initial_freq(gpu, gmu);
out: /* On failure, shut down the GMU to leave it in a good state */ if (ret) {
disable_irq(gmu->gmu_irq);
a6xx_rpmh_stop(gmu);
pm_runtime_put(gmu->gxpd);
pm_runtime_put(gmu->dev);
}
if (reg & A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS_GPUBUSYIGNAHB) returnfalse;
returntrue;
}
/* Gracefully try to shut down the GMU and by extension the GPU */ staticvoid a6xx_gmu_shutdown(struct a6xx_gmu *gmu)
{ struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
u32 val; int ret;
/* * GMU firmware's internal power state gets messed up if we send "prepare_slumber" hfi when * oob_gpu handshake wasn't done after the last wake up. So do a dummy handshake here when * required
*/ if (adreno_gpu->base.needs_hw_init) { if (a6xx_gmu_set_oob(&a6xx_gpu->gmu, GMU_OOB_GPU_SET)) goto force_off;
/* tell the GMU we want to slumber */
ret = a6xx_gmu_notify_slumber(gmu); if (ret) goto force_off;
ret = gmu_poll_timeout(gmu,
REG_A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS, val,
!(val & A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS_GPUBUSYIGNAHB),
100, 10000);
/* * Let the user know we failed to slumber but don't worry too * much because we are powering down anyway
*/
if (ret)
DRM_DEV_ERROR(gmu->dev, "Unable to slumber GMU: status = 0%x/0%x\n",
gmu_read(gmu,
REG_A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS),
gmu_read(gmu,
REG_A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS2));
/* Turn off HFI */
a6xx_hfi_stop(gmu);
/* Stop the interrupts and mask the hardware */
a6xx_gmu_irq_disable(gmu);
/* Tell RPMh to power off the GPU */
a6xx_rpmh_stop(gmu);
/* * Force the GMU off if we detected a hang, otherwise try to shut it * down gracefully
*/ if (gmu->hung)
a6xx_gmu_force_off(gmu); else
a6xx_gmu_shutdown(gmu);
/* Remove the bus vote */
dev_pm_opp_set_opp(&gpu->pdev->dev, NULL);
/* * Make sure the GX domain is off before turning off the GMU (CX) * domain. Usually the GMU does this but only if the shutdown sequence * was successful
*/ if (!IS_ERR_OR_NULL(gmu->gxpd))
pm_runtime_put_sync(gmu->gxpd);
/** * struct bcm_db - Auxiliary data pertaining to each Bus Clock Manager (BCM) * @unit: divisor used to convert bytes/sec bw value to an RPMh msg * @width: multiplier used to convert bytes/sec bw value to an RPMh msg * @vcd: virtual clock domain that this bcm belongs to * @reserved: reserved field
*/ struct bcm_db {
__le32 unit;
__le16 width;
u8 vcd;
u8 reserved;
};
/* Multiply the bandwidth by the width of the connection */
peak = (u64)bw * le16_to_cpu(bcm_data[bcm_index]->width);
do_div(peak, bcm->buswidth);
/* Input bandwidth value is in KBps, scale the value to BCM unit */
peak *= 1000;
do_div(peak, le32_to_cpu(bcm_data[bcm_index]->unit));
vote = clamp(peak, 1, BCM_TCS_CMD_VOTE_MASK);
/* GMUs on A7xx votes on both x & y */ if (adreno_is_a7xx(adreno_gpu))
data[bcm_index] = BCM_TCS_CMD(commit, true, vote, vote); else
data[bcm_index] = BCM_TCS_CMD(commit, true, 0, vote);
}
}
return 0;
}
/* Return the 'arc-level' for the given frequency */ staticunsignedint a6xx_gmu_get_arc_level(struct device *dev, unsignedlong freq)
{ struct dev_pm_opp *opp; unsignedint val;
if (!freq) return 0;
opp = dev_pm_opp_find_freq_exact(dev, freq, true); if (IS_ERR(opp)) return 0;
val = dev_pm_opp_get_level(opp);
dev_pm_opp_put(opp);
return val;
}
staticint a6xx_gmu_rpmh_arc_votes_init(struct device *dev, u32 *votes, unsignedlong *freqs, int freqs_count, constchar *id)
{ int i, j; const u16 *pri, *sec;
size_t pri_count, sec_count;
pri = cmd_db_read_aux_data(id, &pri_count); if (IS_ERR(pri)) return PTR_ERR(pri); /* * The data comes back as an array of unsigned shorts so adjust the * count accordingly
*/
pri_count >>= 1; if (!pri_count) return -EINVAL;
/* * Some targets have a separate gfx mxc rail. So try to read that first and then fall back * to regular mx rail if it is missing
*/
sec = cmd_db_read_aux_data("gmxc.lvl", &sec_count); if (IS_ERR(sec) && sec != ERR_PTR(-EPROBE_DEFER))
sec = cmd_db_read_aux_data("mx.lvl", &sec_count); if (IS_ERR(sec)) return PTR_ERR(sec);
sec_count >>= 1; if (!sec_count) return -EINVAL;
/* Construct a vote for each frequency */ for (i = 0; i < freqs_count; i++) {
u8 pindex = 0, sindex = 0; unsignedint level = a6xx_gmu_get_arc_level(dev, freqs[i]);
/* Get the primary index that matches the arc level */ for (j = 0; j < pri_count; j++) { if (pri[j] >= level) {
pindex = j; break;
}
}
if (j == pri_count) {
DRM_DEV_ERROR(dev, "Level %u not found in the RPMh list\n",
level);
DRM_DEV_ERROR(dev, "Available levels:\n"); for (j = 0; j < pri_count; j++)
DRM_DEV_ERROR(dev, " %u\n", pri[j]);
return -EINVAL;
}
/* * Look for a level in in the secondary list that matches. If * nothing fits, use the maximum non zero vote
*/
/* * The GMU votes with the RPMh for itself and on behalf of the GPU but we need * to construct the list of votes on the CPU and send it over. Query the RPMh * voltage levels and build the votes * The GMU can also vote for DDR interconnects, use the OPP bandwidth entries * and BCM parameters to build the votes.
*/
/* * The GMU handles its own frequency switching so build a list of * available frequencies to send during initialization
*/
ret = devm_pm_opp_of_add_table(gmu->dev); if (ret) {
DRM_DEV_ERROR(gmu->dev, "Unable to set the OPP table for the GMU\n"); return ret;
}
/* * The GMU also handles GPU frequency switching so build a list * from the GPU OPP table
*/
gmu->nr_gpu_freqs = a6xx_gmu_build_freq_table(&gpu->pdev->dev,
gmu->gpu_freqs, ARRAY_SIZE(gmu->gpu_freqs));
gmu->current_perf_index = gmu->nr_gpu_freqs - 1;
/* * The GMU also handles GPU Interconnect Votes so build a list * of DDR bandwidths from the GPU OPP table
*/ if (info->bcms)
gmu->nr_gpu_bws = a6xx_gmu_build_bw_table(&gpu->pdev->dev,
gmu->gpu_bw_table, ARRAY_SIZE(gmu->gpu_bw_table));
/* Build the list of RPMh votes that we'll send to the GMU */ return a6xx_gmu_rpmh_votes_init(gmu);
}
/* Skip freq = 0 and parse acd-level for rest of the OPPs */ for (i = 1; i < gmu->nr_gpu_freqs; i++) { struct dev_pm_opp *opp; struct device_node *np; unsignedlong freq;
u32 val;
/* It is a problem if qmp node is unavailable when ACD is required */ if (cmd->enable_by_level && IS_ERR_OR_NULL(gmu->qmp)) {
DRM_DEV_ERROR(gmu->dev, "Unable to send ACD state to AOSS\n"); return -EINVAL;
}
/* Otherwise, nothing to do if qmp is unavailable */ if (IS_ERR_OR_NULL(gmu->qmp)) return 0;
/* * Notify AOSS about the ACD state. AOSS is supposed to assume that ACD is disabled on * system reset. So it is harmless if we couldn't notify 'OFF' state
*/
ret = qmp_send(gmu->qmp, "{class: gpu, res: acd, val: %d}", !!cmd->enable_by_level); if (ret && cmd->enable_by_level) {
DRM_DEV_ERROR(gmu->dev, "Failed to send ACD state to AOSS\n"); return ret;
}
return 0;
}
staticint a6xx_gmu_clocks_probe(struct a6xx_gmu *gmu)
{ int ret = devm_clk_bulk_get_all(gmu->dev, &gmu->clocks);
if (!res) {
DRM_DEV_ERROR(&pdev->dev, "Unable to find the %s registers\n", name); return ERR_PTR(-EINVAL);
}
ret = ioremap(res->start, resource_size(res)); if (!ret) {
DRM_DEV_ERROR(&pdev->dev, "Unable to map the %s registers\n", name); return ERR_PTR(-EINVAL);
}
mutex_lock(&gmu->lock); if (!gmu->initialized) {
mutex_unlock(&gmu->lock); return;
}
gmu->initialized = false;
mutex_unlock(&gmu->lock);
pm_runtime_force_suspend(gmu->dev);
/* * Since cxpd is a virt device, the devlink with gmu-dev will be removed * automatically when we do detach
*/
dev_pm_domain_detach(gmu->cxpd, false);
if (!IS_ERR_OR_NULL(gmu->gxpd)) {
pm_runtime_disable(gmu->gxpd);
dev_pm_domain_detach(gmu->gxpd, false);
}
/* Get a link to the GX power domain to reset the GPU */
gmu->gxpd = dev_pm_domain_attach_by_name(gmu->dev, "gx"); if (IS_ERR(gmu->gxpd)) {
ret = PTR_ERR(gmu->gxpd); goto err_mmio;
}
ret = of_dma_configure(gmu->dev, node, true); if (ret) return ret;
/* Fow now, don't do anything fancy until we get our feet under us */
gmu->idle_level = GMU_IDLE_STATE_ACTIVE;
pm_runtime_enable(gmu->dev);
/* Get the list of clocks */
ret = a6xx_gmu_clocks_probe(gmu); if (ret) goto err_put_device;
ret = a6xx_gmu_memory_probe(adreno_gpu->base.dev, gmu); if (ret) goto err_put_device;
/* A660 now requires handling "prealloc requests" in GMU firmware * For now just hardcode allocations based on the known firmware. * note: there is no indication that these correspond to "dummy" or * "debug" regions, but this "guess" allows reusing these BOs which * are otherwise unused by a660.
*/
gmu->dummy.size = SZ_4K; if (adreno_is_a660_family(adreno_gpu) ||
adreno_is_a7xx(adreno_gpu)) {
ret = a6xx_gmu_memory_alloc(gmu, &gmu->debug, SZ_4K * 7,
0x60400000, "debug"); if (ret) goto err_memory;
gmu->dummy.size = SZ_8K;
}
/* Allocate memory for the GMU dummy page */
ret = a6xx_gmu_memory_alloc(gmu, &gmu->dummy, gmu->dummy.size,
0x60000000, "dummy"); if (ret) goto err_memory;
/* Note that a650 family also includes a660 family: */ if (adreno_is_a650_family(adreno_gpu) ||
adreno_is_a7xx(adreno_gpu)) {
ret = a6xx_gmu_memory_alloc(gmu, &gmu->icache,
SZ_16M - SZ_16K, 0x04000, "icache"); if (ret) goto err_memory; /* * NOTE: when porting legacy ("pre-650-family") GPUs you may be tempted to add a condition * to allocate icache/dcache here, as per downstream code flow, but it may not actually be * necessary. If you omit this step and you don't get random pagefaults, you are likely * good to go without this!
*/
} elseif (adreno_is_a640_family(adreno_gpu)) {
ret = a6xx_gmu_memory_alloc(gmu, &gmu->icache,
SZ_256K - SZ_16K, 0x04000, "icache"); if (ret) goto err_memory;
ret = a6xx_gmu_memory_alloc(gmu, &gmu->dcache,
SZ_256K - SZ_16K, 0x44000, "dcache"); if (ret) goto err_memory;
} elseif (adreno_is_a630_family(adreno_gpu)) { /* HFI v1, has sptprac */
gmu->legacy = true;
/* Allocate memory for the GMU debug region */
ret = a6xx_gmu_memory_alloc(gmu, &gmu->debug, SZ_16K, 0, "debug"); if (ret) goto err_memory;
}
/* Allocate memory for the GMU log region */
ret = a6xx_gmu_memory_alloc(gmu, &gmu->log, SZ_16K, 0, "log"); if (ret) goto err_memory;
/* Allocate memory for for the HFI queues */
ret = a6xx_gmu_memory_alloc(gmu, &gmu->hfi, SZ_16K, 0, "hfi"); if (ret) goto err_memory;
/* Map the GMU registers */
gmu->mmio = a6xx_gmu_get_mmio(pdev, "gmu"); if (IS_ERR(gmu->mmio)) {
ret = PTR_ERR(gmu->mmio); goto err_memory;
}
if (adreno_is_a650_family(adreno_gpu) ||
adreno_is_a7xx(adreno_gpu)) {
gmu->rscc = a6xx_gmu_get_mmio(pdev, "rscc"); if (IS_ERR(gmu->rscc)) {
ret = -ENODEV; goto err_mmio;
}
} else {
gmu->rscc = gmu->mmio + 0x23000;
}
/* Get the HFI and GMU interrupts */
gmu->hfi_irq = a6xx_gmu_get_irq(gmu, pdev, "hfi", a6xx_hfi_irq);
gmu->gmu_irq = a6xx_gmu_get_irq(gmu, pdev, "gmu", a6xx_gmu_irq);
if (gmu->hfi_irq < 0 || gmu->gmu_irq < 0) {
ret = -ENODEV; goto err_mmio;
}
gmu->cxpd = dev_pm_domain_attach_by_name(gmu->dev, "cx"); if (IS_ERR(gmu->cxpd)) {
ret = PTR_ERR(gmu->cxpd); goto err_mmio;
}
link = device_link_add(gmu->dev, gmu->cxpd, DL_FLAG_PM_RUNTIME); if (!link) {
ret = -ENODEV; goto detach_cxpd;
}
/* Other errors are handled during GPU ACD probe */
gmu->qmp = qmp_get(gmu->dev); if (PTR_ERR_OR_ZERO(gmu->qmp) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER; goto detach_gxpd;
}
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.