// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2026, Advanced Micro Devices, Inc. */ #include "drm/amdxdna_accel.h" #include #include #include #include #include #include "aie.h" #define SMU_RESULT_OK 1 /* SMU commands */ #define AIE_SMU_POWER_ON 0x3 #define AIE_SMU_POWER_OFF 0x4 #define AIE_SMU_SET_MPNPUCLK_FREQ 0x5 #define AIE_SMU_SET_HCLK_FREQ 0x6 #define AIE_SMU_SET_SOFT_DPMLEVEL 0x7 #define AIE_SMU_SET_HARD_DPMLEVEL 0x8 #define SMU_REG(s, reg) ((s)->smu_regs[reg]) struct smu_device { struct drm_device *ddev; struct smu_config conf; void __iomem *smu_regs[SMU_MAX_REGS]; }; static int aie_smu_exec(struct smu_device *smu, u32 reg_cmd, u32 reg_arg, u32 *out) { u32 resp; int ret; writel(0, SMU_REG(smu, SMU_RESP_REG)); writel(reg_arg, SMU_REG(smu, SMU_ARG_REG)); writel(reg_cmd, SMU_REG(smu, SMU_CMD_REG)); /* Clear and set SMU_INTR_REG to kick off */ writel(0, SMU_REG(smu, SMU_INTR_REG)); writel(1, SMU_REG(smu, SMU_INTR_REG)); ret = readx_poll_timeout(readl, SMU_REG(smu, SMU_RESP_REG), resp, resp, AIE_INTERVAL, AIE_TIMEOUT); if (ret) { drm_err(smu->ddev, "smu cmd %d timed out", reg_cmd); return ret; } if (out) *out = readl(SMU_REG(smu, SMU_OUT_REG)); if (resp != SMU_RESULT_OK) { drm_err(smu->ddev, "smu cmd %d failed, 0x%x", reg_cmd, resp); return -EINVAL; } return 0; } int aie_smu_init(struct smu_device *smu) { int ret; /* * Failing to set power off indicates an unrecoverable hardware or * firmware error. */ ret = aie_smu_exec(smu, AIE_SMU_POWER_OFF, 0, NULL); if (ret) { drm_err(smu->ddev, "Access power failed, ret %d", ret); return ret; } ret = aie_smu_exec(smu, AIE_SMU_POWER_ON, 0, NULL); if (ret) { drm_err(smu->ddev, "Power on failed, ret %d", ret); return ret; } return 0; } void aie_smu_fini(struct smu_device *smu) { int ret; ret = aie_smu_exec(smu, AIE_SMU_POWER_OFF, 0, NULL); if (ret) drm_err(smu->ddev, "Power off failed, ret %d", ret); } int aie_smu_set_clocks(struct smu_device *smu, u32 *npuclk, u32 *hclk) { int ret; if (npuclk) { ret = aie_smu_exec(smu, AIE_SMU_SET_MPNPUCLK_FREQ, *npuclk, npuclk); if (ret) { drm_err(smu->ddev, "Set mpnpu clock to %d failed, ret %d", *npuclk, ret); return ret; } } if (hclk) { ret = aie_smu_exec(smu, AIE_SMU_SET_HCLK_FREQ, *hclk, hclk); if (ret) { drm_err(smu->ddev, "Set hclock to %d failed, ret %d", *hclk, ret); return ret; } } return 0; } int aie_smu_set_dpm(struct smu_device *smu, u32 dpm_level) { int ret; ret = aie_smu_exec(smu, AIE_SMU_SET_HARD_DPMLEVEL, dpm_level, NULL); if (ret) { drm_err(smu->ddev, "Set hard dpm level %d failed, ret %d", dpm_level, ret); return ret; } ret = aie_smu_exec(smu, AIE_SMU_SET_SOFT_DPMLEVEL, dpm_level, NULL); if (ret) { drm_err(smu->ddev, "Set soft dpm level %d failed, ret %d", dpm_level, ret); return ret; } return 0; } struct smu_device *aiem_smu_create(struct drm_device *ddev, struct smu_config *conf) { struct smu_device *smu; smu = drmm_kzalloc(ddev, sizeof(*smu), GFP_KERNEL); if (!smu) return NULL; smu->ddev = ddev; memcpy(smu->smu_regs, conf->smu_regs, sizeof(smu->smu_regs)); return smu; }