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
aie2_smu_exec(struct amdxdna_dev_hdl * ndev,u32 reg_cmd,u32 reg_arg,u32 * out)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
npu1_set_dpm(struct amdxdna_dev_hdl * ndev,u32 dpm_level)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 return ret;
68 }
69 ndev->npuclk_freq = freq;
70
71 ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HCLK_FREQ,
72 ndev->priv->dpm_clk_tbl[dpm_level].hclk, &freq);
73 if (ret) {
74 XDNA_ERR(ndev->xdna, "Set h clock to %d failed, ret %d\n",
75 ndev->priv->dpm_clk_tbl[dpm_level].hclk, ret);
76 return ret;
77 }
78 ndev->hclk_freq = freq;
79 ndev->dpm_level = dpm_level;
80
81 XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
82 ndev->npuclk_freq, ndev->hclk_freq);
83
84 return 0;
85 }
86
npu4_set_dpm(struct amdxdna_dev_hdl * ndev,u32 dpm_level)87 int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
88 {
89 int ret;
90
91 ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HARD_DPMLEVEL, dpm_level, NULL);
92 if (ret) {
93 XDNA_ERR(ndev->xdna, "Set hard dpm level %d failed, ret %d ",
94 dpm_level, ret);
95 return ret;
96 }
97
98 ret = aie2_smu_exec(ndev, AIE2_SMU_SET_SOFT_DPMLEVEL, dpm_level, NULL);
99 if (ret) {
100 XDNA_ERR(ndev->xdna, "Set soft dpm level %d failed, ret %d",
101 dpm_level, ret);
102 return ret;
103 }
104
105 ndev->npuclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].npuclk;
106 ndev->hclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].hclk;
107 ndev->dpm_level = dpm_level;
108
109 XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
110 ndev->npuclk_freq, ndev->hclk_freq);
111
112 return 0;
113 }
114
aie2_smu_init(struct amdxdna_dev_hdl * ndev)115 int aie2_smu_init(struct amdxdna_dev_hdl *ndev)
116 {
117 int ret;
118
119 ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_ON, 0, NULL);
120 if (ret) {
121 XDNA_ERR(ndev->xdna, "Power on failed, ret %d", ret);
122 return ret;
123 }
124
125 return 0;
126 }
127
aie2_smu_fini(struct amdxdna_dev_hdl * ndev)128 void aie2_smu_fini(struct amdxdna_dev_hdl *ndev)
129 {
130 int ret;
131
132 ndev->priv->hw_ops.set_dpm(ndev, 0);
133 ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, 0, NULL);
134 if (ret)
135 XDNA_ERR(ndev->xdna, "Power off failed, ret %d", ret);
136 }
137