xref: /linux/drivers/accel/amdxdna/aie_smu.c (revision 0eaed89c18aeedf0898baf2dbf5ff027c6795152)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2026, Advanced Micro Devices, Inc.
4  */
5 
6 #include "drm/amdxdna_accel.h"
7 #include <drm/drm_device.h>
8 #include <drm/drm_managed.h>
9 #include <drm/drm_print.h>
10 #include <drm/gpu_scheduler.h>
11 #include <linux/iopoll.h>
12 
13 #include "aie.h"
14 
15 #define SMU_RESULT_OK   1
16 
17 /* SMU commands */
18 #define AIE_SMU_POWER_ON                0x3
19 #define AIE_SMU_POWER_OFF               0x4
20 #define AIE_SMU_SET_MPNPUCLK_FREQ       0x5
21 #define AIE_SMU_SET_HCLK_FREQ           0x6
22 #define AIE_SMU_SET_SOFT_DPMLEVEL       0x7
23 #define AIE_SMU_SET_HARD_DPMLEVEL       0x8
24 
25 #define SMU_REG(s, reg) ((s)->smu_regs[reg])
26 
27 struct smu_device {
28 	struct drm_device	*ddev;
29 	struct smu_config	conf;
30 	void __iomem		*smu_regs[SMU_MAX_REGS];
31 };
32 
33 static int aie_smu_exec(struct smu_device *smu, u32 reg_cmd, u32 reg_arg, u32 *out)
34 {
35 	u32 resp;
36 	int ret;
37 
38 	writel(0, SMU_REG(smu, SMU_RESP_REG));
39 	writel(reg_arg, SMU_REG(smu, SMU_ARG_REG));
40 	writel(reg_cmd, SMU_REG(smu, SMU_CMD_REG));
41 
42 	/* Clear and set SMU_INTR_REG to kick off */
43 	writel(0, SMU_REG(smu, SMU_INTR_REG));
44 	writel(1, SMU_REG(smu, SMU_INTR_REG));
45 
46 	ret = readx_poll_timeout(readl, SMU_REG(smu, SMU_RESP_REG), resp,
47 				 resp, AIE_INTERVAL, AIE_TIMEOUT);
48 	if (ret) {
49 		drm_err(smu->ddev, "smu cmd %d timed out", reg_cmd);
50 		return ret;
51 	}
52 
53 	if (out)
54 		*out = readl(SMU_REG(smu, SMU_OUT_REG));
55 
56 	if (resp != SMU_RESULT_OK) {
57 		drm_err(smu->ddev, "smu cmd %d failed, 0x%x", reg_cmd, resp);
58 		return -EINVAL;
59 	}
60 
61 	return 0;
62 }
63 
64 int aie_smu_init(struct smu_device *smu)
65 {
66 	int ret;
67 
68 	/*
69 	 * Failing to set power off indicates an unrecoverable hardware or
70 	 * firmware error.
71 	 */
72 	ret = aie_smu_exec(smu, AIE_SMU_POWER_OFF, 0, NULL);
73 	if (ret) {
74 		drm_err(smu->ddev, "Access power failed, ret %d", ret);
75 		return ret;
76 	}
77 
78 	ret = aie_smu_exec(smu, AIE_SMU_POWER_ON, 0, NULL);
79 	if (ret) {
80 		drm_err(smu->ddev, "Power on failed, ret %d", ret);
81 		return ret;
82 	}
83 
84 	return 0;
85 }
86 
87 void aie_smu_fini(struct smu_device *smu)
88 {
89 	int ret;
90 
91 	ret = aie_smu_exec(smu, AIE_SMU_POWER_OFF, 0, NULL);
92 	if (ret)
93 		drm_err(smu->ddev, "Power off failed, ret %d", ret);
94 }
95 
96 int aie_smu_set_clocks(struct smu_device *smu, u32 *npuclk, u32 *hclk)
97 {
98 	int ret;
99 
100 	if (npuclk) {
101 		ret = aie_smu_exec(smu, AIE_SMU_SET_MPNPUCLK_FREQ, *npuclk, npuclk);
102 		if (ret) {
103 			drm_err(smu->ddev, "Set mpnpu clock to %d failed, ret %d", *npuclk, ret);
104 			return ret;
105 		}
106 	}
107 
108 	if (hclk) {
109 		ret = aie_smu_exec(smu, AIE_SMU_SET_HCLK_FREQ, *hclk, hclk);
110 		if (ret) {
111 			drm_err(smu->ddev, "Set hclock to %d failed, ret %d",
112 				*hclk, ret);
113 			return ret;
114 		}
115 	}
116 
117 	return 0;
118 }
119 
120 int aie_smu_set_dpm(struct smu_device *smu, u32 dpm_level)
121 {
122 	int ret;
123 
124 	ret = aie_smu_exec(smu, AIE_SMU_SET_HARD_DPMLEVEL, dpm_level, NULL);
125 	if (ret) {
126 		drm_err(smu->ddev, "Set hard dpm level %d failed, ret %d",
127 			dpm_level, ret);
128 		return ret;
129 	}
130 
131 	ret = aie_smu_exec(smu, AIE_SMU_SET_SOFT_DPMLEVEL, dpm_level, NULL);
132 	if (ret) {
133 		drm_err(smu->ddev, "Set soft dpm level %d failed, ret %d",
134 			dpm_level, ret);
135 		return ret;
136 	}
137 
138 	return 0;
139 }
140 
141 struct smu_device *aiem_smu_create(struct drm_device *ddev, struct smu_config *conf)
142 {
143 	struct smu_device *smu;
144 
145 	smu = drmm_kzalloc(ddev, sizeof(*smu), GFP_KERNEL);
146 	if (!smu)
147 		return NULL;
148 
149 	smu->ddev = ddev;
150 	memcpy(smu->smu_regs, conf->smu_regs, sizeof(smu->smu_regs));
151 
152 	return smu;
153 }
154