Skip to content

Commit

Permalink
Support new version of AIC used by M3
Browse files Browse the repository at this point in the history
Small changes, mostly some offsts are now available in the DT,
and a mask change.

Signed-off-by: Daniel Berlin <dberlin@dberlin.org>
  • Loading branch information
dberlin committed Dec 5, 2023
1 parent 7112506 commit e5575c9
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 31 deletions.
86 changes: 70 additions & 16 deletions src/aic.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,49 @@ 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) {
printf("AIC: failed to get property aic-iack-offset\n");
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);
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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));
}
}

Expand All @@ -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;
Expand All @@ -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;
}
Expand All @@ -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));
}

Expand Down
6 changes: 5 additions & 1 deletion src/aic.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down
33 changes: 19 additions & 14 deletions src/aic_regs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_IRQ_CFG 0x2000
#define AIC2_CAP0 0x0004
#define AIC2_INFO2 0x0008
#define AIC2_MAXNUMIRQ 0x000c
#define AIC2_LATENCY 0x0204

#define AIC2_IRQ_CFG_TARGET GENMASK(3, 0)
#define AIC2_IRQ_CFG 0x2000
#define AIC23_IRQ_CFG_TARGET GENMASK(3, 0)
#define AIC3_IRQ_CFG 0x10000

#define AIC_INFO_NR_HW GENMASK(15, 0)
#define AIC23_CAP0_NR_IRQ GENMASK(15, 0)
#define AIC23_CAP0_LAST_DIE GENMASK(27, 24)

#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)
Expand All @@ -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
4 changes: 4 additions & 0 deletions src/kboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down Expand Up @@ -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");

Expand Down

0 comments on commit e5575c9

Please sign in to comment.