From 1f72c5bfe9525d3b71d30adf4634f998623ff09c Mon Sep 17 00:00:00 2001 From: Daniel Berlin Date: Thu, 30 Nov 2023 13:19:19 -0500 Subject: [PATCH] Support new version of AIC used by M3 Small changes, mostly some offsts are now available in the DT, and a mask change. Signed-off-by: Daniel Berlin --- src/aic.c | 86 ++++++++++++++++++++++++++++++++++++++++---------- src/aic.h | 6 +++- src/aic_regs.h | 31 ++++++++++-------- src/kboot.c | 4 +++ 4 files changed, 97 insertions(+), 30 deletions(-) diff --git a/src/aic.c b/src/aic.c index 6974aac49..d5333e0f5 100644 --- a/src/aic.c +++ b/src/aic.c @@ -31,11 +31,20 @@ static struct aic aic2 = { { .config = AIC2_IRQ_CFG, }, + .cap0_offset = AIC2_CAP0, + .maxnumirq_offset = AIC2_MAXNUMIRQ, +}; + +static struct aic aic3 = { + .version = 3, + /* These are dynamic on AIC3, and filled in from the DT */ + .cap0_offset = -1, + .maxnumirq_offset = -1, }; struct aic *aic; -static int aic2_init(int node) +static int aic23_init(int version, int node) { int ret = ADT_GETPROP(adt, node, "aic-iack-offset", &aic->regs.event); if (ret < 0) { @@ -43,13 +52,28 @@ static int aic2_init(int node) return ret; } - u32 info1 = read32(aic->base + AIC2_INFO1); - aic->nr_die = FIELD_GET(AIC2_INFO1_LAST_DIE, info1) + 1; - aic->nr_irq = FIELD_GET(AIC2_INFO1_NR_IRQ, info1); + int32_t cap0_offset = aic->cap0_offset; + if (cap0_offset == -1) { + ret = ADT_GETPROP(adt, node, "cap0-offset", &cap0_offset); + if (ret < 0) { + printf("AIC: failed to get property cap0-offset\n"); + } + } + u32 cap0 = read32(aic->base + cap0_offset); + aic->nr_die = FIELD_GET(AIC23_CAP0_LAST_DIE, cap0) + 1; + aic->nr_irq = FIELD_GET(AIC23_CAP0_NR_IRQ, cap0); + + int32_t maxnumirq_offset = aic->maxnumirq_offset; + if (maxnumirq_offset == -1) { + ret = ADT_GETPROP(adt, node, "maxnumirq-offset", &maxnumirq_offset); + if (ret < 0) { + printf("AIC: failed to get property maxnumirq-offset\n"); + } + } - u32 info3 = read32(aic->base + AIC2_INFO3); - aic->max_die = FIELD_GET(AIC2_INFO3_MAX_DIE, info3); - aic->max_irq = FIELD_GET(AIC2_INFO3_MAX_IRQ, info3); + u32 info3 = read32(aic->base + maxnumirq_offset); + aic->max_die = FIELD_GET(AIC23_MAXNUMIRQ_MAX_DIE, info3); + aic->max_irq = FIELD_GET(AIC23_MAXNUMIRQ_MAX_IRQ, info3); if (aic->nr_die > AIC_MAX_DIES) { printf("AIC: more dies than supported: %u\n", aic->max_die); @@ -61,9 +85,22 @@ static int aic2_init(int node) return -1; } + /* + * This is dynamic on AIC3+, and already filled in on the AIC2 so the call failing is fine on + * AIC2, but fatal on AIC3. + */ + u32 config_base; + if (ADT_GETPROP(adt, node, "extint-baseaddress", &config_base) > 0) { + aic->regs.config = config_base; + } + + if (!aic->regs.config) { + printf("AIC: Could not find external interrupt config base\n"); + return -1; + } + const u64 start_off = aic->regs.config; u64 off = start_off + sizeof(u32) * aic->max_irq; /* IRQ_CFG */ - aic->regs.sw_set = off; off += sizeof(u32) * (aic->max_irq >> 5); /* SW_SET */ aic->regs.sw_clr = off; @@ -74,11 +111,21 @@ static int aic2_init(int node) off += sizeof(u32) * (aic->max_irq >> 5); /* MASK_CLR */ off += sizeof(u32) * (aic->max_irq >> 5); /* HW_STATE */ - aic->die_stride = off - start_off; + /* Fill in the strides dynamically if we can */ + if (ADT_GETPROP(adt, node, "extintrcfg-stride", &aic->extintrcfg_stride) < 0) + aic->extintrcfg_stride = off - start_off; + if (ADT_GETPROP(adt, node, "intmaskset-stride", &aic->intmaskset_stride) < 0) + aic->intmaskset_stride = off - start_off; + if (ADT_GETPROP(adt, node, "intmaskclear-stride", &aic->intmaskclear_stride) < 0) + aic->intmaskclear_stride = off - start_off; + aic->regs.reg_size = aic->regs.event + 4; - printf("AIC: AIC2 with %u/%u dies, %u/%u IRQs, reg_size:%05lx die_stride:%05x\n", aic->nr_die, - aic->max_die, aic->nr_irq, aic->max_irq, aic->regs.reg_size, aic->die_stride); + printf("AIC: AIC%d with %u/%u dies, %u/%u IRQs, reg_size:%05lx, config:%05lx, " + "extintrcfg_stride:%05x, intmaskset_stride:%05x, intmaskclear_stride:%05x\n", + version, aic->nr_die, aic->max_die, aic->nr_irq, aic->max_irq, aic->regs.reg_size, + aic->regs.config, aic->extintrcfg_stride, aic->intmaskset_stride, + aic->intmaskclear_stride); u32 ext_intr_config_len; const u8 *ext_intr_config = adt_getprop(adt, node, "aic-ext-intr-cfg", &ext_intr_config_len); @@ -91,8 +138,8 @@ static int aic2_init(int node) u8 target = ext_intr_config[i + 2]; assert(die < aic->nr_die); assert(irq < aic->nr_irq); - mask32(aic->base + aic->regs.config + die * aic->die_stride + 4 * irq, - AIC2_IRQ_CFG_TARGET, FIELD_PREP(AIC2_IRQ_CFG_TARGET, target)); + mask32(aic->base + aic->regs.config + die * aic->extintrcfg_stride + 4 * irq, + AIC23_IRQ_CFG_TARGET, FIELD_PREP(AIC23_IRQ_CFG_TARGET, target)); } } @@ -113,6 +160,8 @@ void aic_init(void) aic = &aic1; } else if (adt_is_compatible(adt, node, "aic,2")) { aic = &aic2; + } else if (adt_is_compatible(adt, node, "aic,3")) { + aic = &aic3; } else { printf("AIC: Error: Unsupported version\n"); return; @@ -129,7 +178,12 @@ void aic_init(void) aic->max_irq = AIC1_MAX_IRQ; } else if (aic->version == 2) { printf("AIC: Version 2 @ 0x%lx\n", aic->base); - int ret = aic2_init(node); + int ret = aic23_init(2, node); + if (ret < 0) + aic = NULL; + } else if (aic->version == 3) { + printf("AIC: Version 3 @ 0x%lx\n", aic->base); + int ret = aic23_init(3, node); if (ret < 0) aic = NULL; } @@ -140,10 +194,10 @@ void aic_set_sw(int irq, bool active) u32 die = irq / aic->max_irq; irq = irq % aic->max_irq; if (active) - write32(aic->base + aic->regs.sw_set + die * aic->die_stride + MASK_REG(irq), + write32(aic->base + aic->regs.sw_set + die * aic->intmaskset_stride + MASK_REG(irq), MASK_BIT(irq)); else - write32(aic->base + aic->regs.sw_clr + die * aic->die_stride + MASK_REG(irq), + write32(aic->base + aic->regs.sw_clr + die * aic->intmaskclear_stride + MASK_REG(irq), MASK_BIT(irq)); } diff --git a/src/aic.h b/src/aic.h index 1f401b1c3..d173c786d 100644 --- a/src/aic.h +++ b/src/aic.h @@ -26,8 +26,12 @@ struct aic { uint32_t nr_die; uint32_t max_irq; uint32_t max_die; - uint32_t die_stride; + uint32_t extintrcfg_stride; + uint32_t intmaskset_stride; + uint32_t intmaskclear_stride; + int32_t cap0_offset; + int32_t maxnumirq_offset; struct aic_regs regs; }; diff --git a/src/aic_regs.h b/src/aic_regs.h index 8cc360b7d..470e29857 100644 --- a/src/aic_regs.h +++ b/src/aic_regs.h @@ -19,21 +19,29 @@ #define AIC_CPU_IPI_MASK_SET(cpu) (0x5024 + ((cpu) << 7)) #define AIC_CPU_IPI_MASK_CLR(cpu) (0x5028 + ((cpu) << 7)) -#define AIC2_INFO1 0x0004 -#define AIC2_INFO2 0x0008 -#define AIC2_INFO3 0x000c -#define AIC2_LATENCY 0x0204 +#define AIC2_CAP0 0x0004 +#define AIC2_INFO2 0x0008 +#define AIC2_MAXNUMIRQ 0x000c +#define AIC2_LATENCY 0x0204 + #define AIC2_IRQ_CFG 0x2000 +#define AIC23_IRQ_CFG_TARGET GENMASK(3, 0) +#define AIC3_IRQ_CFG 0x10000 -#define AIC2_IRQ_CFG_TARGET GENMASK(3, 0) +#define AIC23_CAP0_NR_IRQ GENMASK(15, 0) +#define AIC23_CAP0_LAST_DIE GENMASK(27, 24) -#define AIC_INFO_NR_HW GENMASK(15, 0) +#define AIC23_MAXNUMIRQ_MAX_IRQ GENMASK(15, 0) +/* + * This might actually be 8 bits on the M3. + * So far nothing has more than 8 dies anyway + */ +#define AIC23_MAXNUMIRQ_MAX_DIE GENMASK(27, 24) -#define AIC2_INFO1_NR_IRQ GENMASK(15, 0) -#define AIC2_INFO1_LAST_DIE GENMASK(27, 24) +#define AIC_INFO_NR_HW GENMASK(15, 0) -#define AIC2_INFO3_MAX_IRQ GENMASK(15, 0) -#define AIC2_INFO3_MAX_DIE GENMASK(27, 24) +#define AIC1_MAX_IRQ 0x400 +#define AIC_MAX_HW_NUM (0x80 * 32) // max_irq of the M1 Max #define AIC_EVENT_DIE GENMASK(31, 24) #define AIC_EVENT_TYPE GENMASK(23, 16) @@ -48,6 +56,3 @@ #define AIC_IPI_OTHER BIT(0) #define AIC_IPI_SELF BIT(31) - -#define AIC1_MAX_IRQ 0x400 -#define AIC_MAX_HW_NUM (0x80 * 32) // max_irq of the M1 Max diff --git a/src/kboot.c b/src/kboot.c index a0656dc74..7c944a9c9 100644 --- a/src/kboot.c +++ b/src/kboot.c @@ -417,6 +417,8 @@ static int dt_set_cpus(void) int aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic"); if (aic == -FDT_ERR_NOTFOUND) aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic2"); + if (aic == -FDT_ERR_NOTFOUND) + aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic3"); if (aic < 0) bail_cleanup("FDT: Failed to find AIC node\n"); @@ -2141,6 +2143,8 @@ static int dt_transfer_virtios(void) int aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic"); if (aic == -FDT_ERR_NOTFOUND) aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic2"); + if (aic == -FDT_ERR_NOTFOUND) + aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic3"); if (aic < 0) bail("FDT: failed to find AIC node\n");