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 #ifdef FDT 330 static int 331 psci_fdt_callfn(psci_callfn_t *callfn) 332 { 333 phandle_t node; 334 335 node = ofw_bus_find_compatible(OF_peer(0), "arm,psci-0.2"); 336 if (node == 0) { 337 node = ofw_bus_find_compatible(OF_peer(0), "arm,psci-1.0"); 338 if (node == 0) 339 return (PSCI_MISSING); 340 } 341 342 *callfn = psci_fdt_get_callfn(node); 343 return (0); 344 } 345 #endif 346 347 #ifdef DEV_ACPI 348 static int 349 psci_acpi_callfn(psci_callfn_t *callfn) 350 { 351 int flags; 352 353 flags = psci_acpi_bootflags(); 354 if ((flags & ACPI_FADT_PSCI_COMPLIANT) == 0) 355 return (PSCI_MISSING); 356 357 *callfn = psci_acpi_get_callfn(flags); 358 return (0); 359 } 360 #endif 361 362 int 363 psci_cpu_on(unsigned long cpu, unsigned long entry, unsigned long context_id) 364 { 365 psci_callfn_t callfn; 366 uint32_t fnid; 367 int error; 368 369 if (psci_softc == NULL) { 370 fnid = PSCI_FNID_CPU_ON; 371 callfn = NULL; 372 #ifdef FDT 373 if (USE_FDT) { 374 error = psci_fdt_callfn(&callfn); 375 if (error != 0) 376 return (error); 377 } 378 #endif 379 #ifdef DEV_ACPI 380 if (callfn == NULL && USE_ACPI) { 381 error = psci_acpi_callfn(&callfn); 382 if (error != 0) 383 return (error); 384 } 385 #endif 386 387 if (callfn == NULL) 388 return (PSCI_MISSING); 389 } else { 390 callfn = psci_softc->psci_call; 391 fnid = psci_softc->psci_fnids[PSCI_FN_CPU_ON]; 392 } 393 394 /* PSCI v0.1 and v0.2 both support cpu_on. */ 395 return (callfn(fnid, cpu, entry, context_id)); 396 } 397 398 static void 399 psci_shutdown(void *xsc, int howto) 400 { 401 uint32_t fn = 0; 402 403 if (psci_softc == NULL) 404 return; 405 406 /* PSCI system_off and system_reset werent't supported in v0.1. */ 407 if ((howto & RB_POWEROFF) != 0) 408 fn = psci_softc->psci_fnids[PSCI_FN_SYSTEM_OFF]; 409 else if ((howto & RB_HALT) == 0) 410 fn = psci_softc->psci_fnids[PSCI_FN_SYSTEM_RESET]; 411 412 if (fn) 413 psci_softc->psci_call(fn, 0, 0, 0); 414 415 /* System reset and off do not return. */ 416 } 417 418 void 419 psci_reset(void) 420 { 421 422 psci_shutdown(NULL, 0); 423 } 424 425 #ifdef FDT 426 /* Only support PSCI 0.1 on FDT */ 427 static int 428 psci_v0_1_init(device_t dev) 429 { 430 struct psci_softc *sc = device_get_softc(dev); 431 int psci_fn; 432 uint32_t psci_fnid; 433 phandle_t node; 434 int len; 435 436 437 /* Zero out the function ID table - Is this needed ? */ 438 for (psci_fn = PSCI_FN_VERSION, psci_fnid = PSCI_FNID_VERSION; 439 psci_fn < PSCI_FN_MAX; psci_fn++, psci_fnid++) 440 sc->psci_fnids[psci_fn] = 0; 441 442 /* PSCI v0.1 doesn't specify function IDs. Get them from DT */ 443 node = ofw_bus_get_node(dev); 444 445 if ((len = OF_getproplen(node, "cpu_suspend")) > 0) { 446 OF_getencprop(node, "cpu_suspend", &psci_fnid, len); 447 sc->psci_fnids[PSCI_FN_CPU_SUSPEND] = psci_fnid; 448 } 449 450 if ((len = OF_getproplen(node, "cpu_on")) > 0) { 451 OF_getencprop(node, "cpu_on", &psci_fnid, len); 452 sc->psci_fnids[PSCI_FN_CPU_ON] = psci_fnid; 453 } 454 455 if ((len = OF_getproplen(node, "cpu_off")) > 0) { 456 OF_getencprop(node, "cpu_off", &psci_fnid, len); 457 sc->psci_fnids[PSCI_FN_CPU_OFF] = psci_fnid; 458 } 459 460 if ((len = OF_getproplen(node, "migrate")) > 0) { 461 OF_getencprop(node, "migrate", &psci_fnid, len); 462 sc->psci_fnids[PSCI_FN_MIGRATE] = psci_fnid; 463 } 464 465 if (bootverbose) 466 device_printf(dev, "PSCI version 0.1 available\n"); 467 468 return(0); 469 } 470 #endif 471 472 static int 473 psci_v0_2_init(device_t dev) 474 { 475 struct psci_softc *sc = device_get_softc(dev); 476 int version; 477 478 /* PSCI v0.2 specifies explicit function IDs. */ 479 sc->psci_fnids[PSCI_FN_VERSION] = PSCI_FNID_VERSION; 480 sc->psci_fnids[PSCI_FN_CPU_SUSPEND] = PSCI_FNID_CPU_SUSPEND; 481 sc->psci_fnids[PSCI_FN_CPU_OFF] = PSCI_FNID_CPU_OFF; 482 sc->psci_fnids[PSCI_FN_CPU_ON] = PSCI_FNID_CPU_ON; 483 sc->psci_fnids[PSCI_FN_AFFINITY_INFO] = PSCI_FNID_AFFINITY_INFO; 484 sc->psci_fnids[PSCI_FN_MIGRATE] = PSCI_FNID_MIGRATE; 485 sc->psci_fnids[PSCI_FN_MIGRATE_INFO_TYPE] = PSCI_FNID_MIGRATE_INFO_TYPE; 486 sc->psci_fnids[PSCI_FN_MIGRATE_INFO_UP_CPU] = PSCI_FNID_MIGRATE_INFO_UP_CPU; 487 sc->psci_fnids[PSCI_FN_SYSTEM_OFF] = PSCI_FNID_SYSTEM_OFF; 488 sc->psci_fnids[PSCI_FN_SYSTEM_RESET] = PSCI_FNID_SYSTEM_RESET; 489 490 version = psci_get_version(sc); 491 492 if (version == PSCI_RETVAL_NOT_SUPPORTED) 493 return (1); 494 495 if ((PSCI_VER_MAJOR(version) == 0 && PSCI_VER_MINOR(version) == 2) || 496 PSCI_VER_MAJOR(version) == 1) { 497 if (bootverbose) 498 device_printf(dev, "PSCI version 0.2 compatible\n"); 499 500 /* 501 * We only register this for v0.2 since v0.1 doesn't support 502 * system_reset. 503 */ 504 EVENTHANDLER_REGISTER(shutdown_final, psci_shutdown, sc, 505 SHUTDOWN_PRI_LAST); 506 507 return (0); 508 } 509 510 device_printf(dev, "PSCI version number mismatched with DT\n"); 511 return (1); 512 } 513