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:
Wendy Liang
2019-01-30 10:46:37 -08:00
parent 81f85dc244
commit 8c52e979fb
7 changed files with 210 additions and 677 deletions
+11 -21
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
}
+1 -2
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
}