xref: /linux/drivers/accel/amdxdna/aie2_smu.c (revision be1ca3ee8f97067fee87fda73ea5959d5ab75bbf)
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 #define NPU4_DPM_TOPS(ndev, dpm_level) \
26 ({ \
27 	typeof(ndev) _ndev = ndev; \
28 	(4096 * (_ndev)->total_col * \
29 	 (_ndev)->priv->dpm_clk_tbl[dpm_level].hclk / 1000000); \
30 })
31 
32 static int aie2_smu_exec(struct amdxdna_dev_hdl *ndev, u32 reg_cmd,
33 			 u32 reg_arg, u32 *out)
34 {
35 	u32 resp;
36 	int ret;
37 
38 	writel(0, SMU_REG(ndev, SMU_RESP_REG));
39 	writel(reg_arg, SMU_REG(ndev, SMU_ARG_REG));
40 	writel(reg_cmd, SMU_REG(ndev, SMU_CMD_REG));
41 
42 	/* Clear and set SMU_INTR_REG to kick off */
43 	writel(0, SMU_REG(ndev, SMU_INTR_REG));
44 	writel(1, SMU_REG(ndev, SMU_INTR_REG));
45 
46 	ret = readx_poll_timeout(readl, SMU_REG(ndev, SMU_RESP_REG), resp,
47 				 resp, AIE2_INTERVAL, AIE2_TIMEOUT);
48 	if (ret) {
49 		XDNA_ERR(ndev->xdna, "smu cmd %d timed out", reg_cmd);
50 		return ret;
51 	}
52 
53 	if (out)
54 		*out = readl(SMU_REG(ndev, SMU_OUT_REG));
55 
56 	if (resp != SMU_RESULT_OK) {
57 		XDNA_ERR(ndev->xdna, "smu cmd %d failed, 0x%x", reg_cmd, resp);
58 		return -EINVAL;
59 	}
60 
61 	return 0;
62 }
63 
64 int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
65 {
66 	u32 freq;
67 	int ret;
68 
69 	ret = aie2_smu_exec(ndev, AIE2_SMU_SET_MPNPUCLK_FREQ,
70 			    ndev->priv->dpm_clk_tbl[dpm_level].npuclk, &freq);
71 	if (ret) {
72 		XDNA_ERR(ndev->xdna, "Set npu clock to %d failed, ret %d\n",
73 			 ndev->priv->dpm_clk_tbl[dpm_level].npuclk, ret);
74 		return ret;
75 	}
76 	ndev->npuclk_freq = freq;
77 
78 	ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HCLK_FREQ,
79 			    ndev->priv->dpm_clk_tbl[dpm_level].hclk, &freq);
80 	if (ret) {
81 		XDNA_ERR(ndev->xdna, "Set h clock to %d failed, ret %d\n",
82 			 ndev->priv->dpm_clk_tbl[dpm_level].hclk, ret);
83 		return ret;
84 	}
85 
86 	ndev->hclk_freq = freq;
87 	ndev->max_tops = 2 * ndev->total_col;
88 	ndev->curr_tops = ndev->max_tops * freq / 1028;
89 
90 	XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
91 		 ndev->npuclk_freq, ndev->hclk_freq);
92 
93 	return 0;
94 }
95 
96 int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
97 {
98 	int ret;
99 
100 	ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HARD_DPMLEVEL, dpm_level, NULL);
101 	if (ret) {
102 		XDNA_ERR(ndev->xdna, "Set hard dpm level %d failed, ret %d ",
103 			 dpm_level, ret);
104 		return ret;
105 	}
106 
107 	ret = aie2_smu_exec(ndev, AIE2_SMU_SET_SOFT_DPMLEVEL, dpm_level, NULL);
108 	if (ret) {
109 		XDNA_ERR(ndev->xdna, "Set soft dpm level %d failed, ret %d",
110 			 dpm_level, ret);
111 		return ret;
112 	}
113 
114 	ndev->npuclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].npuclk;
115 	ndev->hclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].hclk;
116 	ndev->max_tops = NPU4_DPM_TOPS(ndev, ndev->max_dpm_level);
117 	ndev->curr_tops = NPU4_DPM_TOPS(ndev, dpm_level);
118 
119 	XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
120 		 ndev->npuclk_freq, ndev->hclk_freq);
121 
122 	return 0;
123 }
124 
125 int aie2_smu_init(struct amdxdna_dev_hdl *ndev)
126 {
127 	int ret;
128 
129 	/*
130 	 * Failing to set power off indicates an unrecoverable hardware or
131 	 * firmware error.
132 	 */
133 	ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, 0, NULL);
134 	if (ret) {
135 		XDNA_ERR(ndev->xdna, "Access power failed, ret %d", ret);
136 		return ret;
137 	}
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