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