#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>
#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>
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];
}
static int
-psci_probe(device_t dev)
+psci_fdt_probe(device_t dev)
{
const struct ofw_compat_data *ocd;
}
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);
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 {
{
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];
/* 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)
{
return(0);
}
+#endif
static int
psci_v0_2_init(device_t dev)