vmod/vmodttl: fixed bug related to luns not ordered and/or not starting from zero.
[ht-drivers.git] / vmebridge / driver / vme_irq.c
blob198f051ca9076a2ac202c967eeb7baf4d5faa4d0
1 /*
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>
19 #include "tsi148.h"
20 #include "vme_bridge.h"
23 unsigned int vme_interrupts_enabled;
25 struct vme_irq {
26 int (*handler)(void *arg);
27 void *arg;
28 #ifdef CONFIG_PROC_FS
29 int count;
30 char *name;
31 #endif
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 */
41 enum interrupt_idx {
42 INT_DMA0 = 0,
43 INT_DMA1,
44 INT_MB0,
45 INT_MB1,
46 INT_MB2,
47 INT_MB3,
48 INT_LM0,
49 INT_LM1,
50 INT_LM2,
51 INT_LM3,
52 INT_IRQ1,
53 INT_IRQ2,
54 INT_IRQ3,
55 INT_IRQ4,
56 INT_IRQ5,
57 INT_IRQ6,
58 INT_IRQ7,
59 INT_PERR,
60 INT_VERR,
61 INT_SPURIOUS
64 struct interrupt_stats {
65 unsigned int count;
66 char *name;
67 } int_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"}
77 #ifdef CONFIG_PROC_FS
79 int vme_interrupts_proc_show(char *page, char **start, off_t off,
80 int count, int *eof, void *data)
82 char *p = page;
83 int i;
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,
90 int_stats[i].count);
92 *eof = 1;
93 return p - page;
96 int vme_irq_proc_show(char *page, char **start, off_t off, int count,
97 int *eof, void *data)
99 char *p = page;
100 int i;
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];
109 if (virq->handler)
110 p += sprintf(p, " %3d %10u %s\n",
111 i, virq->count, virq->name);
114 *eof = 1;
115 return p - page;
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++;
136 static int
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;
158 unsigned long flags;
159 spinlock_t *lock;
161 lock = &vme_bridge->verr.lock;
163 spin_lock_irqsave(lock, flags);
164 tsi148_handle_vme_error(&desc.error);
165 desc.valid = 1;
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)
175 if (mb_mask & 1)
176 int_stats[INT_MB0].count++;
178 if (mb_mask & 2)
179 int_stats[INT_MB1].count++;
181 if (mb_mask & 4)
182 int_stats[INT_MB2].count++;
184 if (mb_mask & 8)
185 int_stats[INT_MB3].count++;
188 static void handle_lm_interrupt(int lm_mask)
190 if (lm_mask & 1)
191 int_stats[INT_LM0].count++;
193 if (lm_mask & 2)
194 int_stats[INT_LM1].count++;
196 if (lm_mask & 4)
197 int_stats[INT_LM2].count++;
199 if (lm_mask & 8)
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)
212 int i;
213 int vec;
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];
224 if (virq->handler) {
225 #ifdef CONFIG_PROC_FS
226 virq->count++;
227 #endif
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)
243 unsigned int raised;
244 unsigned int mask;
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 */
261 if (!mask) {
262 int_stats[INT_SPURIOUS].count++;
263 return IRQ_NONE;
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) {
273 handle_pci_error();
274 mask &= ~TSI148_LCSR_INT_PERR;
277 if (mask & TSI148_LCSR_INT_VERR) {
278 handle_vme_error();
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 */
300 if (mask)
301 printk(KERN_WARNING PFX
302 "Unhandled interrupt %08x (enabled %08x)\n",
303 mask, vme_interrupts_enabled);
306 return IRQ_HANDLED;
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;
317 unsigned int new;
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;
334 unsigned int new;
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;
355 int rc = 0;
357 /* Check the vector is within the bound */
358 if (vec >= VME_NUM_VECTORS)
359 return -EINVAL;
361 if ((rc = mutex_lock_interruptible(&vme_irq_table_lock)) != 0)
362 return rc;
364 virq = &vme_irq_table[vec];
366 /* Check if that vector is already used */
367 if (virq->handler) {
368 rc = -EBUSY;
369 goto out_unlock;
372 virq->handler = handler;
373 virq->arg = arg;
375 #ifdef CONFIG_PROC_FS
376 virq->count = 0;
378 if (name)
379 virq->name = (char *)name;
380 else
381 virq->name = "Unknown";
382 #endif
384 out_unlock:
385 mutex_unlock(&vme_irq_table_lock);
387 if (!rc)
388 printk(KERN_DEBUG PFX "Registered vector %d for %s\n",
389 vec, virq->name);
390 else
391 printk(KERN_WARNING PFX "Could not install ISR: vector %d "
392 "already in use by %s", vec, virq->name);
393 return rc;
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;
405 int rc = 0;
407 /* Check the vector is within the bound */
408 if (vec >= VME_NUM_VECTORS)
409 return -EINVAL;
411 if ((rc = mutex_lock_interruptible(&vme_irq_table_lock)) != 0)
412 return rc;
414 virq = &vme_irq_table[vec];
416 /* Check there really was a handler installed */
417 if (!virq->handler) {
418 rc = -EINVAL;
419 goto out_unlock;
422 virq->handler = NULL;
423 virq->arg = NULL;
425 #ifdef CONFIG_PROC_FS
426 virq->count = 0;
427 virq->name = NULL;
428 #endif
430 out_unlock:
431 mutex_unlock(&vme_irq_table_lock);
433 return rc;
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);