1 /* $NetBSD: scmi.c,v 1.1 2025/01/08 22:55:35 jmcneill Exp $ */
2 /* $OpenBSD: scmi.c,v 1.2 2024/11/25 22:12:18 tobhe Exp $ */
3
4 /*
5 * Copyright (c) 2023 Mark Kettenis <kettenis@openbsd.org>
6 * Copyright (c) 2024 Tobias Heider <tobhe@openbsd.org>
7 * Copyright (c) 2025 Jared McNeill <jmcneill@invisible.ca>
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 */
21
22 #include <sys/param.h>
23 #include <sys/device.h>
24 #include <sys/systm.h>
25 #include <sys/kmem.h>
26 #include <sys/sysctl.h>
27 #include <sys/cpu.h>
28
29 #include <arm/arm/smccc.h>
30 #include <dev/ic/scmi.h>
31
32 #define SCMI_SUCCESS 0
33 #define SCMI_NOT_SUPPORTED -1
34 #define SCMI_DENIED -3
35 #define SCMI_BUSY -6
36 #define SCMI_COMMS_ERROR -7
37
38 /* Protocols */
39 #define SCMI_BASE 0x10
40 #define SCMI_PERF 0x13
41 #define SCMI_CLOCK 0x14
42
43 /* Common messages */
44 #define SCMI_PROTOCOL_VERSION 0x0
45 #define SCMI_PROTOCOL_ATTRIBUTES 0x1
46 #define SCMI_PROTOCOL_MESSAGE_ATTRIBUTES 0x2
47
48 /* Clock management messages */
49 #define SCMI_CLOCK_ATTRIBUTES 0x3
50 #define SCMI_CLOCK_DESCRIBE_RATES 0x4
51 #define SCMI_CLOCK_RATE_SET 0x5
52 #define SCMI_CLOCK_RATE_GET 0x6
53 #define SCMI_CLOCK_CONFIG_SET 0x7
54 #define SCMI_CLOCK_CONFIG_SET_ENABLE (1U << 0)
55
56 /* Performance management messages */
57 #define SCMI_PERF_DOMAIN_ATTRIBUTES 0x3
58 #define SCMI_PERF_DESCRIBE_LEVELS 0x4
59 #define SCMI_PERF_LIMITS_GET 0x6
60 #define SCMI_PERF_LEVEL_SET 0x7
61 #define SCMI_PERF_LEVEL_GET 0x8
62
63 struct scmi_resp_perf_domain_attributes_40 {
64 uint32_t pa_attrs;
65 #define SCMI_PERF_ATTR_CAN_LEVEL_SET (1U << 30)
66 #define SCMI_PERF_ATTR_LEVEL_INDEX_MODE (1U << 25)
67 uint32_t pa_ratelimit;
68 uint32_t pa_sustifreq;
69 uint32_t pa_sustperf;
70 char pa_name[16];
71 };
72
73 struct scmi_resp_perf_describe_levels_40 {
74 uint16_t pl_nret;
75 uint16_t pl_nrem;
76 struct {
77 uint32_t pe_perf;
78 uint32_t pe_cost;
79 uint16_t pe_latency;
80 uint16_t pe_reserved;
81 uint32_t pe_ifreq;
82 uint32_t pe_lindex;
83 } pl_entry[];
84 };
85
86 static void scmi_cpufreq_init_sysctl(struct scmi_softc *, uint32_t);
87
88 static inline void
scmi_message_header(volatile struct scmi_shmem * shmem,uint32_t protocol_id,uint32_t message_id)89 scmi_message_header(volatile struct scmi_shmem *shmem,
90 uint32_t protocol_id, uint32_t message_id)
91 {
92 shmem->message_header = (protocol_id << 10) | (message_id << 0);
93 }
94
95 int32_t scmi_smc_command(struct scmi_softc *);
96 int32_t scmi_mbox_command(struct scmi_softc *);
97
98 int
scmi_init_smc(struct scmi_softc * sc)99 scmi_init_smc(struct scmi_softc *sc)
100 {
101 volatile struct scmi_shmem *shmem;
102 int32_t status;
103 uint32_t vers;
104
105 if (sc->sc_smc_id == 0) {
106 aprint_error_dev(sc->sc_dev, "no SMC id\n");
107 return -1;
108 }
109
110 shmem = sc->sc_shmem_tx;
111
112 sc->sc_command = scmi_smc_command;
113
114 if ((shmem->channel_status & SCMI_CHANNEL_FREE) == 0) {
115 aprint_error_dev(sc->sc_dev, "channel busy\n");
116 return -1;
117 }
118
119 scmi_message_header(shmem, SCMI_BASE, SCMI_PROTOCOL_VERSION);
120 shmem->length = sizeof(uint32_t);
121 status = sc->sc_command(sc);
122 if (status != SCMI_SUCCESS) {
123 aprint_error_dev(sc->sc_dev, "protocol version command failed\n");
124 return -1;
125 }
126
127 vers = shmem->message_payload[1];
128 sc->sc_ver_major = vers >> 16;
129 sc->sc_ver_minor = vers & 0xfffff;
130 aprint_normal_dev(sc->sc_dev, "SCMI %d.%d\n",
131 sc->sc_ver_major, sc->sc_ver_minor);
132
133 mutex_init(&sc->sc_shmem_tx_lock, MUTEX_DEFAULT, IPL_NONE);
134 mutex_init(&sc->sc_shmem_rx_lock, MUTEX_DEFAULT, IPL_NONE);
135
136 return 0;
137 }
138
139 int
scmi_init_mbox(struct scmi_softc * sc)140 scmi_init_mbox(struct scmi_softc *sc)
141 {
142 int32_t status;
143 uint32_t vers;
144
145 if (sc->sc_mbox_tx == NULL) {
146 aprint_error_dev(sc->sc_dev, "no tx mbox\n");
147 return -1;
148 }
149 if (sc->sc_mbox_rx == NULL) {
150 aprint_error_dev(sc->sc_dev, "no rx mbox\n");
151 return -1;
152 }
153
154 sc->sc_command = scmi_mbox_command;
155
156 scmi_message_header(sc->sc_shmem_tx, SCMI_BASE, SCMI_PROTOCOL_VERSION);
157 sc->sc_shmem_tx->length = sizeof(uint32_t);
158 status = sc->sc_command(sc);
159 if (status != SCMI_SUCCESS) {
160 aprint_error_dev(sc->sc_dev,
161 "protocol version command failed\n");
162 return -1;
163 }
164
165 vers = sc->sc_shmem_tx->message_payload[1];
166 sc->sc_ver_major = vers >> 16;
167 sc->sc_ver_minor = vers & 0xfffff;
168 aprint_normal_dev(sc->sc_dev, "SCMI %d.%d\n",
169 sc->sc_ver_major, sc->sc_ver_minor);
170
171 mutex_init(&sc->sc_shmem_tx_lock, MUTEX_DEFAULT, IPL_NONE);
172 mutex_init(&sc->sc_shmem_rx_lock, MUTEX_DEFAULT, IPL_NONE);
173
174 return 0;
175 }
176
177 int32_t
scmi_smc_command(struct scmi_softc * sc)178 scmi_smc_command(struct scmi_softc *sc)
179 {
180 volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
181 int32_t status;
182
183 shmem->channel_status = 0;
184 status = smccc_call(sc->sc_smc_id, 0, 0, 0, 0,
185 NULL, NULL, NULL, NULL);
186 if (status != SMCCC_SUCCESS)
187 return SCMI_NOT_SUPPORTED;
188 if ((shmem->channel_status & SCMI_CHANNEL_ERROR))
189 return SCMI_COMMS_ERROR;
190 if ((shmem->channel_status & SCMI_CHANNEL_FREE) == 0)
191 return SCMI_BUSY;
192 return shmem->message_payload[0];
193 }
194
195 int32_t
scmi_mbox_command(struct scmi_softc * sc)196 scmi_mbox_command(struct scmi_softc *sc)
197 {
198 volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
199 int ret;
200 int i;
201
202 shmem->channel_status = 0;
203 ret = sc->sc_mbox_tx_send(sc->sc_mbox_tx);
204 if (ret != 0)
205 return SCMI_NOT_SUPPORTED;
206
207 /* XXX: poll for now */
208 for (i = 0; i < 20; i++) {
209 if (shmem->channel_status & SCMI_CHANNEL_FREE)
210 break;
211 delay(10);
212 }
213 if ((shmem->channel_status & SCMI_CHANNEL_ERROR))
214 return SCMI_COMMS_ERROR;
215 if ((shmem->channel_status & SCMI_CHANNEL_FREE) == 0)
216 return SCMI_BUSY;
217
218 return shmem->message_payload[0];
219 }
220
221 #if notyet
222 /* Clock management. */
223
224 void scmi_clock_enable(void *, uint32_t *, int);
225 uint32_t scmi_clock_get_frequency(void *, uint32_t *);
226 int scmi_clock_set_frequency(void *, uint32_t *, uint32_t);
227
228 void
scmi_attach_clock(struct scmi_softc * sc,int node)229 scmi_attach_clock(struct scmi_softc *sc, int node)
230 {
231 volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
232 int32_t status;
233 int nclocks;
234
235 scmi_message_header(shmem, SCMI_CLOCK, SCMI_PROTOCOL_ATTRIBUTES);
236 shmem->length = sizeof(uint32_t);
237 status = sc->sc_command(sc);
238 if (status != SCMI_SUCCESS)
239 return;
240
241 nclocks = shmem->message_payload[1] & 0xffff;
242 if (nclocks == 0)
243 return;
244
245 sc->sc_cd.cd_node = node;
246 sc->sc_cd.cd_cookie = sc;
247 sc->sc_cd.cd_enable = scmi_clock_enable;
248 sc->sc_cd.cd_get_frequency = scmi_clock_get_frequency;
249 sc->sc_cd.cd_set_frequency = scmi_clock_set_frequency;
250 clock_register(&sc->sc_cd);
251 }
252
253 void
scmi_clock_enable(void * cookie,uint32_t * cells,int on)254 scmi_clock_enable(void *cookie, uint32_t *cells, int on)
255 {
256 struct scmi_softc *sc = cookie;
257 volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
258 uint32_t idx = cells[0];
259
260 scmi_message_header(shmem, SCMI_CLOCK, SCMI_CLOCK_CONFIG_SET);
261 shmem->length = 3 * sizeof(uint32_t);
262 shmem->message_payload[0] = idx;
263 shmem->message_payload[1] = on ? SCMI_CLOCK_CONFIG_SET_ENABLE : 0;
264 sc->sc_command(sc);
265 }
266
267 uint32_t
scmi_clock_get_frequency(void * cookie,uint32_t * cells)268 scmi_clock_get_frequency(void *cookie, uint32_t *cells)
269 {
270 struct scmi_softc *sc = cookie;
271 volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
272 uint32_t idx = cells[0];
273 int32_t status;
274
275 scmi_message_header(shmem, SCMI_CLOCK, SCMI_CLOCK_RATE_GET);
276 shmem->length = 2 * sizeof(uint32_t);
277 shmem->message_payload[0] = idx;
278 status = sc->sc_command(sc);
279 if (status != SCMI_SUCCESS)
280 return 0;
281 if (shmem->message_payload[2] != 0)
282 return 0;
283
284 return shmem->message_payload[1];
285 }
286
287 int
scmi_clock_set_frequency(void * cookie,uint32_t * cells,uint32_t freq)288 scmi_clock_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
289 {
290 struct scmi_softc *sc = cookie;
291 volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
292 uint32_t idx = cells[0];
293 int32_t status;
294
295 scmi_message_header(shmem, SCMI_CLOCK, SCMI_CLOCK_RATE_SET);
296 shmem->length = 5 * sizeof(uint32_t);
297 shmem->message_payload[0] = 0;
298 shmem->message_payload[1] = idx;
299 shmem->message_payload[2] = freq;
300 shmem->message_payload[3] = 0;
301 status = sc->sc_command(sc);
302 if (status != SCMI_SUCCESS)
303 return -1;
304
305 return 0;
306 }
307 #endif
308
309 /* Performance management */
310 void scmi_perf_descr_levels(struct scmi_softc *, int);
311
312 void
scmi_attach_perf(struct scmi_softc * sc)313 scmi_attach_perf(struct scmi_softc *sc)
314 {
315 volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
316 int32_t status;
317 uint32_t vers;
318 int i;
319
320 scmi_message_header(sc->sc_shmem_tx, SCMI_PERF, SCMI_PROTOCOL_VERSION);
321 sc->sc_shmem_tx->length = sizeof(uint32_t);
322 status = sc->sc_command(sc);
323 if (status != SCMI_SUCCESS) {
324 aprint_error_dev(sc->sc_dev,
325 "SCMI_PROTOCOL_VERSION failed\n");
326 return;
327 }
328
329 vers = shmem->message_payload[1];
330 if (vers != 0x40000) {
331 aprint_error_dev(sc->sc_dev,
332 "invalid perf protocol version (0x%x != 0x4000)", vers);
333 return;
334 }
335
336 scmi_message_header(shmem, SCMI_PERF, SCMI_PROTOCOL_ATTRIBUTES);
337 shmem->length = sizeof(uint32_t);
338 status = sc->sc_command(sc);
339 if (status != SCMI_SUCCESS) {
340 aprint_error_dev(sc->sc_dev,
341 "SCMI_PROTOCOL_ATTRIBUTES failed\n");
342 return;
343 }
344
345 sc->sc_perf_ndomains = shmem->message_payload[1] & 0xffff;
346 sc->sc_perf_domains = kmem_zalloc(sc->sc_perf_ndomains *
347 sizeof(struct scmi_perf_domain), KM_SLEEP);
348 sc->sc_perf_power_unit = (shmem->message_payload[1] >> 16) & 0x3;
349
350 /* Add one frequency sensor per perf domain */
351 for (i = 0; i < sc->sc_perf_ndomains; i++) {
352 volatile struct scmi_resp_perf_domain_attributes_40 *pa;
353
354 scmi_message_header(shmem, SCMI_PERF,
355 SCMI_PERF_DOMAIN_ATTRIBUTES);
356 shmem->length = 2 * sizeof(uint32_t);
357 shmem->message_payload[0] = i;
358 status = sc->sc_command(sc);
359 if (status != SCMI_SUCCESS) {
360 aprint_error_dev(sc->sc_dev,
361 "SCMI_PERF_DOMAIN_ATTRIBUTES failed\n");
362 return;
363 }
364
365 pa = (volatile struct scmi_resp_perf_domain_attributes_40 *)
366 &shmem->message_payload[1];
367 aprint_debug_dev(sc->sc_dev,
368 "dom %u attr %#x rate_limit %u sfreq %u sperf %u "
369 "name \"%s\"\n",
370 i, pa->pa_attrs, pa->pa_ratelimit, pa->pa_sustifreq,
371 pa->pa_sustperf, pa->pa_name);
372
373 sc->sc_perf_domains[i].pd_domain_id = i;
374 sc->sc_perf_domains[i].pd_sc = sc;
375 for (int map = 0; map < sc->sc_perf_ndmap; map++) {
376 if (sc->sc_perf_dmap[map].pm_domain == i) {
377 sc->sc_perf_domains[i].pd_ci =
378 sc->sc_perf_dmap[map].pm_ci;
379 break;
380 }
381 }
382 snprintf(sc->sc_perf_domains[i].pd_name,
383 sizeof(sc->sc_perf_domains[i].pd_name), "%s", pa->pa_name);
384 sc->sc_perf_domains[i].pd_can_level_set =
385 (pa->pa_attrs & SCMI_PERF_ATTR_CAN_LEVEL_SET) != 0;
386 sc->sc_perf_domains[i].pd_level_index_mode =
387 (pa->pa_attrs & SCMI_PERF_ATTR_LEVEL_INDEX_MODE) != 0;
388 sc->sc_perf_domains[i].pd_rate_limit = pa->pa_ratelimit;
389 sc->sc_perf_domains[i].pd_sustained_perf = pa->pa_sustperf;
390
391 scmi_perf_descr_levels(sc, i);
392
393 if (sc->sc_perf_domains[i].pd_can_level_set &&
394 sc->sc_perf_domains[i].pd_nlevels > 0 &&
395 sc->sc_perf_domains[i].pd_levels[0].pl_ifreq != 0) {
396 scmi_cpufreq_init_sysctl(sc, i);
397 }
398 }
399 return;
400 }
401
402 void
scmi_perf_descr_levels(struct scmi_softc * sc,int domain)403 scmi_perf_descr_levels(struct scmi_softc *sc, int domain)
404 {
405 volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
406 volatile struct scmi_resp_perf_describe_levels_40 *pl;
407 struct scmi_perf_domain *pd = &sc->sc_perf_domains[domain];
408 int status, i, idx;
409
410 idx = 0;
411 do {
412 scmi_message_header(shmem, SCMI_PERF,
413 SCMI_PERF_DESCRIBE_LEVELS);
414 shmem->length = sizeof(uint32_t) * 3;
415 shmem->message_payload[0] = domain;
416 shmem->message_payload[1] = idx;
417 status = sc->sc_command(sc);
418 if (status != SCMI_SUCCESS) {
419 aprint_error_dev(sc->sc_dev,
420 "SCMI_PERF_DESCRIBE_LEVELS failed\n");
421 return;
422 }
423
424 pl = (volatile struct scmi_resp_perf_describe_levels_40 *)
425 &shmem->message_payload[1];
426
427 if (pd->pd_levels == NULL) {
428 pd->pd_nlevels = pl->pl_nret + pl->pl_nrem;
429 pd->pd_levels = kmem_zalloc(pd->pd_nlevels *
430 sizeof(struct scmi_perf_level),
431 KM_SLEEP);
432 }
433
434 for (i = 0; i < pl->pl_nret; i++) {
435 pd->pd_levels[idx + i].pl_cost =
436 pl->pl_entry[i].pe_cost;
437 pd->pd_levels[idx + i].pl_perf =
438 pl->pl_entry[i].pe_perf;
439 pd->pd_levels[idx + i].pl_ifreq =
440 pl->pl_entry[i].pe_ifreq;
441 aprint_debug_dev(sc->sc_dev,
442 "dom %u pl %u cost %u perf %i ifreq %u\n",
443 domain, idx + i,
444 pl->pl_entry[i].pe_cost,
445 pl->pl_entry[i].pe_perf,
446 pl->pl_entry[i].pe_ifreq);
447 }
448 idx += pl->pl_nret;
449 } while (pl->pl_nrem);
450 }
451
452 static int32_t
scmi_perf_limits_get(struct scmi_perf_domain * pd,uint32_t * max_level,uint32_t * min_level)453 scmi_perf_limits_get(struct scmi_perf_domain *pd, uint32_t *max_level,
454 uint32_t *min_level)
455 {
456 struct scmi_softc *sc = pd->pd_sc;
457 volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
458 int32_t status;
459
460 if (pd->pd_levels == NULL) {
461 return SCMI_NOT_SUPPORTED;
462 }
463
464 mutex_enter(&sc->sc_shmem_tx_lock);
465 scmi_message_header(shmem, SCMI_PERF, SCMI_PERF_LIMITS_GET);
466 shmem->length = sizeof(uint32_t) * 2;
467 shmem->message_payload[0] = pd->pd_domain_id;
468 status = sc->sc_command(sc);
469 if (status == SCMI_SUCCESS) {
470 *max_level = shmem->message_payload[1];
471 *min_level = shmem->message_payload[2];
472 }
473 mutex_exit(&sc->sc_shmem_tx_lock);
474
475 return status;
476 }
477
478 static int32_t
scmi_perf_level_get(struct scmi_perf_domain * pd,uint32_t * perf_level)479 scmi_perf_level_get(struct scmi_perf_domain *pd, uint32_t *perf_level)
480 {
481 struct scmi_softc *sc = pd->pd_sc;
482 volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
483 int32_t status;
484
485 if (pd->pd_levels == NULL) {
486 return SCMI_NOT_SUPPORTED;
487 }
488
489 mutex_enter(&sc->sc_shmem_tx_lock);
490 scmi_message_header(shmem, SCMI_PERF, SCMI_PERF_LEVEL_GET);
491 shmem->length = sizeof(uint32_t) * 2;
492 shmem->message_payload[0] = pd->pd_domain_id;
493 status = sc->sc_command(sc);
494 if (status == SCMI_SUCCESS) {
495 *perf_level = shmem->message_payload[1];
496 }
497 mutex_exit(&sc->sc_shmem_tx_lock);
498
499 return status;
500 }
501
502 static int32_t
scmi_perf_level_set(struct scmi_perf_domain * pd,uint32_t perf_level)503 scmi_perf_level_set(struct scmi_perf_domain *pd, uint32_t perf_level)
504 {
505 struct scmi_softc *sc = pd->pd_sc;
506 volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
507 int32_t status;
508
509 if (pd->pd_levels == NULL) {
510 return SCMI_NOT_SUPPORTED;
511 }
512
513 mutex_enter(&sc->sc_shmem_tx_lock);
514 scmi_message_header(shmem, SCMI_PERF, SCMI_PERF_LEVEL_SET);
515 shmem->length = sizeof(uint32_t) * 3;
516 shmem->message_payload[0] = pd->pd_domain_id;
517 shmem->message_payload[1] = perf_level;
518 status = sc->sc_command(sc);
519 mutex_exit(&sc->sc_shmem_tx_lock);
520
521 return status;
522 }
523
524 static u_int
scmi_cpufreq_level_to_mhz(struct scmi_perf_domain * pd,uint32_t level)525 scmi_cpufreq_level_to_mhz(struct scmi_perf_domain *pd, uint32_t level)
526 {
527 ssize_t n;
528
529 if (pd->pd_level_index_mode) {
530 if (level < pd->pd_nlevels) {
531 return pd->pd_levels[level].pl_ifreq / 1000;
532 }
533 } else {
534 for (n = 0; n < pd->pd_nlevels; n++) {
535 if (pd->pd_levels[n].pl_perf == level) {
536 return pd->pd_levels[n].pl_ifreq / 1000;
537 }
538 }
539 }
540
541 return 0;
542 }
543
544 static int
scmi_cpufreq_set_rate(struct scmi_softc * sc,struct scmi_perf_domain * pd,u_int freq_mhz)545 scmi_cpufreq_set_rate(struct scmi_softc *sc, struct scmi_perf_domain *pd,
546 u_int freq_mhz)
547 {
548 uint32_t perf_level = -1;
549 int32_t status;
550 ssize_t n;
551
552 for (n = 0; n < pd->pd_nlevels; n++) {
553 if (pd->pd_levels[n].pl_ifreq / 1000 == freq_mhz) {
554 perf_level = pd->pd_level_index_mode ?
555 n : pd->pd_levels[n].pl_perf;
556 break;
557 }
558 }
559 if (n == pd->pd_nlevels)
560 return EINVAL;
561
562 status = scmi_perf_level_set(pd, perf_level);
563 if (status != SCMI_SUCCESS) {
564 return EIO;
565 }
566
567 if (pd->pd_rate_limit > 0)
568 delay(pd->pd_rate_limit);
569
570 return 0;
571 }
572
573 static int
scmi_cpufreq_sysctl_helper(SYSCTLFN_ARGS)574 scmi_cpufreq_sysctl_helper(SYSCTLFN_ARGS)
575 {
576 struct scmi_perf_domain * const pd = rnode->sysctl_data;
577 struct scmi_softc * const sc = pd->pd_sc;
578 struct sysctlnode node;
579 u_int fq, oldfq = 0, old_target;
580 uint32_t level;
581 int32_t status;
582 int error;
583
584 node = *rnode;
585 node.sysctl_data = &fq;
586
587 if (rnode->sysctl_num == pd->pd_node_target) {
588 if (pd->pd_freq_target == 0) {
589 status = scmi_perf_level_get(pd, &level);
590 if (status != SCMI_SUCCESS) {
591 return EIO;
592 }
593 pd->pd_freq_target =
594 scmi_cpufreq_level_to_mhz(pd, level);
595 }
596 fq = pd->pd_freq_target;
597 } else {
598 status = scmi_perf_level_get(pd, &level);
599 if (status != SCMI_SUCCESS) {
600 return EIO;
601 }
602 fq = scmi_cpufreq_level_to_mhz(pd, level);
603 }
604
605 if (rnode->sysctl_num == pd->pd_node_target)
606 oldfq = fq;
607
608 if (pd->pd_freq_target == 0)
609 pd->pd_freq_target = fq;
610
611 error = sysctl_lookup(SYSCTLFN_CALL(&node));
612 if (error || newp == NULL)
613 return error;
614
615 if (fq == oldfq || rnode->sysctl_num != pd->pd_node_target)
616 return 0;
617
618 if (atomic_cas_uint(&pd->pd_busy, 0, 1) != 0)
619 return EBUSY;
620
621 old_target = pd->pd_freq_target;
622 pd->pd_freq_target = fq;
623
624 error = scmi_cpufreq_set_rate(sc, pd, fq);
625 if (error != 0) {
626 pd->pd_freq_target = old_target;
627 }
628
629 atomic_dec_uint(&pd->pd_busy);
630
631 return error;
632 }
633
634 static void
scmi_cpufreq_init_sysctl(struct scmi_softc * sc,uint32_t domain_id)635 scmi_cpufreq_init_sysctl(struct scmi_softc *sc, uint32_t domain_id)
636 {
637 const struct sysctlnode *node, *cpunode;
638 struct scmi_perf_domain *pd = &sc->sc_perf_domains[domain_id];
639 struct cpu_info *ci = pd->pd_ci;
640 struct sysctllog *cpufreq_log = NULL;
641 uint32_t max_level, min_level;
642 int32_t status;
643 int error, i;
644
645 if (ci == NULL)
646 return;
647
648 status = scmi_perf_limits_get(pd, &max_level, &min_level);
649 if (status != SCMI_SUCCESS) {
650 /*
651 * Not supposed to happen, but at least one implementation
652 * returns DENIED here. Assume that there are no limits.
653 */
654 min_level = 0;
655 max_level = UINT32_MAX;
656 }
657 aprint_debug_dev(sc->sc_dev, "dom %u limits max %u min %u\n",
658 domain_id, max_level, min_level);
659
660 pd->pd_freq_available = kmem_zalloc(strlen("XXXX ") *
661 pd->pd_nlevels, KM_SLEEP);
662 for (i = 0; i < pd->pd_nlevels; i++) {
663 char buf[6];
664 uint32_t level = pd->pd_level_index_mode ?
665 i : pd->pd_levels[i].pl_perf;
666
667 if (level < min_level) {
668 continue;
669 } else if (level > max_level) {
670 break;
671 }
672
673 snprintf(buf, sizeof(buf), i ? " %u" : "%u",
674 pd->pd_levels[i].pl_ifreq / 1000);
675 strcat(pd->pd_freq_available, buf);
676 if (level == pd->pd_sustained_perf) {
677 break;
678 }
679 }
680
681 error = sysctl_createv(&cpufreq_log, 0, NULL, &node,
682 CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
683 NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
684 if (error)
685 goto sysctl_failed;
686 error = sysctl_createv(&cpufreq_log, 0, &node, &node,
687 0, CTLTYPE_NODE, "cpufreq", NULL,
688 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
689 if (error)
690 goto sysctl_failed;
691 error = sysctl_createv(&cpufreq_log, 0, &node, &cpunode,
692 0, CTLTYPE_NODE, cpu_name(ci), NULL,
693 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
694 if (error)
695 goto sysctl_failed;
696
697 error = sysctl_createv(&cpufreq_log, 0, &cpunode, &node,
698 CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL,
699 scmi_cpufreq_sysctl_helper, 0, (void *)pd, 0,
700 CTL_CREATE, CTL_EOL);
701 if (error)
702 goto sysctl_failed;
703 pd->pd_node_target = node->sysctl_num;
704
705 error = sysctl_createv(&cpufreq_log, 0, &cpunode, &node,
706 CTLFLAG_READWRITE, CTLTYPE_INT, "current", NULL,
707 scmi_cpufreq_sysctl_helper, 0, (void *)pd, 0,
708 CTL_CREATE, CTL_EOL);
709 if (error)
710 goto sysctl_failed;
711 pd->pd_node_current = node->sysctl_num;
712
713 error = sysctl_createv(&cpufreq_log, 0, &cpunode, &node,
714 0, CTLTYPE_STRING, "available", NULL,
715 NULL, 0, pd->pd_freq_available, 0,
716 CTL_CREATE, CTL_EOL);
717 if (error)
718 goto sysctl_failed;
719 pd->pd_node_available = node->sysctl_num;
720
721 return;
722
723 sysctl_failed:
724 aprint_error_dev(sc->sc_dev, "couldn't create sysctl nodes: %d\n",
725 error);
726 sysctl_teardown(&cpufreq_log);
727 }
728