// SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2016 The Linux Foundation. All rights reserved.
*/
#include <linux/pm_opp.h> #include"a5xx_gpu.h"
/* * The GPMU data block is a block of shared registers that can be used to * communicate back and forth. These "registers" are by convention with the GPMU * firwmare and not bound to any specific hardware design
*/
/* * Get the actual voltage value for the operating point at the specified * frequency
*/ staticinline uint32_t _get_mvolts(struct msm_gpu *gpu, uint32_t freq)
{ struct drm_device *dev = gpu->dev; struct msm_drm_private *priv = dev->dev_private; struct platform_device *pdev = priv->gpu_pdev; struct dev_pm_opp *opp;
u32 ret = 0;
opp = dev_pm_opp_find_freq_exact(&pdev->dev, freq, true);
if (!IS_ERR(opp)) {
ret = dev_pm_opp_get_voltage(opp) / 1000;
dev_pm_opp_put(opp);
}
/* Write the block of sequence registers */ for (i = 0; i < ARRAY_SIZE(a5xx_sequence_regs); i++)
gpu_write(gpu, a5xx_sequence_regs[i].reg,
a5xx_sequence_regs[i].value);
/* Hard code the A530 GPU thermal sensor ID for the GPMU */
gpu_write(gpu, REG_A5XX_GPMU_TEMP_SENSOR_ID, 0x60007);
gpu_write(gpu, REG_A5XX_GPMU_DELTA_TEMP_THRESHOLD, 0x01);
gpu_write(gpu, REG_A5XX_GPMU_TEMP_SENSOR_CONFIG, 0x01);
/* Until we get clock scaling 0 is always the active power level */
gpu_write(gpu, REG_A5XX_GPMU_GPMU_VOLTAGE, 0x80000000 | 0);
/* Write the max power - hard coded to 5448 for A530 */
gpu_write(gpu, AGC_MSG_PAYLOAD(0), 5448);
gpu_write(gpu, AGC_MSG_PAYLOAD(1), 1);
/* * For now just write the one voltage level - we will do more when we * can do scaling
*/
gpu_write(gpu, AGC_MSG_PAYLOAD(2), _get_mvolts(gpu, gpu->fast_rate));
gpu_write(gpu, AGC_MSG_PAYLOAD(3), gpu->fast_rate / 1000000);
/* Turn off protected mode for this operation */
OUT_PKT7(ring, CP_SET_PROTECTED_MODE, 1);
OUT_RING(ring, 0);
/* Kick off the IB to load the GPMU microcode */
OUT_PKT7(ring, CP_INDIRECT_BUFFER_PFE, 3);
OUT_RING(ring, lower_32_bits(a5xx_gpu->gpmu_iova));
OUT_RING(ring, upper_32_bits(a5xx_gpu->gpmu_iova));
OUT_RING(ring, a5xx_gpu->gpmu_dwords);
/* Turn back on protected mode */
OUT_PKT7(ring, CP_SET_PROTECTED_MODE, 1);
OUT_RING(ring, 1);
a5xx_flush(gpu, ring, true);
if (!a5xx_idle(gpu, ring)) {
DRM_ERROR("%s: Unable to load GPMU firmware. GPMU will not be active\n",
gpu->name); return -EINVAL;
}
if (adreno_is_a530(adreno_gpu))
gpu_write(gpu, REG_A5XX_GPMU_WFI_CONFIG, 0x4014);
/* Kick off the GPMU */
gpu_write(gpu, REG_A5XX_GPMU_CM3_SYSRESET, 0x0);
/* * Wait for the GPMU to respond. It isn't fatal if it doesn't, we just * won't have advanced power collapse.
*/ if (spin_usecs(gpu, 25, REG_A5XX_GPMU_GENERAL_0, 0xFFFFFFFF,
0xBABEFACE))
DRM_ERROR("%s: GPMU firmware initialization timed out\n",
gpu->name);
if (!adreno_is_a530(adreno_gpu)) {
u32 val = gpu_read(gpu, REG_A5XX_GPMU_GENERAL_1);
if (!(adreno_is_a530(adreno_gpu) || adreno_is_a540(adreno_gpu))) return;
if (a5xx_gpu->gpmu_bo) return;
data = (unsignedint *) adreno_gpu->fw[ADRENO_FW_GPMU]->data;
/* * The first dword is the size of the remaining data in dwords. Use it * as a checksum of sorts and make sure it matches the actual size of * the firmware that we read
*/
/* * A single type4 opcode can only have so many values attached so * add enough opcodes to load the all the commands
*/
bosize = (cmds_size + (cmds_size / TYPE4_MAX_PAYLOAD) + 1) << 2;
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.