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)
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to