4
* @author Guillermo Marcus
6
* @brief Contains the interrupt handler.
13
* $Log: not supported by cvs2svn $
14
* Revision 1.7 2008-01-11 10:18:28 marcus
15
* Modified interrupt mechanism. Added atomic functions and queues, to address race conditions. Removed unused interrupt code.
17
* Revision 1.6 2007-11-04 20:58:22 marcus
18
* Added interrupt generator acknowledge.
19
* Fixed wrong operator.
21
* Revision 1.5 2007-10-31 15:42:21 marcus
22
* Added IG ack for testing, may be removed later.
24
* Revision 1.4 2007-07-17 13:15:56 marcus
26
* Using newest map for the ABB interrupts.
28
* Revision 1.3 2007-07-05 15:30:30 marcus
29
* Added support for both register maps of the ABB.
31
* Revision 1.2 2007-05-29 07:50:18 marcus
32
* Split code into 2 files. May get merged in the future again....
34
* Revision 1.1 2007/03/01 16:57:43 marcus
35
* Divided driver file to ease the interrupt hooks for the user of the driver.
36
* Modified Makefile accordingly.
40
#include <linux/version.h>
41
#include <linux/string.h>
42
#include <linux/types.h>
43
#include <linux/list.h>
44
#include <linux/interrupt.h>
45
#include <linux/pci.h>
46
#include <linux/cdev.h>
47
#include <linux/wait.h>
48
#include <linux/sched.h>
55
#include "pciDriver.h"
62
* The ID between IRQ_SOURCE in irq_outstanding and the actual source is arbitrary.
63
* Therefore, be careful when communicating with multiple implementations.
71
/* See ABB user’s guide, register definitions (3.1) */
72
#define ABB_INT_ENABLE (0x0010 >> 2)
73
#define ABB_INT_STAT (0x0008 >> 2)
75
#define ABB_INT_CH1_TIMEOUT (1 << 4)
76
#define ABB_INT_CH0_TIMEOUT (1 << 5)
77
#define ABB_INT_IG (1 << 2)
78
#define ABB_INT_CH0 (1 << 1) /* downstream */
79
#define ABB_INT_CH1 (1) /* upstream */
81
#define ABB_CH0_CTRL (108 >> 2)
82
#define ABB_CH1_CTRL (72 >> 2)
83
#define ABB_CH_RESET (0x0201000A)
84
#define ABB_IG_CTRL (0x0080 >> 2)
85
#define ABB_IG_ACK (0x00F0)
89
* If IRQ-handling is enabled, this function will be called from pcidriver_probe
90
* to initialize the IRQ handling (maps the BARs)
93
int pcidriver_probe_irq(pcidriver_privdata_t *privdata)
95
unsigned char int_pin, int_line;
96
unsigned long bar_addr, bar_len, bar_flags;
100
for (i = 0; i < 6; i++)
101
privdata->bars_kmapped[i] = NULL;
103
for (i = 0; i < 6; i++) {
104
bar_addr = pci_resource_start(privdata->pdev, i);
105
bar_len = pci_resource_len(privdata->pdev, i);
106
bar_flags = pci_resource_flags(privdata->pdev, i);
108
/* check if it is a valid BAR, skip if not */
109
if ((bar_addr == 0) || (bar_len == 0))
112
/* Skip IO regions (map only mem regions) */
113
if (bar_flags & IORESOURCE_IO)
116
/* Check if the region is available */
117
if ((err = pci_request_region(privdata->pdev, i, NULL)) != 0) {
118
mod_info( "Failed to request BAR memory region.\n" );
122
/* Map it into kernel space. */
123
/* For x86 this is just a dereference of the pointer, but that is
124
* not portable. So we need to do the portable way. Thanks Joern!
127
/* respect the cacheable-bility of the region */
128
if (bar_flags & IORESOURCE_PREFETCH)
129
privdata->bars_kmapped[i] = ioremap(bar_addr, bar_len);
131
privdata->bars_kmapped[i] = ioremap_nocache(bar_addr, bar_len);
133
/* check for error */
134
if (privdata->bars_kmapped[i] == NULL) {
135
mod_info( "Failed to remap BAR%d into kernel space.\n", i );
140
/* Initialize the interrupt handler for this device */
141
/* Initialize the wait queues */
142
for (i = 0; i < PCIDRIVER_INT_MAXSOURCES; i++) {
143
init_waitqueue_head(&(privdata->irq_queues[i]));
144
atomic_set(&(privdata->irq_outstanding[i]), 0);
147
/* Initialize the irq config */
148
if ((err = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_PIN, &int_pin)) != 0) {
149
/* continue without interrupts */
151
mod_info("Error getting the interrupt pin. Disabling interrupts for this device\n");
154
/* Disable interrupts and activate them if everything can be set up properly */
155
privdata->irq_enabled = 0;
160
if ((err = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_LINE, &int_line)) != 0) {
161
mod_info("Error getting the interrupt line. Disabling interrupts for this device\n");
165
/* Enable interrupts using MSI mode */
166
if (!pci_enable_msi(privdata->pdev))
167
privdata->msi_mode = 1;
169
/* register interrupt handler */
170
if ((err = request_irq(privdata->pdev->irq, pcidriver_irq_handler, MODNAME, privdata)) != 0) {
171
mod_info("Error registering the interrupt handler. Disabling interrupts for this device\n");
175
privdata->irq_enabled = 1;
176
mod_info("Registered Interrupt Handler at pin %i, line %i, IRQ %i\n", int_pin, int_line, privdata->pdev->irq );
183
* Frees/cleans up the data structures, called from pcidriver_remove()
186
void pcidriver_remove_irq(pcidriver_privdata_t *privdata)
188
/* Release the IRQ handler */
189
if (privdata->irq_enabled != 0)
190
free_irq(privdata->pdev->irq, privdata);
192
if (privdata->msi_mode) {
193
pci_disable_msi(privdata->pdev);
194
privdata->msi_mode = 0;
197
pcidriver_irq_unmap_bars(privdata);
202
* Unmaps the BARs and releases them
205
void pcidriver_irq_unmap_bars(pcidriver_privdata_t *privdata)
209
for (i = 0; i < 6; i++) {
210
if (privdata->bars_kmapped[i] == NULL)
213
iounmap((void*)privdata->bars_kmapped[i]);
214
pci_release_region(privdata->pdev, i);
220
* Acknowledges the receival of an interrupt to the card.
222
* @returns true if the card was acknowledget
223
* @returns false if the interrupt was not for one of our cards
225
* @see check_acknowlegde_channel
228
static bool pcidriver_irq_acknowledge(pcidriver_privdata_t *privdata)
231
// volatile unsigned int *bar;
232
// bar = privdata->bars_kmapped[0];
233
// mod_info_dbg("interrupt registers. ISR: %x, IER: %x\n", bar[ABB_INT_STAT], bar[ABB_INT_ENABLE]);
235
atomic_inc(&(privdata->irq_outstanding[channel]));
236
wake_up_interruptible(&(privdata->irq_queues[channel]));
243
* Handles IRQs. At the moment, this acknowledges the card that this IRQ
244
* was received and then increases the driver's IRQ counter.
246
* @see pcidriver_irq_acknowledge
249
IRQ_HANDLER_FUNC(pcidriver_irq_handler)
251
pcidriver_privdata_t *privdata = (pcidriver_privdata_t *)dev_id;
253
if (!pcidriver_irq_acknowledge(privdata))
256
privdata->irq_count++;