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