diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2016-11-14 13:06:15 +1100 |
---|---|---|
committer | Stewart Smith <stewart@linux.vnet.ibm.com> | 2016-11-15 15:06:03 +1100 |
commit | aecfaf0146ccfb2a4d31fa812d224f8317225e8b (patch) | |
tree | 69e5fce2a62b4f6307b5a3559c8fb10b13e6f93b /core | |
parent | bd4eaedc2dda6e7b365eb5e104c07c5d114528e6 (diff) | |
download | blackbird-skiboot-aecfaf0146ccfb2a4d31fa812d224f8317225e8b.tar.gz blackbird-skiboot-aecfaf0146ccfb2a4d31fa812d224f8317225e8b.zip |
xive: Provide a way to override some IPI sources
Some devices such as NX or the NPU will use some of the XIVE
provided IPIs for their own interrupts. Thus we need a way for
those to provide a custom irq_source_ops for portions of the IPI
space in order for them to provide their own attributes() and
if needed, interrutps() callbacks.
We achieve that by creating a second list of sources which can
overlap the primary.
The global stock of IPIs is registered by XIVE in the secondary
list which is searched when no match is found in the primary.
A new API xive_register_ipi_source() is provided for those devices
to create an overlapping source structure in the primary list for
a subset of the IPIs. Those IPIs must have been previously allocated
using xive_alloc_ipi_irqs()
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Diffstat (limited to 'core')
-rw-r--r-- | core/interrupts.c | 50 |
1 files changed, 19 insertions, 31 deletions
diff --git a/core/interrupts.c b/core/interrupts.c index 9bef5f5d..1cdf3e41 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) } } - |