Index: sys/arm/arm/pmu.h
===================================================================
--- /dev/null
+++ sys/arm/arm/pmu.h
@@ -0,0 +1,67 @@
+/*-
+ * Copyright (c) 2015 Ruslan Bukin
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _ARM_PMU_H_
+#define _ARM_PMU_H_
+
+#include
+
+#ifdef notyet
+#define MAX_RLEN 8
+#else
+#define MAX_RLEN 1
+#endif
+
+struct pmu_softc {
+ struct resource *res[MAX_RLEN];
+ device_t dev;
+ void *ih[MAX_RLEN];
+ bool intr_per_core;
+};
+
+int pmu_attach(device_t dev);
+int pmu_intr(void *arg);
+
+static struct resource_spec pmu_spec[] = {
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ /* We don't currently handle pmu events, other than on cpu 0 */
+#ifdef notyet
+ { SYS_RES_IRQ, 1, RF_ACTIVE | RF_OPTIONAL },
+ { SYS_RES_IRQ, 2, RF_ACTIVE | RF_OPTIONAL },
+ { SYS_RES_IRQ, 3, RF_ACTIVE | RF_OPTIONAL },
+ { SYS_RES_IRQ, 4, RF_ACTIVE | RF_OPTIONAL },
+ { SYS_RES_IRQ, 5, RF_ACTIVE | RF_OPTIONAL },
+ { SYS_RES_IRQ, 6, RF_ACTIVE | RF_OPTIONAL },
+ { SYS_RES_IRQ, 7, RF_ACTIVE | RF_OPTIONAL },
+#endif
+ { -1, 0 }
+};
+
+#endif
Index: sys/arm/arm/pmu.c
===================================================================
--- sys/arm/arm/pmu.c
+++ sys/arm/arm/pmu.c
@@ -50,42 +50,11 @@
#include
#include
-#ifdef FDT
-#include
-#include
-#include
-#endif
-
#include
#include
#include
-#ifdef notyet
-#define MAX_RLEN 8
-#else
-#define MAX_RLEN 1
-#endif
-
-struct pmu_softc {
- struct resource *res[MAX_RLEN];
- device_t dev;
- void *ih[MAX_RLEN];
-};
-
-static struct resource_spec pmu_spec[] = {
- { SYS_RES_IRQ, 0, RF_ACTIVE },
- /* We don't currently handle pmu events, other than on cpu 0 */
-#ifdef notyet
- { SYS_RES_IRQ, 1, RF_ACTIVE | RF_OPTIONAL },
- { SYS_RES_IRQ, 2, RF_ACTIVE | RF_OPTIONAL },
- { SYS_RES_IRQ, 3, RF_ACTIVE | RF_OPTIONAL },
- { SYS_RES_IRQ, 4, RF_ACTIVE | RF_OPTIONAL },
- { SYS_RES_IRQ, 5, RF_ACTIVE | RF_OPTIONAL },
- { SYS_RES_IRQ, 6, RF_ACTIVE | RF_OPTIONAL },
- { SYS_RES_IRQ, 7, RF_ACTIVE | RF_OPTIONAL },
-#endif
- { -1, 0 }
-};
+#include "pmu.h"
/* CCNT */
#if __ARM_ARCH > 6
@@ -96,7 +65,7 @@
#define PMU_OVSR_C 0x80000000 /* Cycle Counter */
#define PMU_IESR_C 0x80000000 /* Cycle Counter */
-static int
+int
pmu_intr(void *arg)
{
#ifdef HWPMC_HOOKS
@@ -130,36 +99,12 @@
return (FILTER_HANDLED);
}
-static int
+int
pmu_attach(device_t dev)
{
- struct pmu_softc *sc;
#if defined(__arm__) && (__ARM_ARCH > 6)
uint32_t iesr;
#endif
- int err;
- int i;
-
- sc = device_get_softc(dev);
- sc->dev = dev;
-
- if (bus_alloc_resources(dev, pmu_spec, sc->res)) {
- device_printf(dev, "could not allocate resources\n");
- return (ENXIO);
- }
-
- /* Setup interrupt handler */
- for (i = 0; i < MAX_RLEN; i++) {
- if (sc->res[i] == NULL)
- break;
-
- err = bus_setup_intr(dev, sc->res[i], INTR_MPSAFE | INTR_TYPE_MISC,
- pmu_intr, NULL, NULL, &sc->ih[i]);
- if (err) {
- device_printf(dev, "Unable to setup interrupt handler.\n");
- return (ENXIO);
- }
- }
#if defined(__arm__) && (__ARM_ARCH > 6)
/* Initialize to 0. */
@@ -177,52 +122,3 @@
return (0);
}
-
-#ifdef FDT
-static struct ofw_compat_data compat_data[] = {
- {"arm,armv8-pmuv3", 1},
- {"arm,cortex-a17-pmu", 1},
- {"arm,cortex-a15-pmu", 1},
- {"arm,cortex-a12-pmu", 1},
- {"arm,cortex-a9-pmu", 1},
- {"arm,cortex-a8-pmu", 1},
- {"arm,cortex-a7-pmu", 1},
- {"arm,cortex-a5-pmu", 1},
- {"arm,arm11mpcore-pmu", 1},
- {"arm,arm1176-pmu", 1},
- {"arm,arm1136-pmu", 1},
- {"qcom,krait-pmu", 1},
- {NULL, 0}
-};
-
-static int
-pmu_fdt_probe(device_t dev)
-{
-
- if (!ofw_bus_status_okay(dev))
- return (ENXIO);
-
- if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
- device_set_desc(dev, "Performance Monitoring Unit");
- return (BUS_PROBE_DEFAULT);
- }
-
- return (ENXIO);
-}
-
-static device_method_t pmu_fdt_methods[] = {
- DEVMETHOD(device_probe, pmu_fdt_probe),
- DEVMETHOD(device_attach, pmu_attach),
- { 0, 0 }
-};
-
-static driver_t pmu_fdt_driver = {
- "pmu",
- pmu_fdt_methods,
- sizeof(struct pmu_softc),
-};
-
-static devclass_t pmu_fdt_devclass;
-
-DRIVER_MODULE(pmu, simplebus, pmu_fdt_driver, pmu_fdt_devclass, 0, 0);
-#endif
Index: sys/arm/arm/pmu_acpi.c
===================================================================
--- /dev/null
+++ sys/arm/arm/pmu_acpi.c
@@ -0,0 +1,208 @@
+/*-
+ * Copyright (c) 2020 Greg V
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include
+__FBSDID("$FreeBSD$");
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+
+#include "acpi_bus_if.h"
+#include "pmu.h"
+
+struct madt_ctx {
+ device_t parent;
+ device_t dev;
+
+ int32_t cpuid;
+ bool is_per_core;
+};
+
+static void
+madt_handler(ACPI_SUBTABLE_HEADER *entry, void *arg)
+{
+ ACPI_MADT_GENERIC_INTERRUPT *intr;
+ struct madt_ctx *ctx = arg;
+ struct acpi_device *ad;
+ rman_res_t irq;
+
+ if (entry->Type != ACPI_MADT_TYPE_GENERIC_INTERRUPT)
+ return;
+
+ intr = (ACPI_MADT_GENERIC_INTERRUPT *)entry;
+
+ if (bootverbose)
+ device_printf(ctx->dev, "MADT: cpu/rid %d irq %d %s-triggered\n",
+ ctx->cpuid,
+ intr->PerformanceInterrupt,
+ (intr->Flags & ACPI_MADT_PERFORMANCE_IRQ_MODE) ? "edge" : "level");
+
+ if (ctx->cpuid > MAX_RLEN)
+ return;
+
+ /*
+ * BUS_CONFIG_INTR does nothing on arm64, so we manually register an IRQ
+ * resource with correct trigger mode instead of doing BUS_SET_RESOURCE.
+ */
+ irq = (rman_res_t)ACPI_BUS_MAP_INTR(ctx->parent, ctx->dev,
+ intr->PerformanceInterrupt,
+ (intr->Flags & ACPI_MADT_PERFORMANCE_IRQ_MODE) ?
+ INTR_TRIGGER_EDGE : INTR_TRIGGER_LEVEL,
+ INTR_POLARITY_HIGH);
+ ad = device_get_ivars(ctx->dev);
+ resource_list_add(&ad->ad_rl, SYS_RES_IRQ,
+ ctx->cpuid++, irq, irq, 1);
+
+ /*
+ * We have to bind the interrupts to CPUs if the PMU uses multiple
+ * per-core SPIs, but not if it uses a single PPI.
+ *
+ * AArch64 ACPI systems always use the GIC, so this check is correct.
+ */
+ if (intr->PerformanceInterrupt >= GIC_FIRST_SPI)
+ ctx->is_per_core = true;
+}
+
+static void
+pmu_acpi_identify(driver_t *driver, device_t parent)
+{
+ ACPI_TABLE_MADT *madt;
+ vm_paddr_t physaddr;
+ device_t dev;
+ struct madt_ctx ctx;
+
+ physaddr = acpi_find_table(ACPI_SIG_MADT);
+ if (physaddr == 0)
+ return;
+
+ madt = acpi_map_table(physaddr, ACPI_SIG_MADT);
+ if (madt == NULL) {
+ device_printf(parent, "pmu: Unable to map the MADT table\n");
+ return;
+ }
+
+ dev = BUS_ADD_CHILD(parent, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST,
+ "pmu", -1);
+ if (dev == NULL) {
+ device_printf(parent, "pmu: Unable to add pmu child\n");
+ goto out;
+ }
+
+ /*
+ * CPUs are attached in MADT order (sys/arm64/arm64/mp_machdep.c),
+ * with cpuid incremented for each ACPI_MADT_GENERIC_INTERRUPT, so
+ * we are getting the right cpuids by walking the same way.
+ */
+ ctx.parent = parent;
+ ctx.dev = dev;
+ ctx.cpuid = 0;
+ ctx.is_per_core = false;
+ acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
+ madt_handler, &ctx);
+ acpi_set_private(dev, (void *)ctx.is_per_core);
+
+out:
+ acpi_unmap_table(madt);
+}
+
+static int
+pmu_acpi_probe(device_t dev)
+{
+ struct pmu_softc *sc;
+
+ device_set_desc(dev, "Performance Monitoring Unit");
+ sc = device_get_softc(dev);
+ sc->intr_per_core = (bool)acpi_get_private(dev);
+
+ return (BUS_PROBE_NOWILDCARD);
+}
+
+static int
+pmu_acpi_attach(device_t dev)
+{
+ struct pmu_softc *sc;
+ int err;
+ int i;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, pmu_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ device_printf(dev, sc->intr_per_core
+ ? "using per-core SPIs\n" : "using a single PPI\n");
+
+ /* Setup interrupt handler */
+ for (i = 0; i < MAX_RLEN; i++) {
+ if (sc->res[i] == NULL)
+ break;
+
+ err = bus_setup_intr(dev, sc->res[i], INTR_MPSAFE | INTR_TYPE_MISC,
+ pmu_intr, NULL, NULL, &sc->ih[i]);
+ if (err) {
+ device_printf(dev, "Unable to setup interrupt handler.\n");
+ return (ENXIO);
+ }
+
+ if (sc->intr_per_core) {
+ /* Above, we set resource IDs to match CPUs by walking MADT in order */
+ err = bus_bind_intr(dev, sc->res[i], i);
+ if (err) {
+ device_printf(dev, "Unable to bind interrupt handler to CPU.\n");
+ return (ENXIO);
+ }
+ }
+ }
+
+ return (pmu_attach(dev));
+}
+
+static device_method_t pmu_acpi_methods[] = {
+ DEVMETHOD(device_identify, pmu_acpi_identify),
+ DEVMETHOD(device_probe, pmu_acpi_probe),
+ DEVMETHOD(device_attach, pmu_acpi_attach),
+ DEVMETHOD_END,
+};
+
+static driver_t pmu_acpi_driver = {
+ "pmu",
+ pmu_acpi_methods,
+ sizeof(struct pmu_softc),
+};
+
+static devclass_t pmu_acpi_devclass;
+
+DRIVER_MODULE(pmu, acpi, pmu_acpi_driver, pmu_acpi_devclass, 0, 0);
Index: sys/arm/arm/pmu_fdt.c
===================================================================
--- /dev/null
+++ sys/arm/arm/pmu_fdt.c
@@ -0,0 +1,125 @@
+/*-
+ * Copyright (c) 2015 Ruslan Bukin
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include
+__FBSDID("$FreeBSD$");
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include "pmu.h"
+
+static struct ofw_compat_data compat_data[] = {
+ {"arm,armv8-pmuv3", 1},
+ {"arm,cortex-a73-pmu", 1},
+ {"arm,cortex-a72-pmu", 1},
+ {"arm,cortex-a57-pmu", 1},
+ {"arm,cortex-a53-pmu", 1},
+ {"arm,cortex-a17-pmu", 1},
+ {"arm,cortex-a15-pmu", 1},
+ {"arm,cortex-a12-pmu", 1},
+ {"arm,cortex-a9-pmu", 1},
+ {"arm,cortex-a8-pmu", 1},
+ {"arm,cortex-a7-pmu", 1},
+ {"arm,cortex-a5-pmu", 1},
+ {"arm,arm11mpcore-pmu", 1},
+ {"arm,arm1176-pmu", 1},
+ {"arm,arm1136-pmu", 1},
+ {"qcom,krait-pmu", 1},
+ {NULL, 0}
+};
+
+static int
+pmu_fdt_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
+ device_set_desc(dev, "Performance Monitoring Unit");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static int
+pmu_fdt_attach(device_t dev)
+{
+ struct pmu_softc *sc;
+ int err;
+ int i;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, pmu_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Setup interrupt handler */
+ for (i = 0; i < MAX_RLEN; i++) {
+ if (sc->res[i] == NULL)
+ break;
+
+ err = bus_setup_intr(dev, sc->res[i], INTR_MPSAFE | INTR_TYPE_MISC,
+ pmu_intr, NULL, NULL, &sc->ih[i]);
+ if (err) {
+ device_printf(dev, "Unable to setup interrupt handler.\n");
+ return (ENXIO);
+ }
+ }
+
+ return (pmu_attach(dev));
+}
+
+static device_method_t pmu_fdt_methods[] = {
+ DEVMETHOD(device_probe, pmu_fdt_probe),
+ DEVMETHOD(device_attach, pmu_fdt_attach),
+ { 0, 0 }
+};
+
+static driver_t pmu_fdt_driver = {
+ "pmu",
+ pmu_fdt_methods,
+ sizeof(struct pmu_softc),
+};
+
+static devclass_t pmu_fdt_devclass;
+
+DRIVER_MODULE(pmu, simplebus, pmu_fdt_driver, pmu_fdt_devclass, 0, 0);
Index: sys/conf/files.arm64
===================================================================
--- sys/conf/files.arm64
+++ sys/conf/files.arm64
@@ -85,6 +85,8 @@
arm/arm/gic_acpi.c optional acpi
arm/arm/gic_fdt.c optional fdt
arm/arm/pmu.c standard
+arm/arm/pmu_acpi.c optional acpi
+arm/arm/pmu_fdt.c optional fdt
arm/broadcom/bcm2835/bcm2835_audio.c optional sound vchiq fdt \
compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq"
arm/broadcom/bcm2835/bcm2835_bsc.c optional bcm2835_bsc fdt