diff options
| author | Andrew Turner <andrew@FreeBSD.org> | 2016-12-07 14:24:53 +0000 |
|---|---|---|
| committer | Andrew Turner <andrew@FreeBSD.org> | 2016-12-07 14:24:53 +0000 |
| commit | 2b5014f6fe0999aec2704102a13e197a2d9411b8 (patch) | |
| tree | 29192b8e123ea5c9c084a19fed6c36c78233ab2c /sys/dev/psci | |
| parent | 20a9f7715e7cfd20bad3bef79bf0d42a42162030 (diff) | |
Notes
Diffstat (limited to 'sys/dev/psci')
| -rw-r--r-- | sys/dev/psci/psci.c | 244 |
1 files changed, 219 insertions, 25 deletions
diff --git a/sys/dev/psci/psci.c b/sys/dev/psci/psci.c index f7050976c8c6..4452fb45955d 100644 --- a/sys/dev/psci/psci.c +++ b/sys/dev/psci/psci.c @@ -43,6 +43,9 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_acpi.h" +#include "opt_platform.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/bus.h> @@ -52,11 +55,19 @@ __FBSDID("$FreeBSD$"); #include <sys/reboot.h> #include <machine/bus.h> +#include <machine/machdep.h> + +#ifdef DEV_ACPI +#include <contrib/dev/acpica/include/acpi.h> +#include <dev/acpica/acpivar.h> +#endif +#ifdef FDT #include <dev/fdt/fdt_common.h> #include <dev/ofw/openfirm.h> #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> +#endif #include <dev/psci/psci.h> @@ -67,43 +78,60 @@ struct psci_softc { uint32_t psci_fnids[PSCI_FN_MAX]; }; +#ifdef FDT static int psci_v0_1_init(device_t dev); +#endif static int psci_v0_2_init(device_t dev); struct psci_softc *psci_softc = NULL; +#ifdef __arm__ +#define USE_ACPI 0 +#define USE_FDT 1 +#elif defined(__aarch64__) +#define USE_ACPI (arm64_bus_method == ARM64_BUS_ACPI) +#define USE_FDT (arm64_bus_method == ARM64_BUS_FDT) +#else +#error Unknown architecture +#endif + +#ifdef FDT static struct ofw_compat_data compat_data[] = { {"arm,psci-0.2", (uintptr_t)psci_v0_2_init}, {"arm,psci", (uintptr_t)psci_v0_1_init}, {NULL, 0} }; +#endif -static int psci_probe(device_t dev); -static int psci_attach(device_t dev); +static int psci_attach(device_t, psci_initfn_t); static void psci_shutdown(void *, int); -static device_method_t psci_methods[] = { - DEVMETHOD(device_probe, psci_probe), - DEVMETHOD(device_attach, psci_attach), +#ifdef FDT +static int psci_fdt_probe(device_t dev); +static int psci_fdt_attach(device_t dev); + +static device_method_t psci_fdt_methods[] = { + DEVMETHOD(device_probe, psci_fdt_probe), + DEVMETHOD(device_attach, psci_fdt_attach), DEVMETHOD_END }; -static driver_t psci_driver = { +static driver_t psci_fdt_driver = { "psci", - psci_methods, + psci_fdt_methods, sizeof(struct psci_softc), }; -static devclass_t psci_devclass; +static devclass_t psci_fdt_devclass; -EARLY_DRIVER_MODULE(psci, simplebus, psci_driver, psci_devclass, 0, 0, +EARLY_DRIVER_MODULE(psci, simplebus, psci_fdt_driver, psci_fdt_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_FIRST); -EARLY_DRIVER_MODULE(psci, ofwbus, psci_driver, psci_devclass, 0, 0, +EARLY_DRIVER_MODULE(psci, ofwbus, psci_fdt_driver, psci_fdt_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_FIRST); static psci_callfn_t -psci_get_callfn(phandle_t node) +psci_fdt_get_callfn(phandle_t node) { char method[16]; @@ -121,7 +149,7 @@ psci_get_callfn(phandle_t node) } static int -psci_probe(device_t dev) +psci_fdt_probe(device_t dev) { const struct ofw_compat_data *ocd; @@ -138,25 +166,144 @@ psci_probe(device_t dev) } static int -psci_attach(device_t dev) +psci_fdt_attach(device_t dev) { struct psci_softc *sc = device_get_softc(dev); const struct ofw_compat_data *ocd; psci_initfn_t psci_init; phandle_t node; - if (psci_softc != NULL) - return (ENXIO); - ocd = ofw_bus_search_compatible(dev, compat_data); psci_init = (psci_initfn_t)ocd->ocd_data; - KASSERT(psci_init != NULL, ("PSCI init function cannot be NULL")); node = ofw_bus_get_node(dev); - sc->psci_call = psci_get_callfn(node); + sc->psci_call = psci_fdt_get_callfn(node); + + return (psci_attach(dev, psci_init)); +} +#endif + +#ifdef DEV_ACPI +static void psci_acpi_identify(driver_t *, device_t); +static int psci_acpi_probe(device_t); +static int psci_acpi_attach(device_t); + +static device_method_t psci_acpi_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, psci_acpi_identify), + DEVMETHOD(device_probe, psci_acpi_probe), + DEVMETHOD(device_attach, psci_acpi_attach), + + DEVMETHOD_END +}; + +static driver_t psci_acpi_driver = { + "psci", + psci_acpi_methods, + sizeof(struct psci_softc), +}; + +static devclass_t psci_acpi_devclass; + +EARLY_DRIVER_MODULE(psci, acpi, psci_acpi_driver, psci_acpi_devclass, 0, 0, + BUS_PASS_CPU + BUS_PASS_ORDER_FIRST); + +static int +psci_acpi_bootflags(void) +{ + ACPI_TABLE_FADT *fadt; + vm_paddr_t physaddr; + int flags; + + physaddr = acpi_find_table(ACPI_SIG_FADT); + if (physaddr == 0) + return (0); + + fadt = acpi_map_table(physaddr, ACPI_SIG_FADT); + if (fadt == NULL) { + printf("psci: Unable to map the FADT\n"); + return (0); + } + + flags = fadt->ArmBootFlags; + + acpi_unmap_table(fadt); + return (flags); +} + +static psci_callfn_t +psci_acpi_get_callfn(int flags) +{ + + if ((flags & ACPI_FADT_PSCI_COMPLIANT) != 0) { + if ((flags & ACPI_FADT_PSCI_USE_HVC) != 0) + return (psci_hvc_despatch); + else + return (psci_smc_despatch); + } else { + printf("psci: PSCI conduit not supplied in the device tree\n"); + } + + return (NULL); +} + +static void +psci_acpi_identify(driver_t *driver, device_t parent) +{ + device_t dev; + int flags; + + flags = psci_acpi_bootflags(); + if ((flags & ACPI_FADT_PSCI_COMPLIANT) != 0) { + dev = BUS_ADD_CHILD(parent, + BUS_PASS_CPU + BUS_PASS_ORDER_FIRST, "psci", -1); + + if (dev != NULL) + acpi_set_private(dev, (void *)(uintptr_t)flags); + } +} + +static int +psci_acpi_probe(device_t dev) +{ + uintptr_t flags; + + flags = (uintptr_t)acpi_get_private(dev); + if ((flags & ACPI_FADT_PSCI_COMPLIANT) == 0) + return (ENXIO); + + device_set_desc(dev, "ARM Power State Co-ordination Interface Driver"); + return (BUS_PROBE_SPECIFIC); +} + +static int +psci_acpi_attach(device_t dev) +{ + struct psci_softc *sc = device_get_softc(dev); + uintptr_t flags; + + flags = (uintptr_t)acpi_get_private(dev); + if ((flags & ACPI_FADT_PSCI_USE_HVC) != 0) + sc->psci_call = psci_hvc_despatch; + else + sc->psci_call = psci_smc_despatch; + + return (psci_attach(dev, psci_v0_2_init)); +} +#endif + +static int +psci_attach(device_t dev, psci_initfn_t psci_init) +{ + struct psci_softc *sc = device_get_softc(dev); + + if (psci_softc != NULL) + return (ENXIO); + if (sc->psci_call == NULL) return (ENXIO); + KASSERT(psci_init != NULL, ("PSCI init function cannot be NULL")); if (psci_init(dev)) return (ENXIO); @@ -178,21 +325,62 @@ psci_get_version(struct psci_softc *sc) return (PSCI_RETVAL_NOT_SUPPORTED); } +#ifdef FDT +static int +psci_fdt_callfn(psci_callfn_t *callfn) +{ + phandle_t node; + + node = ofw_bus_find_compatible(OF_peer(0), "arm,psci-0.2"); + if (node == 0) + /* TODO: Handle psci 0.1 */ + return (PSCI_MISSING); + + *callfn = psci_fdt_get_callfn(node); + return (0); +} +#endif + +#ifdef DEV_ACPI +static int +psci_acpi_callfn(psci_callfn_t *callfn) +{ + int flags; + + flags = psci_acpi_bootflags(); + if ((flags & ACPI_FADT_PSCI_COMPLIANT) == 0) + return (PSCI_MISSING); + + *callfn = psci_acpi_get_callfn(flags); + return (0); +} +#endif + int psci_cpu_on(unsigned long cpu, unsigned long entry, unsigned long context_id) { psci_callfn_t callfn; - phandle_t node; uint32_t fnid; + int error; if (psci_softc == NULL) { - node = ofw_bus_find_compatible(OF_peer(0), "arm,psci-0.2"); - if (node == 0) - /* TODO: Handle psci 0.1 */ - return (PSCI_MISSING); - fnid = PSCI_FNID_CPU_ON; - callfn = psci_get_callfn(node); + callfn = NULL; +#ifdef FDT + if (USE_FDT) { + error = psci_fdt_callfn(&callfn); + if (error != 0) + return (error); + } +#endif +#ifdef DEV_ACPI + if (callfn == NULL && USE_ACPI) { + error = psci_acpi_callfn(&callfn); + if (error != 0) + return (error); + } +#endif + if (callfn == NULL) return (PSCI_MISSING); } else { @@ -209,6 +397,9 @@ psci_shutdown(void *xsc, int howto) { uint32_t fn = 0; + if (psci_softc == NULL) + return; + /* PSCI system_off and system_reset werent't supported in v0.1. */ if ((howto & RB_POWEROFF) != 0) fn = psci_softc->psci_fnids[PSCI_FN_SYSTEM_OFF]; @@ -221,6 +412,8 @@ psci_shutdown(void *xsc, int howto) /* System reset and off do not return. */ } +#ifdef FDT +/* Only support PSCI 0.1 on FDT */ static int psci_v0_1_init(device_t dev) { @@ -264,6 +457,7 @@ psci_v0_1_init(device_t dev) return(0); } +#endif static int psci_v0_2_init(device_t dev) |
