mirror of
https://github.com/AsahiLinux/m1n1
synced 2025-02-26 20:37:31 +00:00
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 <dberlin@dberlin.org>
This commit is contained in:
parent
6b83e98e30
commit
4b0fde22a3
4 changed files with 98 additions and 31 deletions
86
src/aic.c
86
src/aic.c
|
@ -31,11 +31,20 @@ static struct aic aic2 = {
|
||||||
{
|
{
|
||||||
.config = AIC2_IRQ_CFG,
|
.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;
|
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);
|
int ret = ADT_GETPROP(adt, node, "aic-iack-offset", &aic->regs.event);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -43,13 +52,28 @@ static int aic2_init(int node)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 info1 = read32(aic->base + AIC2_INFO1);
|
int32_t cap0_offset = aic->cap0_offset;
|
||||||
aic->nr_die = FIELD_GET(AIC2_INFO1_LAST_DIE, info1) + 1;
|
if (cap0_offset == -1) {
|
||||||
aic->nr_irq = FIELD_GET(AIC2_INFO1_NR_IRQ, info1);
|
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);
|
||||||
|
|
||||||
u32 info3 = read32(aic->base + AIC2_INFO3);
|
int32_t maxnumirq_offset = aic->maxnumirq_offset;
|
||||||
aic->max_die = FIELD_GET(AIC2_INFO3_MAX_DIE, info3);
|
if (maxnumirq_offset == -1) {
|
||||||
aic->max_irq = FIELD_GET(AIC2_INFO3_MAX_IRQ, info3);
|
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 + 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) {
|
if (aic->nr_die > AIC_MAX_DIES) {
|
||||||
printf("AIC: more dies than supported: %u\n", aic->max_die);
|
printf("AIC: more dies than supported: %u\n", aic->max_die);
|
||||||
|
@ -61,9 +85,22 @@ static int aic2_init(int node)
|
||||||
return -1;
|
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;
|
const u64 start_off = aic->regs.config;
|
||||||
u64 off = start_off + sizeof(u32) * aic->max_irq; /* IRQ_CFG */
|
u64 off = start_off + sizeof(u32) * aic->max_irq; /* IRQ_CFG */
|
||||||
|
|
||||||
aic->regs.sw_set = off;
|
aic->regs.sw_set = off;
|
||||||
off += sizeof(u32) * (aic->max_irq >> 5); /* SW_SET */
|
off += sizeof(u32) * (aic->max_irq >> 5); /* SW_SET */
|
||||||
aic->regs.sw_clr = off;
|
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); /* MASK_CLR */
|
||||||
off += sizeof(u32) * (aic->max_irq >> 5); /* HW_STATE */
|
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;
|
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,
|
printf("AIC: AIC%d with %u/%u dies, %u/%u IRQs, reg_size:%05lx, config:%05lx, "
|
||||||
aic->max_die, aic->nr_irq, aic->max_irq, aic->regs.reg_size, aic->die_stride);
|
"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;
|
u32 ext_intr_config_len;
|
||||||
const u8 *ext_intr_config = adt_getprop(adt, node, "aic-ext-intr-cfg", &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];
|
u8 target = ext_intr_config[i + 2];
|
||||||
assert(die < aic->nr_die);
|
assert(die < aic->nr_die);
|
||||||
assert(irq < aic->nr_irq);
|
assert(irq < aic->nr_irq);
|
||||||
mask32(aic->base + aic->regs.config + die * aic->die_stride + 4 * irq,
|
mask32(aic->base + aic->regs.config + die * aic->extintrcfg_stride + 4 * irq,
|
||||||
AIC2_IRQ_CFG_TARGET, FIELD_PREP(AIC2_IRQ_CFG_TARGET, target));
|
AIC23_IRQ_CFG_TARGET, FIELD_PREP(AIC23_IRQ_CFG_TARGET, target));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +160,8 @@ void aic_init(void)
|
||||||
aic = &aic1;
|
aic = &aic1;
|
||||||
} else if (adt_is_compatible(adt, node, "aic,2")) {
|
} else if (adt_is_compatible(adt, node, "aic,2")) {
|
||||||
aic = &aic2;
|
aic = &aic2;
|
||||||
|
} else if (adt_is_compatible(adt, node, "aic,3")) {
|
||||||
|
aic = &aic3;
|
||||||
} else {
|
} else {
|
||||||
printf("AIC: Error: Unsupported version\n");
|
printf("AIC: Error: Unsupported version\n");
|
||||||
return;
|
return;
|
||||||
|
@ -129,7 +178,12 @@ void aic_init(void)
|
||||||
aic->max_irq = AIC1_MAX_IRQ;
|
aic->max_irq = AIC1_MAX_IRQ;
|
||||||
} else if (aic->version == 2) {
|
} else if (aic->version == 2) {
|
||||||
printf("AIC: Version 2 @ 0x%lx\n", aic->base);
|
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)
|
if (ret < 0)
|
||||||
aic = NULL;
|
aic = NULL;
|
||||||
}
|
}
|
||||||
|
@ -140,10 +194,10 @@ void aic_set_sw(int irq, bool active)
|
||||||
u32 die = irq / aic->max_irq;
|
u32 die = irq / aic->max_irq;
|
||||||
irq = irq % aic->max_irq;
|
irq = irq % aic->max_irq;
|
||||||
if (active)
|
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));
|
MASK_BIT(irq));
|
||||||
else
|
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));
|
MASK_BIT(irq));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,12 @@ struct aic {
|
||||||
uint32_t nr_die;
|
uint32_t nr_die;
|
||||||
uint32_t max_irq;
|
uint32_t max_irq;
|
||||||
uint32_t max_die;
|
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;
|
struct aic_regs regs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -19,21 +19,29 @@
|
||||||
#define AIC_CPU_IPI_MASK_SET(cpu) (0x5024 + ((cpu) << 7))
|
#define AIC_CPU_IPI_MASK_SET(cpu) (0x5024 + ((cpu) << 7))
|
||||||
#define AIC_CPU_IPI_MASK_CLR(cpu) (0x5028 + ((cpu) << 7))
|
#define AIC_CPU_IPI_MASK_CLR(cpu) (0x5028 + ((cpu) << 7))
|
||||||
|
|
||||||
#define AIC2_INFO1 0x0004
|
#define AIC2_CAP0 0x0004
|
||||||
#define AIC2_INFO2 0x0008
|
#define AIC2_INFO2 0x0008
|
||||||
#define AIC2_INFO3 0x000c
|
#define AIC2_MAXNUMIRQ 0x000c
|
||||||
#define AIC2_LATENCY 0x0204
|
#define AIC2_LATENCY 0x0204
|
||||||
#define AIC2_IRQ_CFG 0x2000
|
|
||||||
|
|
||||||
#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 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 AIC_INFO_NR_HW GENMASK(15, 0)
|
#define AIC_INFO_NR_HW GENMASK(15, 0)
|
||||||
|
|
||||||
#define AIC2_INFO1_NR_IRQ GENMASK(15, 0)
|
#define AIC1_MAX_IRQ 0x400
|
||||||
#define AIC2_INFO1_LAST_DIE GENMASK(27, 24)
|
#define AIC_MAX_HW_NUM (0x80 * 32) // max_irq of the M1 Max
|
||||||
|
|
||||||
#define AIC2_INFO3_MAX_IRQ GENMASK(15, 0)
|
|
||||||
#define AIC2_INFO3_MAX_DIE GENMASK(27, 24)
|
|
||||||
|
|
||||||
#define AIC_EVENT_DIE GENMASK(31, 24)
|
#define AIC_EVENT_DIE GENMASK(31, 24)
|
||||||
#define AIC_EVENT_TYPE GENMASK(23, 16)
|
#define AIC_EVENT_TYPE GENMASK(23, 16)
|
||||||
|
@ -48,6 +56,3 @@
|
||||||
|
|
||||||
#define AIC_IPI_OTHER BIT(0)
|
#define AIC_IPI_OTHER BIT(0)
|
||||||
#define AIC_IPI_SELF BIT(31)
|
#define AIC_IPI_SELF BIT(31)
|
||||||
|
|
||||||
#define AIC1_MAX_IRQ 0x400
|
|
||||||
#define AIC_MAX_HW_NUM (0x80 * 32) // max_irq of the M1 Max
|
|
||||||
|
|
|
@ -418,6 +418,8 @@ static int dt_set_cpus(void)
|
||||||
int aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic");
|
int aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic");
|
||||||
if (aic == -FDT_ERR_NOTFOUND)
|
if (aic == -FDT_ERR_NOTFOUND)
|
||||||
aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic2");
|
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)
|
if (aic < 0)
|
||||||
bail_cleanup("FDT: Failed to find AIC node\n");
|
bail_cleanup("FDT: Failed to find AIC node\n");
|
||||||
|
|
||||||
|
@ -2168,6 +2170,8 @@ static int dt_transfer_virtios(void)
|
||||||
int aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic");
|
int aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic");
|
||||||
if (aic == -FDT_ERR_NOTFOUND)
|
if (aic == -FDT_ERR_NOTFOUND)
|
||||||
aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic2");
|
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)
|
if (aic < 0)
|
||||||
bail("FDT: failed to find AIC node\n");
|
bail("FDT: failed to find AIC node\n");
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue