diff options
Diffstat (limited to 'tools/src/aic.c')
| -rw-r--r-- | tools/src/aic.c | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/tools/src/aic.c b/tools/src/aic.c new file mode 100644 index 0000000..6974aac --- /dev/null +++ b/tools/src/aic.c @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: MIT */ + +#include "aic.h" +#include "adt.h" +#include "aic_regs.h" +#include "assert.h" +#include "utils.h" + +#define MASK_REG(x) (4 * ((x) >> 5)) +#define MASK_BIT(x) BIT((x)&GENMASK(4, 0)) + +static struct aic aic1 = { + .version = 1, + .nr_die = 1, + .max_die = 1, + .regs = + { + .reg_size = AIC_REG_SIZE, + .event = AIC_EVENT, + .tgt_cpu = AIC_TARGET_CPU, + .sw_set = AIC_SW_SET, + .sw_clr = AIC_SW_CLR, + .mask_set = AIC_MASK_SET, + .mask_clr = AIC_MASK_CLR, + }, +}; + +static struct aic aic2 = { + .version = 2, + .regs = + { + .config = AIC2_IRQ_CFG, + }, +}; + +struct aic *aic; + +static int aic2_init(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); + + 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); + + if (aic->nr_die > AIC_MAX_DIES) { + printf("AIC: more dies than supported: %u\n", aic->max_die); + return -1; + } + + if (aic->max_irq > AIC_MAX_HW_NUM) { + printf("AIC: more IRQs than supported: %u\n", aic->max_irq); + 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; + off += sizeof(u32) * (aic->max_irq >> 5); /* SW_CLR */ + aic->regs.mask_set = off; + off += sizeof(u32) * (aic->max_irq >> 5); /* MASK_SET */ + aic->regs.mask_clr = off; + off += sizeof(u32) * (aic->max_irq >> 5); /* MASK_CLR */ + off += sizeof(u32) * (aic->max_irq >> 5); /* HW_STATE */ + + aic->die_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); + + u32 ext_intr_config_len; + const u8 *ext_intr_config = adt_getprop(adt, node, "aic-ext-intr-cfg", &ext_intr_config_len); + + if (ext_intr_config) { + printf("AIC: Configuring %d external interrupts\n", ext_intr_config_len / 3); + for (u32 i = 0; i < ext_intr_config_len; i += 3) { + u8 die = ext_intr_config[i + 1] >> 4; + u16 irq = ext_intr_config[i] | ((ext_intr_config[i + 1] & 0xf) << 8); + 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)); + } + } + + return 0; +} + +void aic_init(void) +{ + int path[8]; + int node = adt_path_offset_trace(adt, "/arm-io/aic", path); + + if (node < 0) { + printf("AIC node not found!\n"); + return; + } + + if (adt_is_compatible(adt, node, "aic,1")) { + aic = &aic1; + } else if (adt_is_compatible(adt, node, "aic,2")) { + aic = &aic2; + } else { + printf("AIC: Error: Unsupported version\n"); + return; + } + + if (adt_get_reg(adt, path, "reg", 0, &aic->base, NULL)) { + printf("Failed to get AIC reg property!\n"); + return; + } + + if (aic->version == 1) { + printf("AIC: Version 1 @ 0x%lx\n", aic->base); + aic->nr_irq = FIELD_GET(AIC_INFO_NR_HW, read32(aic->base + AIC_INFO)); + 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); + if (ret < 0) + aic = NULL; + } +} + +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), + MASK_BIT(irq)); + else + write32(aic->base + aic->regs.sw_clr + die * aic->die_stride + MASK_REG(irq), + MASK_BIT(irq)); +} + +uint32_t aic_ack(void) +{ + return read32(aic->base + aic->regs.event); +} |
