diff --git a/lib/irq.h b/lib/irq.h index ce34c6c..5d2ccf5 100644 --- a/lib/irq.h +++ b/lib/irq.h @@ -36,7 +36,11 @@ typedef int (*metal_irq_handler) (int irq, void *priv); struct metal_device; /** - * @brief Register interrupt handler for driver ID/device. + * @brief Register interrupt handler for interrupt. + * Only allow single interrupt handler for a interrupt. + * + * If irq_handler is NULL, it will unregister interrupt + * handler from interrupt * * @param[in] irq interrupt id * @param[in] irq_handler interrupt handler @@ -51,29 +55,15 @@ int metal_irq_register(int irq, void *drv_id); /** - * @brief Unregister interrupt handler for driver ID and/or device. - * - * If interrupt handler (hd), driver ID (drv_id) and device (dev) - * are NULL, unregister all handlers for this interrupt. - * - * If interrupt handler (hd), device (dev) or driver ID (drv_id), - * are not NULL, unregister handlers matching non NULL criterias. - * e.g: when call is made with drv_id and dev non NULL, - * all handlers matching both are unregistered. - * - * If interrupt is not found, or other criterias not matching, - * return -ENOENT + * @brief Unregister interrupt handler for interrupt. * * @param[in] irq interrupt id - * @param[in] irq_handler interrupt handler - * @param[in] dev metal device this irq belongs to - * @param[in] drv_id driver id. It can be used for driver data. - * @return 0 for success, non-zero on failure */ -int metal_irq_unregister(int irq, - metal_irq_handler irq_handler, - struct metal_device *dev, - void *drv_id); +static inline +void metal_irq_unregister(int irq) +{ + metal_irq_register(irq, 0, NULL, NULL); +} /** * @brief disable interrupts diff --git a/lib/system/freertos/irq.c b/lib/system/freertos/irq.c index 9e035da..31d5ea2 100644 --- a/lib/system/freertos/irq.c +++ b/lib/system/freertos/irq.c @@ -18,8 +18,9 @@ #include #include -/** IRQ handlers descriptor structure */ -struct metal_irq_hddesc { +/** IRQ descriptor structure */ +struct metal_irq_desc { + int irq; /**< interrupt number */ metal_irq_handler hd; /**< irq handler */ void *drv_id; /**< id to identify the driver of the irq handler */ @@ -27,17 +28,10 @@ struct metal_irq_hddesc { struct metal_list node; /**< node on irq handlers list */ }; -/** IRQ descriptor structure */ -struct metal_irq_desc { - int irq; /**< interrupt number */ - struct metal_list hdls; /**< interrupt handlers */ - struct metal_list node; /**< node on irqs list */ -}; - /** IRQ state structure */ struct metal_irqs_state { - struct metal_list irqs; /**< interrupt descriptors */ - metal_mutex_t irq_lock; /**< access lock */ + struct metal_list irqs; /**< interrupt descriptors */ + metal_mutex_t irq_lock; /**< access lock */ }; static struct metal_irqs_state _irqs = { @@ -46,12 +40,11 @@ static struct metal_irqs_state _irqs = { }; int metal_irq_register(int irq, - metal_irq_handler hd, - struct metal_device *dev, - void *drv_id) + metal_irq_handler hd, + struct metal_device *dev, + void *drv_id) { struct metal_irq_desc *irq_p = NULL; - struct metal_irq_hddesc *hdl_p; struct metal_list *node; unsigned int irq_flags_save; @@ -62,176 +55,58 @@ int metal_irq_register(int irq, return -EINVAL; } - if ((drv_id == NULL) || (hd == NULL)) { - metal_log(METAL_LOG_ERROR, "%s: irq %d need drv_id and hd.\n", - __func__, irq); - return -EINVAL; - } - /* Search for irq in list */ metal_mutex_acquire(&_irqs.irq_lock); metal_list_for_each(&_irqs.irqs, node) { irq_p = metal_container_of(node, struct metal_irq_desc, node); if (irq_p->irq == irq) { - struct metal_list *h_node; - - /* Check if drv_id already exist */ - metal_list_for_each(&irq_p->hdls, h_node) { - hdl_p = metal_container_of(h_node, - struct metal_irq_hddesc, - node); - - /* if drv_id already exist reject */ - if ((hdl_p->drv_id == drv_id) && - ((dev == NULL) || (hdl_p->dev == dev))) { - metal_log(METAL_LOG_ERROR, - "%s: irq %d already registered." - "Will not register again.\n", - __func__, irq); - metal_mutex_release(&_irqs.irq_lock); - return -EINVAL; + /* Check if handler has already registered */ + if (irq_p->hd != NULL && hd != NULL && + irq_p->hd != hd) { + metal_log(METAL_LOG_ERROR, + "%s: irq %d already registered." + "Will not register again.\n", + __func__, irq); + metal_mutex_release(&_irqs.irq_lock); + return -EINVAL; + } else { + if (hd == NULL) { + irq_flags_save = metal_irq_save_disable(); + metal_list_del(node); + metal_irq_restore_enable(irq_flags_save); + metal_free_memory(irq_p); } + metal_mutex_release(&_irqs.irq_lock); + return 0; } - /* irq found and drv_id not used, get out of metal_list_for_each */ - break; } } /* Either need to add handler to an existing list or to a new one */ - hdl_p = metal_allocate_memory(sizeof(struct metal_irq_hddesc)); - if (hdl_p == NULL) { + irq_p = metal_allocate_memory(sizeof(*irq_p)); + if (irq_p == NULL) { metal_log(METAL_LOG_ERROR, "%s: irq %d cannot allocate mem for drv_id %d.\n", __func__, irq, drv_id); metal_mutex_release(&_irqs.irq_lock); return -ENOMEM; } - hdl_p->hd = hd; - hdl_p->drv_id = drv_id; - hdl_p->dev = dev; - - /* interrupt already registered, add handler to existing list*/ - if ((irq_p != NULL) && (irq_p->irq == irq)) { - irq_flags_save = metal_irq_save_disable(); - metal_list_add_tail(&irq_p->hdls, &hdl_p->node); - metal_irq_restore_enable(irq_flags_save); - - metal_log(METAL_LOG_DEBUG, "%s: success, irq %d add drv_id %p \n", - __func__, irq, drv_id); - metal_mutex_release(&_irqs.irq_lock); - return 0; - } - - /* interrupt was not already registered, add */ - irq_p = metal_allocate_memory(sizeof(struct metal_irq_desc)); - if (irq_p == NULL) { - metal_log(METAL_LOG_ERROR, "%s: irq %d cannot allocate mem.\n", - __func__, irq); - metal_mutex_release(&_irqs.irq_lock); - return -ENOMEM; - } + irq_p->hd = hd; + irq_p->drv_id = drv_id; + irq_p->dev = dev; irq_p->irq = irq; - metal_list_init(&irq_p->hdls); - metal_list_add_tail(&irq_p->hdls, &hdl_p->node); irq_flags_save = metal_irq_save_disable(); metal_list_add_tail(&_irqs.irqs, &irq_p->node); metal_irq_restore_enable(irq_flags_save); - - metal_log(METAL_LOG_DEBUG, "%s: success, added irq %d\n", __func__, irq); metal_mutex_release(&_irqs.irq_lock); + + metal_log(METAL_LOG_DEBUG, "%s: success, irq %d add drv_id %p \n", + __func__, irq, drv_id); return 0; } -/* helper function for metal_irq_unregister() */ -static void metal_irq_delete_node(struct metal_list *node, void *p_to_free) -{ - unsigned int irq_flags_save; - - irq_flags_save=metal_irq_save_disable(); - metal_list_del(node); - metal_irq_restore_enable(irq_flags_save); - metal_free_memory(p_to_free); -} - -int metal_irq_unregister(int irq, - metal_irq_handler hd, - struct metal_device *dev, - void *drv_id) -{ - struct metal_irq_desc *irq_p; - struct metal_list *node; - - if (irq < 0) { - metal_log(METAL_LOG_ERROR, "%s: irq %d need to be a positive number\n", - __func__, irq); - return -EINVAL; - } - - /* Search for irq in list */ - metal_mutex_acquire(&_irqs.irq_lock); - metal_list_for_each(&_irqs.irqs, node) { - - irq_p = metal_container_of(node, struct metal_irq_desc, node); - - if (irq_p->irq == irq) { - struct metal_list *h_node, *h_prenode; - struct metal_irq_hddesc *hdl_p; - unsigned int delete_count = 0; - - metal_log(METAL_LOG_DEBUG, "%s: found irq %d\n", - __func__, irq); - - /* Search through handlers */ - metal_list_for_each(&irq_p->hdls, h_node) { - hdl_p = metal_container_of(h_node, - struct metal_irq_hddesc, - node); - - if (((hd == NULL) || (hdl_p->hd == hd)) && - ((drv_id == NULL) || (hdl_p->drv_id == drv_id)) && - ((dev == NULL) || (hdl_p->dev == dev))) { - metal_log(METAL_LOG_DEBUG, - "%s: unregister hd=%p drv_id=%p dev=%p\n", - __func__, hdl_p->hd, hdl_p->drv_id, hdl_p->dev); - h_prenode = h_node->prev; - metal_irq_delete_node(h_node, hdl_p); - delete_count++; - h_node = h_prenode; - } - } - - /* we did not find any handler to delete */ - if (!delete_count) { - metal_log(METAL_LOG_DEBUG, "%s: No matching entry\n", - __func__); - metal_mutex_release(&_irqs.irq_lock); - return -ENOENT; - - } - - /* if interrupt handlers list is empty, unregister interrupt */ - if (metal_list_is_empty(&irq_p->hdls)) { - metal_log(METAL_LOG_DEBUG, - "%s: handlers list empty, unregister interrupt\n", - __func__); - metal_irq_delete_node(node, irq_p); - } - - metal_log(METAL_LOG_DEBUG, "%s: success\n", __func__); - - metal_mutex_release(&_irqs.irq_lock); - return 0; - } - } - - metal_log(METAL_LOG_DEBUG, "%s: No matching IRQ entry\n", __func__); - - metal_mutex_release(&_irqs.irq_lock); - return -ENOENT; -} - unsigned int metal_irq_save_disable(void) { return sys_irq_save_disable(); @@ -263,17 +138,7 @@ void metal_irq_isr(unsigned int vector) metal_list_for_each(&_irqs.irqs, node) { irq_p = metal_container_of(node, struct metal_irq_desc, node); - if ((unsigned int)irq_p->irq == vector) { - struct metal_list *h_node; - struct metal_irq_hddesc *hdl_p; - - metal_list_for_each(&irq_p->hdls, h_node) { - hdl_p = metal_container_of(h_node, - struct metal_irq_hddesc, - node); - - (hdl_p->hd)(vector, hdl_p->drv_id); - } - } + if ((unsigned int)irq_p->irq == vector) + (irq_p->hd)(vector, irq_p->drv_id); } } diff --git a/lib/system/generic/irq.c b/lib/system/generic/irq.c index dc12a69..f2b8839 100644 --- a/lib/system/generic/irq.c +++ b/lib/system/generic/irq.c @@ -18,8 +18,9 @@ #include #include -/** IRQ handlers descriptor structure */ -struct metal_irq_hddesc { +/** IRQ descriptor structure */ +struct metal_irq_desc { + int irq; /**< interrupt number */ metal_irq_handler hd; /**< irq handler */ void *drv_id; /**< id to identify the driver of the irq handler */ @@ -27,13 +28,6 @@ struct metal_irq_hddesc { struct metal_list node; /**< node on irq handlers list */ }; -/** IRQ descriptor structure */ -struct metal_irq_desc { - int irq; /**< interrupt number */ - struct metal_list hdls; /**< interrupt handlers */ - struct metal_list node; /**< node on irqs list */ -}; - /** IRQ state structure */ struct metal_irqs_state { struct metal_list irqs; /**< interrupt descriptors */ @@ -46,12 +40,11 @@ static struct metal_irqs_state _irqs = { }; int metal_irq_register(int irq, - metal_irq_handler hd, - struct metal_device *dev, - void *drv_id) + metal_irq_handler hd, + struct metal_device *dev, + void *drv_id) { struct metal_irq_desc *irq_p = NULL; - struct metal_irq_hddesc *hdl_p; struct metal_list *node; unsigned int irq_flags_save; @@ -62,176 +55,58 @@ int metal_irq_register(int irq, return -EINVAL; } - if ((drv_id == NULL) || (hd == NULL)) { - metal_log(METAL_LOG_ERROR, "%s: irq %d need drv_id and hd.\n", - __func__, irq); - return -EINVAL; - } - /* Search for irq in list */ metal_mutex_acquire(&_irqs.irq_lock); metal_list_for_each(&_irqs.irqs, node) { irq_p = metal_container_of(node, struct metal_irq_desc, node); if (irq_p->irq == irq) { - struct metal_list *h_node; - - /* Check if drv_id already exist */ - metal_list_for_each(&irq_p->hdls, h_node) { - hdl_p = metal_container_of(h_node, - struct metal_irq_hddesc, - node); - - /* if drv_id already exist reject */ - if ((hdl_p->drv_id == drv_id) && - ((dev == NULL) || (hdl_p->dev == dev))) { - metal_log(METAL_LOG_ERROR, - "%s: irq %d already registered." - "Will not register again.\n", - __func__, irq); - metal_mutex_release(&_irqs.irq_lock); - return -EINVAL; + /* Check if handler has already registered */ + if (irq_p->hd != NULL && hd != NULL && + irq_p->hd != hd) { + metal_log(METAL_LOG_ERROR, + "%s: irq %d already registered." + "Will not register again.\n", + __func__, irq); + metal_mutex_release(&_irqs.irq_lock); + return -EINVAL; + } else { + if (hd == NULL) { + irq_flags_save = metal_irq_save_disable(); + metal_list_del(node); + metal_irq_restore_enable(irq_flags_save); + metal_free_memory(irq_p); } + metal_mutex_release(&_irqs.irq_lock); + return 0; } - /* irq found and drv_id not used, get out of metal_list_for_each */ - break; } } /* Either need to add handler to an existing list or to a new one */ - hdl_p = metal_allocate_memory(sizeof(struct metal_irq_hddesc)); - if (hdl_p == NULL) { + irq_p = metal_allocate_memory(sizeof(*irq_p)); + if (irq_p == NULL) { metal_log(METAL_LOG_ERROR, "%s: irq %d cannot allocate mem for drv_id %d.\n", __func__, irq, drv_id); metal_mutex_release(&_irqs.irq_lock); return -ENOMEM; } - hdl_p->hd = hd; - hdl_p->drv_id = drv_id; - hdl_p->dev = dev; - - /* interrupt already registered, add handler to existing list*/ - if ((irq_p != NULL) && (irq_p->irq == irq)) { - irq_flags_save = metal_irq_save_disable(); - metal_list_add_tail(&irq_p->hdls, &hdl_p->node); - metal_irq_restore_enable(irq_flags_save); - - metal_log(METAL_LOG_DEBUG, "%s: success, irq %d add drv_id %p \n", - __func__, irq, drv_id); - metal_mutex_release(&_irqs.irq_lock); - return 0; - } - - /* interrupt was not already registered, add */ - irq_p = metal_allocate_memory(sizeof(struct metal_irq_desc)); - if (irq_p == NULL) { - metal_log(METAL_LOG_ERROR, "%s: irq %d cannot allocate mem.\n", - __func__, irq); - metal_mutex_release(&_irqs.irq_lock); - return -ENOMEM; - } + irq_p->hd = hd; + irq_p->drv_id = drv_id; + irq_p->dev = dev; irq_p->irq = irq; - metal_list_init(&irq_p->hdls); - metal_list_add_tail(&irq_p->hdls, &hdl_p->node); irq_flags_save = metal_irq_save_disable(); metal_list_add_tail(&_irqs.irqs, &irq_p->node); metal_irq_restore_enable(irq_flags_save); - - metal_log(METAL_LOG_DEBUG, "%s: success, added irq %d\n", __func__, irq); metal_mutex_release(&_irqs.irq_lock); + + metal_log(METAL_LOG_DEBUG, "%s: success, irq %d add drv_id %p \n", + __func__, irq, drv_id); return 0; } -/* helper function for metal_irq_unregister() */ -static void metal_irq_delete_node(struct metal_list *node, void *p_to_free) -{ - unsigned int irq_flags_save; - - irq_flags_save=metal_irq_save_disable(); - metal_list_del(node); - metal_irq_restore_enable(irq_flags_save); - metal_free_memory(p_to_free); -} - -int metal_irq_unregister(int irq, - metal_irq_handler hd, - struct metal_device *dev, - void *drv_id) -{ - struct metal_irq_desc *irq_p; - struct metal_list *node; - - if (irq < 0) { - metal_log(METAL_LOG_ERROR, "%s: irq %d need to be a positive number\n", - __func__, irq); - return -EINVAL; - } - - /* Search for irq in list */ - metal_mutex_acquire(&_irqs.irq_lock); - metal_list_for_each(&_irqs.irqs, node) { - - irq_p = metal_container_of(node, struct metal_irq_desc, node); - - if (irq_p->irq == irq) { - struct metal_list *h_node, *h_prenode; - struct metal_irq_hddesc *hdl_p; - unsigned int delete_count = 0; - - metal_log(METAL_LOG_DEBUG, "%s: found irq %d\n", - __func__, irq); - - /* Search through handlers */ - metal_list_for_each(&irq_p->hdls, h_node) { - hdl_p = metal_container_of(h_node, - struct metal_irq_hddesc, - node); - - if (((hd == NULL) || (hdl_p->hd == hd)) && - ((drv_id == NULL) || (hdl_p->drv_id == drv_id)) && - ((dev == NULL) || (hdl_p->dev == dev))) { - metal_log(METAL_LOG_DEBUG, - "%s: unregister hd=%p drv_id=%p dev=%p\n", - __func__, hdl_p->hd, hdl_p->drv_id, hdl_p->dev); - h_prenode = h_node->prev; - metal_irq_delete_node(h_node, hdl_p); - h_node = h_prenode; - delete_count++; - } - } - - /* we did not find any handler to delete */ - if (!delete_count) { - metal_log(METAL_LOG_DEBUG, "%s: No matching entry\n", - __func__); - metal_mutex_release(&_irqs.irq_lock); - return -ENOENT; - - } - - /* if interrupt handlers list is empty, unregister interrupt */ - if (metal_list_is_empty(&irq_p->hdls)) { - metal_log(METAL_LOG_DEBUG, - "%s: handlers list empty, unregister interrupt\n", - __func__); - metal_irq_delete_node(node, irq_p); - } - - metal_log(METAL_LOG_DEBUG, "%s: success\n", __func__); - - metal_mutex_release(&_irqs.irq_lock); - return 0; - } - } - - metal_log(METAL_LOG_DEBUG, "%s: No matching IRQ entry\n", __func__); - - metal_mutex_release(&_irqs.irq_lock); - return -ENOENT; -} - unsigned int metal_irq_save_disable(void) { return sys_irq_save_disable(); @@ -263,17 +138,7 @@ void metal_irq_isr(unsigned int vector) metal_list_for_each(&_irqs.irqs, node) { irq_p = metal_container_of(node, struct metal_irq_desc, node); - if ((unsigned int)irq_p->irq == vector) { - struct metal_list *h_node; - struct metal_irq_hddesc *hdl_p; - - metal_list_for_each(&irq_p->hdls, h_node) { - hdl_p = metal_container_of(h_node, - struct metal_irq_hddesc, - node); - - (hdl_p->hd)(vector, hdl_p->drv_id); - } - } + if ((unsigned int)irq_p->irq == vector) + (irq_p->hd)(vector, irq_p->drv_id); } } diff --git a/lib/system/generic/irq.h b/lib/system/generic/irq.h index 5744275..d4be10c 100644 --- a/lib/system/generic/irq.h +++ b/lib/system/generic/irq.h @@ -21,12 +21,11 @@ extern "C" { #endif /** - * @brief default interrupt handler + * @brief default interrupt handler * @param[in] vector interrupt vector */ void metal_irq_isr(unsigned int vector); - #ifdef __cplusplus } #endif diff --git a/lib/system/linux/device.c b/lib/system/linux/device.c index 6fc3cb6..4a5e493 100644 --- a/lib/system/linux/device.c +++ b/lib/system/linux/device.c @@ -264,7 +264,7 @@ static void metal_uio_dev_close(struct linux_bus *lbus, /* Normally this call would not be needed, and is added as precaution. Also for uio there is only 1 interrupt associated to the fd/device, we therefore do not need to specify a particular device */ - metal_irq_unregister(ldev->fd, NULL, NULL, NULL); + metal_irq_unregister(ldev->fd); if (ldev->override) { sysfs_write_attribute(ldev->override, "", 1); diff --git a/lib/system/linux/irq.c b/lib/system/linux/irq.c index 89e5f2f..8955181 100644 --- a/lib/system/linux/irq.c +++ b/lib/system/linux/irq.c @@ -20,30 +20,30 @@ #include #include #include +#include +#include #include -#include -#include #include #include +#include #define MAX_IRQS FD_SETSIZE /**< maximum number of irqs */ #define METAL_IRQ_STOP 0xFFFFFFFF /**< stop interrupts handling thread */ -/** IRQ handler descriptor structure */ -struct metal_irq_hddesc { +#define METAL_LINUX_IRQ_DISABLED 0 /**< IRQ is disabled */ +#define METAL_LINUX_IRQ_ENABLED 1 /**< IRQ is enabled */ + +/** IRQ descriptor structure */ +struct metal_irq_desc { metal_irq_handler hd; /**< irq handler */ struct metal_device *dev; /**< metal device */ void *drv_id; /**< id to identify the driver of the irq handler*/ - struct metal_list list; /**< handler list container */ + bool state; /**< IRQ enabling state */ }; struct metal_irqs_state { - struct metal_irq_hddesc hds[MAX_IRQS]; /**< irqs handlers descriptor */ - signed char irq_reg_stat[MAX_IRQS]; /**< irqs registration statistics. - It restore how many handlers have - been registered for each IRQ. */ - + struct metal_irq_desc hds[MAX_IRQS]; /**< irqs handlers descriptor */ int irq_reg_fd; /**< irqs registration notification file descriptor */ @@ -62,8 +62,6 @@ int metal_irq_register(int irq, void *drv_id) { uint64_t val = 1; - struct metal_irq_hddesc *hd_desc; - struct metal_list *h_node; int ret; if ((irq < 0) || (irq >= MAX_IRQS)) { @@ -73,12 +71,6 @@ int metal_irq_register(int irq, return -EINVAL; } - if ((drv_id == NULL) || (hd == NULL)) { - metal_log(METAL_LOG_ERROR, "%s: irq %d need drv_id and hd.\n", - __func__, irq); - return -EINVAL; - } - metal_mutex_acquire(&_irqs.irq_lock); if (_irqs.irq_state == METAL_IRQ_STOP) { metal_log(METAL_LOG_ERROR, @@ -88,117 +80,38 @@ int metal_irq_register(int irq, return -EINVAL; } - metal_list_for_each(&_irqs.hds[irq].list, h_node) { - hd_desc = metal_container_of(h_node, struct metal_irq_hddesc, list); - - /* if drv_id already exist reject */ - if ((hd_desc->drv_id == drv_id) && - ((dev == NULL) || (hd_desc->dev == dev))) { - metal_log(METAL_LOG_ERROR, "%s: irq %d already registered." - "Will not register again.\n", - __func__, irq); - metal_mutex_release(&_irqs.irq_lock); - return -EINVAL; - } - /* drv_id not used, get out of metal_list_for_each */ - break; - } - - /* Add to the end */ - hd_desc = metal_allocate_memory(sizeof(struct metal_irq_hddesc)); - if (hd_desc == NULL) { - metal_log(METAL_LOG_ERROR, - "%s: irq %d cannot allocate mem for drv_id %d.\n", - __func__, irq, drv_id); + if (_irqs.hds[irq].hd != NULL && hd != NULL && + _irqs.hds[irq].hd != hd) { + metal_log(METAL_LOG_ERROR, "%s: irq %d already registered." + "Will not register again.\n", __func__, irq); metal_mutex_release(&_irqs.irq_lock); - return -ENOMEM; + return -EINVAL; } - hd_desc->hd = hd; - hd_desc->drv_id = drv_id; - hd_desc->dev = dev; - metal_list_add_tail(&_irqs.hds[irq].list, &hd_desc->list); - _irqs.irq_reg_stat[irq]++; + _irqs.hds[irq].hd = hd; + _irqs.hds[irq].dev = dev; + _irqs.hds[irq].drv_id = drv_id; + if (hd != NULL) + _irqs.hds[irq].state = METAL_LINUX_IRQ_ENABLED; + else + _irqs.hds[irq].state = METAL_LINUX_IRQ_DISABLED; metal_mutex_release(&_irqs.irq_lock); ret = write(_irqs.irq_reg_fd, &val, sizeof(val)); if (ret < 0) { - metal_log(METAL_LOG_DEBUG, "%s: write failed IRQ %d\n", __func__, irq); + metal_log(METAL_LOG_DEBUG, "%s: write failed IRQ %d\n", + __func__, irq); } metal_log(METAL_LOG_DEBUG, "%s: registered IRQ %d\n", __func__, irq); return 0; } -int metal_irq_unregister(int irq, - metal_irq_handler hd, - struct metal_device *dev, - void *drv_id) -{ - uint64_t val = 1; - struct metal_irq_hddesc *hd_desc; - struct metal_list *h_node; - int ret; - unsigned int delete_count = 0; - - if ((irq < 0) || (irq >= MAX_IRQS)) { - metal_log(METAL_LOG_ERROR, - "%s: irq %d is larger than the max supported %d.\n", - __func__, irq, MAX_IRQS); - return -EINVAL; - } - - metal_mutex_acquire(&_irqs.irq_lock); - if (_irqs.irq_state == METAL_IRQ_STOP) { - metal_log(METAL_LOG_ERROR, - "%s: failed. metal IRQ handling has stopped.\n", __func__); - metal_mutex_release(&_irqs.irq_lock); - return -EINVAL; - } - - if (!hd && !drv_id && !dev) { - if (0 == _irqs.irq_reg_stat[irq]) - goto no_entry; - - _irqs.irq_reg_stat[irq] = 0; - goto out; - } - - /* Search through handlers */ - metal_list_for_each(&_irqs.hds[irq].list, h_node) { - hd_desc = metal_container_of(h_node, struct metal_irq_hddesc, list); - - if (((hd == NULL) || (hd_desc->hd == hd)) && - ((drv_id == NULL) || (hd_desc->drv_id == drv_id)) && - ((dev == NULL) || (hd_desc->dev == dev))) { - if (_irqs.irq_reg_stat[irq] > 0) - _irqs.irq_reg_stat[irq]--; - h_node = h_node->prev; - metal_list_del(h_node->next); - metal_free_memory(hd_desc); - delete_count++; - } - } - - if (delete_count) - goto out; - -no_entry: - metal_log(METAL_LOG_DEBUG, "%s: No matching entry.\n", __func__); - metal_mutex_release(&_irqs.irq_lock); - return -ENOENT; -out: - metal_mutex_release(&_irqs.irq_lock); - ret = write(_irqs.irq_reg_fd, &val, sizeof(val)); - if (ret < 0) { - metal_log(METAL_LOG_DEBUG, "%s: write failed IRQ %d\n", __func__, irq); - } - metal_log(METAL_LOG_DEBUG, "%s: unregistered IRQ %d (%d)\n", __func__, irq, delete_count); - return 0; -} - unsigned int metal_irq_save_disable() { + /* This is to avoid deadlock if it is called in ISR */ + if (pthread_self() == _irqs.irq_pthread) + return 0; metal_mutex_acquire(&_irqs.irq_lock); return 0; } @@ -206,17 +119,62 @@ unsigned int metal_irq_save_disable() void metal_irq_restore_enable(unsigned flags) { (void)flags; - metal_mutex_release(&_irqs.irq_lock); + if (pthread_self() != _irqs.irq_pthread) + metal_mutex_release(&_irqs.irq_lock); } void metal_irq_enable(unsigned int vector) { - (void)vector; + uint64_t val = 1; + int ret; + + if (vector >= MAX_IRQS) { + metal_log(METAL_LOG_ERROR, + "%s: irq %d is larger than the max supported %d.\n", + __func__, vector, MAX_IRQS - 1); + return; + } + + metal_mutex_acquire(&_irqs.irq_lock); + if (_irqs.irq_state == METAL_IRQ_STOP) { + metal_mutex_release(&_irqs.irq_lock); + return; + } + _irqs.hds[vector].state = METAL_LINUX_IRQ_ENABLED; + metal_mutex_release(&_irqs.irq_lock); + + ret = write(_irqs.irq_reg_fd, &val, sizeof(val)); + if (ret < 0) { + metal_log(METAL_LOG_DEBUG, "%s: write failed IRQ %d\n", + __func__, vector); + } } void metal_irq_disable(unsigned int vector) { - (void)vector; + uint64_t val = 1; + int ret; + + if (vector >= MAX_IRQS) { + metal_log(METAL_LOG_ERROR, + "%s: irq %d is larger than the max supported %d.\n", + __func__, vector, MAX_IRQS - 1); + return; + } + + metal_mutex_acquire(&_irqs.irq_lock); + if (_irqs.irq_state == METAL_IRQ_STOP) { + metal_mutex_release(&_irqs.irq_lock); + return; + } + _irqs.hds[vector].state = METAL_LINUX_IRQ_DISABLED; + metal_mutex_release(&_irqs.irq_lock); + + ret = write(_irqs.irq_reg_fd, &val, sizeof(val)); + if (ret < 0) { + metal_log(METAL_LOG_DEBUG, "%s: write failed IRQ %d\n", + __func__, vector); + } } /** @@ -261,7 +219,8 @@ static void *metal_linux_irq_handling(void *args) pfds[0].fd = _irqs.irq_reg_fd; pfds[0].events = POLLIN; for(i = 0, j = 1; i < MAX_IRQS && j < MAX_IRQS; i++) { - if (_irqs.irq_reg_stat[i] > 0) { + if (_irqs.hds[i].hd != NULL && + _irqs.hds[i].state == METAL_LINUX_IRQ_ENABLED) { pfds[j].fd = i; pfds[j].events = POLLIN; j++; @@ -283,32 +242,28 @@ static void *metal_linux_irq_handling(void *args) /* IRQ registration change notification */ if (read(pfds[i].fd, (void*)&val, sizeof(uint64_t)) < 0) metal_log(METAL_LOG_ERROR, - "%s, read irq fd %d failed.\n", - __func__, pfds[i].fd); + "%s, read irq fd %d failed.\n", + __func__, pfds[i].fd); } else if ((pfds[i].revents & (POLLIN | POLLRDNORM))) { - struct metal_irq_hddesc *hd_desc; /**< irq handler descriptor */ - struct metal_device *dev = NULL; /**< metal device IRQ belongs to */ - int irq_handled = 0; /**< flag to indicate if irq is handled */ - struct metal_list *h_node; + struct metal_irq_desc *desc; + struct metal_device *dev = NULL; + int irq_handled = 0; - metal_list_for_each(&_irqs.hds[pfds[i].fd].list, h_node) { - hd_desc = metal_container_of(h_node, struct metal_irq_hddesc, list); + metal_mutex_acquire(&_irqs.irq_lock); + desc = &_irqs.hds[pfds[i].fd]; + dev = desc->dev; - metal_mutex_acquire(&_irqs.irq_lock); - if (!dev) - dev = hd_desc->dev; - metal_mutex_release(&_irqs.irq_lock); - - if ((hd_desc->hd)(pfds[i].fd, hd_desc->drv_id) == METAL_IRQ_HANDLED) - irq_handled = 1; - } + if (desc->hd(pfds[i].fd, desc->drv_id) + == METAL_IRQ_HANDLED) + irq_handled = 1; if (irq_handled) { if (dev && dev->bus->ops.dev_irq_ack) - dev->bus->ops.dev_irq_ack(dev->bus, dev, i); + dev->bus->ops.dev_irq_ack(dev->bus, dev, i); } + metal_mutex_release(&_irqs.irq_lock); } else if (pfds[i].revents) { metal_log(METAL_LOG_DEBUG, - "%s: poll unexpected. fd %d: %d\n", + "%s: poll unexpected. fd %d: %d\n", __func__, pfds[i].fd, pfds[i].revents); } } @@ -323,15 +278,10 @@ static void *metal_linux_irq_handling(void *args) */ int metal_linux_irq_init() { - int ret, irq; + int ret; memset(&_irqs, 0, sizeof(_irqs)); - /* init handlers list for each interrupt in table */ - for (irq=0; irq < MAX_IRQS; irq++) { - metal_list_init(&_irqs.hds[irq].list); - } - _irqs.irq_reg_fd = eventfd(0,0); if (_irqs.irq_reg_fd < 0) { metal_log(METAL_LOG_ERROR, "Failed to create eventfd for IRQ handling.\n"); diff --git a/lib/system/zephyr/irq.c b/lib/system/zephyr/irq.c index dbd2b98..66a241b 100644 --- a/lib/system/zephyr/irq.c +++ b/lib/system/zephyr/irq.c @@ -17,10 +17,10 @@ #include #include #include -#include -/** IRQ handlers descriptor structure */ -struct metal_irq_hddesc { +/** IRQ descriptor structure */ +struct metal_irq_desc { + int irq; /**< interrupt number */ metal_irq_handler hd; /**< irq handler */ void *drv_id; /**< id to identify the driver of the irq handler */ @@ -28,13 +28,6 @@ struct metal_irq_hddesc { struct metal_list node; /**< node on irq handlers list */ }; -/** IRQ descriptor structure */ -struct metal_irq_desc { - int irq; /**< interrupt number */ - struct metal_list hdls; /**< interrupt handlers */ - struct metal_list node; /**< node on irqs list */ -}; - /** IRQ state structure */ struct metal_irqs_state { struct metal_list irqs; /**< interrupt descriptors */ @@ -47,12 +40,11 @@ static struct metal_irqs_state _irqs = { }; int metal_irq_register(int irq, - metal_irq_handler hd, - struct metal_device *dev, - void *drv_id) + metal_irq_handler hd, + struct metal_device *dev, + void *drv_id) { struct metal_irq_desc *irq_p = NULL; - struct metal_irq_hddesc *hdl_p; struct metal_list *node; unsigned int irq_flags_save; @@ -63,194 +55,76 @@ int metal_irq_register(int irq, return -EINVAL; } - if ((drv_id == NULL) || (hd == NULL)) { - metal_log(METAL_LOG_ERROR, "%s: irq %d need drv_id and hd.\n", - __func__, irq); - return -EINVAL; - } - /* Search for irq in list */ metal_mutex_acquire(&_irqs.irq_lock); metal_list_for_each(&_irqs.irqs, node) { irq_p = metal_container_of(node, struct metal_irq_desc, node); if (irq_p->irq == irq) { - struct metal_list *h_node; - - /* Check if drv_id already exist */ - metal_list_for_each(&irq_p->hdls, h_node) { - hdl_p = metal_container_of(h_node, - struct metal_irq_hddesc, - node); - - /* if drv_id already exist reject */ - if ((hdl_p->drv_id == drv_id) && - ((dev == NULL) || (hdl_p->dev == dev))) { - metal_log(METAL_LOG_ERROR, - "%s: irq %d already registered." - "Will not register again.\n", - __func__, irq); - metal_mutex_release(&_irqs.irq_lock); - return -EINVAL; + /* Check if handler has already registered */ + if (irq_p->hd != NULL && hd != NULL && + irq_p->hd != hd) { + metal_log(METAL_LOG_ERROR, + "%s: irq %d already registered." + "Will not register again.\n", + __func__, irq); + metal_mutex_release(&_irqs.irq_lock); + return -EINVAL; + } else { + if (hd == NULL) { + irq_flags_save = metal_irq_save_disable(); + metal_list_del(node); + metal_irq_restore_enable(irq_flags_save); + metal_free_memory(irq_p); } + metal_mutex_release(&_irqs.irq_lock); + return 0; } - /* irq found and drv_id not used, get out of metal_list_for_each */ - break; } } /* Either need to add handler to an existing list or to a new one */ - hdl_p = metal_allocate_memory(sizeof(struct metal_irq_hddesc)); - if (hdl_p == NULL) { + irq_p = metal_allocate_memory(sizeof(*irq_p)); + if (irq_p == NULL) { metal_log(METAL_LOG_ERROR, "%s: irq %d cannot allocate mem for drv_id %d.\n", __func__, irq, drv_id); metal_mutex_release(&_irqs.irq_lock); return -ENOMEM; } - hdl_p->hd = hd; - hdl_p->drv_id = drv_id; - hdl_p->dev = dev; - - /* interrupt already registered, add handler to existing list*/ - if ((irq_p != NULL) && (irq_p->irq == irq)) { - irq_flags_save = metal_irq_save_disable(); - metal_list_add_tail(&irq_p->hdls, &hdl_p->node); - metal_irq_restore_enable(irq_flags_save); - - metal_log(METAL_LOG_DEBUG, "%s: success, irq %d add drv_id %p \n", - __func__, irq, drv_id); - metal_mutex_release(&_irqs.irq_lock); - return 0; - } - - /* interrupt was not already registered, add */ - irq_p = metal_allocate_memory(sizeof(struct metal_irq_desc)); - if (irq_p == NULL) { - metal_log(METAL_LOG_ERROR, "%s: irq %d cannot allocate mem.\n", - __func__, irq); - metal_mutex_release(&_irqs.irq_lock); - return -ENOMEM; - } + irq_p->hd = hd; + irq_p->drv_id = drv_id; + irq_p->dev = dev; irq_p->irq = irq; - metal_list_init(&irq_p->hdls); - metal_list_add_tail(&irq_p->hdls, &hdl_p->node); irq_flags_save = metal_irq_save_disable(); metal_list_add_tail(&_irqs.irqs, &irq_p->node); metal_irq_restore_enable(irq_flags_save); - - metal_log(METAL_LOG_DEBUG, "%s: success, added irq %d\n", __func__, irq); metal_mutex_release(&_irqs.irq_lock); + + metal_log(METAL_LOG_DEBUG, "%s: success, irq %d add drv_id %p \n", + __func__, irq, drv_id); return 0; } -/* helper function for metal_irq_unregister() */ -static void metal_irq_delete_node(struct metal_list *node, void *p_to_free) -{ - unsigned int irq_flags_save; - - irq_flags_save=metal_irq_save_disable(); - metal_list_del(node); - metal_irq_restore_enable(irq_flags_save); - metal_free_memory(p_to_free); -} - -int metal_irq_unregister(int irq, - metal_irq_handler hd, - struct metal_device *dev, - void *drv_id) -{ - struct metal_irq_desc *irq_p; - struct metal_list *node; - - if (irq < 0) { - metal_log(METAL_LOG_ERROR, "%s: irq %d need to be a positive number\n", - __func__, irq); - return -EINVAL; - } - - /* Search for irq in list */ - metal_mutex_acquire(&_irqs.irq_lock); - metal_list_for_each(&_irqs.irqs, node) { - - irq_p = metal_container_of(node, struct metal_irq_desc, node); - - if (irq_p->irq == irq) { - struct metal_list *h_node, *h_prenode; - struct metal_irq_hddesc *hdl_p; - unsigned int delete_count = 0; - - metal_log(METAL_LOG_DEBUG, "%s: found irq %d\n", - __func__, irq); - - /* Search through handlers */ - metal_list_for_each(&irq_p->hdls, h_node) { - hdl_p = metal_container_of(h_node, - struct metal_irq_hddesc, - node); - - if (((hd == NULL) || (hdl_p->hd == hd)) && - ((drv_id == NULL) || (hdl_p->drv_id == drv_id)) && - ((dev == NULL) || (hdl_p->dev == dev))) { - metal_log(METAL_LOG_DEBUG, - "%s: unregister hd=%p drv_id=%p dev=%p\n", - __func__, hdl_p->hd, hdl_p->drv_id, hdl_p->dev); - h_prenode = h_node->prev; - metal_irq_delete_node(h_node, hdl_p); - h_node = h_prenode; - delete_count++; - } - } - - /* we did not find any handler to delete */ - if (!delete_count) { - metal_log(METAL_LOG_DEBUG, "%s: No matching entry\n", - __func__); - metal_mutex_release(&_irqs.irq_lock); - return -ENOENT; - - } - - /* if interrupt handlers list is empty, unregister interrupt */ - if (metal_list_is_empty(&irq_p->hdls)) { - metal_log(METAL_LOG_DEBUG, - "%s: handlers list empty, unregister interrupt\n", - __func__); - metal_irq_delete_node(node, irq_p); - } - - metal_log(METAL_LOG_DEBUG, "%s: success\n", __func__); - - metal_mutex_release(&_irqs.irq_lock); - return 0; - } - } - - metal_log(METAL_LOG_DEBUG, "%s: No matching IRQ entry\n", __func__); - - metal_mutex_release(&_irqs.irq_lock); - return -ENOENT; -} - unsigned int metal_irq_save_disable(void) { - return irq_lock(); + return sys_irq_save_disable(); } void metal_irq_restore_enable(unsigned int flags) { - irq_unlock(flags); + sys_irq_restore_enable(flags); } void metal_irq_enable(unsigned int vector) { - irq_enable(vector); + sys_irq_enable(vector); } void metal_irq_disable(unsigned int vector) { - irq_disable(vector); + sys_irq_disable(vector); } /** @@ -264,17 +138,7 @@ void metal_irq_isr(unsigned int vector) metal_list_for_each(&_irqs.irqs, node) { irq_p = metal_container_of(node, struct metal_irq_desc, node); - if ((unsigned int)irq_p->irq == vector) { - struct metal_list *h_node; - struct metal_irq_hddesc *hdl_p; - - metal_list_for_each(&irq_p->hdls, h_node) { - hdl_p = metal_container_of(h_node, - struct metal_irq_hddesc, - node); - - (hdl_p->hd)(vector, hdl_p->drv_id); - } - } + if ((unsigned int)irq_p->irq == vector) + (irq_p->hd)(vector, irq_p->drv_id); } }