]> Pileus Git - ~andy/linux/blob - drivers/staging/comedi/drivers/amplc_pc263.c
staging: comedi: comedidev.h: add PCI_VENDOR_ID_AMPLICON
[~andy/linux] / drivers / staging / comedi / drivers / amplc_pc263.c
1 /*
2     comedi/drivers/amplc_pc263.c
3     Driver for Amplicon PC263 and PCI263 relay boards.
4
5     Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>
6
7     COMEDI - Linux Control and Measurement Device Interface
8     Copyright (C) 2000 David A. Schleef <ds@schleef.org>
9
10     This program is free software; you can redistribute it and/or modify
11     it under the terms of the GNU General Public License as published by
12     the Free Software Foundation; either version 2 of the License, or
13     (at your option) any later version.
14
15     This program is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18     GNU General Public License for more details.
19
20     You should have received a copy of the GNU General Public License
21     along with this program; if not, write to the Free Software
22     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23
24 */
25 /*
26 Driver: amplc_pc263
27 Description: Amplicon PC263, PCI263
28 Author: Ian Abbott <abbotti@mev.co.uk>
29 Devices: [Amplicon] PC263 (pc263), PCI263 (pci263 or amplc_pc263)
30 Updated: Wed, 22 Oct 2008 14:10:53 +0100
31 Status: works
32
33 Configuration options - PC263:
34   [0] - I/O port base address
35
36 Configuration options - PCI263:
37   [0] - PCI bus of device (optional)
38   [1] - PCI slot of device (optional)
39   If bus/slot is not specified, the first available PCI device will be
40   used.
41
42 Each board appears as one subdevice, with 16 digital outputs, each
43 connected to a reed-relay. Relay contacts are closed when output is 1.
44 The state of the outputs can be read.
45 */
46
47 #include "../comedidev.h"
48
49 #define PC263_DRIVER_NAME       "amplc_pc263"
50
51 #define DO_ISA  IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_ISA)
52 #define DO_PCI  IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI)
53
54 /* PCI263 PCI configuration register information */
55 #define PCI_DEVICE_ID_AMPLICON_PCI263 0x000c
56 #define PCI_DEVICE_ID_INVALID 0xffff
57
58 /* PC263 / PCI263 registers */
59 #define PC263_IO_SIZE   2
60
61 /*
62  * Board descriptions for Amplicon PC263 / PCI263.
63  */
64
65 enum pc263_bustype { isa_bustype, pci_bustype };
66 enum pc263_model { pc263_model, pci263_model, anypci_model };
67
68 struct pc263_board {
69         const char *name;
70         unsigned short devid;
71         enum pc263_bustype bustype;
72         enum pc263_model model;
73 };
74 static const struct pc263_board pc263_boards[] = {
75 #if DO_ISA
76         {
77                 .name = "pc263",
78                 .bustype = isa_bustype,
79                 .model = pc263_model,
80         },
81 #endif
82 #if DO_PCI
83         {
84                 .name = "pci263",
85                 .devid = PCI_DEVICE_ID_AMPLICON_PCI263,
86                 .bustype = pci_bustype,
87                 .model = pci263_model,
88         },
89         {
90                 .name = PC263_DRIVER_NAME,
91                 .devid = PCI_DEVICE_ID_INVALID,
92                 .bustype = pci_bustype,
93                 .model = anypci_model,  /* wildcard */
94         },
95 #endif
96 };
97
98 /* test if ISA supported and this is an ISA board */
99 static inline bool is_isa_board(const struct pc263_board *board)
100 {
101         return DO_ISA && board->bustype == isa_bustype;
102 }
103
104 /* test if PCI supported and this is a PCI board */
105 static inline bool is_pci_board(const struct pc263_board *board)
106 {
107         return DO_PCI && board->bustype == pci_bustype;
108 }
109
110 /*
111  * This function looks for a board matching the supplied PCI device.
112  */
113 static const struct pc263_board *pc263_find_pci_board(struct pci_dev *pci_dev)
114 {
115         unsigned int i;
116
117         for (i = 0; i < ARRAY_SIZE(pc263_boards); i++)
118                 if (is_pci_board(&pc263_boards[i]) &&
119                     pci_dev->device == pc263_boards[i].devid)
120                         return &pc263_boards[i];
121         return NULL;
122 }
123
124
125 /*
126  * This function looks for a PCI device matching the requested board name,
127  * bus and slot.
128  */
129 static struct pci_dev *pc263_find_pci_dev(struct comedi_device *dev,
130                                           struct comedi_devconfig *it)
131 {
132         const struct pc263_board *thisboard = comedi_board(dev);
133         struct pci_dev *pci_dev = NULL;
134         int bus = it->options[0];
135         int slot = it->options[1];
136
137         for_each_pci_dev(pci_dev) {
138                 if (bus || slot) {
139                         if (bus != pci_dev->bus->number ||
140                             slot != PCI_SLOT(pci_dev->devfn))
141                                 continue;
142                 }
143                 if (pci_dev->vendor != PCI_VENDOR_ID_AMPLICON)
144                         continue;
145
146                 if (thisboard->model == anypci_model) {
147                         /* Wildcard board matches any supported PCI board. */
148                         const struct pc263_board *foundboard;
149
150                         foundboard = pc263_find_pci_board(pci_dev);
151                         if (foundboard == NULL)
152                                 continue;
153                         /* Replace wildcard board_ptr. */
154                         dev->board_ptr = thisboard = foundboard;
155                 } else {
156                         /* Match specific model name. */
157                         if (pci_dev->device != thisboard->devid)
158                                 continue;
159                 }
160                 return pci_dev;
161         }
162         dev_err(dev->class_dev,
163                 "No supported board found! (req. bus %d, slot %d)\n",
164                 bus, slot);
165         return NULL;
166 }
167 /*
168  * This function checks and requests an I/O region, reporting an error
169  * if there is a conflict.
170  */
171 static int pc263_request_region(struct comedi_device *dev, unsigned long from,
172                                 unsigned long extent)
173 {
174         if (!from || !request_region(from, extent, PC263_DRIVER_NAME)) {
175                 dev_err(dev->class_dev, "I/O port conflict (%#lx,%lu)!\n",
176                         from, extent);
177                 return -EIO;
178         }
179         return 0;
180 }
181
182 static int pc263_do_insn_bits(struct comedi_device *dev,
183                               struct comedi_subdevice *s,
184                               struct comedi_insn *insn, unsigned int *data)
185 {
186         /* The insn data is a mask in data[0] and the new data
187          * in data[1], each channel cooresponding to a bit. */
188         if (data[0]) {
189                 s->state &= ~data[0];
190                 s->state |= data[0] & data[1];
191                 /* Write out the new digital output lines */
192                 outb(s->state & 0xFF, dev->iobase);
193                 outb(s->state >> 8, dev->iobase + 1);
194         }
195         return insn->n;
196 }
197
198 static void pc263_report_attach(struct comedi_device *dev)
199 {
200         const struct pc263_board *thisboard = comedi_board(dev);
201         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
202         char tmpbuf[40];
203
204         if (is_isa_board(thisboard))
205                 snprintf(tmpbuf, sizeof(tmpbuf), "(base %#lx) ", dev->iobase);
206         else if (is_pci_board(thisboard))
207                 snprintf(tmpbuf, sizeof(tmpbuf), "(pci %s) ",
208                          pci_name(pcidev));
209         else
210                 tmpbuf[0] = '\0';
211         dev_info(dev->class_dev, "%s %sattached\n", dev->board_name, tmpbuf);
212 }
213
214 static int pc263_common_attach(struct comedi_device *dev, unsigned long iobase)
215 {
216         const struct pc263_board *thisboard = comedi_board(dev);
217         struct comedi_subdevice *s;
218         int ret;
219
220         dev->board_name = thisboard->name;
221         dev->iobase = iobase;
222
223         ret = comedi_alloc_subdevices(dev, 1);
224         if (ret)
225                 return ret;
226
227         s = &dev->subdevices[0];
228         /* digital output subdevice */
229         s->type = COMEDI_SUBD_DO;
230         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
231         s->n_chan = 16;
232         s->maxdata = 1;
233         s->range_table = &range_digital;
234         s->insn_bits = pc263_do_insn_bits;
235         /* read initial relay state */
236         s->state = inb(dev->iobase) | (inb(dev->iobase + 1) << 8);
237
238         pc263_report_attach(dev);
239         return 1;
240 }
241
242 static int pc263_pci_common_attach(struct comedi_device *dev,
243                                    struct pci_dev *pci_dev)
244 {
245         unsigned long iobase;
246         int ret;
247
248         comedi_set_hw_dev(dev, &pci_dev->dev);
249
250         ret = comedi_pci_enable(pci_dev, PC263_DRIVER_NAME);
251         if (ret < 0) {
252                 dev_err(dev->class_dev,
253                         "error! cannot enable PCI device and request regions!\n");
254                 return ret;
255         }
256         iobase = pci_resource_start(pci_dev, 2);
257         return pc263_common_attach(dev, iobase);
258 }
259
260 /*
261  * Attach is called by the Comedi core to configure the driver
262  * for a particular board.  If you specified a board_name array
263  * in the driver structure, dev->board_ptr contains that
264  * address.
265  */
266 static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it)
267 {
268         const struct pc263_board *thisboard = comedi_board(dev);
269         int ret;
270
271         dev_info(dev->class_dev, PC263_DRIVER_NAME ": attach\n");
272
273         /* Process options and reserve resources according to bus type. */
274         if (is_isa_board(thisboard)) {
275                 unsigned long iobase = it->options[0];
276                 ret = pc263_request_region(dev, iobase, PC263_IO_SIZE);
277                 if (ret < 0)
278                         return ret;
279                 return pc263_common_attach(dev, iobase);
280         } else if (is_pci_board(thisboard)) {
281                 struct pci_dev *pci_dev;
282
283                 pci_dev = pc263_find_pci_dev(dev, it);
284                 if (!pci_dev)
285                         return -EIO;
286                 return pc263_pci_common_attach(dev, pci_dev);
287         } else {
288                 dev_err(dev->class_dev, PC263_DRIVER_NAME
289                         ": BUG! cannot determine board type!\n");
290                 return -EINVAL;
291         }
292 }
293 /*
294  * The attach_pci hook (if non-NULL) is called at PCI probe time in preference
295  * to the "manual" attach hook.  dev->board_ptr is NULL on entry.  There should
296  * be a board entry matching the supplied PCI device.
297  */
298 static int __devinit pc263_attach_pci(struct comedi_device *dev,
299                                       struct pci_dev *pci_dev)
300 {
301         if (!DO_PCI)
302                 return -EINVAL;
303
304         dev_info(dev->class_dev, PC263_DRIVER_NAME ": attach pci %s\n",
305                  pci_name(pci_dev));
306         dev->board_ptr = pc263_find_pci_board(pci_dev);
307         if (dev->board_ptr == NULL) {
308                 dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
309                 return -EINVAL;
310         }
311         /*
312          * Need to 'get' the PCI device to match the 'put' in pc263_detach().
313          * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
314          * support for manual attachment of PCI devices via pc263_attach()
315          * has been removed.
316          */
317         pci_dev_get(pci_dev);
318         return pc263_pci_common_attach(dev, pci_dev);
319 }
320
321 static void pc263_detach(struct comedi_device *dev)
322 {
323         const struct pc263_board *thisboard = comedi_board(dev);
324
325         if (is_isa_board(thisboard)) {
326                 if (dev->iobase)
327                         release_region(dev->iobase, PC263_IO_SIZE);
328         } else if (is_pci_board(thisboard)) {
329                 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
330                 if (pcidev) {
331                         if (dev->iobase)
332                                 comedi_pci_disable(pcidev);
333                         pci_dev_put(pcidev);
334                 }
335         }
336 }
337
338 /*
339  * The struct comedi_driver structure tells the Comedi core module
340  * which functions to call to configure/deconfigure (attach/detach)
341  * the board, and also about the kernel module that contains
342  * the device code.
343  */
344 static struct comedi_driver amplc_pc263_driver = {
345         .driver_name = PC263_DRIVER_NAME,
346         .module = THIS_MODULE,
347         .attach = pc263_attach,
348         .attach_pci = pc263_attach_pci,
349         .detach = pc263_detach,
350         .board_name = &pc263_boards[0].name,
351         .offset = sizeof(struct pc263_board),
352         .num_names = ARRAY_SIZE(pc263_boards),
353 };
354
355 #if DO_PCI
356 static DEFINE_PCI_DEVICE_TABLE(pc263_pci_table) = {
357         { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI263) },
358         {0}
359 };
360 MODULE_DEVICE_TABLE(pci, pc263_pci_table);
361
362 static int __devinit amplc_pc263_pci_probe(struct pci_dev *dev,
363                                                   const struct pci_device_id
364                                                   *ent)
365 {
366         return comedi_pci_auto_config(dev, &amplc_pc263_driver);
367 }
368
369 static void __devexit amplc_pc263_pci_remove(struct pci_dev *dev)
370 {
371         comedi_pci_auto_unconfig(dev);
372 }
373
374 static struct pci_driver amplc_pc263_pci_driver = {
375         .name = PC263_DRIVER_NAME,
376         .id_table = pc263_pci_table,
377         .probe = &amplc_pc263_pci_probe,
378         .remove = __devexit_p(&amplc_pc263_pci_remove)
379 };
380 module_comedi_pci_driver(amplc_pc263_driver, amplc_pc263_pci_driver);
381 #else
382 module_comedi_driver(amplc_pc263_driver);
383 #endif
384
385 MODULE_AUTHOR("Comedi http://www.comedi.org");
386 MODULE_DESCRIPTION("Comedi low-level driver");
387 MODULE_LICENSE("GPL");