1 /*- 2 * Copyright (c) 2014 Robin Randhawa 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28 /* 29 * This implements support for ARM's Power State Co-ordination Interface 30 * [PSCI]. The implementation adheres to version 0.2 of the PSCI specification 31 * but also supports v0.1. PSCI standardizes operations such as system reset, CPU 32 * on/off/suspend. PSCI requires a compliant firmware implementation. 33 * 34 * The PSCI specification used for this implementation is available at: 35 * 36 * <http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0022b/index.html>. 37 * 38 * TODO: 39 * - Add support for remaining PSCI calls [this implementation only 40 * supports get_version, system_reset and cpu_on]. 41 */ 42 43 #include <sys/cdefs.h> 44 __FBSDID("$FreeBSD$"); 45 46 #include "opt_acpi.h" 47 #include "opt_platform.h" 48 49 #include <sys/param.h> 50 #include <sys/systm.h> 51 #include <sys/bus.h> 52 #include <sys/eventhandler.h> 53 #include <sys/kernel.h> 54 #include <sys/module.h> 55 #include <sys/reboot.h> 56 57 #include <machine/bus.h> 58 #include <machine/machdep.h> 59 60 #ifdef DEV_ACPI 61 #include <contrib/dev/acpica/include/acpi.h> 62 #include <dev/acpica/acpivar.h> 63 #endif 64 65 #ifdef FDT 66 #include <dev/fdt/fdt_common.h> 67 #include <dev/ofw/openfirm.h> 68 #include <dev/ofw/ofw_bus.h> 69 #include <dev/ofw/ofw_bus_subr.h> 70 #endif 71 72 #include <dev/psci/psci.h> 73 74 struct psci_softc { 75 device_t dev; 76 77 psci_callfn_t psci_call; 78 uint32_t psci_fnids[PSCI_FN_MAX]; 79 }; 80 81 #ifdef FDT 82 static int psci_v0_1_init(device_t dev); 83 #endif 84 static int psci_v0_2_init(device_t dev); 85 86 struct psci_softc *psci_softc = NULL; 87 88 #ifdef __arm__ 89 #define USE_ACPI 0 90 #define USE_FDT 1 91 #elif defined(__aarch64__) 92 #define USE_ACPI (arm64_bus_method == ARM64_BUS_ACPI) 93 #define USE_FDT (arm64_bus_method == ARM64_BUS_FDT) 94 #else 95 #error Unknown architecture 96 #endif 97 98 #ifdef FDT 99 static struct ofw_compat_data compat_data[] = { 100 {"arm,psci-1.0", (uintptr_t)psci_v0_2_init}, 101 {"arm,psci-0.2", (uintptr_t)psci_v0_2_init}, 102 {"arm,psci", (uintptr_t)psci_v0_1_init}, 103 {NULL, 0} 104 }; 105 #endif 106 107 static int psci_attach(device_t, psci_initfn_t); 108 static void psci_shutdown(void *, int); 109 110 #ifdef FDT 111 static int psci_fdt_probe(device_t dev); 112 static int psci_fdt_attach(device_t dev); 113 114 static device_method_t psci_fdt_methods[] = { 115 DEVMETHOD(device_probe, psci_fdt_probe), 116 DEVMETHOD(device_attach, psci_fdt_attach), 117 118 DEVMETHOD_END 119 }; 120 121 static driver_t psci_fdt_driver = { 122 "psci", 123 psci_fdt_methods, 124 sizeof(struct psci_softc), 125 }; 126 127 static devclass_t psci_fdt_devclass; 128 129 EARLY_DRIVER_MODULE(psci, simplebus, psci_fdt_driver, psci_fdt_devclass, 0, 0, 130 BUS_PASS_CPU + BUS_PASS_ORDER_FIRST); 131 EARLY_DRIVER_MODULE(psci, ofwbus, psci_fdt_driver, psci_fdt_devclass, 0, 0, 132 BUS_PASS_CPU + BUS_PASS_ORDER_FIRST); 133 134 static psci_callfn_t 135 psci_fdt_get_callfn(phandle_t node) 136 { 137 char method[16]; 138 139 if ((OF_getprop(node, "method", method, sizeof(method))) > 0) { 140 if (strcmp(method, "hvc") == 0) 141 return (psci_hvc_despatch); 142 else if (strcmp(method, "smc") == 0) 143 return (psci_smc_despatch); 144 else 145 printf("psci: PSCI conduit \"%s\" invalid\n", method); 146 } else 147 printf("psci: PSCI conduit not supplied in the device tree\n"); 148 149 return (NULL); 150 } 151 152 static int 153 psci_fdt_probe(device_t dev) 154 { 155 const struct ofw_compat_data *ocd; 156 157 if (!ofw_bus_status_okay(dev)) 158 return (ENXIO); 159 160 ocd = ofw_bus_search_compatible(dev, compat_data); 161 if (ocd->ocd_str == NULL) 162 return (ENXIO); 163 164 device_set_desc(dev, "ARM Power State Co-ordination Interface Driver"); 165 166 return (BUS_PROBE_SPECIFIC); 167 } 168 169 static int 170 psci_fdt_attach(device_t dev) 171 { 172 struct psci_softc *sc = device_get_softc(dev); 173 const struct ofw_compat_data *ocd; 174 psci_initfn_t psci_init; 175 phandle_t node; 176 177 ocd = ofw_bus_search_compatible(dev, compat_data); 178 psci_init = (psci_initfn_t)ocd->ocd_data; 179 180 node = ofw_bus_get_node(dev); 181 sc->psci_call = psci_fdt_get_callfn(node); 182 183 return (psci_attach(dev, psci_init)); 184 } 185 #endif 186 187 #ifdef DEV_ACPI 188 static void psci_acpi_identify(driver_t *, device_t); 189 static int psci_acpi_probe(device_t); 190 static int psci_acpi_attach(device_t); 191 192 static device_method_t psci_acpi_methods[] = { 193 /* Device interface */ 194 DEVMETHOD(device_identify, psci_acpi_identify), 195 DEVMETHOD(device_probe, psci_acpi_probe), 196 DEVMETHOD(device_attach, psci_acpi_attach), 197 198 DEVMETHOD_END 199 }; 200 201 static driver_t psci_acpi_driver = { 202 "psci", 203 psci_acpi_methods, 204 sizeof(struct psci_softc), 205 }; 206 207 static devclass_t psci_acpi_devclass; 208 209 EARLY_DRIVER_MODULE(psci, acpi, psci_acpi_driver, psci_acpi_devclass, 0, 0, 210 BUS_PASS_CPU + BUS_PASS_ORDER_FIRST); 211 212 static int 213 psci_acpi_bootflags(void) 214 { 215 ACPI_TABLE_FADT *fadt; 216 vm_paddr_t physaddr; 217 int flags; 218 219 physaddr = acpi_find_table(ACPI_SIG_FADT); 220 if (physaddr == 0) 221 return (0); 222 223 fadt = acpi_map_table(physaddr, ACPI_SIG_FADT); 224 if (fadt == NULL) { 225 printf("psci: Unable to map the FADT\n"); 226 return (0); 227 } 228 229 flags = fadt->ArmBootFlags; 230 231 acpi_unmap_table(fadt); 232 return (flags); 233 } 234 235 static psci_callfn_t 236 psci_acpi_get_callfn(int flags) 237 { 238 239 if ((flags & ACPI_FADT_PSCI_COMPLIANT) != 0) { 240 if ((flags & ACPI_FADT_PSCI_USE_HVC) != 0) 241 return (psci_hvc_despatch); 242 else 243 return (psci_smc_despatch); 244 } else { 245 printf("psci: PSCI conduit not supplied in the device tree\n"); 246 } 247 248 return (NULL); 249 } 250 251 static void 252 psci_acpi_identify(driver_t *driver, device_t parent) 253 { 254 device_t dev; 255 int flags; 256 257 flags = psci_acpi_bootflags(); 258 if ((flags & ACPI_FADT_PSCI_COMPLIANT) != 0) { 259 dev = BUS_ADD_CHILD(parent, 260 BUS_PASS_CPU + BUS_PASS_ORDER_FIRST, "psci", -1); 261 262 if (dev != NULL) 263 acpi_set_private(dev, (void *)(uintptr_t)flags); 264 } 265 } 266 267 static int 268 psci_acpi_probe(device_t dev) 269 { 270 uintptr_t flags; 271 272 flags = (uintptr_t)acpi_get_private(dev); 273 if ((flags & ACPI_FADT_PSCI_COMPLIANT) == 0) 274 return (ENXIO); 275 276 device_set_desc(dev, "ARM Power State Co-ordination Interface Driver"); 277 return (BUS_PROBE_SPECIFIC); 278 } 279 280 static int 281 psci_acpi_attach(device_t dev) 282 { 283 struct psci_softc *sc = device_get_softc(dev); 284 uintptr_t flags; 285 286 flags = (uintptr_t)acpi_get_private(dev); 287 if ((flags & ACPI_FADT_PSCI_USE_HVC) != 0) 288 sc->psci_call = psci_hvc_despatch; 289 else 290 sc->psci_call = psci_smc_despatch; 291 292 return (psci_attach(dev, psci_v0_2_init)); 293 } 294 #endif 295 296 static int 297 psci_attach(device_t dev, psci_initfn_t psci_init) 298 { 299 struct psci_softc *sc = device_get_softc(dev); 300 301 if (psci_softc != NULL) 302 return (ENXIO); 303 304 if (sc->psci_call == NULL) 305 return (ENXIO); 306 307 KASSERT(psci_init != NULL, ("PSCI init function cannot be NULL")); 308 if (psci_init(dev)) 309 return (ENXIO); 310 311 psci_softc = sc; 312 313 return (0); 314 } 315 316 static int 317 _psci_get_version(struct psci_softc *sc) 318 { 319 uint32_t fnid; 320 321 /* PSCI version wasn't supported in v0.1. */ 322 fnid = sc->psci_fnids[PSCI_FN_VERSION]; 323 if (fnid) 324 return (sc->psci_call(fnid, 0, 0, 0)); 325 326 return (PSCI_RETVAL_NOT_SUPPORTED); 327 } 328 329 int 330 psci_get_version(void) 331 { 332 333 if (psci_softc == NULL) 334 return (PSCI_RETVAL_NOT_SUPPORTED); 335 return (_psci_get_version(psci_softc)); 336 } 337 338 #ifdef FDT 339 static int 340 psci_fdt_callfn(psci_callfn_t *callfn) 341 { 342 phandle_t node; 343 344 node = ofw_bus_find_compatible(OF_peer(0), "arm,psci-0.2"); 345 if (node == 0) { 346 node = ofw_bus_find_compatible(OF_peer(0), "arm,psci-1.0"); 347 if (node == 0) 348 return (PSCI_MISSING); 349 } 350 351 *callfn = psci_fdt_get_callfn(node); 352 return (0); 353 } 354 #endif 355 356 #ifdef DEV_ACPI 357 static int 358 psci_acpi_callfn(psci_callfn_t *callfn) 359 { 360 int flags; 361 362 flags = psci_acpi_bootflags(); 363 if ((flags & ACPI_FADT_PSCI_COMPLIANT) == 0) 364 return (PSCI_MISSING); 365 366 *callfn = psci_acpi_get_callfn(flags); 367 return (0); 368 } 369 #endif 370 371 int 372 psci_cpu_on(unsigned long cpu, unsigned long entry, unsigned long context_id) 373 { 374 psci_callfn_t callfn; 375 uint32_t fnid; 376 int error; 377 378 if (psci_softc == NULL) { 379 fnid = PSCI_FNID_CPU_ON; 380 callfn = NULL; 381 #ifdef FDT 382 if (USE_FDT) { 383 error = psci_fdt_callfn(&callfn); 384 if (error != 0) 385 return (error); 386 } 387 #endif 388 #ifdef DEV_ACPI 389 if (callfn == NULL && USE_ACPI) { 390 error = psci_acpi_callfn(&callfn); 391 if (error != 0) 392 return (error); 393 } 394 #endif 395 396 if (callfn == NULL) 397 return (PSCI_MISSING); 398 } else { 399 callfn = psci_softc->psci_call; 400 fnid = psci_softc->psci_fnids[PSCI_FN_CPU_ON]; 401 } 402 403 /* PSCI v0.1 and v0.2 both support cpu_on. */ 404 return (callfn(fnid, cpu, entry, context_id)); 405 } 406 407 static void 408 psci_shutdown(void *xsc, int howto) 409 { 410 uint32_t fn = 0; 411 412 if (psci_softc == NULL) 413 return; 414 415 /* PSCI system_off and system_reset werent't supported in v0.1. */ 416 if ((howto & RB_POWEROFF) != 0) 417 fn = psci_softc->psci_fnids[PSCI_FN_SYSTEM_OFF]; 418 else if ((howto & RB_HALT) == 0) 419 fn = psci_softc->psci_fnids[PSCI_FN_SYSTEM_RESET]; 420 421 if (fn) 422 psci_softc->psci_call(fn, 0, 0, 0); 423 424 /* System reset and off do not return. */ 425 } 426 427 void 428 psci_reset(void) 429 { 430 431 psci_shutdown(NULL, 0); 432 } 433 434 #ifdef FDT 435 /* Only support PSCI 0.1 on FDT */ 436 static int 437 psci_v0_1_init(device_t dev) 438 { 439 struct psci_softc *sc = device_get_softc(dev); 440 int psci_fn; 441 uint32_t psci_fnid; 442 phandle_t node; 443 int len; 444 445 446 /* Zero out the function ID table - Is this needed ? */ 447 for (psci_fn = PSCI_FN_VERSION, psci_fnid = PSCI_FNID_VERSION; 448 psci_fn < PSCI_FN_MAX; psci_fn++, psci_fnid++) 449 sc->psci_fnids[psci_fn] = 0; 450 451 /* PSCI v0.1 doesn't specify function IDs. Get them from DT */ 452 node = ofw_bus_get_node(dev); 453 454 if ((len = OF_getproplen(node, "cpu_suspend")) > 0) { 455 OF_getencprop(node, "cpu_suspend", &psci_fnid, len); 456 sc->psci_fnids[PSCI_FN_CPU_SUSPEND] = psci_fnid; 457 } 458 459 if ((len = OF_getproplen(node, "cpu_on")) > 0) { 460 OF_getencprop(node, "cpu_on", &psci_fnid, len); 461 sc->psci_fnids[PSCI_FN_CPU_ON] = psci_fnid; 462 } 463 464 if ((len = OF_getproplen(node, "cpu_off")) > 0) { 465 OF_getencprop(node, "cpu_off", &psci_fnid, len); 466 sc->psci_fnids[PSCI_FN_CPU_OFF] = psci_fnid; 467 } 468 469 if ((len = OF_getproplen(node, "migrate")) > 0) { 470 OF_getencprop(node, "migrate", &psci_fnid, len); 471 sc->psci_fnids[PSCI_FN_MIGRATE] = psci_fnid; 472 } 473 474 if (bootverbose) 475 device_printf(dev, "PSCI version 0.1 available\n"); 476 477 return(0); 478 } 479 #endif 480 481 static int 482 psci_v0_2_init(device_t dev) 483 { 484 struct psci_softc *sc = device_get_softc(dev); 485 int version; 486 487 /* PSCI v0.2 specifies explicit function IDs. */ 488 sc->psci_fnids[PSCI_FN_VERSION] = PSCI_FNID_VERSION; 489 sc->psci_fnids[PSCI_FN_CPU_SUSPEND] = PSCI_FNID_CPU_SUSPEND; 490 sc->psci_fnids[PSCI_FN_CPU_OFF] = PSCI_FNID_CPU_OFF; 491 sc->psci_fnids[PSCI_FN_CPU_ON] = PSCI_FNID_CPU_ON; 492 sc->psci_fnids[PSCI_FN_AFFINITY_INFO] = PSCI_FNID_AFFINITY_INFO; 493 sc->psci_fnids[PSCI_FN_MIGRATE] = PSCI_FNID_MIGRATE; 494 sc->psci_fnids[PSCI_FN_MIGRATE_INFO_TYPE] = PSCI_FNID_MIGRATE_INFO_TYPE; 495 sc->psci_fnids[PSCI_FN_MIGRATE_INFO_UP_CPU] = PSCI_FNID_MIGRATE_INFO_UP_CPU; 496 sc->psci_fnids[PSCI_FN_SYSTEM_OFF] = PSCI_FNID_SYSTEM_OFF; 497 sc->psci_fnids[PSCI_FN_SYSTEM_RESET] = PSCI_FNID_SYSTEM_RESET; 498 499 version = _psci_get_version(sc); 500 501 if (version == PSCI_RETVAL_NOT_SUPPORTED) 502 return (1); 503 504 if ((PSCI_VER_MAJOR(version) == 0 && PSCI_VER_MINOR(version) == 2) || 505 PSCI_VER_MAJOR(version) == 1) { 506 if (bootverbose) 507 device_printf(dev, "PSCI version 0.2 compatible\n"); 508 509 /* 510 * We only register this for v0.2 since v0.1 doesn't support 511 * system_reset. 512 */ 513 EVENTHANDLER_REGISTER(shutdown_final, psci_shutdown, sc, 514 SHUTDOWN_PRI_LAST); 515 516 return (0); 517 } 518 519 device_printf(dev, "PSCI version number mismatched with DT\n"); 520 return (1); 521 } 522