1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2022-2024, Advanced Micro Devices, Inc. 4 */ 5 6 #include <drm/drm_device.h> 7 #include <drm/drm_gem_shmem_helper.h> 8 #include <drm/drm_print.h> 9 #include <drm/gpu_scheduler.h> 10 #include <linux/iopoll.h> 11 12 #include "aie2_pci.h" 13 #include "amdxdna_pci_drv.h" 14 #include "amdxdna_pm.h" 15 16 #define SMU_RESULT_OK 1 17 18 /* SMU commands */ 19 #define AIE2_SMU_POWER_ON 0x3 20 #define AIE2_SMU_POWER_OFF 0x4 21 #define AIE2_SMU_SET_MPNPUCLK_FREQ 0x5 22 #define AIE2_SMU_SET_HCLK_FREQ 0x6 23 #define AIE2_SMU_SET_SOFT_DPMLEVEL 0x7 24 #define AIE2_SMU_SET_HARD_DPMLEVEL 0x8 25 26 static int aie2_smu_exec(struct amdxdna_dev_hdl *ndev, u32 reg_cmd, 27 u32 reg_arg, u32 *out) 28 { 29 u32 resp; 30 int ret; 31 32 writel(0, SMU_REG(ndev, SMU_RESP_REG)); 33 writel(reg_arg, SMU_REG(ndev, SMU_ARG_REG)); 34 writel(reg_cmd, SMU_REG(ndev, SMU_CMD_REG)); 35 36 /* Clear and set SMU_INTR_REG to kick off */ 37 writel(0, SMU_REG(ndev, SMU_INTR_REG)); 38 writel(1, SMU_REG(ndev, SMU_INTR_REG)); 39 40 ret = readx_poll_timeout(readl, SMU_REG(ndev, SMU_RESP_REG), resp, 41 resp, AIE2_INTERVAL, AIE2_TIMEOUT); 42 if (ret) { 43 XDNA_ERR(ndev->xdna, "smu cmd %d timed out", reg_cmd); 44 return ret; 45 } 46 47 if (out) 48 *out = readl(SMU_REG(ndev, SMU_OUT_REG)); 49 50 if (resp != SMU_RESULT_OK) { 51 XDNA_ERR(ndev->xdna, "smu cmd %d failed, 0x%x", reg_cmd, resp); 52 return -EINVAL; 53 } 54 55 return 0; 56 } 57 58 int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level) 59 { 60 u32 freq; 61 int ret; 62 63 ret = amdxdna_pm_resume_get(ndev->xdna); 64 if (ret) 65 return ret; 66 67 ret = aie2_smu_exec(ndev, AIE2_SMU_SET_MPNPUCLK_FREQ, 68 ndev->priv->dpm_clk_tbl[dpm_level].npuclk, &freq); 69 if (ret) { 70 XDNA_ERR(ndev->xdna, "Set npu clock to %d failed, ret %d\n", 71 ndev->priv->dpm_clk_tbl[dpm_level].npuclk, ret); 72 goto suspend_put; 73 } 74 ndev->npuclk_freq = freq; 75 76 ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HCLK_FREQ, 77 ndev->priv->dpm_clk_tbl[dpm_level].hclk, &freq); 78 if (ret) { 79 XDNA_ERR(ndev->xdna, "Set h clock to %d failed, ret %d\n", 80 ndev->priv->dpm_clk_tbl[dpm_level].hclk, ret); 81 goto suspend_put; 82 } 83 84 amdxdna_pm_suspend_put(ndev->xdna); 85 ndev->hclk_freq = freq; 86 ndev->dpm_level = dpm_level; 87 88 XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n", 89 ndev->npuclk_freq, ndev->hclk_freq); 90 91 return 0; 92 93 suspend_put: 94 amdxdna_pm_suspend_put(ndev->xdna); 95 return ret; 96 } 97 98 int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level) 99 { 100 int ret; 101 102 ret = amdxdna_pm_resume_get(ndev->xdna); 103 if (ret) 104 return ret; 105 106 ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HARD_DPMLEVEL, dpm_level, NULL); 107 if (ret) { 108 XDNA_ERR(ndev->xdna, "Set hard dpm level %d failed, ret %d ", 109 dpm_level, ret); 110 goto suspend_put; 111 } 112 113 ret = aie2_smu_exec(ndev, AIE2_SMU_SET_SOFT_DPMLEVEL, dpm_level, NULL); 114 if (ret) { 115 XDNA_ERR(ndev->xdna, "Set soft dpm level %d failed, ret %d", 116 dpm_level, ret); 117 goto suspend_put; 118 } 119 120 amdxdna_pm_suspend_put(ndev->xdna); 121 ndev->npuclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].npuclk; 122 ndev->hclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].hclk; 123 ndev->dpm_level = dpm_level; 124 125 XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n", 126 ndev->npuclk_freq, ndev->hclk_freq); 127 128 return 0; 129 130 suspend_put: 131 amdxdna_pm_suspend_put(ndev->xdna); 132 return ret; 133 } 134 135 int aie2_smu_init(struct amdxdna_dev_hdl *ndev) 136 { 137 int ret; 138 139 ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_ON, 0, NULL); 140 if (ret) { 141 XDNA_ERR(ndev->xdna, "Power on failed, ret %d", ret); 142 return ret; 143 } 144 145 return 0; 146 } 147 148 void aie2_smu_fini(struct amdxdna_dev_hdl *ndev) 149 { 150 int ret; 151 152 ndev->priv->hw_ops.set_dpm(ndev, 0); 153 ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, 0, NULL); 154 if (ret) 155 XDNA_ERR(ndev->xdna, "Power off failed, ret %d", ret); 156 } 157