The issue for original data structure and algorithm is that: (1)it only support 2 level interrupt architecture. (2)it doesn't support two HW interrupt triggered by one irq pin.
Unify irq parent and child to irq_desc. Now each irq_desc maybe directly attach to xtensa core or another irq_desc.Each irq_desc may have 32 child list instead of 32 child.
--- V2: check result of memory allocation
Signed-off-by: Rander Wang rander.wang@linux.intel.com Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- src/include/sof/interrupt.h | 32 ++++++------ src/lib/interrupt.c | 120 ++++++++++++++++++++++++-------------------- 2 files changed, 82 insertions(+), 70 deletions(-)
diff --git a/src/include/sof/interrupt.h b/src/include/sof/interrupt.h index 2dc8954..f5c37c3 100644 --- a/src/include/sof/interrupt.h +++ b/src/include/sof/interrupt.h @@ -37,27 +37,29 @@ #include <sof/trace.h> #include <sof/debug.h> #include <sof/lock.h> +#include <sof/list.h>
#define trace_irq(__e) trace_event(TRACE_CLASS_IRQ, __e) #define trace_irq_error(__e) trace_error(TRACE_CLASS_IRQ, __e)
-/* child interrupt source */ -struct irq_child { - uint32_t enabled; +struct irq_desc { + /* irq must be first for constructor */ + int irq; /* logical IRQ number */
+ /* handler is optional for constructor */ void (*handler)(void *arg); void *handler_arg; -};
-/* parent source */ -struct irq_parent { - int num; - void (*handler)(void *arg); - uint32_t enabled_count; + /* to identify interrupt with the same IRQ */ + int id; spinlock_t lock; + uint32_t enabled_count; + + /* to link to other irq_desc */ + struct list_item irq_list;
uint32_t num_children; - struct irq_child *child[PLATFORM_IRQ_CHILDREN]; + struct list_item child[PLATFORM_IRQ_CHILDREN]; };
int interrupt_register(uint32_t irq, @@ -87,10 +89,10 @@ static inline void interrupt_global_enable(uint32_t flags) }
/* called by platform interrupt ops */ -int irq_register_child(struct irq_parent *parent, int irq, - void (*handler)(void *arg), void *arg); -void irq_unregister_child(struct irq_parent *parent, int irq); -uint32_t irq_enable_child(struct irq_parent *parent, int irq); -uint32_t irq_disable_child(struct irq_parent *parent, int irq); +int irq_register_child(struct irq_desc *parent, int irq, + void (*handler)(void *arg), void *arg); +void irq_unregister_child(struct irq_desc *parent, int irq); +uint32_t irq_enable_child(struct irq_desc *parent, int irq); +uint32_t irq_disable_child(struct irq_desc *parent, int irq);
#endif diff --git a/src/lib/interrupt.c b/src/lib/interrupt.c index e8e2899..c765cbc 100644 --- a/src/lib/interrupt.c +++ b/src/lib/interrupt.c @@ -38,127 +38,137 @@ #include <stdint.h> #include <stdlib.h>
-int irq_register_child(struct irq_parent *parent, int irq, - void (*handler)(void *arg), void *arg) +int irq_register_child(struct irq_desc *parent, int irq, + void (*handler)(void *arg), void *arg) { int ret = 0; + struct irq_desc *child;
if (parent == NULL) return -EINVAL;
spin_lock(&parent->lock);
- /* does child already exist ? */ - if (parent->child[SOF_IRQ_BIT(irq)]) { - /* already registered, return */ + /* init child */ + child = rzalloc(RZONE_SYS, SOF_MEM_CAPS_RAM, + sizeof(struct irq_desc)); + if (!child) { + ret = -ENOMEM; goto finish; }
- /* init child */ - parent->child[SOF_IRQ_BIT(irq)] = - rzalloc(RZONE_SYS, SOF_MEM_CAPS_RAM, - sizeof(struct irq_child)); - parent->child[SOF_IRQ_BIT(irq)]->enabled = 0; - parent->child[SOF_IRQ_BIT(irq)]->handler = handler; - parent->child[SOF_IRQ_BIT(irq)]->handler_arg = arg; + child->enabled_count = 0; + child->handler = handler; + child->handler_arg = arg; + child->id = SOF_IRQ_ID(irq); + + list_item_append(&child->irq_list, &parent->child[SOF_IRQ_BIT(irq)]);
/* do we need to register parent ? */ if (parent->num_children == 0) { - ret = arch_interrupt_register(parent->num, - parent->handler, parent); + ret = arch_interrupt_register(parent->irq, + parent->handler, parent); }
/* increment number of children */ - parent->num_children += 1; + parent->num_children++;
finish: spin_unlock(&parent->lock); return ret; - }
-void irq_unregister_child(struct irq_parent *parent, int irq) +void irq_unregister_child(struct irq_desc *parent, int irq) { spin_lock(&parent->lock); + struct irq_desc *child; + struct list_item *clist;
/* does child already exist ? */ - if (!parent->child[SOF_IRQ_BIT(irq)]) + if (list_is_empty(&parent->child[SOF_IRQ_BIT(irq)])) goto finish;
- /* free child */ - parent->num_children -= 1; - rfree(parent->child[SOF_IRQ_BIT(irq)]); - parent->child[SOF_IRQ_BIT(irq)] = NULL; + list_for_item(clist, &parent->child[SOF_IRQ_BIT(irq)]) { + child = container_of(clist, struct irq_desc, irq_list); + + if (SOF_IRQ_ID(irq) == child->id) { + list_item_del(&child->irq_list); + rfree(child); + parent->num_children--; + } + }
/* * unregister the root interrupt if the this l2 is * the last registered one. */ if (parent->num_children == 0) - arch_interrupt_unregister(parent->num); + arch_interrupt_unregister(parent->irq);
finish: spin_unlock(&parent->lock); }
-uint32_t irq_enable_child(struct irq_parent *parent, int irq) +uint32_t irq_enable_child(struct irq_desc *parent, int irq) { - struct irq_child *child; + struct irq_desc *child; + struct list_item *clist;
spin_lock(&parent->lock);
- child = parent->child[SOF_IRQ_BIT(irq)]; - - /* already enabled ? */ - if (child->enabled) - goto finish; - /* enable the parent interrupt */ if (parent->enabled_count == 0) arch_interrupt_enable_mask(1 << SOF_IRQ_NUMBER(irq)); - child->enabled = 1; - parent->enabled_count++;
- /* enable the child interrupt */ - platform_interrupt_unmask(irq, 0); + list_for_item(clist, &parent->child[SOF_IRQ_BIT(irq)]) { + child = container_of(clist, struct irq_desc, irq_list); + + if ((SOF_IRQ_ID(irq) == child->id) && + !child->enabled_count) { + child->enabled_count = 1; + parent->enabled_count++; + + /* enable the child interrupt */ + platform_interrupt_unmask(irq, 0); + } + }
-finish: spin_unlock(&parent->lock); return 0;
}
-uint32_t irq_disable_child(struct irq_parent *parent, int irq) +uint32_t irq_disable_child(struct irq_desc *parent, int irq) { - struct irq_child *child; + struct irq_desc *child; + struct list_item *clist;
spin_lock(&parent->lock);
- child = parent->child[SOF_IRQ_BIT(irq)]; - - /* already disabled ? */ - if (!child->enabled) - goto finish; + list_for_item(clist, &parent->child[SOF_IRQ_BIT(irq)]) { + child = container_of(clist, struct irq_desc, irq_list);
- /* disable the child interrupt */ - platform_interrupt_mask(irq, 0); - child->enabled = 0; + if ((SOF_IRQ_ID(irq) == child->id) && + child->enabled_count) { + child->enabled_count = 0; + parent->enabled_count--; + } + }
- /* disable the parent interrupt */ - parent->enabled_count--; - if (parent->enabled_count == 0) + if (parent->enabled_count == 0) { + /* disable the child interrupt */ + platform_interrupt_mask(irq, 0); arch_interrupt_disable_mask(1 << SOF_IRQ_NUMBER(irq)); + }
-finish: spin_unlock(&parent->lock); return 0; - }
int interrupt_register(uint32_t irq, void (*handler)(void *arg), void *arg) { - struct irq_parent *parent; + struct irq_desc *parent;
/* no parent means we are registering DSP internal IRQ */ parent = platform_irq_get_parent(irq); @@ -170,7 +180,7 @@ int interrupt_register(uint32_t irq,
void interrupt_unregister(uint32_t irq) { - struct irq_parent *parent; + struct irq_desc *parent;
/* no parent means we are unregistering DSP internal IRQ */ parent = platform_irq_get_parent(irq); @@ -182,7 +192,7 @@ void interrupt_unregister(uint32_t irq)
uint32_t interrupt_enable(uint32_t irq) { - struct irq_parent *parent; + struct irq_desc *parent;
/* no parent means we are enabling DSP internal IRQ */ parent = platform_irq_get_parent(irq); @@ -194,7 +204,7 @@ uint32_t interrupt_enable(uint32_t irq)
uint32_t interrupt_disable(uint32_t irq) { - struct irq_parent *parent; + struct irq_desc *parent;
/* no parent means we are disabling DSP internal IRQ */ parent = platform_irq_get_parent(irq);