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