diff --git a/core/interrupts.c b/core/interrupts.c index 9bef5f5def75..1cdf3e4175a7 100644 --- a/core/interrupts.c +++ b/core/interrupts.c @@ -32,17 +32,20 @@ #define ICP_MFRR 0xc /* 8-bit access */ static LIST_HEAD(irq_sources); +static LIST_HEAD(irq_sources2); static struct lock irq_lock = LOCK_UNLOCKED; -void __register_irq_source(struct irq_source *is) +void __register_irq_source(struct irq_source *is, bool secondary) { struct irq_source *is1; + struct list_head *list = secondary ? &irq_sources2 : &irq_sources; - prlog(PR_DEBUG, "IRQ: Registering %04x..%04x ops @%p (data %p)\n", - is->start, is->end - 1, is->ops, is->data); + prlog(PR_DEBUG, "IRQ: Registering %04x..%04x ops @%p (data %p)%s\n", + is->start, is->end - 1, is->ops, is->data, + secondary ? " [secondary]" : ""); lock(&irq_lock); - list_for_each(&irq_sources, is1, link) { + list_for_each(list, is1, link) { if (is->end > is1->start && is->start < is1->end) { prerror("register IRQ source overlap !\n"); prerror(" new: %x..%x old: %x..%x\n", @@ -51,7 +54,7 @@ void __register_irq_source(struct irq_source *is) assert(0); } } - list_add_tail(&irq_sources, &is->link); + list_add_tail(list, &is->link); unlock(&irq_lock); } @@ -67,13 +70,14 @@ void register_irq_source(const struct irq_source_ops *ops, void *data, is->ops = ops; is->data = data; - __register_irq_source(is); + __register_irq_source(is, false); } void unregister_irq_source(uint32_t start, uint32_t count) { struct irq_source *is; + /* Note: We currently only unregister from the primary sources */ lock(&irq_lock); list_for_each(&irq_sources, is, link) { if (start >= is->start && start < is->end) { @@ -102,39 +106,24 @@ static struct irq_source *irq_find_source(uint32_t isn) struct irq_source *is; lock(&irq_lock); + /* + * XXX This really needs some kind of caching ! + */ list_for_each(&irq_sources, is, link) { if (isn >= is->start && isn < is->end) { unlock(&irq_lock); return is; } } - unlock(&irq_lock); - - return NULL; -} - -void adjust_irq_source(struct irq_source *is, uint32_t new_count) -{ - struct irq_source *is1; - uint32_t new_end = is->start + new_count; - - prlog(PR_DEBUG, "IRQ: Adjusting %04x..%04x to %04x..%04x\n", - is->start, is->end - 1, is->start, new_end - 1); - - lock(&irq_lock); - list_for_each(&irq_sources, is1, link) { - if (is1 == is) - continue; - if (new_end > is1->start && is->start < is1->end) { - prerror("adjust IRQ source overlap !\n"); - prerror(" new: %x..%x old: %x..%x\n", - is->start, new_end - 1, - is1->start, is1->end - 1); - assert(0); + list_for_each(&irq_sources2, is, link) { + if (isn >= is->start && isn < is->end) { + unlock(&irq_lock); + return is; } } - is->end = new_end; unlock(&irq_lock); + + return NULL; } /* @@ -474,4 +463,3 @@ void init_interrupts(void) } } - diff --git a/hw/phb4.c b/hw/phb4.c index a21aa1afc5f1..a6a6a513fbff 100644 --- a/hw/phb4.c +++ b/hw/phb4.c @@ -3174,11 +3174,11 @@ static void phb4_create(struct dt_node *np) phb4_init_hw(p, true); /* Register all interrupt sources with XIVE */ - xive_register_source(p->base_msi, p->num_irqs - 8, 16, p->int_mmio, 0, - NULL, NULL); - xive_register_source(p->base_lsi, 8, 16, - p->int_mmio + ((p->num_irqs - 8) << 16), - XIVE_SRC_LSI, p, &phb4_lsi_ops); + xive_register_hw_source(p->base_msi, p->num_irqs - 8, 16, p->int_mmio, 0, + NULL, NULL); + xive_register_hw_source(p->base_lsi, 8, 16, + p->int_mmio + ((p->num_irqs - 8) << 16), + XIVE_SRC_LSI, p, &phb4_lsi_ops); /* Platform additional setup */ if (platform.pci_setup_phb) diff --git a/hw/psi.c b/hw/psi.c index a7ea12cf7e72..9913c3aca2bc 100644 --- a/hw/psi.c +++ b/hw/psi.c @@ -785,9 +785,9 @@ static void psi_init_p9_interrupts(struct psi *psi) out_be64(psi->regs + PSIHB_IVT_OFFSET, val); /* Register sources */ - xive_register_source(psi->interrupt, P9_PSI_NUM_IRQS, - 12, psi->esb_mmio, XIVE_SRC_LSI, - psi, &psi_p9_irq_ops); + xive_register_hw_source(psi->interrupt, P9_PSI_NUM_IRQS, + 12, psi->esb_mmio, XIVE_SRC_LSI, + psi, &psi_p9_irq_ops); } static void psi_init_interrupts(struct psi *psi) diff --git a/hw/xive.c b/hw/xive.c index a9150edfc1e6..80a95c261c00 100644 --- a/hw/xive.c +++ b/hw/xive.c @@ -103,6 +103,7 @@ /* Use 64K for everything by default */ #define IC_PAGE_SIZE 0x10000 #define TM_PAGE_SIZE 0x10000 +#define IPI_ESB_SHIFT (16 + 1) #define IC_BAR_DEFAULT 0x30203100000ull #define IC_BAR_SIZE (8 * IC_PAGE_SIZE) @@ -1243,9 +1244,6 @@ uint32_t xive_alloc_hw_irqs(uint32_t chip_id, uint32_t count, uint32_t align) } x->int_hw_bot = base; - /* Adjust the irq source to avoid overlaps */ - adjust_irq_source(&x->ipis.is, base - x->int_base); - /* Initialize the corresponding IVT entries to sane defaults, * IE entry is valid, not routed and masked, EQ data is set * to the GIRQ number. @@ -1590,7 +1588,7 @@ static void xive_source_eoi(struct irq_source *is, uint32_t isn) ive = s->xive->ivt_base; if (!ive) return; - ive += GIRQ_TO_IDX(isn); + ive += GIRQ_TO_IDX(isn); /* If it's invalid or masked, don't do anything */ if ((ive->w & IVE_MASKED) || !(ive->w & IVE_VALID)) @@ -1649,9 +1647,10 @@ static const struct irq_source_ops xive_irq_source_ops = { .attributes = xive_source_attributes, }; -static void __xive_register_source(struct xive_src *s, uint32_t base, - uint32_t count, uint32_t shift, - void *mmio, uint32_t flags, void *data, +static void __xive_register_source(struct xive *x, struct xive_src *s, + uint32_t base, uint32_t count, + uint32_t shift, void *mmio, uint32_t flags, + bool secondary, void *data, const struct irq_source_ops *orig_ops) { s->esb_base = base; @@ -1659,24 +1658,50 @@ static void __xive_register_source(struct xive_src *s, uint32_t base, s->esb_mmio = mmio; s->flags = flags; s->orig_ops = orig_ops; - + s->xive = x; s->is.start = base; s->is.end = base + count; s->is.ops = &xive_irq_source_ops; s->is.data = data; - __register_irq_source(&s->is); + __register_irq_source(&s->is, secondary); } -void xive_register_source(uint32_t base, uint32_t count, uint32_t shift, - void *mmio, uint32_t flags, void *data, - const struct irq_source_ops *ops) +void xive_register_hw_source(uint32_t base, uint32_t count, uint32_t shift, + void *mmio, uint32_t flags, void *data, + const struct irq_source_ops *ops) { struct xive_src *s; + struct xive *x = xive_from_isn(base); + + assert(x); s = malloc(sizeof(struct xive_src)); assert(s); - __xive_register_source(s, base, count, shift, mmio, flags, data, ops); + __xive_register_source(x, s, base, count, shift, mmio, flags, + false, data, ops); +} + +void xive_register_ipi_source(uint32_t base, uint32_t count, void *data, + const struct irq_source_ops *ops) +{ + struct xive_src *s; + struct xive *x = xive_from_isn(base); + uint32_t base_idx = GIRQ_TO_IDX(base); + void *mmio_base; + + assert(x); + assert(base >= x->int_base && (base + count) <= x->int_ipi_top); + + s = malloc(sizeof(struct xive_src)); + assert(s); + + /* Callbacks assume the MMIO base corresponds to the first + * interrupt of that source structure so adjust it + */ + mmio_base = x->esb_mmio + (1ul << IPI_ESB_SHIFT) * base_idx; + __xive_register_source(x, s, base, count, IPI_ESB_SHIFT, mmio_base, + XIVE_SRC_EOI_PAGE1, false, data, ops); } static void init_one_xive(struct dt_node *np) @@ -1744,9 +1769,12 @@ static void init_one_xive(struct dt_node *np) /* Register built-in source controllers (aka IPIs) */ /* XXX Add new EOI mode for DD2 */ - __xive_register_source(&x->ipis, x->int_base, - x->int_hw_bot - x->int_base, 16 + 1, - x->esb_mmio, XIVE_SRC_EOI_PAGE1, NULL, NULL); + __xive_register_source(x, &x->ipis, x->int_base, + x->int_hw_bot - x->int_base, IPI_ESB_SHIFT, + x->esb_mmio, XIVE_SRC_EOI_PAGE1, + true, NULL, NULL); + + /* XXX Add registration of escalation sources too */ /* Create a device-tree node for Linux use */ xive_create_mmio_dt_node(x); diff --git a/include/interrupts.h b/include/interrupts.h index 465073b8de3e..6b55099f5e6a 100644 --- a/include/interrupts.h +++ b/include/interrupts.h @@ -291,11 +291,10 @@ struct irq_source { struct list_node link; }; -extern void __register_irq_source(struct irq_source *is); +extern void __register_irq_source(struct irq_source *is, bool secondary); extern void register_irq_source(const struct irq_source_ops *ops, void *data, uint32_t start, uint32_t count); extern void unregister_irq_source(uint32_t start, uint32_t count); -extern void adjust_irq_source(struct irq_source *is, uint32_t new_count); extern uint32_t get_psi_interrupt(uint32_t chip_id); diff --git a/include/xive.h b/include/xive.h index 367daec21e72..065aa698e08a 100644 --- a/include/xive.h +++ b/include/xive.h @@ -376,9 +376,11 @@ uint32_t xive_get_notify_base(uint32_t girq); #define XIVE_SRC_LSI 0x00000004 /* No Q bit, no retrigger */ struct irq_source_ops; -void xive_register_source(uint32_t base, uint32_t count, uint32_t shift, - void *mmio, uint32_t flags, void *data, - const struct irq_source_ops *ops); +void xive_register_hw_source(uint32_t base, uint32_t count, uint32_t shift, + void *mmio, uint32_t flags, void *data, + const struct irq_source_ops *ops); +void xive_register_ipi_source(uint32_t base, uint32_t count, void *data, + const struct irq_source_ops *ops); void xive_cpu_callin(struct cpu_thread *cpu);