/alps/pcitool

To get this branch, use:
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