From d38be46655846948d3c7f6bb149fb757bf0498ea Mon Sep 17 00:00:00 2001 From: zhuguangqing Date: Fri, 24 Aug 2018 15:10:23 -0600 Subject: [PATCH] sched/irq: Add support interrupt chains in NuttX. IRQ chain is very useful in these cases: (1) Multiple hardware connect to the same request line(e.g. PCI), (2) Need multiple driver to support one hardware block (like Linux MFD) --- include/nuttx/irq.h | 13 +- sched/Kconfig | 19 +++ sched/irq/Make.defs | 4 + sched/irq/irq.h | 6 + sched/irq/irq_attach.c | 15 ++ sched/irq/irq_chain.c | 285 +++++++++++++++++++++++++++++++++++++ sched/irq/irq_initialize.c | 6 + 7 files changed, 345 insertions(+), 3 deletions(-) create mode 100644 sched/irq/irq_chain.c diff --git a/include/nuttx/irq.h b/include/nuttx/irq.h index 56f24ff897..8cb3fd63d0 100644 --- a/include/nuttx/irq.h +++ b/include/nuttx/irq.h @@ -53,11 +53,12 @@ ****************************************************************************/ #ifndef __ASSEMBLY__ -/* IRQ detach is a convenience definition. Detaching an interrupt handler - * is equivalent to setting a NULL interrupt handler. +/* IRQ detach is a convenience definition, it detach all the handlers + * sharing the same IRQ. Detaching an interrupt handler is equivalent to + * setting a NULL interrupt handler. */ -# define irq_detach(isr) irq_attach(isr, NULL, NULL) +# define irq_detach(irq) irq_attach(irq, NULL, NULL) /* Maximum/minimum values of IRQ integer types */ @@ -163,6 +164,12 @@ extern "C" int irq_attach(int irq, xcpt_t isr, FAR void *arg); +#ifdef CONFIG_IRQCHAIN +int irqchain_detach(int irq, xcpt_t isr, FAR void *arg); +#else +# define irqchain_detach(irq, isr, arg) irq_detach(irq) +#endif + /**************************************************************************** * Name: enter_critical_section * diff --git a/sched/Kconfig b/sched/Kconfig index bd46f6513d..f352318f02 100644 --- a/sched/Kconfig +++ b/sched/Kconfig @@ -257,6 +257,25 @@ config SPINLOCK_IRQ Enables suppport for spinlocks with IRQ control. This feature can be used to protect data in SMP mode. +config IRQCHAIN + bool "Enable multi handler sharing a IRQ" + default n + ---help--- + Enable support for IRQCHAIN. + +if IRQCHAIN + +config PREALLOC_IRQCHAIN + int "Number of pre-allocated irq chains" + default 8 + ---help--- + The number of pre-allocated irq chain structures. The system manages + a pool of preallocated irq chain structures to minimize dynamic + allocations. You will, however, get better performance and memory + usage if this value is tuned to minimize such allocations. + +endif # IRQCHAIN + config SMP bool "Symmetric Multi-Processing (SMP)" default n diff --git a/sched/irq/Make.defs b/sched/irq/Make.defs index a7223c9d29..c2c11b3dd9 100644 --- a/sched/irq/Make.defs +++ b/sched/irq/Make.defs @@ -53,6 +53,10 @@ CSRCS += irq_procfs.c endif endif +ifeq ($(CONFIG_IRQCHAIN),y) +CSRCS += irq_chain.c +endif + # Include irq build support DEPPATH += --dep-path irq diff --git a/sched/irq/irq.h b/sched/irq/irq.h index 8c9a8c818f..5df258366f 100644 --- a/sched/irq/irq.h +++ b/sched/irq/irq.h @@ -227,6 +227,12 @@ bool irq_cpu_locked(int cpu); int irq_foreach(irq_foreach_t callback, FAR void *arg); #endif +#ifdef CONFIG_IRQCHAIN +void irqchain_initialize(void); +bool is_irqchain(int ndx, xcpt_t isr); +int irqchain_attach(int ndx, xcpt_t isr, FAR void *arg); +#endif + #undef EXTERN #ifdef __cplusplus } diff --git a/sched/irq/irq_attach.c b/sched/irq/irq_attach.c index da03f09427..f1ea3bd32e 100644 --- a/sched/irq/irq_attach.c +++ b/sched/irq/irq_attach.c @@ -112,6 +112,21 @@ int irq_attach(int irq, xcpt_t isr, FAR void *arg) arg = NULL; } +#ifdef CONFIG_IRQCHAIN + /* Save the new ISR and its argument in the table. + * If there is only one ISR on this irq, then .handler point to the ISR + * and .arg point to the ISR parameter; Otherwise, .handler point to + * irqchain_dispatch and .arg point to irqchain_s. + */ + + if (is_irqchain(ndx, isr)) + { + ret = irqchain_attach(ndx, isr, arg); + leave_critical_section(flags); + return ret; + } +#endif + /* Save the new ISR and its argument in the table. */ g_irqvector[ndx].handler = isr; diff --git a/sched/irq/irq_chain.c b/sched/irq/irq_chain.c new file mode 100644 index 0000000000..214dbac652 --- /dev/null +++ b/sched/irq/irq_chain.c @@ -0,0 +1,285 @@ +/**************************************************************************** + * sched/irq/irq_chain.c + * + * Copyright (C) 2018 Pinecone Inc. All rights reserved. + * Author: Zhu GuanqQing + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "irq/irq.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct irqchain_s +{ + FAR struct irqchain_s *next; + + xcpt_t handler; /* Address of the interrupt handler */ + FAR void *arg; /* The argument provided to the interrupt handler. */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* g_irqchainpool is a list of pre-allocated irq chain. The number of irq chains + * in the pool is a configuration item. + */ + +static struct irqchain_s g_irqchainpool[CONFIG_PREALLOC_IRQCHAIN]; + +/* The g_irqchainfreelist data structure is a single linked list of irqchains + * available to the system for delayed function use. + */ + +static sq_queue_t g_irqchainfreelist; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void irqchain_detach_all(int ndx) +{ + FAR struct irqchain_s *curr; + FAR struct irqchain_s *prev; + + g_irqvector[ndx].handler = irq_unexpected_isr; + + curr = g_irqvector[ndx].arg; + while (curr != NULL) + { + prev = curr; + curr = curr->next; + sq_addlast((FAR struct sq_entry_s *)prev, &g_irqchainfreelist); + } +} + +static int irqchain_dispatch(int irq, FAR void *context, FAR void *arg) +{ + FAR struct irqchain_s *curr; + FAR struct irqchain_s *prev; + int ndx; + int ret = 0; + +#ifdef CONFIG_ARCH_MINIMAL_VECTORTABLE + ndx = g_irqmap[irq]; +#else + ndx = irq; +#endif + + curr = g_irqvector[ndx].arg; + while (curr != NULL) + { + prev = curr; + curr = curr->next; + ret |= prev->handler(irq, context, prev->arg); + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void irqchain_initialize(void) +{ + FAR struct irqchain_s *irqchain = g_irqchainpool; + int i; + + /* Initialize irqchain free lists */ + + sq_init(&g_irqchainfreelist); + + /* The g_irqchainfreelist must be loaded at initialization time to hold the + * configured number of irqchain. + */ + + for (i = 0; i < CONFIG_PREALLOC_IRQCHAIN; i++) + { + sq_addlast((FAR struct sq_entry_s *)irqchain++, &g_irqchainfreelist); + } +} + +bool is_irqchain(int ndx, xcpt_t isr) +{ + if (g_irqvector[ndx].handler == irq_unexpected_isr || + g_irqvector[ndx].handler == NULL) + { + return false; + } + else if (g_irqvector[ndx].handler == irqchain_dispatch) + { + return true; + } + else + { + return isr != irq_unexpected_isr; + } +} + +int irqchain_attach(int ndx, xcpt_t isr, FAR void *arg) +{ + FAR struct irqchain_s *node; + FAR struct irqchain_s *curr; + + if (isr != irq_unexpected_isr) + { + if (g_irqvector[ndx].handler != irqchain_dispatch) + { + if (sq_count(&g_irqchainfreelist) < 2) + { + return -ENOMEM; + } + + node = (FAR struct irqchain_s *)sq_remfirst(&g_irqchainfreelist); + DEBUGASSERT(node != NULL); + + node->handler = g_irqvector[ndx].handler; + node->arg = g_irqvector[ndx].arg; + node->next = NULL; + + g_irqvector[ndx].handler = irqchain_dispatch; + g_irqvector[ndx].arg = node; + } + + node = (FAR struct irqchain_s *)sq_remfirst(&g_irqchainfreelist); + if (node == NULL) + { + return -ENOMEM; + } + + node->handler = isr; + node->arg = arg; + node->next = NULL; + + curr = g_irqvector[ndx].arg; + while (curr->next != NULL) + { + curr = curr->next; + } + + curr->next = node; + } + else + { + irqchain_detach_all(ndx); + } + + return OK; +} + +int irqchain_detach(int irq, xcpt_t isr, FAR void *arg) +{ +#if NR_IRQS > 0 + FAR struct irqchain_s *prev; + FAR struct irqchain_s *curr; + FAR struct irqchain_s *first; + int ret = -EINVAL; + + if ((unsigned)irq < NR_IRQS) + { + irqstate_t flags; + int ndx; + +#ifdef CONFIG_ARCH_MINIMAL_VECTORTABLE + /* Is there a mapping for this IRQ number? */ + + ndx = g_irqmap[irq]; + if ((unsigned)ndx >= CONFIG_ARCH_NUSER_INTERRUPTS) + { + /* No.. then return failure. */ + + return ret; + } +#else + ndx = irq; +#endif + + flags = enter_critical_section(); + + if (g_irqvector[ndx].handler == irqchain_dispatch) + { + first = g_irqvector[ndx].arg; + for (prev = NULL, curr = first; + curr != NULL; + prev = curr, curr = curr->next) + { + if (curr->handler == isr && curr->arg == arg) + { + if (curr == first) + { + g_irqvector[ndx].arg = curr->next; + } + else if (curr->next == NULL) + { + prev->next = NULL; + } + else + { + prev->next = curr->next; + } + + sq_addlast((FAR struct sq_entry_s *)curr, &g_irqchainfreelist); + + first = g_irqvector[ndx].arg; + if (first->next == NULL) + { + g_irqvector[ndx].handler = first->handler; + g_irqvector[ndx].arg = first->arg; + sq_addlast((FAR struct sq_entry_s *)first, &g_irqchainfreelist); + } + + ret = OK; + break; + } + } + } + else + { + ret = irq_detach(irq); + } + + leave_critical_section(flags); + } + + return ret; +#else + return OK; +#endif +} diff --git a/sched/irq/irq_initialize.c b/sched/irq/irq_initialize.c index b2355f9a90..ef3f60554a 100644 --- a/sched/irq/irq_initialize.c +++ b/sched/irq/irq_initialize.c @@ -99,4 +99,10 @@ void irq_initialize(void) #endif #endif } + +#ifdef CONFIG_IRQCHAIN + /* Initialize IRQ chain support */ + + irqchain_initialize(); +#endif }