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