svn commit: r309674 - in head/sys: conf dev/psci

Andrew Turner andrew at FreeBSD.org
Wed Dec 7 14:24:54 UTC 2016


Author: andrew
Date: Wed Dec  7 14:24:53 2016
New Revision: 309674
URL: https://svnweb.freebsd.org/changeset/base/309674

Log:
  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

Modified:
  head/sys/conf/files.arm64
  head/sys/dev/psci/psci.c

Modified: head/sys/conf/files.arm64
==============================================================================
--- head/sys/conf/files.arm64	Wed Dec  7 13:19:19 2016	(r309673)
+++ head/sys/conf/files.arm64	Wed Dec  7 14:24:53 2016	(r309674)
@@ -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

Modified: head/sys/dev/psci/psci.c
==============================================================================
--- head/sys/dev/psci/psci.c	Wed Dec  7 13:19:19 2016	(r309673)
+++ head/sys/dev/psci/psci.c	Wed Dec  7 14:24:53 2016	(r309674)
@@ -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)


More information about the svn-src-head mailing list