]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/comedi/drivers/addi_apci_3501.c
staging: comedi: addi_apci_3501: don't read the unused PCI bars
[karo-tx-linux.git] / drivers / staging / comedi / drivers / addi_apci_3501.c
1 #include "../comedidev.h"
2 #include "comedi_fc.h"
3 #include "amcc_s5933.h"
4
5 #include "addi-data/addi_common.h"
6
7 #include "addi-data/addi_eeprom.c"
8 #include "addi-data/hwdrv_apci3501.c"
9
10 static const struct addi_board apci3501_boardtypes[] = {
11         {
12                 .pc_DriverName          = "apci3501",
13                 .i_VendorId             = PCI_VENDOR_ID_ADDIDATA,
14                 .i_DeviceId             = 0x3001,
15                 .i_IorangeBase0         = 64,
16                 .i_IorangeBase1         = APCI3501_ADDRESS_RANGE,
17                 .i_PCIEeprom            = ADDIDATA_EEPROM,
18                 .pc_EepromChip          = ADDIDATA_S5933,
19                 .i_AoMaxdata            = 16383,
20                 .pr_AoRangelist         = &range_apci3501_ao,
21                 .ao_config              = i_APCI3501_ConfigAnalogOutput,
22                 .ao_write               = i_APCI3501_WriteAnalogOutput,
23         },
24 };
25
26 static int apci3501_di_insn_bits(struct comedi_device *dev,
27                                  struct comedi_subdevice *s,
28                                  struct comedi_insn *insn,
29                                  unsigned int *data)
30 {
31         data[1] = inl(dev->iobase + APCI3501_DIGITAL_IP) & 0x3;
32
33         return insn->n;
34 }
35
36 static int apci3501_do_insn_bits(struct comedi_device *dev,
37                                  struct comedi_subdevice *s,
38                                  struct comedi_insn *insn,
39                                  unsigned int *data)
40 {
41         unsigned int mask = data[0];
42         unsigned int bits = data[1];
43
44         s->state = inl(dev->iobase + APCI3501_DIGITAL_OP);
45         if (mask) {
46                 s->state &= ~mask;
47                 s->state |= (bits & mask);
48
49                 outl(s->state, dev->iobase + APCI3501_DIGITAL_OP);
50         }
51
52         data[1] = s->state;
53
54         return insn->n;
55 }
56
57 static int i_ADDIDATA_InsnReadEeprom(struct comedi_device *dev,
58                                      struct comedi_subdevice *s,
59                                      struct comedi_insn *insn,
60                                      unsigned int *data)
61 {
62         const struct addi_board *this_board = comedi_board(dev);
63         struct addi_private *devpriv = dev->private;
64         unsigned short w_Address = CR_CHAN(insn->chanspec);
65         unsigned short w_Data;
66
67         w_Data = addi_eeprom_readw(devpriv->i_IobaseAmcc,
68                 this_board->pc_EepromChip, 2 * w_Address);
69         data[0] = w_Data;
70
71         return insn->n;
72 }
73
74 static irqreturn_t apci3501_interrupt(int irq, void *d)
75 {
76         struct comedi_device *dev = d;
77         struct addi_private *devpriv = dev->private;
78         unsigned int ui_Timer_AOWatchdog;
79         unsigned long ul_Command1;
80         int i_temp;
81
82         /*  Disable Interrupt */
83         ul_Command1 =
84                 inl(dev->iobase + APCI3501_WATCHDOG + APCI3501_TCW_PROG);
85
86         ul_Command1 = (ul_Command1 & 0xFFFFF9FDul);
87         outl(ul_Command1,
88                 dev->iobase + APCI3501_WATCHDOG + APCI3501_TCW_PROG);
89
90         ui_Timer_AOWatchdog =
91                 inl(dev->iobase + APCI3501_WATCHDOG +
92                 APCI3501_TCW_IRQ) & 0x1;
93
94         if ((!ui_Timer_AOWatchdog)) {
95                 comedi_error(dev, "IRQ from unknown source");
96                 return IRQ_NONE;
97         }
98
99         /* Enable Interrupt Send a signal to from kernel to user space */
100         send_sig(SIGIO, devpriv->tsk_Current, 0);
101         ul_Command1 =
102                 inl(dev->iobase + APCI3501_WATCHDOG + APCI3501_TCW_PROG);
103         ul_Command1 = ((ul_Command1 & 0xFFFFF9FDul) | 1 << 1);
104         outl(ul_Command1,
105                 dev->iobase + APCI3501_WATCHDOG + APCI3501_TCW_PROG);
106         i_temp = inl(dev->iobase + APCI3501_WATCHDOG +
107                 APCI3501_TCW_TRIG_STATUS) & 0x1;
108
109         return IRQ_HANDLED;
110 }
111
112 static int apci3501_reset(struct comedi_device *dev)
113 {
114         int i_Count = 0, i_temp = 0;
115         unsigned int ul_Command1 = 0, ul_Polarity, ul_DAC_Ready = 0;
116
117         outl(0x0, dev->iobase + APCI3501_DIGITAL_OP);
118         outl(1, dev->iobase + APCI3501_ANALOG_OUTPUT +
119                 APCI3501_AO_VOLT_MODE);
120
121         ul_Polarity = 0x80000000;
122
123         for (i_Count = 0; i_Count <= 7; i_Count++) {
124                 ul_DAC_Ready = inl(dev->iobase + APCI3501_ANALOG_OUTPUT);
125
126                 while (ul_DAC_Ready == 0) {
127                         ul_DAC_Ready =
128                                 inl(dev->iobase + APCI3501_ANALOG_OUTPUT);
129                         ul_DAC_Ready = (ul_DAC_Ready >> 8) & 1;
130                 }
131
132                 if (ul_DAC_Ready) {
133                         /*  Output the Value on the output channels. */
134                         ul_Command1 =
135                                 (unsigned int) ((unsigned int) (i_Count & 0xFF) |
136                                 (unsigned int) ((i_temp << 0x8) & 0x7FFFFF00L) |
137                                 (unsigned int) (ul_Polarity));
138                         outl(ul_Command1,
139                                 dev->iobase + APCI3501_ANALOG_OUTPUT +
140                                 APCI3501_AO_PROG);
141                 }
142         }
143
144         return 0;
145 }
146
147 static const void *addi_find_boardinfo(struct comedi_device *dev,
148                                        struct pci_dev *pcidev)
149 {
150         const void *p = dev->driver->board_name;
151         const struct addi_board *this_board;
152         int i;
153
154         for (i = 0; i < dev->driver->num_names; i++) {
155                 this_board = p;
156                 if (this_board->i_VendorId == pcidev->vendor &&
157                     this_board->i_DeviceId == pcidev->device)
158                         return this_board;
159                 p += dev->driver->offset;
160         }
161         return NULL;
162 }
163
164 static int apci3501_auto_attach(struct comedi_device *dev,
165                                 unsigned long context_unused)
166 {
167         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
168         const struct addi_board *this_board;
169         struct addi_private *devpriv;
170         struct comedi_subdevice *s;
171         int ret, n_subdevices;
172
173         this_board = addi_find_boardinfo(dev, pcidev);
174         if (!this_board)
175                 return -ENODEV;
176         dev->board_ptr = this_board;
177         dev->board_name = this_board->pc_DriverName;
178
179         devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
180         if (!devpriv)
181                 return -ENOMEM;
182         dev->private = devpriv;
183
184         ret = comedi_pci_enable(pcidev, dev->board_name);
185         if (ret)
186                 return ret;
187
188         dev->iobase = pci_resource_start(pcidev, 1);
189         devpriv->i_IobaseAmcc = pci_resource_start(pcidev, 0);
190
191         /* Initialize parameters that can be overridden in EEPROM */
192         devpriv->s_EeParameters.i_NbrAoChannel = this_board->i_NbrAoChannel;
193         devpriv->s_EeParameters.i_AoMaxdata = this_board->i_AoMaxdata;
194
195         if (pcidev->irq > 0) {
196                 ret = request_irq(pcidev->irq, apci3501_interrupt, IRQF_SHARED,
197                                   dev->board_name, dev);
198                 if (ret == 0)
199                         dev->irq = pcidev->irq;
200         }
201
202         addi_eeprom_read_info(dev, pci_resource_start(pcidev, 0));
203
204         n_subdevices = 7;
205         ret = comedi_alloc_subdevices(dev, n_subdevices);
206         if (ret)
207                 return ret;
208
209         /*  Allocate and Initialise AI Subdevice Structures */
210         s = &dev->subdevices[0];
211         s->type = COMEDI_SUBD_UNUSED;
212
213         /*  Allocate and Initialise AO Subdevice Structures */
214         s = &dev->subdevices[1];
215         if (devpriv->s_EeParameters.i_NbrAoChannel) {
216                 s->type = COMEDI_SUBD_AO;
217                 s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON;
218                 s->n_chan = devpriv->s_EeParameters.i_NbrAoChannel;
219                 s->maxdata = devpriv->s_EeParameters.i_AoMaxdata;
220                 s->len_chanlist =
221                         devpriv->s_EeParameters.i_NbrAoChannel;
222                 s->range_table = this_board->pr_AoRangelist;
223                 s->insn_config = this_board->ao_config;
224                 s->insn_write = this_board->ao_write;
225         } else {
226                 s->type = COMEDI_SUBD_UNUSED;
227         }
228         /*  Allocate and Initialise DI Subdevice Structures */
229         s = &dev->subdevices[2];
230         s->type         = COMEDI_SUBD_DI;
231         s->subdev_flags = SDF_READABLE;
232         s->n_chan       = 2;
233         s->maxdata      = 1;
234         s->range_table  = &range_digital;
235         s->insn_bits    = apci3501_di_insn_bits;
236
237         /* Initialize the digital output subdevice */
238         s = &dev->subdevices[3];
239         s->type         = COMEDI_SUBD_DO;
240         s->subdev_flags = SDF_WRITEABLE;
241         s->n_chan       = 2;
242         s->maxdata      = 1;
243         s->range_table  = &range_digital;
244         s->insn_bits    = apci3501_do_insn_bits;
245
246         /*  Allocate and Initialise Timer Subdevice Structures */
247         s = &dev->subdevices[4];
248         s->type = COMEDI_SUBD_TIMER;
249         s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON;
250         s->n_chan = 1;
251         s->maxdata = 0;
252         s->len_chanlist = 1;
253         s->range_table = &range_digital;
254         s->insn_write = i_APCI3501_StartStopWriteTimerCounterWatchdog;
255         s->insn_read = i_APCI3501_ReadTimerCounterWatchdog;
256         s->insn_config = i_APCI3501_ConfigTimerCounterWatchdog;
257
258         /*  Allocate and Initialise TTL */
259         s = &dev->subdevices[5];
260         s->type = COMEDI_SUBD_UNUSED;
261
262         /* EEPROM */
263         s = &dev->subdevices[6];
264         if (this_board->i_PCIEeprom) {
265                 s->type = COMEDI_SUBD_MEMORY;
266                 s->subdev_flags = SDF_READABLE | SDF_INTERNAL;
267                 s->n_chan = 256;
268                 s->maxdata = 0xffff;
269                 s->insn_read = i_ADDIDATA_InsnReadEeprom;
270         } else {
271                 s->type = COMEDI_SUBD_UNUSED;
272         }
273
274         apci3501_reset(dev);
275         return 0;
276 }
277
278 static void apci3501_detach(struct comedi_device *dev)
279 {
280         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
281         struct addi_private *devpriv = dev->private;
282
283         if (devpriv) {
284                 if (dev->iobase)
285                         apci3501_reset(dev);
286                 if (dev->irq)
287                         free_irq(dev->irq, dev);
288         }
289         if (pcidev) {
290                 if (dev->iobase)
291                         comedi_pci_disable(pcidev);
292         }
293 }
294
295 static struct comedi_driver apci3501_driver = {
296         .driver_name    = "addi_apci_3501",
297         .module         = THIS_MODULE,
298         .auto_attach    = apci3501_auto_attach,
299         .detach         = apci3501_detach,
300         .num_names      = ARRAY_SIZE(apci3501_boardtypes),
301         .board_name     = &apci3501_boardtypes[0].pc_DriverName,
302         .offset         = sizeof(struct addi_board),
303 };
304
305 static int apci3501_pci_probe(struct pci_dev *dev,
306                                         const struct pci_device_id *ent)
307 {
308         return comedi_pci_auto_config(dev, &apci3501_driver);
309 }
310
311 static void apci3501_pci_remove(struct pci_dev *dev)
312 {
313         comedi_pci_auto_unconfig(dev);
314 }
315
316 static DEFINE_PCI_DEVICE_TABLE(apci3501_pci_table) = {
317         { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x3001) },
318         { 0 }
319 };
320 MODULE_DEVICE_TABLE(pci, apci3501_pci_table);
321
322 static struct pci_driver apci3501_pci_driver = {
323         .name           = "addi_apci_3501",
324         .id_table       = apci3501_pci_table,
325         .probe          = apci3501_pci_probe,
326         .remove         = apci3501_pci_remove,
327 };
328 module_comedi_pci_driver(apci3501_driver, apci3501_pci_driver);
329
330 MODULE_AUTHOR("Comedi http://www.comedi.org");
331 MODULE_DESCRIPTION("Comedi low-level driver");
332 MODULE_LICENSE("GPL");