Commit c813f592 authored by Martyn Welch's avatar Martyn Welch Committed by Greg Kroah-Hartman

Staging: vme: Pull common VME interrupt handling into core code

Currently the VME callback infrastructure is replicated in each VME driver.
Move this common code into the VME core. Rename functions to fit in better
with naming of other VME functions.
Signed-off-by: default avatarMartyn Welch <martyn.welch@gefanuc.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent beb9ccc6
......@@ -53,9 +53,6 @@ struct mutex vme_int; /*
* Only one VME interrupt can be
* generated at a time, provide locking
*/
struct mutex vme_irq; /* Locking for VME irq callback configuration */
static char driver_name[] = "vme_ca91cx42";
......@@ -157,23 +154,13 @@ static u32 ca91cx42_LERR_irqhandler(void)
static u32 ca91cx42_VIRQ_irqhandler(int stat)
{
int vec, i, serviced = 0;
void (*call)(int, int, void *);
void *priv_data;
for (i = 7; i > 0; i--) {
if (stat & (1 << i)) {
vec = ioread32(ca91cx42_bridge->base +
CA91CX42_V_STATID[i]) & 0xff;
call = ca91cx42_bridge->irq[i - 1].callback[vec].func;
priv_data =
ca91cx42_bridge->irq[i - 1].callback[vec].priv_data;
if (call != NULL)
call(i, vec, priv_data);
else
printk("Spurilous VME interrupt, level:%x, "
"vector:%x\n", i, vec);
vme_irq_handler(ca91cx42_bridge, i, vec);
serviced |= (1 << i);
}
......@@ -234,6 +221,8 @@ static int ca91cx42_irq_init(struct vme_bridge *bridge)
/* Initialise list for VME bus errors */
INIT_LIST_HEAD(&(bridge->vme_errors));
mutex_init(&(bridge->irq_mtx));
/* Disable interrupts from PCI to VME */
iowrite32(0, bridge->base + VINT_EN);
......@@ -281,66 +270,31 @@ static void ca91cx42_irq_exit(struct pci_dev *pdev)
/*
* Set up an VME interrupt
*/
int ca91cx42_request_irq(int level, int statid,
void (*callback)(int level, int vector, void *priv_data),
void *priv_data)
void ca91cx42_irq_set(int level, int state, int sync)
{
struct pci_dev *pdev;
u32 tmp;
mutex_lock(&(vme_irq));
if (ca91cx42_bridge->irq[level - 1].callback[statid].func) {
mutex_unlock(&(vme_irq));
printk("VME Interrupt already taken\n");
return -EBUSY;
}
ca91cx42_bridge->irq[level - 1].count++;
ca91cx42_bridge->irq[level - 1].callback[statid].priv_data = priv_data;
ca91cx42_bridge->irq[level - 1].callback[statid].func = callback;
/* Enable IRQ level */
tmp = ioread32(ca91cx42_bridge->base + LINT_EN);
tmp |= CA91CX42_LINT_VIRQ[level];
iowrite32(tmp, ca91cx42_bridge->base + LINT_EN);
mutex_unlock(&(vme_irq));
return 0;
}
/*
* Free VME interrupt
*/
void ca91cx42_free_irq(int level, int statid)
{
u32 tmp;
struct pci_dev *pdev;
mutex_lock(&(vme_irq));
ca91cx42_bridge->irq[level - 1].count--;
/* Disable IRQ level if no more interrupts attached at this level*/
if (ca91cx42_bridge->irq[level - 1].count == 0) {
tmp = ioread32(ca91cx42_bridge->base + LINT_EN);
if (state == 0)
tmp &= ~CA91CX42_LINT_VIRQ[level];
iowrite32(tmp, ca91cx42_bridge->base + LINT_EN);
else
tmp |= CA91CX42_LINT_VIRQ[level];
iowrite32(tmp, ca91cx42_bridge->base + LINT_EN);
if ((state == 0) && (sync != 0)) {
pdev = container_of(ca91cx42_bridge->parent, struct pci_dev,
dev);
synchronize_irq(pdev->irq);
}
ca91cx42_bridge->irq[level - 1].callback[statid].func = NULL;
ca91cx42_bridge->irq[level - 1].callback[statid].priv_data = NULL;
mutex_unlock(&(vme_irq));
}
int ca91cx42_generate_irq(int level, int statid)
int ca91cx42_irq_generate(int level, int statid)
{
u32 tmp;
......@@ -1064,7 +1018,6 @@ static int ca91cx42_probe(struct pci_dev *pdev, const struct pci_device_id *id)
init_waitqueue_head(&dma_queue);
init_waitqueue_head(&iack_queue);
mutex_init(&(vme_int));
mutex_init(&(vme_irq));
mutex_init(&(vme_rmw));
ca91cx42_bridge->parent = &(pdev->dev);
......@@ -1181,9 +1134,8 @@ static int ca91cx42_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ca91cx42_bridge->dma_list_exec = ca91cx42_dma_list_exec;
ca91cx42_bridge->dma_list_empty = ca91cx42_dma_list_empty;
#endif
ca91cx42_bridge->request_irq = ca91cx42_request_irq;
ca91cx42_bridge->free_irq = ca91cx42_free_irq;
ca91cx42_bridge->generate_irq = ca91cx42_generate_irq;
ca91cx42_bridge->irq_set = ca91cx42_irq_set;
ca91cx42_bridge->irq_generate = ca91cx42_irq_generate;
#if 0
ca91cx42_bridge->lm_set = ca91cx42_lm_set;
ca91cx42_bridge->lm_get = ca91cx42_lm_get;
......
......@@ -77,8 +77,6 @@ struct mutex vme_int; /*
* Only one VME interrupt can be
* generated at a time, provide locking
*/
struct mutex vme_irq; /* Locking for VME irq callback configuration */
static char driver_name[] = "vme_tsi148";
......@@ -251,8 +249,6 @@ static u32 tsi148_IACK_irqhandler(void)
static u32 tsi148_VIRQ_irqhandler(u32 stat)
{
int vec, i, serviced = 0;
void (*call)(int, int, void *);
void *priv_data;
for (i = 7; i > 0; i--) {
if (stat & (1 << i)) {
......@@ -265,15 +261,7 @@ static u32 tsi148_VIRQ_irqhandler(u32 stat)
vec = ioread8(tsi148_bridge->base +
TSI148_LCSR_VIACK[i] + 3);
call = tsi148_bridge->irq[i - 1].callback[vec].func;
priv_data =
tsi148_bridge->irq[i-1].callback[vec].priv_data;
if (call != NULL)
call(i, vec, priv_data);
else
printk("Spurilous VME interrupt, level:%x, "
"vector:%x\n", i, vec);
vme_irq_handler(tsi148_bridge, i, vec);
serviced |= (1 << i);
}
......@@ -353,6 +341,8 @@ static int tsi148_irq_init(struct vme_bridge *bridge)
/* Initialise list for VME bus errors */
INIT_LIST_HEAD(&(bridge->vme_errors));
mutex_init(&(bridge->irq_mtx));
result = request_irq(pdev->irq,
tsi148_irqhandler,
IRQF_SHARED,
......@@ -432,55 +422,15 @@ int tsi148_iack_received(void)
}
/*
* Set up an VME interrupt
* Configure VME interrupt
*/
int tsi148_request_irq(int level, int statid,
void (*callback)(int level, int vector, void *priv_data),
void *priv_data)
void tsi148_irq_set(int level, int state, int sync)
{
u32 tmp;
mutex_lock(&(vme_irq));
if(tsi148_bridge->irq[level - 1].callback[statid].func) {
mutex_unlock(&(vme_irq));
printk("VME Interrupt already taken\n");
return -EBUSY;
}
tsi148_bridge->irq[level - 1].count++;
tsi148_bridge->irq[level - 1].callback[statid].priv_data = priv_data;
tsi148_bridge->irq[level - 1].callback[statid].func = callback;
/* Enable IRQ level */
tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
tmp |= TSI148_LCSR_INTEO_IRQEO[level - 1];
iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEN);
tmp |= TSI148_LCSR_INTEN_IRQEN[level - 1];
iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEN);
mutex_unlock(&(vme_irq));
return 0;
}
/*
* Free VME interrupt
*/
void tsi148_free_irq(int level, int statid)
{
u32 tmp;
struct pci_dev *pdev;
u32 tmp;
mutex_lock(&(vme_irq));
tsi148_bridge->irq[level - 1].count--;
/* Disable IRQ level if no more interrupts attached at this level*/
if (tsi148_bridge->irq[level - 1].count == 0) {
/* We need to do the ordering differently for enabling and disabling */
if (state == 0) {
tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEN);
tmp &= ~TSI148_LCSR_INTEN_IRQEN[level - 1];
iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEN);
......@@ -489,22 +439,28 @@ void tsi148_free_irq(int level, int statid)
tmp &= ~TSI148_LCSR_INTEO_IRQEO[level - 1];
iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
pdev = container_of(tsi148_bridge->parent, struct pci_dev, dev);
if (sync != 0) {
pdev = container_of(tsi148_bridge->parent,
struct pci_dev, dev);
synchronize_irq(pdev->irq);
}
tsi148_bridge->irq[level - 1].callback[statid].func = NULL;
tsi148_bridge->irq[level - 1].callback[statid].priv_data = NULL;
synchronize_irq(pdev->irq);
}
} else {
tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEO);
tmp |= TSI148_LCSR_INTEO_IRQEO[level - 1];
iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEO);
mutex_unlock(&(vme_irq));
tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEN);
tmp |= TSI148_LCSR_INTEN_IRQEN[level - 1];
iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEN);
}
}
/*
* Generate a VME bus interrupt at the requested level & vector. Wait for
* interrupt to be acked.
*/
int tsi148_generate_irq(int level, int statid)
int tsi148_irq_generate(int level, int statid)
{
u32 tmp;
......@@ -2333,7 +2289,6 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id)
init_waitqueue_head(&dma_queue[1]);
init_waitqueue_head(&iack_queue);
mutex_init(&(vme_int));
mutex_init(&(vme_irq));
mutex_init(&(vme_rmw));
tsi148_bridge->parent = &(pdev->dev);
......@@ -2481,9 +2436,8 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id)
tsi148_bridge->dma_list_add = tsi148_dma_list_add;
tsi148_bridge->dma_list_exec = tsi148_dma_list_exec;
tsi148_bridge->dma_list_empty = tsi148_dma_list_empty;
tsi148_bridge->request_irq = tsi148_request_irq;
tsi148_bridge->free_irq = tsi148_free_irq;
tsi148_bridge->generate_irq = tsi148_generate_irq;
tsi148_bridge->irq_set = tsi148_irq_set;
tsi148_bridge->irq_generate = tsi148_irq_generate;
tsi148_bridge->lm_set = tsi148_lm_set;
tsi148_bridge->lm_get = tsi148_lm_get;
tsi148_bridge->lm_attach = tsi148_lm_attach;
......
......@@ -974,7 +974,23 @@ int vme_dma_free(struct vme_resource *resource)
}
EXPORT_SYMBOL(vme_dma_free);
int vme_request_irq(struct device *dev, int level, int statid,
void vme_irq_handler(struct vme_bridge *bridge, int level, int statid)
{
void (*call)(int, int, void *);
void *priv_data;
call = bridge->irq[level - 1].callback[statid].func;
priv_data = bridge->irq[level - 1].callback[statid].priv_data;
if (call != NULL)
call(level, statid, priv_data);
else
printk(KERN_WARNING "Spurilous VME interrupt, level:%x, "
"vector:%x\n", level, statid);
}
EXPORT_SYMBOL(vme_irq_handler);
int vme_irq_request(struct device *dev, int level, int statid,
void (*callback)(int level, int vector, void *priv_data),
void *priv_data)
{
......@@ -987,20 +1003,37 @@ int vme_request_irq(struct device *dev, int level, int statid,
}
if((level < 1) || (level > 7)) {
printk(KERN_WARNING "Invalid interrupt level\n");
printk(KERN_ERR "Invalid interrupt level\n");
return -EINVAL;
}
if (bridge->request_irq == NULL) {
printk("Registering interrupts not supported\n");
if (bridge->irq_set == NULL) {
printk(KERN_ERR "Configuring interrupts not supported\n");
return -EINVAL;
}
return bridge->request_irq(level, statid, callback, priv_data);
mutex_lock(&(bridge->irq_mtx));
if (bridge->irq[level - 1].callback[statid].func) {
mutex_unlock(&(bridge->irq_mtx));
printk(KERN_WARNING "VME Interrupt already taken\n");
return -EBUSY;
}
bridge->irq[level - 1].count++;
bridge->irq[level - 1].callback[statid].priv_data = priv_data;
bridge->irq[level - 1].callback[statid].func = callback;
/* Enable IRQ level */
bridge->irq_set(level, 1, 1);
mutex_unlock(&(bridge->irq_mtx));
return 0;
}
EXPORT_SYMBOL(vme_request_irq);
EXPORT_SYMBOL(vme_irq_request);
void vme_free_irq(struct device *dev, int level, int statid)
void vme_irq_free(struct device *dev, int level, int statid)
{
struct vme_bridge *bridge;
......@@ -1011,20 +1044,31 @@ void vme_free_irq(struct device *dev, int level, int statid)
}
if((level < 1) || (level > 7)) {
printk(KERN_WARNING "Invalid interrupt level\n");
printk(KERN_ERR "Invalid interrupt level\n");
return;
}
if (bridge->free_irq == NULL) {
printk("Freeing interrupts not supported\n");
if (bridge->irq_set == NULL) {
printk(KERN_ERR "Configuring interrupts not supported\n");
return;
}
bridge->free_irq(level, statid);
mutex_lock(&(bridge->irq_mtx));
bridge->irq[level - 1].count--;
/* Disable IRQ level if no more interrupts attached at this level*/
if (bridge->irq[level - 1].count == 0)
bridge->irq_set(level, 0, 1);
bridge->irq[level - 1].callback[statid].func = NULL;
bridge->irq[level - 1].callback[statid].priv_data = NULL;
mutex_unlock(&(bridge->irq_mtx));
}
EXPORT_SYMBOL(vme_free_irq);
EXPORT_SYMBOL(vme_irq_free);
int vme_generate_irq(struct device *dev, int level, int statid)
int vme_irq_generate(struct device *dev, int level, int statid)
{
struct vme_bridge *bridge;
......@@ -1039,14 +1083,14 @@ int vme_generate_irq(struct device *dev, int level, int statid)
return -EINVAL;
}
if (bridge->generate_irq == NULL) {
if (bridge->irq_generate == NULL) {
printk("Interrupt generation not supported\n");
return -EINVAL;
}
return bridge->generate_irq(level, statid);
return bridge->irq_generate(level, statid);
}
EXPORT_SYMBOL(vme_generate_irq);
EXPORT_SYMBOL(vme_irq_generate);
/*
* Request the location monitor, return resource or NULL
......
......@@ -136,10 +136,10 @@ int vme_dma_list_exec(struct vme_dma_list *);
int vme_dma_list_free(struct vme_dma_list *);
int vme_dma_free(struct vme_resource *);
int vme_request_irq(struct device *, int, int,
int vme_irq_request(struct device *, int, int,
void (*callback)(int, int, void *), void *);
void vme_free_irq(struct device *, int, int);
int vme_generate_irq(struct device *, int, int);
void vme_irq_free(struct device *, int, int);
int vme_irq_generate(struct device *, int, int);
struct vme_resource * vme_lm_request(struct device *);
int vme_lm_count(struct vme_resource *);
......
......@@ -290,10 +290,10 @@ status ID combination. Any given combination can only be assigned a single
callback function. A void pointer parameter is provided, the value of which is
passed to the callback function, the use of this pointer is user undefined:
int vme_request_irq(struct device *dev, int level, int statid,
int vme_irq_request(struct device *dev, int level, int statid,
void (*callback)(int, int, void *), void *priv);
void vme_free_irq(struct device *dev, int level, int statid);
void vme_irq_free(struct device *dev, int level, int statid);
The callback parameters are as follows. Care must be taken in writing a callback
function, callback functions run in interrupt context:
......@@ -307,7 +307,7 @@ Interrupt Generation
The following function can be used to generate a VME interrupt at a given VME
level and VME status ID:
int vme_generate_irq(struct device *dev, int level, int statid);
int vme_irq_generate(struct device *dev, int level, int statid);
Location monitors
......
......@@ -120,6 +120,8 @@ struct vme_bridge {
/* Interrupt callbacks */
struct vme_irq irq[7];
/* Locking for VME irq callback configuration */
struct mutex irq_mtx;
/* Slave Functions */
int (*slave_get) (struct vme_slave_resource *, int *,
......@@ -149,9 +151,8 @@ struct vme_bridge {
int (*dma_list_empty) (struct vme_dma_list *);
/* Interrupt Functions */
int (*request_irq) (int, int, void (*cback)(int, int, void*), void *);
void (*free_irq) (int, int);
int (*generate_irq) (int, int);
void (*irq_set) (int, int, int);
int (*irq_generate) (int, int);
/* Location monitor functions */
int (*lm_set) (struct vme_lm_resource *, unsigned long long,
......@@ -175,6 +176,8 @@ struct vme_bridge {
#endif
};
void vme_irq_handler(struct vme_bridge *, int, int);
int vme_register_bridge (struct vme_bridge *);
void vme_unregister_bridge (struct vme_bridge *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment