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 + * Copyright (c) 2022 Kyle Evans + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef SMP +#include +#include +#endif + +#include + +#include +#include +#include + +#include + +#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