mirror of
https://github.com/OpenAMP/libmetal.git
synced 2026-09-24 11:13:47 +08:00
lib: one handler per IRQ
Change IRQ registration API to support one handler per IRQ. Signed-off-by: Wendy Liang <wendy.liang@xilinx.com>
This commit is contained in:
@@ -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
|
||||
|
||||
+35
-170
@@ -18,8 +18,9 @@
|
||||
#include <metal/utilities.h>
|
||||
#include <metal/alloc.h>
|
||||
|
||||
/** 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);
|
||||
}
|
||||
}
|
||||
|
||||
+33
-168
@@ -18,8 +18,9 @@
|
||||
#include <metal/utilities.h>
|
||||
#include <metal/alloc.h>
|
||||
|
||||
/** 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
+92
-142
@@ -20,30 +20,30 @@
|
||||
#include <metal/alloc.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sched.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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");
|
||||
|
||||
+37
-173
@@ -17,10 +17,10 @@
|
||||
#include <metal/list.h>
|
||||
#include <metal/utilities.h>
|
||||
#include <metal/alloc.h>
|
||||
#include <irq.h>
|
||||
|
||||
/** 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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user