2 * vme_irq.c - PCI-VME bridge interrupt management
4 * Copyright (c) 2009 Sebastien Dugue
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
14 * This file provides the PCI-VME bridge interrupt management code.
17 #include <linux/interrupt.h>
20 #include "vme_bridge.h"
23 unsigned int vme_interrupts_enabled
;
26 int (*handler
)(void *arg
);
34 #define VME_NUM_VECTORS 256
36 /* Mutex to prevent concurrent access to the IRQ table */
37 static DEFINE_MUTEX(vme_irq_table_lock
);
38 static struct vme_irq vme_irq_table
[VME_NUM_VECTORS
];
40 /* Interrupt counters */
64 struct interrupt_stats
{
68 {.name
= "DMA0"}, {.name
= "DMA1"},
69 {.name
= "MB0"}, {.name
= "MB1"}, {.name
= "MB2"}, {.name
= "MB3"},
70 {.name
= "LM0"}, {.name
= "LM1"}, {.name
= "LM2"}, {.name
= "LM3"},
71 {.name
= "IRQ1"}, {.name
= "IRQ2"}, {.name
= "IRQ3"}, {.name
= "IRQ4"},
72 {.name
= "IRQ5"}, {.name
= "IRQ6"}, {.name
= "IRQ7"},
73 {.name
= "PERR"}, {.name
= "VERR"}, {.name
= "SPURIOUS"}
79 int vme_interrupts_proc_show(char *page
, char **start
, off_t off
,
80 int count
, int *eof
, void *data
)
85 p
+= sprintf(p
, " Source Count\n");
86 p
+= sprintf(p
, "--------------------------\n\n");
88 for (i
= 0; i
< ARRAY_SIZE(int_stats
); i
++)
89 p
+= sprintf(p
, "%-8s %d\n", int_stats
[i
].name
,
96 int vme_irq_proc_show(char *page
, char **start
, off_t off
, int count
,
101 struct vme_irq
*virq
;
103 p
+= sprintf(p
, "Vector Count Client\n");
104 p
+= sprintf(p
, "------------------------------------------------\n\n");
106 for (i
= 0; i
< VME_NUM_VECTORS
; i
++) {
107 virq
= &vme_irq_table
[i
];
110 p
+= sprintf(p
, " %3d %10u %s\n",
111 i
, virq
->count
, virq
->name
);
118 #endif /* CONFIG_PROC_FS */
120 void account_dma_interrupt(int channel_mask
)
122 if (channel_mask
& 1)
123 int_stats
[INT_DMA0
].count
++;
125 if (channel_mask
& 2)
126 int_stats
[INT_DMA1
].count
++;
129 static void handle_pci_error(void)
131 tsi148_handle_pci_error();
133 int_stats
[INT_PERR
].count
++;
137 vme_berr_match(struct vme_bus_error
*error
, struct vme_berr_handler
*handler
)
139 struct vme_bus_error
*err
= &handler
->error
;
141 return error
->am
== err
->am
&& error
->address
>= err
->address
&&
142 error
->address
< err
->address
+ handler
->size
;
145 static void __vme_dispatch_berr(struct vme_bus_error
*error
)
147 struct vme_berr_handler
*handler
;
149 list_for_each_entry(handler
, &vme_bridge
->verr
.h_list
, h_list
) {
150 if (vme_berr_match(error
, handler
))
151 handler
->func(error
);
155 static void handle_vme_error(void)
157 struct vme_bus_error_desc desc
;
161 lock
= &vme_bridge
->verr
.lock
;
163 spin_lock_irqsave(lock
, flags
);
164 tsi148_handle_vme_error(&desc
.error
);
166 memcpy(&vme_bridge
->verr
.desc
, &desc
, sizeof(desc
));
167 __vme_dispatch_berr(&desc
.error
);
168 spin_unlock_irqrestore(lock
, flags
);
170 int_stats
[INT_VERR
].count
++;
173 static void handle_mbox_interrupt(int mb_mask
)
176 int_stats
[INT_MB0
].count
++;
179 int_stats
[INT_MB1
].count
++;
182 int_stats
[INT_MB2
].count
++;
185 int_stats
[INT_MB3
].count
++;
188 static void handle_lm_interrupt(int lm_mask
)
191 int_stats
[INT_LM0
].count
++;
194 int_stats
[INT_LM1
].count
++;
197 int_stats
[INT_LM2
].count
++;
200 int_stats
[INT_LM3
].count
++;
204 * handle_vme_interrupt() - VME IRQ handler
205 * @irq_mask: Mask of the raised IRQs
207 * Get the IRQ vector through an IACK cycle and call the handler for
208 * that vector if installed.
210 static void handle_vme_interrupt(int irq_mask
)
214 struct vme_irq
*virq
;
216 for (i
= 7; i
> 0; i
--) {
218 if (irq_mask
& (1 << i
)) {
219 /* Generate an 8-bit IACK cycle and get the vector */
220 vec
= tsi148_iack8(vme_bridge
->regs
, i
);
222 virq
= &vme_irq_table
[vec
];
225 #ifdef CONFIG_PROC_FS
228 virq
->handler(virq
->arg
);
231 int_stats
[INT_IRQ1
+ i
- 1].count
++;
238 * vme_bridge_interrupt() - VME bridge main interrupt handler
241 irqreturn_t
vme_bridge_interrupt(int irq
, void *ptr
)
247 * We need to read the interrupt status from the VME bus to make
248 * sure the internal FIFO has been flushed of pending writes.
250 while ((raised
= tsi148_get_int_status(crg_base
)) != 0) {
253 * Clearing of the interrupts must be done by writing to the
254 * INTS register through the VME bus.
256 tsi148_clear_int(crg_base
, raised
);
258 mask
= raised
& vme_interrupts_enabled
;
260 /* Only handle enabled interrupts */
262 int_stats
[INT_SPURIOUS
].count
++;
266 if (mask
& TSI148_LCSR_INT_DMA_M
) {
267 handle_dma_interrupt((mask
& TSI148_LCSR_INT_DMA_M
) >>
268 TSI148_LCSR_INT_DMA_SHIFT
);
269 mask
&= ~TSI148_LCSR_INT_DMA_M
;
272 if (mask
& TSI148_LCSR_INT_PERR
) {
274 mask
&= ~TSI148_LCSR_INT_PERR
;
277 if (mask
& TSI148_LCSR_INT_VERR
) {
279 mask
&= ~TSI148_LCSR_INT_VERR
;
282 if (mask
& TSI148_LCSR_INT_MB_M
) {
283 handle_mbox_interrupt((mask
& TSI148_LCSR_INT_MB_M
) >>
284 TSI148_LCSR_INT_MB_SHIFT
);
285 mask
&= ~TSI148_LCSR_INT_MB_M
;
288 if (mask
& TSI148_LCSR_INT_LM_M
) {
289 handle_lm_interrupt((mask
& TSI148_LCSR_INT_LM_M
) >>
290 TSI148_LCSR_INT_LM_SHIFT
);
291 mask
&= ~TSI148_LCSR_INT_LM_M
;
294 if (mask
& TSI148_LCSR_INT_IRQM
) {
295 handle_vme_interrupt(mask
& TSI148_LCSR_INT_IRQM
);
296 mask
&= ~TSI148_LCSR_INT_IRQM
;
299 /* Check that we handled everything */
301 printk(KERN_WARNING PFX
302 "Unhandled interrupt %08x (enabled %08x)\n",
303 mask
, vme_interrupts_enabled
);
310 * vme_enable_interrupts() - Enable VME bridge interrupts
311 * @mask: Interrupts to enable
314 int vme_enable_interrupts(unsigned int mask
)
316 unsigned int enabled
;
319 enabled
= tsi148_get_int_enabled(vme_bridge
->regs
);
320 new = enabled
| mask
;
322 vme_interrupts_enabled
= new;
323 return tsi148_set_interrupts(vme_bridge
->regs
, new);
327 * vme_disable_interrupts() - Disable VME bridge interrupts
328 * @mask: Interrupts to disable
331 int vme_disable_interrupts(unsigned int mask
)
333 unsigned int enabled
;
336 enabled
= tsi148_get_int_enabled(vme_bridge
->regs
);
337 new = enabled
& ~mask
;
339 vme_interrupts_enabled
= new;
340 return tsi148_set_interrupts(vme_bridge
->regs
, new);
344 * vme_request_irq() - Install handler for a given VME IRQ vector
345 * @vec: VME IRQ vector
346 * @handler: Interrupt handler
347 * @arg: Interrupt handler argument
348 * @name: Interrupt name (only used for stats in Procfs)
351 int vme_request_irq(unsigned int vec
, int (*handler
)(void *),
352 void *arg
, const char *name
)
354 struct vme_irq
*virq
;
357 /* Check the vector is within the bound */
358 if (vec
>= VME_NUM_VECTORS
)
361 if ((rc
= mutex_lock_interruptible(&vme_irq_table_lock
)) != 0)
364 virq
= &vme_irq_table
[vec
];
366 /* Check if that vector is already used */
372 virq
->handler
= handler
;
375 #ifdef CONFIG_PROC_FS
379 virq
->name
= (char *)name
;
381 virq
->name
= "Unknown";
385 mutex_unlock(&vme_irq_table_lock
);
388 printk(KERN_DEBUG PFX
"Registered vector %d for %s\n",
391 printk(KERN_WARNING PFX
"Could not install ISR: vector %d "
392 "already in use by %s", vec
, virq
->name
);
395 EXPORT_SYMBOL_GPL(vme_request_irq
);
398 * vme_free_irq() - Uninstall handler for a given VME IRQ vector
399 * @vec: VME IRQ vector
402 int vme_free_irq(unsigned int vec
)
404 struct vme_irq
*virq
;
407 /* Check the vector is within the bound */
408 if (vec
>= VME_NUM_VECTORS
)
411 if ((rc
= mutex_lock_interruptible(&vme_irq_table_lock
)) != 0)
414 virq
= &vme_irq_table
[vec
];
416 /* Check there really was a handler installed */
417 if (!virq
->handler
) {
422 virq
->handler
= NULL
;
425 #ifdef CONFIG_PROC_FS
431 mutex_unlock(&vme_irq_table_lock
);
435 EXPORT_SYMBOL_GPL(vme_free_irq
);
438 * vme_generate_interrupt() - Generate an interrupt on the VME bus
439 * @level: IRQ level (1-7)
440 * @vector: IRQ vector (0-255)
441 * @msecs: Timeout for IACK in milliseconds
443 * This function generates an interrupt on the VME bus and waits for IACK
444 * for msecs milliseconds.
446 * Returns 0 on success or -ETIME if the timeout expired.
449 int vme_generate_interrupt(int level
, int vector
, signed long msecs
)
451 return tsi148_generate_interrupt(level
, vector
, msecs
);
453 EXPORT_SYMBOL_GPL(vme_generate_interrupt
);