]> git.karo-electronics.de Git - mv-sheeva.git/blob - drivers/staging/comedi/drivers/amplc_pc263.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[mv-sheeva.git] / 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 #include "comedi_pci.h"
50
51 #define PC263_DRIVER_NAME       "amplc_pc263"
52
53 /* PCI263 PCI configuration register information */
54 #define PCI_VENDOR_ID_AMPLICON 0x14dc
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         const char *fancy_name;
71         unsigned short devid;
72         enum pc263_bustype bustype;
73         enum pc263_model model;
74 };
75 static const struct pc263_board pc263_boards[] = {
76         {
77          .name = "pc263",
78          .fancy_name = "PC263",
79          .bustype = isa_bustype,
80          .model = pc263_model,
81          },
82 #ifdef CONFIG_COMEDI_PCI
83         {
84          .name = "pci263",
85          .fancy_name = "PCI263",
86          .devid = PCI_DEVICE_ID_AMPLICON_PCI263,
87          .bustype = pci_bustype,
88          .model = pci263_model,
89          },
90 #endif
91 #ifdef CONFIG_COMEDI_PCI
92         {
93          .name = PC263_DRIVER_NAME,
94          .fancy_name = PC263_DRIVER_NAME,
95          .devid = PCI_DEVICE_ID_INVALID,
96          .bustype = pci_bustype,
97          .model = anypci_model, /* wildcard */
98          },
99 #endif
100 };
101
102 #ifdef CONFIG_COMEDI_PCI
103 static DEFINE_PCI_DEVICE_TABLE(pc263_pci_table) = {
104         {
105         PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI263,
106                     PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
107         0}
108 };
109
110 MODULE_DEVICE_TABLE(pci, pc263_pci_table);
111 #endif /* CONFIG_COMEDI_PCI */
112
113 /*
114  * Useful for shorthand access to the particular board structure
115  */
116 #define thisboard ((const struct pc263_board *)dev->board_ptr)
117
118 /* this structure is for data unique to this hardware driver.  If
119    several hardware drivers keep similar information in this structure,
120    feel free to suggest moving the variable to the struct comedi_device struct.
121 */
122 #ifdef CONFIG_COMEDI_PCI
123 struct pc263_private {
124         /* PCI device. */
125         struct pci_dev *pci_dev;
126 };
127
128 #define devpriv ((struct pc263_private *)dev->private)
129 #endif /* CONFIG_COMEDI_PCI */
130
131 /*
132  * The struct comedi_driver structure tells the Comedi core module
133  * which functions to call to configure/deconfigure (attach/detach)
134  * the board, and also about the kernel module that contains
135  * the device code.
136  */
137 static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it);
138 static int pc263_detach(struct comedi_device *dev);
139 static struct comedi_driver driver_amplc_pc263 = {
140         .driver_name = PC263_DRIVER_NAME,
141         .module = THIS_MODULE,
142         .attach = pc263_attach,
143         .detach = pc263_detach,
144         .board_name = &pc263_boards[0].name,
145         .offset = sizeof(struct pc263_board),
146         .num_names = ARRAY_SIZE(pc263_boards),
147 };
148
149 static int pc263_request_region(unsigned minor, unsigned long from,
150                                 unsigned long extent);
151 static int pc263_dio_insn_bits(struct comedi_device *dev,
152                                struct comedi_subdevice *s,
153                                struct comedi_insn *insn, unsigned int *data);
154 static int pc263_dio_insn_config(struct comedi_device *dev,
155                                  struct comedi_subdevice *s,
156                                  struct comedi_insn *insn, unsigned int *data);
157
158 /*
159  * This function looks for a PCI device matching the requested board name,
160  * bus and slot.
161  */
162 #ifdef CONFIG_COMEDI_PCI
163 static int
164 pc263_find_pci(struct comedi_device *dev, int bus, int slot,
165                struct pci_dev **pci_dev_p)
166 {
167         struct pci_dev *pci_dev = NULL;
168
169         *pci_dev_p = NULL;
170
171         /* Look for matching PCI device. */
172         for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
173              pci_dev != NULL;
174              pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
175                                       PCI_ANY_ID, pci_dev)) {
176                 /* If bus/slot specified, check them. */
177                 if (bus || slot) {
178                         if (bus != pci_dev->bus->number
179                             || slot != PCI_SLOT(pci_dev->devfn))
180                                 continue;
181                 }
182                 if (thisboard->model == anypci_model) {
183                         /* Match any supported model. */
184                         int i;
185
186                         for (i = 0; i < ARRAY_SIZE(pc263_boards); i++) {
187                                 if (pc263_boards[i].bustype != pci_bustype)
188                                         continue;
189                                 if (pci_dev->device == pc263_boards[i].devid) {
190                                         /* Change board_ptr to matched board. */
191                                         dev->board_ptr = &pc263_boards[i];
192                                         break;
193                                 }
194                         }
195                         if (i == ARRAY_SIZE(pc263_boards))
196                                 continue;
197                 } else {
198                         /* Match specific model name. */
199                         if (pci_dev->device != thisboard->devid)
200                                 continue;
201                 }
202
203                 /* Found a match. */
204                 *pci_dev_p = pci_dev;
205                 return 0;
206         }
207         /* No match found. */
208         if (bus || slot) {
209                 printk(KERN_ERR
210                        "comedi%d: error! no %s found at pci %02x:%02x!\n",
211                        dev->minor, thisboard->name, bus, slot);
212         } else {
213                 printk(KERN_ERR "comedi%d: error! no %s found!\n",
214                        dev->minor, thisboard->name);
215         }
216         return -EIO;
217 }
218 #endif
219
220 /*
221  * Attach is called by the Comedi core to configure the driver
222  * for a particular board.  If you specified a board_name array
223  * in the driver structure, dev->board_ptr contains that
224  * address.
225  */
226 static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it)
227 {
228         struct comedi_subdevice *s;
229         unsigned long iobase = 0;
230 #ifdef CONFIG_COMEDI_PCI
231         struct pci_dev *pci_dev = NULL;
232         int bus = 0, slot = 0;
233 #endif
234         int ret;
235
236         printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor,
237                PC263_DRIVER_NAME);
238 /*
239  * Allocate the private structure area.  alloc_private() is a
240  * convenient macro defined in comedidev.h.
241  */
242 #ifdef CONFIG_COMEDI_PCI
243         ret = alloc_private(dev, sizeof(struct pc263_private));
244         if (ret < 0) {
245                 printk(KERN_ERR "comedi%d: error! out of memory!\n",
246                        dev->minor);
247                 return ret;
248         }
249 #endif
250         /* Process options. */
251         switch (thisboard->bustype) {
252         case isa_bustype:
253                 iobase = it->options[0];
254                 break;
255 #ifdef CONFIG_COMEDI_PCI
256         case pci_bustype:
257                 bus = it->options[0];
258                 slot = it->options[1];
259
260                 ret = pc263_find_pci(dev, bus, slot, &pci_dev);
261                 if (ret < 0)
262                         return ret;
263                 devpriv->pci_dev = pci_dev;
264                 break;
265 #endif /* CONFIG_COMEDI_PCI */
266         default:
267                 printk(KERN_ERR
268                        "comedi%d: %s: BUG! cannot determine board type!\n",
269                        dev->minor, PC263_DRIVER_NAME);
270                 return -EINVAL;
271                 break;
272         }
273
274 /*
275  * Initialize dev->board_name.
276  */
277         dev->board_name = thisboard->name;
278
279         /* Enable device and reserve I/O spaces. */
280 #ifdef CONFIG_COMEDI_PCI
281         if (pci_dev) {
282                 ret = comedi_pci_enable(pci_dev, PC263_DRIVER_NAME);
283                 if (ret < 0) {
284                         printk(KERN_ERR
285                                "comedi%d: error! cannot enable PCI device and "
286                                 "request regions!\n",
287                                dev->minor);
288                         return ret;
289                 }
290                 iobase = pci_resource_start(pci_dev, 2);
291         } else
292 #endif
293         {
294                 ret = pc263_request_region(dev->minor, iobase, PC263_IO_SIZE);
295                 if (ret < 0)
296                         return ret;
297         }
298         dev->iobase = iobase;
299
300 /*
301  * Allocate the subdevice structures.  alloc_subdevice() is a
302  * convenient macro defined in comedidev.h.
303  */
304         ret = alloc_subdevices(dev, 1);
305         if (ret < 0) {
306                 printk(KERN_ERR "comedi%d: error! out of memory!\n",
307                        dev->minor);
308                 return ret;
309         }
310
311         s = dev->subdevices + 0;
312         /* digital i/o subdevice */
313         s->type = COMEDI_SUBD_DIO;
314         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
315         s->n_chan = 16;
316         s->maxdata = 1;
317         s->range_table = &range_digital;
318         s->insn_bits = pc263_dio_insn_bits;
319         s->insn_config = pc263_dio_insn_config;
320         /* all outputs */
321         s->io_bits = 0xffff;
322         /* read initial relay state */
323         s->state = inb(dev->iobase);
324         s->state = s->state | (inb(dev->iobase) << 8);
325
326         printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
327         if (thisboard->bustype == isa_bustype) {
328                 printk("(base %#lx) ", iobase);
329         } else {
330 #ifdef CONFIG_COMEDI_PCI
331                 printk("(pci %s) ", pci_name(pci_dev));
332 #endif
333         }
334
335         printk("attached\n");
336
337         return 1;
338 }
339
340 /*
341  * _detach is called to deconfigure a device.  It should deallocate
342  * resources.
343  * This function is also called when _attach() fails, so it should be
344  * careful not to release resources that were not necessarily
345  * allocated by _attach().  dev->private and dev->subdevices are
346  * deallocated automatically by the core.
347  */
348 static int pc263_detach(struct comedi_device *dev)
349 {
350         printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor,
351                PC263_DRIVER_NAME);
352
353 #ifdef CONFIG_COMEDI_PCI
354         if (devpriv) {
355 #endif
356 #ifdef CONFIG_COMEDI_PCI
357                 if (devpriv->pci_dev) {
358                         if (dev->iobase)
359                                 comedi_pci_disable(devpriv->pci_dev);
360                         pci_dev_put(devpriv->pci_dev);
361                 } else
362 #endif
363                 {
364                         if (dev->iobase)
365                                 release_region(dev->iobase, PC263_IO_SIZE);
366                 }
367         }
368         if (dev->board_name) {
369                 printk(KERN_INFO "comedi%d: %s removed\n",
370                        dev->minor, dev->board_name);
371         }
372         return 0;
373 }
374
375 /*
376  * This function checks and requests an I/O region, reporting an error
377  * if there is a conflict.
378  */
379 static int pc263_request_region(unsigned minor, unsigned long from,
380                                 unsigned long extent)
381 {
382         if (!from || !request_region(from, extent, PC263_DRIVER_NAME)) {
383                 printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n",
384                        minor, from, extent);
385                 return -EIO;
386         }
387         return 0;
388 }
389
390 /* DIO devices are slightly special.  Although it is possible to
391  * implement the insn_read/insn_write interface, it is much more
392  * useful to applications if you implement the insn_bits interface.
393  * This allows packed reading/writing of the DIO channels.  The
394  * comedi core can convert between insn_bits and insn_read/write */
395 static int pc263_dio_insn_bits(struct comedi_device *dev,
396                                struct comedi_subdevice *s,
397                                struct comedi_insn *insn, unsigned int *data)
398 {
399         if (insn->n != 2)
400                 return -EINVAL;
401
402         /* The insn data is a mask in data[0] and the new data
403          * in data[1], each channel cooresponding to a bit. */
404         if (data[0]) {
405                 s->state &= ~data[0];
406                 s->state |= data[0] & data[1];
407                 /* Write out the new digital output lines */
408                 outb(s->state & 0xFF, dev->iobase);
409                 outb(s->state >> 8, dev->iobase + 1);
410         }
411
412         /* on return, data[1] contains the value of the digital
413          * input and output lines. */
414         /* or we could just return the software copy of the output values if
415          * it was a purely digital output subdevice */
416         data[1] = s->state;
417
418         return 2;
419 }
420
421 static int pc263_dio_insn_config(struct comedi_device *dev,
422                                  struct comedi_subdevice *s,
423                                  struct comedi_insn *insn, unsigned int *data)
424 {
425         if (insn->n != 1)
426                 return -EINVAL;
427         return 1;
428 }
429
430 /*
431  * A convenient macro that defines init_module() and cleanup_module(),
432  * as necessary.
433  */
434 #ifdef CONFIG_COMEDI_PCI
435 COMEDI_PCI_INITCLEANUP(driver_amplc_pc263, pc263_pci_table);
436 #else
437 COMEDI_INITCLEANUP(driver_amplc_pc263);
438 #endif