]> Pileus Git - ~andy/linux/blob - drivers/staging/comedi/drivers/addi_apci_1516.c
staging: comedi: conditionally build in PCI driver support
[~andy/linux] / drivers / staging / comedi / drivers / addi_apci_1516.c
1 /*
2  * addi_apci_1516.c
3  * Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.
4  * Project manager: Eric Stolz
5  *
6  *      ADDI-DATA GmbH
7  *      Dieselstrasse 3
8  *      D-77833 Ottersweier
9  *      Tel: +19(0)7223/9493-0
10  *      Fax: +49(0)7223/9493-92
11  *      http://www.addi-data.com
12  *      info@addi-data.com
13  *
14  * This program is free software; you can redistribute it and/or modify it
15  * under the terms of the GNU General Public License as published by the
16  * Free Software Foundation; either version 2 of the License, or (at your
17  * option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful, but WITHOUT
20  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
22  * more details.
23  *
24  * You should have received a copy of the GNU General Public License along
25  * with this program; if not, write to the Free Software Foundation, Inc.,
26  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27  *
28  * You should also find the complete GPL in the COPYING file accompanying
29  * this source code.
30  */
31
32 #include <linux/pci.h>
33
34 #include "../comedidev.h"
35 #include "addi_watchdog.h"
36 #include "comedi_fc.h"
37
38 /*
39  * PCI device ids supported by this driver
40  */
41 #define PCI_DEVICE_ID_APCI1016          0x1000
42 #define PCI_DEVICE_ID_APCI1516          0x1001
43 #define PCI_DEVICE_ID_APCI2016          0x1002
44
45 /*
46  * PCI bar 1 I/O Register map - Digital input/output
47  */
48 #define APCI1516_DI_REG                 0x00
49 #define APCI1516_DO_REG                 0x04
50
51 /*
52  * PCI bar 2 I/O Register map - Watchdog (APCI-1516 and APCI-2016)
53  */
54 #define APCI1516_WDOG_REG               0x00
55
56 struct apci1516_boardinfo {
57         const char *name;
58         unsigned short device;
59         int di_nchan;
60         int do_nchan;
61         int has_wdog;
62 };
63
64 static const struct apci1516_boardinfo apci1516_boardtypes[] = {
65         {
66                 .name           = "apci1016",
67                 .device         = PCI_DEVICE_ID_APCI1016,
68                 .di_nchan       = 16,
69         }, {
70                 .name           = "apci1516",
71                 .device         = PCI_DEVICE_ID_APCI1516,
72                 .di_nchan       = 8,
73                 .do_nchan       = 8,
74                 .has_wdog       = 1,
75         }, {
76                 .name           = "apci2016",
77                 .device         = PCI_DEVICE_ID_APCI2016,
78                 .do_nchan       = 16,
79                 .has_wdog       = 1,
80         },
81 };
82
83 struct apci1516_private {
84         unsigned long wdog_iobase;
85 };
86
87 static int apci1516_di_insn_bits(struct comedi_device *dev,
88                                  struct comedi_subdevice *s,
89                                  struct comedi_insn *insn,
90                                  unsigned int *data)
91 {
92         data[1] = inw(dev->iobase + APCI1516_DI_REG);
93
94         return insn->n;
95 }
96
97 static int apci1516_do_insn_bits(struct comedi_device *dev,
98                                  struct comedi_subdevice *s,
99                                  struct comedi_insn *insn,
100                                  unsigned int *data)
101 {
102         unsigned int mask = data[0];
103         unsigned int bits = data[1];
104
105         s->state = inw(dev->iobase + APCI1516_DO_REG);
106         if (mask) {
107                 s->state &= ~mask;
108                 s->state |= (bits & mask);
109
110                 outw(s->state, dev->iobase + APCI1516_DO_REG);
111         }
112
113         data[1] = s->state;
114
115         return insn->n;
116 }
117
118 static int apci1516_reset(struct comedi_device *dev)
119 {
120         const struct apci1516_boardinfo *this_board = comedi_board(dev);
121         struct apci1516_private *devpriv = dev->private;
122
123         if (!this_board->has_wdog)
124                 return 0;
125
126         outw(0x0, dev->iobase + APCI1516_DO_REG);
127
128         addi_watchdog_reset(devpriv->wdog_iobase);
129
130         return 0;
131 }
132
133 static const void *apci1516_find_boardinfo(struct comedi_device *dev,
134                                            struct pci_dev *pcidev)
135 {
136         const struct apci1516_boardinfo *this_board;
137         int i;
138
139         for (i = 0; i < dev->driver->num_names; i++) {
140                 this_board = &apci1516_boardtypes[i];
141                 if (this_board->device == pcidev->device)
142                         return this_board;
143         }
144         return NULL;
145 }
146
147 static int apci1516_auto_attach(struct comedi_device *dev,
148                                           unsigned long context_unused)
149 {
150         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
151         const struct apci1516_boardinfo *this_board;
152         struct apci1516_private *devpriv;
153         struct comedi_subdevice *s;
154         int ret;
155
156         this_board = apci1516_find_boardinfo(dev, pcidev);
157         if (!this_board)
158                 return -ENODEV;
159         dev->board_ptr = this_board;
160         dev->board_name = this_board->name;
161
162         devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
163         if (!devpriv)
164                 return -ENOMEM;
165         dev->private = devpriv;
166
167         ret = comedi_pci_enable(pcidev, dev->board_name);
168         if (ret)
169                 return ret;
170
171         dev->iobase = pci_resource_start(pcidev, 1);
172         devpriv->wdog_iobase = pci_resource_start(pcidev, 2);
173
174         ret = comedi_alloc_subdevices(dev, 3);
175         if (ret)
176                 return ret;
177
178         /* Initialize the digital input subdevice */
179         s = &dev->subdevices[0];
180         if (this_board->di_nchan) {
181                 s->type         = COMEDI_SUBD_DI;
182                 s->subdev_flags = SDF_READABLE;
183                 s->n_chan       = this_board->di_nchan;
184                 s->maxdata      = 1;
185                 s->range_table  = &range_digital;
186                 s->insn_bits    = apci1516_di_insn_bits;
187         } else {
188                 s->type         = COMEDI_SUBD_UNUSED;
189         }
190
191         /* Initialize the digital output subdevice */
192         s = &dev->subdevices[1];
193         if (this_board->do_nchan) {
194                 s->type         = COMEDI_SUBD_DO;
195                 s->subdev_flags = SDF_WRITEABLE;
196                 s->n_chan       = this_board->do_nchan;
197                 s->maxdata      = 1;
198                 s->range_table  = &range_digital;
199                 s->insn_bits    = apci1516_do_insn_bits;
200         } else {
201                 s->type         = COMEDI_SUBD_UNUSED;
202         }
203
204         /* Initialize the watchdog subdevice */
205         s = &dev->subdevices[2];
206         if (this_board->has_wdog) {
207                 ret = addi_watchdog_init(s, devpriv->wdog_iobase);
208                 if (ret)
209                         return ret;
210         } else {
211                 s->type         = COMEDI_SUBD_UNUSED;
212         }
213
214         apci1516_reset(dev);
215         return 0;
216 }
217
218 static void apci1516_detach(struct comedi_device *dev)
219 {
220         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
221
222         if (dev->iobase)
223                 apci1516_reset(dev);
224         if (dev->subdevices)
225                 addi_watchdog_cleanup(&dev->subdevices[2]);
226         if (dev->iobase)
227                 comedi_pci_disable(pcidev);
228 }
229
230 static struct comedi_driver apci1516_driver = {
231         .driver_name    = "addi_apci_1516",
232         .module         = THIS_MODULE,
233         .auto_attach    = apci1516_auto_attach,
234         .detach         = apci1516_detach,
235 };
236
237 static int apci1516_pci_probe(struct pci_dev *dev,
238                                         const struct pci_device_id *ent)
239 {
240         return comedi_pci_auto_config(dev, &apci1516_driver);
241 }
242
243 static DEFINE_PCI_DEVICE_TABLE(apci1516_pci_table) = {
244         { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, PCI_DEVICE_ID_APCI1016) },
245         { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, PCI_DEVICE_ID_APCI1516) },
246         { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, PCI_DEVICE_ID_APCI2016) },
247         { 0 }
248 };
249 MODULE_DEVICE_TABLE(pci, apci1516_pci_table);
250
251 static struct pci_driver apci1516_pci_driver = {
252         .name           = "addi_apci_1516",
253         .id_table       = apci1516_pci_table,
254         .probe          = apci1516_pci_probe,
255         .remove         = comedi_pci_auto_unconfig,
256 };
257 module_comedi_pci_driver(apci1516_driver, apci1516_pci_driver);
258
259 MODULE_DESCRIPTION("ADDI-DATA APCI-1016/1516/2016, 16 channel DIO boards");
260 MODULE_AUTHOR("Comedi http://www.comedi.org");
261 MODULE_LICENSE("GPL");