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