/alps/ipecamera

To get this branch, use:
bzr branch http://darksoft.org/webbzr/alps/ipecamera

« back to all changes in this revision

Viewing changes to driver/int.c

  • Committer: Suren A. Chilingaryan
  • Date: 2015-04-27 00:28:57 UTC
  • Revision ID: csa@suren.me-20150427002857-82fk6r3e8rfgy4wr
First stand-alone ipecamera implementation

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
/*
11
 
 * Change History:
12
 
 * 
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.
16
 
 *
17
 
 * Revision 1.6  2007-11-04 20:58:22  marcus
18
 
 * Added interrupt generator acknowledge.
19
 
 * Fixed wrong operator.
20
 
 *
21
 
 * Revision 1.5  2007-10-31 15:42:21  marcus
22
 
 * Added IG ack for testing, may be removed later.
23
 
 *
24
 
 * Revision 1.4  2007-07-17 13:15:56  marcus
25
 
 * Removed Tasklets.
26
 
 * Using newest map for the ABB interrupts.
27
 
 *
28
 
 * Revision 1.3  2007-07-05 15:30:30  marcus
29
 
 * Added support for both register maps of the ABB.
30
 
 *
31
 
 * Revision 1.2  2007-05-29 07:50:18  marcus
32
 
 * Split code into 2 files. May get merged in the future again....
33
 
 *
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.
37
 
 *
38
 
 */
39
 
 
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>
49
 
#include <stdbool.h>
50
 
 
51
 
#include "config.h"
52
 
 
53
 
#include "compat.h"
54
 
 
55
 
#include "pciDriver.h"
56
 
 
57
 
#include "common.h"
58
 
 
59
 
#include "int.h"
60
 
 
61
 
/*
62
 
 * The ID between IRQ_SOURCE in irq_outstanding and the actual source is arbitrary.
63
 
 * Therefore, be careful when communicating with multiple implementations. 
64
 
 */
65
 
 
66
 
/* IRQ_SOURCES */
67
 
#define ABB_IRQ_CH0             0
68
 
#define ABB_IRQ_CH1             1
69
 
#define ABB_IRQ_IG              2
70
 
 
71
 
/* See ABB user’s guide, register definitions (3.1) */
72
 
#define ABB_INT_ENABLE          (0x0010 >> 2)
73
 
#define ABB_INT_STAT            (0x0008 >> 2)
74
 
 
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 */
80
 
 
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)
86
 
 
87
 
/**
88
 
 *
89
 
 * If IRQ-handling is enabled, this function will be called from pcidriver_probe
90
 
 * to initialize the IRQ handling (maps the BARs)
91
 
 *
92
 
 */
93
 
int pcidriver_probe_irq(pcidriver_privdata_t *privdata)
94
 
{
95
 
        unsigned char int_pin, int_line;
96
 
        unsigned long bar_addr, bar_len, bar_flags;
97
 
        int i;
98
 
        int err;
99
 
 
100
 
        for (i = 0; i < 6; i++)
101
 
                privdata->bars_kmapped[i] = NULL;
102
 
 
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);
107
 
 
108
 
                /* check if it is a valid BAR, skip if not */
109
 
                if ((bar_addr == 0) || (bar_len == 0))
110
 
                        continue;
111
 
 
112
 
                /* Skip IO regions (map only mem regions) */
113
 
                if (bar_flags & IORESOURCE_IO)
114
 
                        continue;
115
 
 
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" );
119
 
                        return err;
120
 
                }
121
 
 
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!
125
 
                 */
126
 
 
127
 
                /* respect the cacheable-bility of the region */
128
 
                if (bar_flags & IORESOURCE_PREFETCH)
129
 
                        privdata->bars_kmapped[i] = ioremap(bar_addr, bar_len);
130
 
                else
131
 
                        privdata->bars_kmapped[i] = ioremap_nocache(bar_addr, bar_len);
132
 
 
133
 
                /* check for error */
134
 
                if (privdata->bars_kmapped[i] == NULL) {
135
 
                        mod_info( "Failed to remap BAR%d into kernel space.\n", i );
136
 
                        return -EIO;
137
 
                }
138
 
        }
139
 
 
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);
145
 
        }
146
 
 
147
 
        /* Initialize the irq config */
148
 
        if ((err = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_PIN, &int_pin)) != 0) {
149
 
                /* continue without interrupts */
150
 
                int_pin = 0;
151
 
                mod_info("Error getting the interrupt pin. Disabling interrupts for this device\n");
152
 
        }
153
 
 
154
 
        /* Disable interrupts and activate them if everything can be set up properly */
155
 
        privdata->irq_enabled = 0;
156
 
 
157
 
        if (int_pin == 0)
158
 
                return 0;
159
 
 
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");
162
 
                return 0;
163
 
        }
164
 
 
165
 
        /* Enable interrupts using MSI mode */
166
 
        if (!pci_enable_msi(privdata->pdev)) 
167
 
                privdata->msi_mode = 1;
168
 
        
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");
172
 
                return 0;
173
 
        }
174
 
 
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 );
177
 
 
178
 
        return 0;
179
 
}
180
 
 
181
 
/**
182
 
 *
183
 
 * Frees/cleans up the data structures, called from pcidriver_remove()
184
 
 *
185
 
 */
186
 
void pcidriver_remove_irq(pcidriver_privdata_t *privdata)
187
 
{
188
 
        /* Release the IRQ handler */
189
 
        if (privdata->irq_enabled != 0)
190
 
                free_irq(privdata->pdev->irq, privdata);
191
 
        
192
 
        if (privdata->msi_mode) {
193
 
                pci_disable_msi(privdata->pdev);
194
 
                privdata->msi_mode = 0;
195
 
        }
196
 
 
197
 
        pcidriver_irq_unmap_bars(privdata);
198
 
}
199
 
 
200
 
/**
201
 
 *
202
 
 * Unmaps the BARs and releases them
203
 
 *
204
 
 */
205
 
void pcidriver_irq_unmap_bars(pcidriver_privdata_t *privdata)
206
 
{
207
 
        int i;
208
 
 
209
 
        for (i = 0; i < 6; i++) {
210
 
                if (privdata->bars_kmapped[i] == NULL)
211
 
                        continue;
212
 
 
213
 
                iounmap((void*)privdata->bars_kmapped[i]);
214
 
                pci_release_region(privdata->pdev, i);
215
 
        }
216
 
}
217
 
 
218
 
/**
219
 
 *
220
 
 * Acknowledges the receival of an interrupt to the card.
221
 
 *
222
 
 * @returns true if the card was acknowledget
223
 
 * @returns false if the interrupt was not for one of our cards
224
 
 *
225
 
 * @see check_acknowlegde_channel
226
 
 *
227
 
 */
228
 
static bool pcidriver_irq_acknowledge(pcidriver_privdata_t *privdata)
229
 
{
230
 
        int channel = 0;
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]);
234
 
 
235
 
        atomic_inc(&(privdata->irq_outstanding[channel]));
236
 
        wake_up_interruptible(&(privdata->irq_queues[channel]));
237
 
        
238
 
        return true;
239
 
}
240
 
 
241
 
/**
242
 
 *
243
 
 * Handles IRQs. At the moment, this acknowledges the card that this IRQ
244
 
 * was received and then increases the driver's IRQ counter.
245
 
 *
246
 
 * @see pcidriver_irq_acknowledge
247
 
 *
248
 
 */
249
 
IRQ_HANDLER_FUNC(pcidriver_irq_handler)
250
 
{
251
 
        pcidriver_privdata_t *privdata = (pcidriver_privdata_t *)dev_id;
252
 
 
253
 
        if (!pcidriver_irq_acknowledge(privdata))
254
 
                return IRQ_NONE;
255
 
 
256
 
        privdata->irq_count++;
257
 
        return IRQ_HANDLED;
258
 
}