diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2022-03-01 21:29:11 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2022-03-01 21:29:11 +0000 |
commit | ef558c755da6dc26c56fdb3bea5d891a1da7aaaa (patch) | |
tree | a0e3e02e09e5b6be38303068f2b4c3b294037d8d | |
parent | 896b6fb28ce3d62ef00e55038e492d8d0c38af38 (diff) |
Apple M1 Pro/Max SoCs come with a new version of the interrupt controller.
This version appears to be more scalable (supports more cores, more
interrupts) but is still fairly similar to the previous generation so
it makes sense for them to share a driver. One major difference is that
it seems that the new interrupt controller no longer has the ability to
have external interrupts target specific CPU cores. Instead it delivers
them to whichever core is willing to handle the interrupt. Since this
currently isn't compatible with what OpenBSD wants we disable the
delivery of external interrupts to the secondary cores by setting some
bits in an implementation-defined system register.
This makes OpenBSD run on M1 Pro/Max machines.
ok jsg@
-rw-r--r-- | sys/arch/arm64/dev/aplintc.c | 107 |
1 files changed, 89 insertions, 18 deletions
diff --git a/sys/arch/arm64/dev/aplintc.c b/sys/arch/arm64/dev/aplintc.c index 16cc4ded993..69c7f2a1411 100644 --- a/sys/arch/arm64/dev/aplintc.c +++ b/sys/arch/arm64/dev/aplintc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aplintc.c,v 1.6 2022/02/13 15:54:07 kettenis Exp $ */ +/* $OpenBSD: aplintc.c,v 1.7 2022/03/01 21:29:10 kettenis Exp $ */ /* * Copyright (c) 2021 Mark Kettenis * @@ -31,6 +31,9 @@ #include <ddb/db_output.h> +#define APL_IRQ_CR_EL1 s3_4_c15_c10_4 +#define APL_IRQ_CR_EL1_DISABLE (3 << 0) + #define APL_IPI_LOCAL_RR_EL1 s3_5_c15_c0_1 #define APL_IPI_GLOBAL_RR_EL1 s3_5_c15_c0_1 #define APL_IPI_SR_EL1 s3_5_c15_c1_1 @@ -45,6 +48,7 @@ #define AIC_WHOAMI 0x2000 #define AIC_EVENT 0x2004 #define AIC_EVENT_TYPE(val) ((val) >> 16) +#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) @@ -64,12 +68,24 @@ #define AIC_MASK_CLR(irq) (0x4180 + (((irq) >> 5) << 2)) #define AIC_MASK_BIT(irq) (1U << ((irq) & 0x1f)) +#define AIC2_CONFIG 0x0014 +#define AIC2_CONFIG_ENABLE (1 << 0) +#define AIC2_SW_SET(irq) (0x6000 + (((irq) >> 5) << 2)) +#define AIC2_SW_CLR(irq) (0x6200 + (((irq) >> 5) << 2)) +#define AIC2_MASK_SET(irq) (0x6400 + (((irq) >> 5) << 2)) +#define AIC2_MASK_CLR(irq) (0x6600 + (((irq) >> 5) << 2)) +#define AIC2_EVENT 0xc000 + #define AIC_MAXCPUS 32 #define HREAD4(sc, reg) \ (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) #define HWRITE4(sc, reg, val) \ bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define HSET4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) +#define HCLR4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) struct intrhand { TAILQ_ENTRY(intrhand) ih_list; @@ -88,6 +104,8 @@ struct aplintc_softc { bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; + int sc_version; + struct interrupt_controller sc_ic; struct intrhand *sc_fiq_handler; @@ -135,7 +153,8 @@ aplintc_match(struct device *parent, void *match, void *aux) { struct fdt_attach_args *faa = aux; - return OF_is_compatible(faa->fa_node, "apple,aic"); + return OF_is_compatible(faa->fa_node, "apple,aic") || + OF_is_compatible(faa->fa_node, "apple,aic2"); } void @@ -158,6 +177,11 @@ aplintc_attach(struct device *parent, struct device *self, void *aux) return; } + if (OF_is_compatible(faa->fa_node, "apple,aic2")) + sc->sc_version = 2; + else + sc->sc_version = 1; + info = HREAD4(sc, AIC_INFO); sc->sc_nirq = AIC_INFO_NIRQ(info); sc->sc_irq_handler = mallocarray(sc->sc_nirq, @@ -185,6 +209,9 @@ aplintc_attach(struct device *parent, struct device *self, void *aux) arm_intr_register_fdt(&sc->sc_ic); intr_send_ipi_func = aplintc_send_ipi; + + if (sc->sc_version == 2) + HSET4(sc, AIC2_CONFIG, AIC2_CONFIG_ENABLE); } void @@ -196,9 +223,20 @@ aplintc_cpuinit(void) KASSERT(ci->ci_cpuid < AIC_MAXCPUS); - hwid = HREAD4(sc, AIC_WHOAMI); - KASSERT(hwid < AIC_MAXCPUS); - sc->sc_cpuremap[ci->ci_cpuid] = hwid; + /* + * AIC2 does not provide us with a way to target external + * interrupts to a particular core. Therefore, disable IRQ + * delivery to the secondary CPUs which makes sure all + * external interrupts are delivered to the primary CPU. + */ + if (!CPU_IS_PRIMARY(ci)) + WRITE_SPECIALREG(APL_IRQ_CR_EL1, APL_IRQ_CR_EL1_DISABLE); + + if (sc->sc_version == 1) { + hwid = HREAD4(sc, AIC_WHOAMI); + KASSERT(hwid < AIC_MAXCPUS); + sc->sc_cpuremap[ci->ci_cpuid] = hwid; + } } void @@ -244,12 +282,18 @@ aplintc_irq_handler(void *frame) uint32_t irq, type; int s; - event = HREAD4(sc, AIC_EVENT); + if (sc->sc_version == 1) + event = HREAD4(sc, AIC_EVENT); + else + event = HREAD4(sc, AIC2_EVENT); irq = AIC_EVENT_IRQ(event); type = AIC_EVENT_TYPE(event); if (type != AIC_EVENT_TYPE_IRQ) { - printf("%s: unexpected event type %d\n", __func__, type); + if (type != AIC_EVENT_TYPE_NONE) { + printf("%s: unexpected event type %d\n", + __func__, type); + } return; } @@ -259,7 +303,10 @@ aplintc_irq_handler(void *frame) if (sc->sc_irq_handler[irq] == NULL) return; - HWRITE4(sc, AIC_SW_CLR(irq), AIC_SW_BIT(irq)); + if (sc->sc_version == 1) + HWRITE4(sc, AIC_SW_CLR(irq), AIC_SW_BIT(irq)); + else + HWRITE4(sc, AIC2_SW_CLR(irq), AIC_SW_BIT(irq)); ih = sc->sc_irq_handler[irq]; @@ -273,7 +320,10 @@ aplintc_irq_handler(void *frame) intr_disable(); aplintc_splx(s); - HWRITE4(sc, AIC_MASK_CLR(irq), AIC_MASK_BIT(irq)); + if (sc->sc_version == 1) + HWRITE4(sc, AIC_MASK_CLR(irq), AIC_MASK_BIT(irq)); + else + HWRITE4(sc, AIC2_MASK_CLR(irq), AIC_MASK_BIT(irq)); } } @@ -367,10 +417,17 @@ aplintc_splx(int new) TAILQ_REMOVE(&sc->sc_irq_list[ipl], ih, ih_list); - HWRITE4(sc, AIC_SW_SET(ih->ih_irq), - AIC_SW_BIT(ih->ih_irq)); - HWRITE4(sc, AIC_MASK_CLR(ih->ih_irq), - AIC_MASK_BIT(ih->ih_irq)); + if (sc->sc_version == 1) { + HWRITE4(sc, AIC_SW_SET(ih->ih_irq), + AIC_SW_BIT(ih->ih_irq)); + HWRITE4(sc, AIC_MASK_CLR(ih->ih_irq), + AIC_MASK_BIT(ih->ih_irq)); + } else { + HWRITE4(sc, AIC2_SW_SET(ih->ih_irq), + AIC_SW_BIT(ih->ih_irq)); + HWRITE4(sc, AIC2_MASK_CLR(ih->ih_irq), + AIC_MASK_BIT(ih->ih_irq)); + } } } } @@ -397,7 +454,12 @@ aplintc_intr_establish(void *cookie, int *cell, int level, struct aplintc_softc *sc = cookie; struct intrhand *ih; uint32_t type = cell[0]; - uint32_t irq = cell[1]; + uint32_t irq; + + if (sc->sc_version == 1) + irq = cell[1]; + else + irq = cell[2]; if (type == 0) { KASSERT(level != (IPL_CLOCK | IPL_MPSAFE)); @@ -429,8 +491,12 @@ aplintc_intr_establish(void *cookie, int *cell, int level, if (type == 0) { sc->sc_irq_handler[irq] = ih; - HWRITE4(sc, AIC_TARGET_CPU(irq), 1); - HWRITE4(sc, AIC_MASK_CLR(irq), AIC_MASK_BIT(irq)); + if (sc->sc_version == 1) { + HWRITE4(sc, AIC_TARGET_CPU(irq), 1); + HWRITE4(sc, AIC_MASK_CLR(irq), AIC_MASK_BIT(irq)); + } else { + HWRITE4(sc, AIC2_MASK_CLR(irq), AIC_MASK_BIT(irq)); + } } else sc->sc_fiq_handler = ih; @@ -449,8 +515,13 @@ aplintc_intr_disestablish(void *cookie) daif = intr_disable(); - HWRITE4(sc, AIC_SW_CLR(ih->ih_irq), AIC_SW_BIT(ih->ih_irq)); - HWRITE4(sc, AIC_MASK_SET(ih->ih_irq), AIC_MASK_BIT(ih->ih_irq)); + if (sc->sc_version == 1) { + HWRITE4(sc, AIC_SW_CLR(ih->ih_irq), AIC_SW_BIT(ih->ih_irq)); + HWRITE4(sc, AIC_MASK_SET(ih->ih_irq), AIC_MASK_BIT(ih->ih_irq)); + } else { + HWRITE4(sc, AIC2_SW_CLR(ih->ih_irq), AIC_SW_BIT(ih->ih_irq)); + HWRITE4(sc, AIC2_MASK_SET(ih->ih_irq), AIC_MASK_BIT(ih->ih_irq)); + } /* Remove ourselves from the list of pending IRQs. */ TAILQ_FOREACH(tmp, &sc->sc_irq_list[ih->ih_ipl], ih_list) { |