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 15 #define SMU_RESULT_OK 1 16 17 /* SMU commands */ 18 #define AIE2_SMU_POWER_ON 0x3 19 #define AIE2_SMU_POWER_OFF 0x4 20 #define AIE2_SMU_SET_MPNPUCLK_FREQ 0x5 21 #define AIE2_SMU_SET_HCLK_FREQ 0x6 22 #define AIE2_SMU_SET_SOFT_DPMLEVEL 0x7 23 #define AIE2_SMU_SET_HARD_DPMLEVEL 0x8 24 25 static int aie2_smu_exec(struct amdxdna_dev_hdl *ndev, u32 reg_cmd, 26 u32 reg_arg, u32 *out) 27 { 28 u32 resp; 29 int ret; 30 31 writel(0, SMU_REG(ndev, SMU_RESP_REG)); 32 writel(reg_arg, SMU_REG(ndev, SMU_ARG_REG)); 33 writel(reg_cmd, SMU_REG(ndev, SMU_CMD_REG)); 34 35 /* Clear and set SMU_INTR_REG to kick off */ 36 writel(0, SMU_REG(ndev, SMU_INTR_REG)); 37 writel(1, SMU_REG(ndev, SMU_INTR_REG)); 38 39 ret = readx_poll_timeout(readl, SMU_REG(ndev, SMU_RESP_REG), resp, 40 resp, AIE2_INTERVAL, AIE2_TIMEOUT); 41 if (ret) { 42 XDNA_ERR(ndev->xdna, "smu cmd %d timed out", reg_cmd); 43 return ret; 44 } 45 46 if (out) 47 *out = readl(SMU_REG(ndev, SMU_OUT_REG)); 48 49 if (resp != SMU_RESULT_OK) { 50 XDNA_ERR(ndev->xdna, "smu cmd %d failed, 0x%x", reg_cmd, resp); 51 return -EINVAL; 52 } 53 54 return 0; 55 } 56 57 int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level) 58 { 59 u32 freq; 60 int ret; 61 62 ret = aie2_smu_exec(ndev, AIE2_SMU_SET_MPNPUCLK_FREQ, 63 ndev->priv->dpm_clk_tbl[dpm_level].npuclk, &freq); 64 if (ret) { 65 XDNA_ERR(ndev->xdna, "Set npu clock to %d failed, ret %d\n", 66 ndev->priv->dpm_clk_tbl[dpm_level].npuclk, ret); 67 } 68 ndev->npuclk_freq = freq; 69 70 ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HCLK_FREQ, 71 ndev->priv->dpm_clk_tbl[dpm_level].hclk, &freq); 72 if (ret) { 73 XDNA_ERR(ndev->xdna, "Set h clock to %d failed, ret %d\n", 74 ndev->priv->dpm_clk_tbl[dpm_level].hclk, ret); 75 } 76 ndev->hclk_freq = freq; 77 ndev->dpm_level = dpm_level; 78 79 XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n", 80 ndev->npuclk_freq, ndev->hclk_freq); 81 82 return 0; 83 } 84 85 int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level) 86 { 87 int ret; 88 89 ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HARD_DPMLEVEL, dpm_level, NULL); 90 if (ret) { 91 XDNA_ERR(ndev->xdna, "Set hard dpm level %d failed, ret %d ", 92 dpm_level, ret); 93 return ret; 94 } 95 96 ret = aie2_smu_exec(ndev, AIE2_SMU_SET_SOFT_DPMLEVEL, dpm_level, NULL); 97 if (ret) { 98 XDNA_ERR(ndev->xdna, "Set soft dpm level %d failed, ret %d", 99 dpm_level, ret); 100 return ret; 101 } 102 103 ndev->npuclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].npuclk; 104 ndev->hclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].hclk; 105 ndev->dpm_level = dpm_level; 106 107 XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n", 108 ndev->npuclk_freq, ndev->hclk_freq); 109 110 return 0; 111 } 112 113 int aie2_smu_init(struct amdxdna_dev_hdl *ndev) 114 { 115 int ret; 116 117 ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_ON, 0, NULL); 118 if (ret) { 119 XDNA_ERR(ndev->xdna, "Power on failed, ret %d", ret); 120 return ret; 121 } 122 123 return 0; 124 } 125 126 void aie2_smu_fini(struct amdxdna_dev_hdl *ndev) 127 { 128 int ret; 129 130 ndev->priv->hw_ops.set_dpm(ndev, 0); 131 ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, 0, NULL); 132 if (ret) 133 XDNA_ERR(ndev->xdna, "Power off failed, ret %d", ret); 134 } 135