]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/comedi/drivers/amplc_pc263.c
Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
[karo-tx-linux.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 <linux/pci.h>
48
49 #include "../comedidev.h"
50
51 #define PC263_DRIVER_NAME       "amplc_pc263"
52
53 #define DO_ISA  IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_ISA)
54 #define DO_PCI  IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI)
55
56 /* PCI263 PCI configuration register information */
57 #define PCI_DEVICE_ID_AMPLICON_PCI263 0x000c
58 #define PCI_DEVICE_ID_INVALID 0xffff
59
60 /* PC263 / PCI263 registers */
61 #define PC263_IO_SIZE   2
62
63 /*
64  * Board descriptions for Amplicon PC263 / PCI263.
65  */
66
67 enum pc263_bustype { isa_bustype, pci_bustype };
68 enum pc263_model { pc263_model, pci263_model, anypci_model };
69
70 struct pc263_board {
71         const char *name;
72         unsigned short devid;
73         enum pc263_bustype bustype;
74         enum pc263_model model;
75 };
76 static const struct pc263_board pc263_boards[] = {
77 #if DO_ISA
78         {
79                 .name = "pc263",
80                 .bustype = isa_bustype,
81                 .model = pc263_model,
82         },
83 #endif
84 #if DO_PCI
85         {
86                 .name = "pci263",
87                 .devid = PCI_DEVICE_ID_AMPLICON_PCI263,
88                 .bustype = pci_bustype,
89                 .model = pci263_model,
90         },
91         {
92                 .name = PC263_DRIVER_NAME,
93                 .devid = PCI_DEVICE_ID_INVALID,
94                 .bustype = pci_bustype,
95                 .model = anypci_model,  /* wildcard */
96         },
97 #endif
98 };
99
100 /* test if ISA supported and this is an ISA board */
101 static inline bool is_isa_board(const struct pc263_board *board)
102 {
103         return DO_ISA && board->bustype == isa_bustype;
104 }
105
106 /* test if PCI supported and this is a PCI board */
107 static inline bool is_pci_board(const struct pc263_board *board)
108 {
109         return DO_PCI && board->bustype == pci_bustype;
110 }
111
112 /*
113  * This function looks for a board matching the supplied PCI device.
114  */
115 static const struct pc263_board *pc263_find_pci_board(struct pci_dev *pci_dev)
116 {
117         unsigned int i;
118
119         for (i = 0; i < ARRAY_SIZE(pc263_boards); i++)
120                 if (is_pci_board(&pc263_boards[i]) &&
121                     pci_dev->device == pc263_boards[i].devid)
122                         return &pc263_boards[i];
123         return NULL;
124 }
125
126
127 /*
128  * This function looks for a PCI device matching the requested board name,
129  * bus and slot.
130  */
131 static struct pci_dev *pc263_find_pci_dev(struct comedi_device *dev,
132                                           struct comedi_devconfig *it)
133 {
134         const struct pc263_board *thisboard = comedi_board(dev);
135         struct pci_dev *pci_dev = NULL;
136         int bus = it->options[0];
137         int slot = it->options[1];
138
139         for_each_pci_dev(pci_dev) {
140                 if (bus || slot) {
141                         if (bus != pci_dev->bus->number ||
142                             slot != PCI_SLOT(pci_dev->devfn))
143                                 continue;
144                 }
145                 if (pci_dev->vendor != PCI_VENDOR_ID_AMPLICON)
146                         continue;
147
148                 if (thisboard->model == anypci_model) {
149                         /* Wildcard board matches any supported PCI board. */
150                         const struct pc263_board *foundboard;
151
152                         foundboard = pc263_find_pci_board(pci_dev);
153                         if (foundboard == NULL)
154                                 continue;
155                         /* Replace wildcard board_ptr. */
156                         dev->board_ptr = thisboard = foundboard;
157                 } else {
158                         /* Match specific model name. */
159                         if (pci_dev->device != thisboard->devid)
160                                 continue;
161                 }
162                 return pci_dev;
163         }
164         dev_err(dev->class_dev,
165                 "No supported board found! (req. bus %d, slot %d)\n",
166                 bus, slot);
167         return NULL;
168 }
169 /*
170  * This function checks and requests an I/O region, reporting an error
171  * if there is a conflict.
172  */
173 static int pc263_request_region(struct comedi_device *dev, unsigned long from,
174                                 unsigned long extent)
175 {
176         if (!from || !request_region(from, extent, PC263_DRIVER_NAME)) {
177                 dev_err(dev->class_dev, "I/O port conflict (%#lx,%lu)!\n",
178                         from, extent);
179                 return -EIO;
180         }
181         return 0;
182 }
183
184 static int pc263_do_insn_bits(struct comedi_device *dev,
185                               struct comedi_subdevice *s,
186                               struct comedi_insn *insn, unsigned int *data)
187 {
188         /* The insn data is a mask in data[0] and the new data
189          * in data[1], each channel cooresponding to a bit. */
190         if (data[0]) {
191                 s->state &= ~data[0];
192                 s->state |= data[0] & data[1];
193                 /* Write out the new digital output lines */
194                 outb(s->state & 0xFF, dev->iobase);
195                 outb(s->state >> 8, dev->iobase + 1);
196         }
197         return insn->n;
198 }
199
200 static void pc263_report_attach(struct comedi_device *dev)
201 {
202         const struct pc263_board *thisboard = comedi_board(dev);
203         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
204         char tmpbuf[40];
205
206         if (is_isa_board(thisboard))
207                 snprintf(tmpbuf, sizeof(tmpbuf), "(base %#lx) ", dev->iobase);
208         else if (is_pci_board(thisboard))
209                 snprintf(tmpbuf, sizeof(tmpbuf), "(pci %s) ",
210                          pci_name(pcidev));
211         else
212                 tmpbuf[0] = '\0';
213         dev_info(dev->class_dev, "%s %sattached\n", dev->board_name, tmpbuf);
214 }
215
216 static int pc263_common_attach(struct comedi_device *dev, unsigned long iobase)
217 {
218         const struct pc263_board *thisboard = comedi_board(dev);
219         struct comedi_subdevice *s;
220         int ret;
221
222         dev->board_name = thisboard->name;
223         dev->iobase = iobase;
224
225         ret = comedi_alloc_subdevices(dev, 1);
226         if (ret)
227                 return ret;
228
229         s = &dev->subdevices[0];
230         /* digital output subdevice */
231         s->type = COMEDI_SUBD_DO;
232         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
233         s->n_chan = 16;
234         s->maxdata = 1;
235         s->range_table = &range_digital;
236         s->insn_bits = pc263_do_insn_bits;
237         /* read initial relay state */
238         s->state = inb(dev->iobase) | (inb(dev->iobase + 1) << 8);
239
240         pc263_report_attach(dev);
241         return 1;
242 }
243
244 static int pc263_pci_common_attach(struct comedi_device *dev,
245                                    struct pci_dev *pci_dev)
246 {
247         unsigned long iobase;
248         int ret;
249
250         comedi_set_hw_dev(dev, &pci_dev->dev);
251
252         ret = comedi_pci_enable(pci_dev, PC263_DRIVER_NAME);
253         if (ret < 0) {
254                 dev_err(dev->class_dev,
255                         "error! cannot enable PCI device and request regions!\n");
256                 return ret;
257         }
258         iobase = pci_resource_start(pci_dev, 2);
259         return pc263_common_attach(dev, iobase);
260 }
261
262 /*
263  * Attach is called by the Comedi core to configure the driver
264  * for a particular board.  If you specified a board_name array
265  * in the driver structure, dev->board_ptr contains that
266  * address.
267  */
268 static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it)
269 {
270         const struct pc263_board *thisboard = comedi_board(dev);
271         int ret;
272
273         dev_info(dev->class_dev, PC263_DRIVER_NAME ": attach\n");
274
275         /* Process options and reserve resources according to bus type. */
276         if (is_isa_board(thisboard)) {
277                 unsigned long iobase = it->options[0];
278                 ret = pc263_request_region(dev, iobase, PC263_IO_SIZE);
279                 if (ret < 0)
280                         return ret;
281                 return pc263_common_attach(dev, iobase);
282         } else if (is_pci_board(thisboard)) {
283                 struct pci_dev *pci_dev;
284
285                 pci_dev = pc263_find_pci_dev(dev, it);
286                 if (!pci_dev)
287                         return -EIO;
288                 return pc263_pci_common_attach(dev, pci_dev);
289         } else {
290                 dev_err(dev->class_dev, PC263_DRIVER_NAME
291                         ": BUG! cannot determine board type!\n");
292                 return -EINVAL;
293         }
294 }
295
296 /*
297  * The auto_attach hook is called at PCI probe time via
298  * comedi_pci_auto_config().  dev->board_ptr is NULL on entry.
299  * There should be a board entry matching the supplied PCI device.
300  */
301 static int pc263_auto_attach(struct comedi_device *dev,
302                                        unsigned long context_unused)
303 {
304         struct pci_dev *pci_dev;
305
306         if (!DO_PCI)
307                 return -EINVAL;
308
309         pci_dev = comedi_to_pci_dev(dev);
310         dev_info(dev->class_dev, PC263_DRIVER_NAME ": attach pci %s\n",
311                  pci_name(pci_dev));
312         dev->board_ptr = pc263_find_pci_board(pci_dev);
313         if (dev->board_ptr == NULL) {
314                 dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
315                 return -EINVAL;
316         }
317         /*
318          * Need to 'get' the PCI device to match the 'put' in pc263_detach().
319          * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
320          * support for manual attachment of PCI devices via pc263_attach()
321          * has been removed.
322          */
323         pci_dev_get(pci_dev);
324         return pc263_pci_common_attach(dev, pci_dev);
325 }
326
327 static void pc263_detach(struct comedi_device *dev)
328 {
329         const struct pc263_board *thisboard = comedi_board(dev);
330
331         if (!thisboard)
332                 return;
333         if (is_isa_board(thisboard)) {
334                 if (dev->iobase)
335                         release_region(dev->iobase, PC263_IO_SIZE);
336         } else if (is_pci_board(thisboard)) {
337                 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
338                 if (pcidev) {
339                         if (dev->iobase)
340                                 comedi_pci_disable(pcidev);
341                         pci_dev_put(pcidev);
342                 }
343         }
344 }
345
346 /*
347  * The struct comedi_driver structure tells the Comedi core module
348  * which functions to call to configure/deconfigure (attach/detach)
349  * the board, and also about the kernel module that contains
350  * the device code.
351  */
352 static struct comedi_driver amplc_pc263_driver = {
353         .driver_name = PC263_DRIVER_NAME,
354         .module = THIS_MODULE,
355         .attach = pc263_attach,
356         .auto_attach = pc263_auto_attach,
357         .detach = pc263_detach,
358         .board_name = &pc263_boards[0].name,
359         .offset = sizeof(struct pc263_board),
360         .num_names = ARRAY_SIZE(pc263_boards),
361 };
362
363 #if DO_PCI
364 static DEFINE_PCI_DEVICE_TABLE(pc263_pci_table) = {
365         { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI263) },
366         {0}
367 };
368 MODULE_DEVICE_TABLE(pci, pc263_pci_table);
369
370 static int amplc_pc263_pci_probe(struct pci_dev *dev,
371                                                   const struct pci_device_id
372                                                   *ent)
373 {
374         return comedi_pci_auto_config(dev, &amplc_pc263_driver);
375 }
376
377 static struct pci_driver amplc_pc263_pci_driver = {
378         .name = PC263_DRIVER_NAME,
379         .id_table = pc263_pci_table,
380         .probe = &amplc_pc263_pci_probe,
381         .remove         = comedi_pci_auto_unconfig,
382 };
383 module_comedi_pci_driver(amplc_pc263_driver, amplc_pc263_pci_driver);
384 #else
385 module_comedi_driver(amplc_pc263_driver);
386 #endif
387
388 MODULE_AUTHOR("Comedi http://www.comedi.org");
389 MODULE_DESCRIPTION("Comedi low-level driver");
390 MODULE_LICENSE("GPL");