Add ACPI support to the PSCI driver. This checks the Fixed ACPI Description

Table to find if the hardware supports PSCI, and if so what method the
kernel should use to interact with it.

Obtained from:	ABT Systems Ltd
Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Andrew Turner 2016-12-07 14:24:53 +00:00
parent 20a9f7715e
commit 2b5014f6fe
2 changed files with 221 additions and 27 deletions

View File

@ -156,7 +156,7 @@ dev/ofw/ofw_cpu.c optional fdt
dev/ofw/ofwpci.c optional fdt pci
dev/pci/pci_host_generic.c optional pci
dev/pci/pci_host_generic_fdt.c optional pci fdt
dev/psci/psci.c optional psci fdt
dev/psci/psci.c optional psci
dev/psci/psci_arm64.S optional psci
dev/uart/uart_cpu_arm64.c optional uart
dev/uart/uart_dev_pl011.c optional uart pl011

View File

@ -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;
ocd = ofw_bus_search_compatible(dev, compat_data);
psci_init = (psci_initfn_t)ocd->ocd_data;
node = ofw_bus_get_node(dev);
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);
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);
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)