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: Martyn Welch <martyn.welch@gefanuc.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
beb9ccc635
commit
c813f592a5
6 changed files with 112 additions and 159 deletions
|
@ -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];
|
||||
|
||||
if (state == 0)
|
||||
tmp &= ~CA91CX42_LINT_VIRQ[level];
|
||||
else
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
|
||||
tmp = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEN);
|
||||
tmp |= TSI148_LCSR_INTEN_IRQEN[level - 1];
|
||||
iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_INTEN);
|
||||
}
|
||||
|
||||
tsi148_bridge->irq[level - 1].callback[statid].func = NULL;
|
||||
tsi148_bridge->irq[level - 1].callback[statid].priv_data = NULL;
|
||||
|
||||
mutex_unlock(&(vme_irq));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 *);
|
||||
|
||||
|
|
Loading…
Reference in a new issue