Page MenuHomeFreeBSD

D48079.diff
No OneTemporary

D48079.diff

diff --git a/sys/arm64/apple/apple_aic.c b/sys/arm64/apple/apple_aic.c
new file mode 100644
--- /dev/null
+++ b/sys/arm64/apple/apple_aic.c
@@ -0,0 +1,779 @@
+/*-
+ * Copyright (c) 2021 Andrew Turner
+ * Copyright (c) 2022 Michael J. Karels <karels@freebsd.org>
+ * Copyright (c) 2022 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * 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 "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <sys/smp.h>
+
+#include <machine/bus.h>
+#include <machine/machdep.h>
+#ifdef SMP
+#include <machine/intr.h>
+#include <machine/smp.h>
+#endif
+
+#include <dev/fdt/fdt_intr.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dt-bindings/interrupt-controller/apple-aic.h>
+
+#include "pic_if.h"
+
+#define AIC_INFO 0x0004
+#define AIC_INFO_NDIE(val) (((val) >> 24) & 0xf)
+#define AIC_INFO_NIRQS(val) ((val) & 0x0000ffff)
+
+#define AIC_WHOAMI 0x2000
+#define AIC_EVENT 0x2004
+#define AIC_EVENT_DIE(val) (((val) >> 24) & 0xff)
+#define AIC_EVENT_TYPE(val) (((val) >> 16) & 0xff)
+#define AIC_EVENT_TYPE_NONE 0
+#define AIC_EVENT_TYPE_IRQ 1
+#define AIC_EVENT_TYPE_IPI 4
+#define AIC_EVENT_IRQ(val) ((val) & 0xffff)
+#define AIC_EVENT_IPI_OTHER 1
+#define AIC_EVENT_IPI_SELF 2
+#define AIC_IPI_SEND 0x2008
+#define AIC_IPI_ACK 0x200c
+#define AIC_IPI_MASK_SET 0x2024
+#define AIC_IPI_MASK_CLR 0x2028
+#define AIC_IPI_OTHER 0x00000001
+#define AIC_IPI_SELF 0x80000000
+#define AIC_TARGET_CPU(irq) (0x3000 + (irq) * 4)
+#define AIC_SW_SET(irq) (0x4000 + (((irq) >> 5) * 4))
+#define AIC_SW_CLEAR(irq) (0x4080 + (((irq) >> 5) * 4))
+#define AIC_MASK_SET(irq) (0x4100 + (((irq) >> 5) * 4))
+#define AIC_MASK_CLEAR(irq) (0x4180 + (((irq) >> 5) * 4))
+#define AIC_IRQ_MASK(irq) (1u << ((irq) & 0x1f))
+
+#define AIC_IPI_LOCAL_RR_EL1 s3_5_c15_c0_0
+#define AIC_IPI_GLOBAL_RR_EL1 s3_5_c15_c0_1
+
+#define AIC_IPI_SR_EL1 s3_5_c15_c1_1
+#define AIC_IPI_SR_EL1_PENDING (1 << 0)
+
+#define AIC_FIQ_VM_TIMER s3_5_c15_c1_3
+#define AIC_FIQ_VM_TIMER_VEN (1 << 0)
+#define AIC_FIQ_VM_TIMER_PEN (1 << 1)
+#define AIC_FIQ_VM_TIMER_BITS (AIC_FIQ_VM_TIMER_VEN | AIC_FIQ_VM_TIMER_PEN)
+
+#define CNTV_CTL_ENABLE (1 << 0)
+#define CNTV_CTL_IMASK (1 << 1)
+#define CNTV_CTL_ISTATUS (1 << 2)
+#define CNTV_CTL_BITS \
+ (CNTV_CTL_ENABLE | CNTV_CTL_IMASK | CNTV_CTL_ISTATUS)
+
+#define AIC_MAXCPUS 32
+#define AIC_MAXDIES 4
+
+static struct ofw_compat_data compat_data[] = {
+ { "apple,aic", 1 },
+ { NULL, 0 }
+};
+
+enum apple_aic_irq_type {
+ AIC_TYPE_INVAL,
+ AIC_TYPE_IRQ,
+ AIC_TYPE_FIQ,
+ AIC_TYPE_IPI,
+};
+
+struct apple_aic_irqsrc {
+ struct intr_irqsrc ai_isrc;
+ enum apple_aic_irq_type ai_type;
+ struct {
+ /* AIC_TYPE_IRQ */
+ enum intr_polarity ai_pol;
+ enum intr_trigger ai_trig;
+ u_int ai_irq;
+ };
+};
+
+#ifdef SMP
+#define AIC_NIPIS INTR_IPI_COUNT
+#endif
+
+struct apple_aic_softc {
+ device_t sc_dev;
+ struct resource *sc_mem;
+ struct apple_aic_irqsrc *sc_isrcs[AIC_MAXDIES];
+ u_int sc_nirqs;
+ u_int sc_ndie;
+#ifdef SMP
+ struct apple_aic_irqsrc sc_ipi_srcs[AIC_NIPIS];
+ u_int *sc_cpuids; /* cpu index to AIC CPU ID */
+ uint32_t *sc_ipimasks;
+#endif
+};
+
+static u_int aic_next_cpu;
+
+static device_probe_t apple_aic_probe;
+static device_attach_t apple_aic_attach;
+
+static pic_disable_intr_t apple_aic_disable_intr;
+static pic_enable_intr_t apple_aic_enable_intr;
+static pic_map_intr_t apple_aic_map_intr;
+static pic_setup_intr_t apple_aic_setup_intr;
+static pic_teardown_intr_t apple_aic_teardown_intr;
+static pic_post_filter_t apple_aic_post_filter;
+static pic_post_ithread_t apple_aic_post_ithread;
+static pic_pre_ithread_t apple_aic_pre_ithread;
+#ifdef SMP
+static pic_bind_intr_t apple_aic_bind_intr;
+static pic_init_secondary_t apple_aic_init_secondary;
+static pic_ipi_send_t apple_aic_ipi_send;
+static pic_ipi_setup_t apple_aic_ipi_setup;
+#endif
+
+static int apple_aic_irq(void *);
+static int apple_aic_fiq(void *);
+
+static int
+apple_aic_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Apple Interrupt Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+apple_aic_attach(device_t dev)
+{
+ struct apple_aic_softc *sc;
+ struct intr_irqsrc *isrc;
+ const char *name;
+ intptr_t xref;
+ int error, rid;
+ u_int i, cpu, j, info;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ rid = 0;
+ sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->sc_mem == NULL) {
+ device_printf(dev, "Unable to allocate memory\n");
+ return (ENXIO);
+ }
+
+ info = bus_read_4(sc->sc_mem, AIC_INFO);
+ sc->sc_nirqs = AIC_INFO_NIRQS(info);
+ sc->sc_ndie = AIC_INFO_NDIE(info) + 1;
+ if (bootverbose)
+ device_printf(dev, "Found %d interrupts, %d die\n",
+ sc->sc_nirqs, sc->sc_ndie);
+
+ for (i = 0; i < sc->sc_ndie; i++) {
+ sc->sc_isrcs[i] = mallocarray(sc->sc_nirqs,
+ sizeof(**sc->sc_isrcs), M_DEVBUF, M_WAITOK | M_ZERO);
+ }
+
+#ifdef SMP
+ sc->sc_ipimasks = malloc(sizeof(*sc->sc_ipimasks) * mp_maxid + 1,
+ M_DEVBUF, M_WAITOK | M_ZERO);
+ sc->sc_cpuids = malloc(sizeof(*sc->sc_cpuids) * mp_maxid + 1,
+ M_DEVBUF, M_WAITOK | M_ZERO);
+
+ cpu = PCPU_GET(cpuid);
+ sc->sc_cpuids[cpu] = bus_read_4(sc->sc_mem, AIC_WHOAMI);
+ if (bootverbose)
+ device_printf(dev, "BSP CPU %d: whoami %x\n", cpu,
+ sc->sc_cpuids[cpu]);
+#endif
+
+
+ name = device_get_nameunit(dev);
+ for (i = 0; i < sc->sc_ndie; i++) {
+ struct apple_aic_irqsrc *die_isrcs;
+
+ die_isrcs = sc->sc_isrcs[i];
+ for (j = 0; j < sc->sc_nirqs; j++) {
+ isrc = &die_isrcs[j].ai_isrc;
+ die_isrcs[j].ai_pol = INTR_POLARITY_CONFORM;
+ die_isrcs[j].ai_trig = INTR_TRIGGER_CONFORM;
+ die_isrcs[j].ai_type = AIC_TYPE_INVAL;
+ die_isrcs[j].ai_irq = j;
+
+ error = intr_isrc_register(isrc, dev, 0, "%s,d%us%u", name,
+ i, j);
+ if (error != 0) {
+ device_printf(dev, "Unable to register irq %u:%u\n",
+ i, j);
+ return (error);
+ }
+ }
+ }
+
+ xref = OF_xref_from_node(ofw_bus_get_node(dev));
+ if (intr_pic_register(dev, xref) == NULL) {
+ device_printf(dev, "Unable to register interrupt handler\n");
+ return (ENXIO);
+ }
+
+ if (intr_pic_claim_root(dev, xref, apple_aic_irq, sc,
+ INTR_ROOT_IRQ) != 0) {
+ device_printf(dev,
+ "Unable to set root interrupt controller\n");
+ intr_pic_deregister(dev, xref);
+ return (ENXIO);
+ }
+
+ if (intr_pic_claim_root(dev, xref, apple_aic_fiq, sc,
+ INTR_ROOT_FIQ) != 0) {
+ device_printf(dev,
+ "Unable to set root fiq controller\n");
+ intr_pic_deregister(dev, xref);
+ return (ENXIO);
+ }
+
+#ifdef SMP
+ if (intr_ipi_pic_register(dev, 0) != 0) {
+ device_printf(dev, "could not register for IPIs\n");
+ return (ENXIO);
+ }
+#endif
+
+ OF_device_register_xref(xref, dev);
+
+ return (0);
+}
+
+static int
+apple_aic_map_intr_fdt(struct apple_aic_softc *sc,
+ struct intr_map_data_fdt *data, u_int *irq, enum apple_aic_irq_type *typep,
+ enum intr_polarity *polp, enum intr_trigger *trigp, u_int *die)
+{
+ if (data->ncells != 3)
+ return (EINVAL);
+
+ /* XXX AIC2 */
+ *die = 0;
+
+ /*
+ * The first cell is the interrupt type:
+ * 0 = IRQ
+ * 1 = FIQ
+ * The second cell is the interrupt number
+ * The third cell is the flags
+ */
+ switch(data->cells[0]) {
+ case 0:
+ if (typep != NULL)
+ *typep = AIC_TYPE_IRQ;
+ break;
+ case 1:
+ if (typep != NULL)
+ *typep = AIC_TYPE_FIQ;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ *irq = data->cells[1];
+ if (*irq > sc->sc_nirqs)
+ return (EINVAL);
+
+ if (trigp != NULL) {
+ if ((data->cells[2] & FDT_INTR_EDGE_MASK) != 0)
+ *trigp = INTR_TRIGGER_EDGE;
+ else
+ *trigp = INTR_TRIGGER_LEVEL;
+ }
+ if (polp != NULL) {
+ if ((data->cells[2] & FDT_INTR_LEVEL_HIGH) != 0)
+ *polp = INTR_POLARITY_HIGH;
+ else
+ *polp = INTR_POLARITY_LOW;
+ }
+
+ return (0);
+}
+
+static int
+apple_aic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ struct apple_aic_softc *sc;
+ int error;
+ u_int irq;
+ u_int die;
+
+ sc = device_get_softc(dev);
+
+ error = 0;
+ switch(data->type) {
+ case INTR_MAP_DATA_FDT:
+ error = apple_aic_map_intr_fdt(sc,
+ (struct intr_map_data_fdt *)data, &irq, NULL, NULL, NULL,
+ &die);
+ if (error == 0)
+ *isrcp = &sc->sc_isrcs[0 /* XXX */][irq].ai_isrc;
+ break;
+ default:
+ return (ENOTSUP);
+ }
+
+ return (error);
+}
+
+static int
+apple_aic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct apple_aic_softc *sc;
+ enum apple_aic_irq_type type;
+ struct apple_aic_irqsrc *ai;
+ enum intr_trigger trig;
+ enum intr_polarity pol;
+ int error;
+ u_int die, irq;
+
+ sc = device_get_softc(dev);
+ ai = (struct apple_aic_irqsrc *)isrc;
+
+ if (data != NULL) {
+ KASSERT(data->type == INTR_MAP_DATA_FDT,
+ ("%s: Only FDT data is supported (got %#x)", __func__,
+ data->type));
+ error = apple_aic_map_intr_fdt(sc,
+ (struct intr_map_data_fdt *)data, &irq, &type, &pol, &trig,
+ &die);
+ if (error != 0)
+ return (error);
+ } else {
+ pol = INTR_POLARITY_CONFORM;
+ trig = INTR_TRIGGER_CONFORM;
+ }
+
+ if (isrc->isrc_handlers != 0) {
+ /* TODO */
+ return (0);
+ }
+
+ if (pol == INTR_POLARITY_CONFORM)
+ pol = INTR_POLARITY_LOW;
+ if (trig == INTR_TRIGGER_CONFORM)
+ trig = INTR_TRIGGER_EDGE;
+
+ ai->ai_pol = pol;
+ ai->ai_trig = trig;
+ ai->ai_type = type;
+
+ /*
+ * Only the timer uses FIQs. These could be sent to any CPU.
+ */
+ switch (type) {
+ case AIC_TYPE_IRQ:
+ /* XXX die sensitive? */
+ aic_next_cpu = intr_irq_next_cpu(aic_next_cpu, &all_cpus);
+ bus_write_4(sc->sc_mem, AIC_TARGET_CPU(irq),
+ 1 << sc->sc_cpuids[aic_next_cpu]);
+ break;
+ case AIC_TYPE_FIQ:
+ isrc->isrc_flags |= INTR_ISRCF_PPI;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+static int
+apple_aic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ panic("%s\n", __func__);
+}
+
+static void
+apple_aic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct apple_aic_irqsrc *ai;
+ struct apple_aic_softc *sc;
+ u_int irq;
+
+ ai = (struct apple_aic_irqsrc *)isrc;
+ irq = ai->ai_irq;
+ switch(ai->ai_type) {
+ case AIC_TYPE_IRQ:
+ sc = device_get_softc(dev);
+ bus_write_4(sc->sc_mem, AIC_MASK_CLEAR(irq), AIC_IRQ_MASK(irq));
+ break;
+ case AIC_TYPE_IPI:
+ /* Nothing needed here. */
+ break;
+ case AIC_TYPE_FIQ:
+ /* TODO */
+ break;
+ default:
+ panic("%s: %x\n", __func__, ai->ai_type);
+ }
+}
+
+static void
+apple_aic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct apple_aic_irqsrc *ai;
+ struct apple_aic_softc *sc;
+ u_int irq;
+
+ ai = (struct apple_aic_irqsrc *)isrc;
+ irq = ai->ai_irq;
+ switch(ai->ai_type) {
+ case AIC_TYPE_IRQ:
+ sc = device_get_softc(dev);
+ bus_write_4(sc->sc_mem, AIC_MASK_SET(irq), AIC_IRQ_MASK(irq));
+ break;
+ case AIC_TYPE_IPI:
+ /* Nothing needed here. */
+ break;
+ case AIC_TYPE_FIQ:
+ /* TODO */
+ break;
+ default:
+ panic("%s: %x\n", __func__, ai->ai_type);
+ }
+}
+
+static void
+apple_aic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct apple_aic_softc *sc;
+ struct apple_aic_irqsrc *ai;
+ int irq;
+
+ ai = (struct apple_aic_irqsrc *)isrc;
+ irq = ai->ai_irq;
+ switch(ai->ai_type) {
+ case AIC_TYPE_IRQ:
+ sc = device_get_softc(dev);
+ bus_write_4(sc->sc_mem, AIC_SW_CLEAR(irq), AIC_IRQ_MASK(irq));
+ bus_write_4(sc->sc_mem, AIC_MASK_CLEAR(irq), AIC_IRQ_MASK(irq));
+ break;
+ case AIC_TYPE_FIQ:
+ /* TODO */
+ break;
+ default:
+ panic("%s: %x\n", __func__, ai->ai_type);
+ }
+}
+
+static void
+apple_aic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct apple_aic_softc *sc;
+ struct apple_aic_irqsrc *ai;
+ int irq;
+
+ ai = (struct apple_aic_irqsrc *)isrc;
+ sc = device_get_softc(dev);
+ irq = ai->ai_irq;
+ bus_write_4(sc->sc_mem, AIC_SW_CLEAR(irq), AIC_IRQ_MASK(irq));
+ apple_aic_disable_intr(dev, isrc);
+ /* ACK IT */
+}
+
+static void
+apple_aic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct apple_aic_softc *sc;
+ struct apple_aic_irqsrc *ai;
+ int irq;
+
+ ai = (struct apple_aic_irqsrc *)isrc;
+ sc = device_get_softc(dev);
+ irq = ai->ai_irq;
+
+ bus_write_4(sc->sc_mem, AIC_MASK_CLEAR(irq), AIC_IRQ_MASK(irq));
+ apple_aic_enable_intr(dev, isrc);
+}
+
+#ifdef SMP
+static void
+apple_aic_ipi_received(struct apple_aic_softc *sc, struct trapframe *tf)
+{
+ uint32_t mask;
+ uint32_t ipi;
+ int cpu;
+
+ cpu = PCPU_GET(cpuid);
+
+ mask = atomic_readandclear_32(&sc->sc_ipimasks[cpu]);
+
+ while (mask != 0) {
+ ipi = ffs(mask) - 1;
+ mask &= ~(1 << ipi);
+
+ intr_ipi_dispatch(ipi);
+ }
+}
+#endif
+
+static int
+apple_aic_irq(void *arg)
+{
+ struct apple_aic_softc *sc;
+ uint32_t die, event, irq, type;
+ struct apple_aic_irqsrc *aisrc;
+ struct trapframe *tf;
+
+ sc = arg;
+ tf = curthread->td_intr_frame;
+
+ event = bus_read_4(sc->sc_mem, AIC_EVENT);
+ type = AIC_EVENT_TYPE(event);
+
+ /* If we get an IPI here, we really goofed. */
+ MPASS(type != AIC_EVENT_TYPE_IPI);
+
+ if (type != AIC_EVENT_TYPE_IRQ) {
+ if (type != AIC_EVENT_TYPE_NONE)
+ device_printf(sc->sc_dev, "unexpected event type %d\n",
+ type);
+ return (FILTER_STRAY);
+ }
+
+ die = AIC_EVENT_DIE(event);
+ irq = AIC_EVENT_IRQ(event);
+
+ if (die >= sc->sc_ndie)
+ panic("%s: unexpected die %d", __func__, die);
+ if (irq >= sc->sc_nirqs)
+ panic("%s: unexpected irq %d", __func__, irq);
+
+ aisrc = &sc->sc_isrcs[die][irq];
+ if (intr_isrc_dispatch(&aisrc->ai_isrc, tf) != 0) {
+ device_printf(sc->sc_dev, "Stray irq %u:%u disabled\n",
+ die, irq);
+ return (FILTER_STRAY);
+ }
+
+ return (FILTER_HANDLED);
+}
+
+static int
+apple_aic_fiq(void *arg)
+{
+ struct apple_aic_softc *sc;
+ struct apple_aic_irqsrc *isrcs;
+ struct trapframe *tf;
+
+ sc = arg;
+ tf = curthread->td_intr_frame;
+
+#ifdef SMP
+ /* Handle IPIs. */
+ if ((READ_SPECIALREG(AIC_IPI_SR_EL1) & AIC_IPI_SR_EL1_PENDING) != 0) {
+ WRITE_SPECIALREG(AIC_IPI_SR_EL1, AIC_IPI_SR_EL1_PENDING);
+ apple_aic_ipi_received(sc, tf);
+ }
+#endif
+
+ /*
+ * FIQs don't store any state in the interrupt controller at all outside
+ * of IPI handling, so we have to probe around outside of AIC to
+ * determine if we might have been fired off due to a timer.
+ */
+ isrcs = sc->sc_isrcs[0];
+ if ((READ_SPECIALREG(cntv_ctl_el0) & CNTV_CTL_BITS) ==
+ (CNTV_CTL_ENABLE | CNTV_CTL_ISTATUS)) {
+ intr_isrc_dispatch(&isrcs[AIC_TMR_GUEST_VIRT].ai_isrc, tf);
+ }
+
+ if (has_hyp()) {
+ uint64_t reg;
+
+ if ((READ_SPECIALREG(cntp_ctl_el0) & CNTV_CTL_ISTATUS) != 0) {
+ intr_isrc_dispatch(&isrcs[AIC_TMR_GUEST_PHYS].ai_isrc,
+ tf);
+ }
+
+ reg = READ_SPECIALREG(AIC_FIQ_VM_TIMER);
+ if ((reg & AIC_FIQ_VM_TIMER_PEN) != 0) {
+ intr_isrc_dispatch(&isrcs[AIC_TMR_HV_PHYS].ai_isrc, tf);
+ }
+
+ if ((reg & AIC_FIQ_VM_TIMER_VEN) != 0) {
+ intr_isrc_dispatch(&isrcs[AIC_TMR_HV_VIRT].ai_isrc, tf);
+ }
+ }
+
+ return (FILTER_HANDLED);
+}
+
+#ifdef SMP
+static int
+apple_aic_bind_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct apple_aic_softc *sc = device_get_softc(dev);
+ static int aic_next_cpu;
+ uint32_t targets = 0;
+ u_int irq, cpu;
+
+ MPASS(((struct apple_aic_irqsrc *)isrc)->ai_type == AIC_TYPE_IRQ);
+ irq = ((struct apple_aic_irqsrc *)isrc)->ai_irq;
+ if (CPU_EMPTY(&isrc->isrc_cpu)) {
+ aic_next_cpu = intr_irq_next_cpu(aic_next_cpu, &all_cpus);
+ CPU_SETOF(aic_next_cpu, &isrc->isrc_cpu);
+ bus_write_4(sc->sc_mem, AIC_TARGET_CPU(irq),
+ sc->sc_cpuids[aic_next_cpu] << 1);
+ } else {
+ CPU_FOREACH_ISSET(cpu, &isrc->isrc_cpu) {
+ targets |= sc->sc_cpuids[cpu] << 1;
+ }
+ bus_write_4(sc->sc_mem, AIC_TARGET_CPU(irq), targets);
+ }
+ return (0);
+}
+
+static void
+apple_aic_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus,
+ u_int ipi)
+{
+ struct apple_aic_softc *sc;
+ uint64_t aff, localgrp, sendmask;
+ u_int cpu;
+
+ sc = device_get_softc(dev);
+ sendmask = 0;
+ localgrp = CPU_AFF1(CPU_AFFINITY(PCPU_GET(cpuid)));
+
+ KASSERT(isrc == &sc->sc_ipi_srcs[ipi].ai_isrc,
+ ("%s: bad ISRC %p argument", __func__, isrc));
+ for (cpu = 0; cpu <= mp_maxid; cpu++) {
+ if (CPU_ISSET(cpu, &cpus)) {
+ aff = CPU_AFFINITY(cpu);
+ sendmask = CPU_AFF0(aff);
+ atomic_set_32(&sc->sc_ipimasks[cpu], 1 << ipi);
+
+ /*
+ * The above write to sc_ipimasks needs to be visible
+ * before we write to the ipi register to avoid the
+ * targetted CPU missing the dispatch in
+ * apple_aic_ipi_received(). Note that WRITE_SPECIALREG
+ * isn't a memory operation, so we can't relax this to a
+ * a dmb.
+ */
+ dsb(ishst);
+
+ if (CPU_AFF1(aff) == localgrp) {
+ WRITE_SPECIALREG(AIC_IPI_LOCAL_RR_EL1,
+ sendmask);
+ } else {
+ sendmask |= CPU_AFF1(aff) << 16;
+ WRITE_SPECIALREG(AIC_IPI_GLOBAL_RR_EL1,
+ sendmask);
+ }
+
+ isb();
+ }
+ }
+}
+
+static int
+apple_aic_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc **isrcp)
+{
+ struct apple_aic_softc *sc = device_get_softc(dev);
+ struct apple_aic_irqsrc *ai;
+
+ KASSERT(ipi < AIC_NIPIS, ("%s: ipi %u too high", __func__, ipi));
+
+ ai = &sc->sc_ipi_srcs[ipi];
+ ai->ai_type = AIC_TYPE_IPI;
+
+ *isrcp = &ai->ai_isrc;
+ return (0);
+}
+
+static void
+apple_aic_init_secondary(device_t dev, uint32_t rootnum)
+{
+ struct apple_aic_softc *sc = device_get_softc(dev);
+ u_int cpu = PCPU_GET(cpuid);
+
+ /* We don't need to re-initialize for the FIQ root. */
+ if (rootnum != INTR_ROOT_IRQ)
+ return;
+
+ sc->sc_cpuids[cpu] = bus_read_4(sc->sc_mem, AIC_WHOAMI);
+ if (bootverbose)
+ device_printf(dev, "CPU %d: whoami %x\n", cpu,
+ sc->sc_cpuids[cpu]);
+
+ bus_write_4(sc->sc_mem, AIC_IPI_MASK_SET, AIC_IPI_SELF | AIC_IPI_OTHER);
+}
+#endif
+
+static device_method_t apple_aic_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, apple_aic_probe),
+ DEVMETHOD(device_attach, apple_aic_attach),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_disable_intr, apple_aic_disable_intr),
+ DEVMETHOD(pic_enable_intr, apple_aic_enable_intr),
+ DEVMETHOD(pic_map_intr, apple_aic_map_intr),
+ DEVMETHOD(pic_setup_intr, apple_aic_setup_intr),
+ DEVMETHOD(pic_teardown_intr, apple_aic_teardown_intr),
+ DEVMETHOD(pic_post_filter, apple_aic_post_filter),
+ DEVMETHOD(pic_post_ithread, apple_aic_post_ithread),
+ DEVMETHOD(pic_pre_ithread, apple_aic_pre_ithread),
+#ifdef SMP
+ DEVMETHOD(pic_bind_intr, apple_aic_bind_intr),
+ DEVMETHOD(pic_init_secondary, apple_aic_init_secondary),
+ DEVMETHOD(pic_ipi_send, apple_aic_ipi_send),
+ DEVMETHOD(pic_ipi_setup, apple_aic_ipi_setup),
+#endif
+
+ /* End */
+ DEVMETHOD_END
+};
+
+static DEFINE_CLASS_0(aic, apple_aic_driver, apple_aic_methods,
+ sizeof(struct apple_aic_softc));
+
+EARLY_DRIVER_MODULE(aic, simplebus, apple_aic_driver, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64
--- a/sys/conf/files.arm64
+++ b/sys/conf/files.arm64
@@ -539,6 +539,7 @@
compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${.IMPSRC}"
# Apple
+arm64/apple/apple_aic.c optional soc_apple_t8103 fdt
arm64/apple/apple_wdog.c optional soc_apple_t8103 fdt
# Broadcom

File Metadata

Mime Type
text/plain
Expires
Wed, Jan 28, 12:49 AM (4 h, 16 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28061680
Default Alt Text
D48079.diff (20 KB)

Event Timeline