bzr branch
http://suren.me/webbzr/alps/pcitool
1
by Suren A. Chilingaryan
Initial import |
1 |
/**
|
2 |
*
|
|
3 |
* @file int.c
|
|
4 |
* @author Guillermo Marcus
|
|
5 |
* @date 2009-04-05
|
|
6 |
* @brief Contains the interrupt handler.
|
|
7 |
*
|
|
8 |
*/
|
|
9 |
||
10 |
#include <linux/version.h> |
|
11 |
#include <linux/string.h> |
|
12 |
#include <linux/types.h> |
|
13 |
#include <linux/list.h> |
|
14 |
#include <linux/interrupt.h> |
|
15 |
#include <linux/pci.h> |
|
16 |
#include <linux/cdev.h> |
|
17 |
#include <linux/wait.h> |
|
18 |
#include <linux/sched.h> |
|
19 |
#include <stdbool.h> |
|
20 |
||
365
by Suren A. Chilingaryan
Restructure driver headers |
21 |
#include "base.h" |
22 |
||
23 |
/**
|
|
24 |
*
|
|
25 |
* Acknowledges the receival of an interrupt to the card.
|
|
26 |
*
|
|
27 |
* @returns true if the card was acknowledget
|
|
28 |
* @returns false if the interrupt was not for one of our cards
|
|
29 |
*
|
|
30 |
* @see check_acknowlegde_channel
|
|
31 |
*
|
|
32 |
*/
|
|
33 |
static bool pcidriver_irq_acknowledge(pcidriver_privdata_t *privdata) |
|
34 |
{
|
|
35 |
int channel = 0; |
|
36 |
||
37 |
atomic_inc(&(privdata->irq_outstanding[channel])); |
|
38 |
wake_up_interruptible(&(privdata->irq_queues[channel])); |
|
39 |
||
40 |
return true; |
|
41 |
}
|
|
42 |
||
43 |
/**
|
|
44 |
*
|
|
45 |
* Handles IRQs. At the moment, this acknowledges the card that this IRQ
|
|
46 |
* was received and then increases the driver's IRQ counter.
|
|
47 |
*
|
|
48 |
* @see pcidriver_irq_acknowledge
|
|
49 |
*
|
|
50 |
*/
|
|
51 |
static irqreturn_t pcidriver_irq_handler(int irq, void *dev_id) |
|
52 |
{
|
|
53 |
pcidriver_privdata_t *privdata = (pcidriver_privdata_t *)dev_id; |
|
54 |
||
55 |
if (!pcidriver_irq_acknowledge(privdata)) |
|
56 |
return IRQ_NONE; |
|
57 |
||
58 |
privdata->irq_count++; |
|
59 |
return IRQ_HANDLED; |
|
60 |
}
|
|
61 |
||
1
by Suren A. Chilingaryan
Initial import |
62 |
|
63 |
/**
|
|
64 |
*
|
|
65 |
* If IRQ-handling is enabled, this function will be called from pcidriver_probe
|
|
66 |
* to initialize the IRQ handling (maps the BARs)
|
|
67 |
*
|
|
68 |
*/
|
|
69 |
int pcidriver_probe_irq(pcidriver_privdata_t *privdata) |
|
70 |
{
|
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
71 |
unsigned char int_pin, int_line; |
72 |
unsigned long bar_addr, bar_len, bar_flags; |
|
73 |
int i; |
|
74 |
int err; |
|
75 |
||
76 |
for (i = 0; i < 6; i++) |
|
77 |
privdata->bars_kmapped[i] = NULL; |
|
78 |
||
79 |
for (i = 0; i < 6; i++) { |
|
80 |
bar_addr = pci_resource_start(privdata->pdev, i); |
|
81 |
bar_len = pci_resource_len(privdata->pdev, i); |
|
82 |
bar_flags = pci_resource_flags(privdata->pdev, i); |
|
83 |
||
84 |
/* check if it is a valid BAR, skip if not */
|
|
85 |
if ((bar_addr == 0) || (bar_len == 0)) |
|
86 |
continue; |
|
87 |
||
88 |
/* Skip IO regions (map only mem regions) */
|
|
89 |
if (bar_flags & IORESOURCE_IO) |
|
90 |
continue; |
|
91 |
||
92 |
/* Check if the region is available */
|
|
93 |
if ((err = pci_request_region(privdata->pdev, i, NULL)) != 0) { |
|
94 |
mod_info( "Failed to request BAR memory region.\n" ); |
|
95 |
return err; |
|
96 |
}
|
|
97 |
||
98 |
/* Map it into kernel space. */
|
|
99 |
/* For x86 this is just a dereference of the pointer, but that is
|
|
100 |
* not portable. So we need to do the portable way. Thanks Joern!
|
|
101 |
*/
|
|
102 |
||
103 |
/* respect the cacheable-bility of the region */
|
|
104 |
if (bar_flags & IORESOURCE_PREFETCH) |
|
105 |
privdata->bars_kmapped[i] = ioremap(bar_addr, bar_len); |
|
106 |
else
|
|
107 |
privdata->bars_kmapped[i] = ioremap_nocache(bar_addr, bar_len); |
|
108 |
||
109 |
/* check for error */
|
|
110 |
if (privdata->bars_kmapped[i] == NULL) { |
|
111 |
mod_info( "Failed to remap BAR%d into kernel space.\n", i ); |
|
112 |
return -EIO; |
|
113 |
}
|
|
114 |
}
|
|
115 |
||
116 |
/* Initialize the interrupt handler for this device */
|
|
117 |
/* Initialize the wait queues */
|
|
118 |
for (i = 0; i < PCIDRIVER_INT_MAXSOURCES; i++) { |
|
119 |
init_waitqueue_head(&(privdata->irq_queues[i])); |
|
120 |
atomic_set(&(privdata->irq_outstanding[i]), 0); |
|
121 |
}
|
|
122 |
||
123 |
/* Initialize the irq config */
|
|
124 |
if ((err = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_PIN, &int_pin)) != 0) { |
|
125 |
/* continue without interrupts */
|
|
126 |
int_pin = 0; |
|
127 |
mod_info("Error getting the interrupt pin. Disabling interrupts for this device\n"); |
|
128 |
}
|
|
129 |
||
130 |
/* Disable interrupts and activate them if everything can be set up properly */
|
|
131 |
privdata->irq_enabled = 0; |
|
132 |
||
133 |
if (int_pin == 0) |
|
134 |
return 0; |
|
135 |
||
136 |
if ((err = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_LINE, &int_line)) != 0) { |
|
137 |
mod_info("Error getting the interrupt line. Disabling interrupts for this device\n"); |
|
138 |
return 0; |
|
139 |
}
|
|
140 |
||
141 |
/* Enable interrupts using MSI mode */
|
|
142 |
if (!pci_enable_msi(privdata->pdev)) |
|
143 |
privdata->msi_mode = 1; |
|
144 |
||
145 |
/* register interrupt handler */
|
|
365
by Suren A. Chilingaryan
Restructure driver headers |
146 |
if ((err = request_irq(privdata->pdev->irq, pcidriver_irq_handler, IRQF_SHARED, MODNAME, privdata)) != 0) { |
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
147 |
mod_info("Error registering the interrupt handler. Disabling interrupts for this device\n"); |
148 |
return 0; |
|
149 |
}
|
|
150 |
||
151 |
privdata->irq_enabled = 1; |
|
152 |
mod_info("Registered Interrupt Handler at pin %i, line %i, IRQ %i\n", int_pin, int_line, privdata->pdev->irq ); |
|
153 |
||
154 |
return 0; |
|
1
by Suren A. Chilingaryan
Initial import |
155 |
}
|
156 |
||
157 |
/**
|
|
158 |
*
|
|
159 |
* Frees/cleans up the data structures, called from pcidriver_remove()
|
|
160 |
*
|
|
161 |
*/
|
|
162 |
void pcidriver_remove_irq(pcidriver_privdata_t *privdata) |
|
163 |
{
|
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
164 |
/* Release the IRQ handler */
|
165 |
if (privdata->irq_enabled != 0) |
|
166 |
free_irq(privdata->pdev->irq, privdata); |
|
167 |
||
168 |
if (privdata->msi_mode) { |
|
169 |
pci_disable_msi(privdata->pdev); |
|
170 |
privdata->msi_mode = 0; |
|
171 |
}
|
|
172 |
||
173 |
pcidriver_irq_unmap_bars(privdata); |
|
1
by Suren A. Chilingaryan
Initial import |
174 |
}
|
175 |
||
176 |
/**
|
|
177 |
*
|
|
178 |
* Unmaps the BARs and releases them
|
|
179 |
*
|
|
180 |
*/
|
|
181 |
void pcidriver_irq_unmap_bars(pcidriver_privdata_t *privdata) |
|
182 |
{
|
|
364
by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) |
183 |
int i; |
184 |
||
185 |
for (i = 0; i < 6; i++) { |
|
186 |
if (privdata->bars_kmapped[i] == NULL) |
|
187 |
continue; |
|
188 |
||
189 |
iounmap((void*)privdata->bars_kmapped[i]); |
|
190 |
pci_release_region(privdata->pdev, i); |
|
191 |
}
|
|
1
by Suren A. Chilingaryan
Initial import |
192 |
}
|
193 |