xref: /dpdk/drivers/power/amd_uncore/amd_uncore.c (revision da4d64d0e80343d54ec22b583c6cc606826e73ba)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2024 Advanced Micro Devices, Inc.
3  */
4 
5 #include <errno.h>
6 #include <dirent.h>
7 #include <fnmatch.h>
8 
9 #include <rte_memcpy.h>
10 
11 #include "amd_uncore.h"
12 #include "power_common.h"
13 #include "e_smi/e_smi.h"
14 
15 #define MAX_NUMA_DIE 8
16 
17 struct  __rte_cache_aligned uncore_power_info {
18 	unsigned int die;                  /* Core die id */
19 	unsigned int pkg;                  /* Package id */
20 	uint32_t freqs[RTE_MAX_UNCORE_FREQS];  /* Frequency array */
21 	uint32_t nb_freqs;                 /* Number of available freqs */
22 	uint32_t curr_idx;                 /* Freq index in freqs array */
23 	uint32_t max_freq;            /* System max uncore freq */
24 	uint32_t min_freq;            /* System min uncore freq */
25 };
26 
27 static struct uncore_power_info uncore_info[RTE_MAX_NUMA_NODES][MAX_NUMA_DIE];
28 static int esmi_initialized;
29 static unsigned int hsmp_proto_ver;
30 
31 static int
32 set_uncore_freq_internal(struct uncore_power_info *ui, uint32_t idx)
33 {
34 	int ret;
35 
36 	if (idx >= RTE_MAX_UNCORE_FREQS || idx >= ui->nb_freqs) {
37 		POWER_LOG(DEBUG, "Invalid uncore frequency index %u, which "
38 				"should be less than %u", idx, ui->nb_freqs);
39 		return -1;
40 	}
41 
42 	ret = esmi_apb_disable(ui->pkg, idx);
43 	if (ret != ESMI_SUCCESS) {
44 		POWER_LOG(ERR, "DF P-state '%u' set failed for pkg %02u",
45 				idx, ui->pkg);
46 		return -1;
47 	}
48 
49 	POWER_DEBUG_LOG("DF P-state '%u' to be set for pkg %02u die %02u",
50 				idx, ui->pkg, ui->die);
51 
52 	/* write the minimum value first if the target freq is less than current max */
53 	ui->curr_idx = idx;
54 
55 	return 0;
56 }
57 
58 static int
59 power_init_for_setting_uncore_freq(struct uncore_power_info *ui)
60 {
61 	switch (hsmp_proto_ver) {
62 	case HSMP_PROTO_VER5:
63 		ui->max_freq = 1800000; /* Hz */
64 		ui->min_freq = 1200000; /* Hz */
65 		break;
66 	case HSMP_PROTO_VER2:
67 	default:
68 		ui->max_freq = 1600000; /* Hz */
69 		ui->min_freq = 1200000; /* Hz */
70 	}
71 
72 	return 0;
73 }
74 
75 /*
76  * Get the available uncore frequencies of the specific die.
77  */
78 static int
79 power_get_available_uncore_freqs(struct uncore_power_info *ui)
80 {
81 	ui->nb_freqs = 3;
82 	if (ui->nb_freqs >= RTE_MAX_UNCORE_FREQS) {
83 		POWER_LOG(ERR, "Too many available uncore frequencies: %d",
84 				ui->nb_freqs);
85 		return -1;
86 	}
87 
88 	/* Generate the uncore freq bucket array. */
89 	switch (hsmp_proto_ver) {
90 	case HSMP_PROTO_VER5:
91 		ui->freqs[0] = 1800000;
92 		ui->freqs[1] = 1440000;
93 		ui->freqs[2] = 1200000;
94 		break;
95 	case HSMP_PROTO_VER2:
96 	default:
97 		ui->freqs[0] = 1600000;
98 		ui->freqs[1] = 1333000;
99 		ui->freqs[2] = 1200000;
100 	}
101 
102 	POWER_DEBUG_LOG("%d frequency(s) of pkg %02u die %02u are available",
103 			ui->num_uncore_freqs, ui->pkg, ui->die);
104 
105 	return 0;
106 }
107 
108 static int
109 check_pkg_die_values(unsigned int pkg, unsigned int die)
110 {
111 	unsigned int max_pkgs, max_dies;
112 	max_pkgs = power_amd_uncore_get_num_pkgs();
113 	if (max_pkgs == 0)
114 		return -1;
115 	if (pkg >= max_pkgs) {
116 		POWER_LOG(DEBUG, "Package number %02u can not exceed %u",
117 				pkg, max_pkgs);
118 		return -1;
119 	}
120 
121 	max_dies = power_amd_uncore_get_num_dies(pkg);
122 	if (max_dies == 0)
123 		return -1;
124 	if (die >= max_dies) {
125 		POWER_LOG(DEBUG, "Die number %02u can not exceed %u",
126 				die, max_dies);
127 		return -1;
128 	}
129 
130 	return 0;
131 }
132 
133 static void
134 power_amd_uncore_esmi_init(void)
135 {
136 	if (esmi_init() == ESMI_SUCCESS) {
137 		if (esmi_hsmp_proto_ver_get(&hsmp_proto_ver) ==
138 				ESMI_SUCCESS)
139 			esmi_initialized = 1;
140 	}
141 }
142 
143 int
144 power_amd_uncore_init(unsigned int pkg, unsigned int die)
145 {
146 	struct uncore_power_info *ui;
147 	int ret;
148 
149 	if (!esmi_initialized) {
150 		ret = esmi_init();
151 		if (ret != ESMI_SUCCESS) {
152 			POWER_LOG(DEBUG, "ESMI Not initialized, drivers not found");
153 			return -1;
154 		}
155 		ret = esmi_hsmp_proto_ver_get(&hsmp_proto_ver);
156 		if (ret != ESMI_SUCCESS) {
157 			POWER_LOG(DEBUG, "HSMP Proto Version Get failed with "
158 					"error %s", esmi_get_err_msg(ret));
159 			esmi_exit();
160 			return -1;
161 		}
162 		esmi_initialized = 1;
163 	}
164 
165 	ret = check_pkg_die_values(pkg, die);
166 	if (ret < 0)
167 		return -1;
168 
169 	ui = &uncore_info[pkg][die];
170 	ui->die = die;
171 	ui->pkg = pkg;
172 
173 	/* Init for setting uncore die frequency */
174 	if (power_init_for_setting_uncore_freq(ui) < 0) {
175 		POWER_LOG(DEBUG, "Cannot init for setting uncore frequency for "
176 				"pkg %02u die %02u", pkg, die);
177 		return -1;
178 	}
179 
180 	/* Get the available frequencies */
181 	if (power_get_available_uncore_freqs(ui) < 0) {
182 		POWER_LOG(DEBUG, "Cannot get available uncore frequencies of "
183 				"pkg %02u die %02u", pkg, die);
184 		return -1;
185 	}
186 
187 	return 0;
188 }
189 
190 int
191 power_amd_uncore_exit(unsigned int pkg, unsigned int die)
192 {
193 	struct uncore_power_info *ui;
194 
195 	int ret = check_pkg_die_values(pkg, die);
196 	if (ret < 0)
197 		return -1;
198 
199 	ui = &uncore_info[pkg][die];
200 	ui->nb_freqs = 0;
201 
202 	if (esmi_initialized) {
203 		esmi_exit();
204 		esmi_initialized = 0;
205 	}
206 
207 	return 0;
208 }
209 
210 uint32_t
211 power_get_amd_uncore_freq(unsigned int pkg, unsigned int die)
212 {
213 	int ret = check_pkg_die_values(pkg, die);
214 	if (ret < 0)
215 		return -1;
216 
217 	return uncore_info[pkg][die].curr_idx;
218 }
219 
220 int
221 power_set_amd_uncore_freq(unsigned int pkg, unsigned int die, uint32_t index)
222 {
223 	int ret = check_pkg_die_values(pkg, die);
224 	if (ret < 0)
225 		return -1;
226 
227 	return set_uncore_freq_internal(&(uncore_info[pkg][die]), index);
228 }
229 
230 int
231 power_amd_uncore_freq_max(unsigned int pkg, unsigned int die)
232 {
233 	int ret = check_pkg_die_values(pkg, die);
234 	if (ret < 0)
235 		return -1;
236 
237 	return set_uncore_freq_internal(&(uncore_info[pkg][die]), 0);
238 }
239 
240 
241 int
242 power_amd_uncore_freq_min(unsigned int pkg, unsigned int die)
243 {
244 	int ret = check_pkg_die_values(pkg, die);
245 	if (ret < 0)
246 		return -1;
247 
248 	struct uncore_power_info *ui = &uncore_info[pkg][die];
249 
250 	return set_uncore_freq_internal(&(uncore_info[pkg][die]), ui->nb_freqs - 1);
251 }
252 
253 int
254 power_amd_uncore_freqs(unsigned int pkg, unsigned int die, uint32_t *freqs, uint32_t num)
255 {
256 	struct uncore_power_info *ui;
257 
258 	int ret = check_pkg_die_values(pkg, die);
259 	if (ret < 0)
260 		return -1;
261 
262 	if (freqs == NULL) {
263 		POWER_LOG(ERR, "NULL buffer supplied");
264 		return 0;
265 	}
266 
267 	ui = &uncore_info[pkg][die];
268 	if (num < ui->nb_freqs) {
269 		POWER_LOG(ERR, "Buffer size is not enough");
270 		return 0;
271 	}
272 	rte_memcpy(freqs, ui->freqs, ui->nb_freqs * sizeof(uint32_t));
273 
274 	return ui->nb_freqs;
275 }
276 
277 int
278 power_amd_uncore_get_num_freqs(unsigned int pkg, unsigned int die)
279 {
280 	int ret = check_pkg_die_values(pkg, die);
281 	if (ret < 0)
282 		return -1;
283 
284 	return uncore_info[pkg][die].nb_freqs;
285 }
286 
287 unsigned int
288 power_amd_uncore_get_num_pkgs(void)
289 {
290 	uint32_t num_pkgs = 0;
291 	int ret;
292 
293 	if (esmi_initialized) {
294 		ret = esmi_number_of_sockets_get(&num_pkgs);
295 		if (ret != ESMI_SUCCESS) {
296 			POWER_LOG(ERR, "Failed to get number of sockets");
297 			num_pkgs = 0;
298 		}
299 	}
300 	return num_pkgs;
301 }
302 
303 unsigned int
304 power_amd_uncore_get_num_dies(unsigned int pkg)
305 {
306 	if (pkg >= power_amd_uncore_get_num_pkgs()) {
307 		POWER_LOG(ERR, "Invalid package ID");
308 		return 0;
309 	}
310 
311 	return 1;
312 }
313 
314 static struct rte_power_uncore_ops amd_uncore_ops = {
315 	.name = "amd-hsmp",
316 	.cb = power_amd_uncore_esmi_init,
317 	.init = power_amd_uncore_init,
318 	.exit = power_amd_uncore_exit,
319 	.get_avail_freqs = power_amd_uncore_freqs,
320 	.get_num_pkgs = power_amd_uncore_get_num_pkgs,
321 	.get_num_dies = power_amd_uncore_get_num_dies,
322 	.get_num_freqs = power_amd_uncore_get_num_freqs,
323 	.get_freq = power_get_amd_uncore_freq,
324 	.set_freq = power_set_amd_uncore_freq,
325 	.freq_max = power_amd_uncore_freq_max,
326 	.freq_min = power_amd_uncore_freq_min,
327 };
328 
329 RTE_POWER_REGISTER_UNCORE_OPS(amd_uncore_ops);
330