Merge branch 'for-airlied' of git://people.freedesktop.org/~danvet/drm-intel into...
[deliverable/linux.git] / drivers / staging / comedi / drivers / amplc_pc236.c
CommitLineData
6a5c8664
IA
1/*
2 comedi/drivers/amplc_pc236.c
3 Driver for Amplicon PC36AT and PCI236 DIO 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/*
26Driver: amplc_pc236
27Description: Amplicon PC36AT, PCI236
28Author: Ian Abbott <abbotti@mev.co.uk>
29Devices: [Amplicon] PC36AT (pc36at), PCI236 (pci236 or amplc_pc236)
0d6e5dad 30Updated: Wed, 01 Apr 2009 15:41:25 +0100
6a5c8664
IA
31Status: works
32
33Configuration options - PC36AT:
34 [0] - I/O port base address
35 [1] - IRQ (optional)
36
37Configuration options - PCI236:
38 [0] - PCI bus of device (optional)
39 [1] - PCI slot of device (optional)
40 If bus/slot is not specified, the first available PCI device will be
41 used.
42
43The PC36AT ISA board and PCI236 PCI board have a single 8255 appearing
44as subdevice 0.
45
46Subdevice 1 pretends to be a digital input device, but it always returns
470 when read. However, if you run a command with scan_begin_src=TRIG_EXT,
0d6e5dad 48a rising edge on port C bit 3 acts as an external trigger, which can be
6a5c8664
IA
49used to wake up tasks. This is like the comedi_parport device, but the
50only way to physically disable the interrupt on the PC36AT is to remove
51the IRQ jumper. If no interrupt is connected, then subdevice 1 is
52unused.
53*/
54
70265d24
JS
55#include <linux/interrupt.h>
56
6a5c8664
IA
57#include "../comedidev.h"
58
6a5c8664
IA
59#include "8255.h"
60#include "plx9052.h"
61
62#define PC236_DRIVER_NAME "amplc_pc236"
63
64/* PCI236 PCI configuration register information */
65#define PCI_VENDOR_ID_AMPLICON 0x14dc
66#define PCI_DEVICE_ID_AMPLICON_PCI236 0x0009
67#define PCI_DEVICE_ID_INVALID 0xffff
68
69/* PC36AT / PCI236 registers */
70
71#define PC236_IO_SIZE 4
72#define PC236_LCR_IO_SIZE 128
73
74/*
75 * INTCSR values for PCI236.
76 */
77/* Disable interrupt, also clear any interrupt there */
53106ae6 78#define PCI236_INTR_DISABLE (PLX9052_INTCSR_LI1ENAB_DISABLED \
9f7a344b
BA
79 | PLX9052_INTCSR_LI1POL_HIGH \
80 | PLX9052_INTCSR_LI2POL_HIGH \
81 | PLX9052_INTCSR_PCIENAB_DISABLED \
82 | PLX9052_INTCSR_LI1SEL_EDGE \
83 | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
6a5c8664 84/* Enable interrupt, also clear any interrupt there. */
53106ae6 85#define PCI236_INTR_ENABLE (PLX9052_INTCSR_LI1ENAB_ENABLED \
9f7a344b
BA
86 | PLX9052_INTCSR_LI1POL_HIGH \
87 | PLX9052_INTCSR_LI2POL_HIGH \
88 | PLX9052_INTCSR_PCIENAB_ENABLED \
89 | PLX9052_INTCSR_LI1SEL_EDGE \
90 | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
6a5c8664
IA
91
92/*
93 * Board descriptions for Amplicon PC36AT and PCI236.
94 */
95
96enum pc236_bustype { isa_bustype, pci_bustype };
97enum pc236_model { pc36at_model, pci236_model, anypci_model };
98
57ad8696 99struct pc236_board {
6a5c8664 100 const char *name;
6a5c8664
IA
101 unsigned short devid;
102 enum pc236_bustype bustype;
103 enum pc236_model model;
57ad8696
BP
104};
105static const struct pc236_board pc236_boards[] = {
1bb6dfc4 106#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA)
6a5c8664 107 {
916d7028
IA
108 .name = "pc36at",
109 .bustype = isa_bustype,
110 .model = pc36at_model,
111 },
717ab674 112#endif
1bb6dfc4 113#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
6a5c8664 114 {
916d7028
IA
115 .name = "pci236",
116 .devid = PCI_DEVICE_ID_AMPLICON_PCI236,
117 .bustype = pci_bustype,
118 .model = pci236_model,
119 },
6a5c8664 120 {
916d7028
IA
121 .name = PC236_DRIVER_NAME,
122 .devid = PCI_DEVICE_ID_INVALID,
123 .bustype = pci_bustype,
124 .model = anypci_model, /* wildcard */
125 },
6a5c8664
IA
126#endif
127};
128
6a5c8664
IA
129/* this structure is for data unique to this hardware driver. If
130 several hardware drivers keep similar information in this structure,
9f7a344b
BA
131 feel free to suggest moving the variable to the struct comedi_device struct.
132 */
f1ee810a 133struct pc236_private {
9f7a344b 134 unsigned long lcr_iobase; /* PLX PCI9052 config registers in PCIBAR1 */
6a5c8664 135 int enable_irq;
f1ee810a 136};
6a5c8664 137
18e41de0
IA
138/*
139 * This function looks for a board matching the supplied PCI device.
140 */
18e41de0
IA
141static const struct pc236_board *pc236_find_pci_board(struct pci_dev *pci_dev)
142{
143 unsigned int i;
144
145 for (i = 0; i < ARRAY_SIZE(pc236_boards); i++)
146 if (pc236_boards[i].bustype == pci_bustype &&
147 pci_dev->device == pc236_boards[i].devid)
148 return &pc236_boards[i];
149 return NULL;
150}
18e41de0 151
6a5c8664
IA
152/*
153 * This function looks for a PCI device matching the requested board name,
154 * bus and slot.
155 */
ed7fd225
HS
156static struct pci_dev *pc236_find_pci_dev(struct comedi_device *dev,
157 struct comedi_devconfig *it)
6a5c8664 158{
24ffe74c 159 const struct pc236_board *thisboard = comedi_board(dev);
6a5c8664 160 struct pci_dev *pci_dev = NULL;
ed7fd225
HS
161 int bus = it->options[0];
162 int slot = it->options[1];
6a5c8664 163
ed7fd225 164 for_each_pci_dev(pci_dev) {
6a5c8664 165 if (bus || slot) {
ed7fd225
HS
166 if (bus != pci_dev->bus->number ||
167 slot != PCI_SLOT(pci_dev->devfn))
6a5c8664
IA
168 continue;
169 }
ed7fd225
HS
170 if (pci_dev->vendor != PCI_VENDOR_ID_AMPLICON)
171 continue;
172
6a5c8664 173 if (thisboard->model == anypci_model) {
18e41de0
IA
174 /* Wildcard board matches any supported PCI board. */
175 const struct pc236_board *foundboard;
ed7fd225 176
18e41de0
IA
177 foundboard = pc236_find_pci_board(pci_dev);
178 if (foundboard == NULL)
6a5c8664 179 continue;
18e41de0 180 /* Replace wildcard board_ptr. */
ed7fd225 181 dev->board_ptr = foundboard;
6a5c8664
IA
182 } else {
183 /* Match specific model name. */
184 if (pci_dev->device != thisboard->devid)
185 continue;
186 }
273ba547 187 return pci_dev;
6a5c8664 188 }
ed7fd225
HS
189 dev_err(dev->class_dev,
190 "No supported board found! (req. bus %d, slot %d)\n",
191 bus, slot);
273ba547 192 return NULL;
6a5c8664 193}
6a5c8664 194
6a5c8664
IA
195/*
196 * This function checks and requests an I/O region, reporting an error
197 * if there is a conflict.
198 */
62bc23d1 199static int pc236_request_region(struct comedi_device *dev, unsigned long from,
0a85b6f0 200 unsigned long extent)
6a5c8664
IA
201{
202 if (!from || !request_region(from, extent, PC236_DRIVER_NAME)) {
62bc23d1
IA
203 dev_err(dev->class_dev, "I/O port conflict (%#lx,%lu)!\n",
204 from, extent);
6a5c8664
IA
205 return -EIO;
206 }
207 return 0;
208}
209
210/*
211 * This function is called to mark the interrupt as disabled (no command
212 * configured on subdevice 1) and to physically disable the interrupt
213 * (not possible on the PC36AT, except by removing the IRQ jumper!).
214 */
da91b269 215static void pc236_intr_disable(struct comedi_device *dev)
6a5c8664 216{
24ffe74c 217 struct pc236_private *devpriv = dev->private;
6a5c8664
IA
218 unsigned long flags;
219
5f74ea14 220 spin_lock_irqsave(&dev->spinlock, flags);
6a5c8664 221 devpriv->enable_irq = 0;
a46e759f 222 if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI) && devpriv->lcr_iobase)
6a5c8664 223 outl(PCI236_INTR_DISABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
5f74ea14 224 spin_unlock_irqrestore(&dev->spinlock, flags);
6a5c8664
IA
225}
226
227/*
228 * This function is called to mark the interrupt as enabled (a command
229 * configured on subdevice 1) and to physically enable the interrupt
230 * (not possible on the PC36AT, except by (re)connecting the IRQ jumper!).
231 */
da91b269 232static void pc236_intr_enable(struct comedi_device *dev)
6a5c8664 233{
24ffe74c 234 struct pc236_private *devpriv = dev->private;
6a5c8664
IA
235 unsigned long flags;
236
5f74ea14 237 spin_lock_irqsave(&dev->spinlock, flags);
6a5c8664 238 devpriv->enable_irq = 1;
a46e759f 239 if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI) && devpriv->lcr_iobase)
6a5c8664 240 outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
5f74ea14 241 spin_unlock_irqrestore(&dev->spinlock, flags);
6a5c8664
IA
242}
243
244/*
245 * This function is called when an interrupt occurs to check whether
246 * the interrupt has been marked as enabled and was generated by the
247 * board. If so, the function prepares the hardware for the next
248 * interrupt.
249 * Returns 0 if the interrupt should be ignored.
250 */
da91b269 251static int pc236_intr_check(struct comedi_device *dev)
6a5c8664 252{
24ffe74c 253 struct pc236_private *devpriv = dev->private;
6a5c8664
IA
254 int retval = 0;
255 unsigned long flags;
256
5f74ea14 257 spin_lock_irqsave(&dev->spinlock, flags);
6a5c8664
IA
258 if (devpriv->enable_irq) {
259 retval = 1;
a46e759f
IA
260 if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI) &&
261 devpriv->lcr_iobase) {
6a5c8664 262 if ((inl(devpriv->lcr_iobase + PLX9052_INTCSR)
0a85b6f0
MT
263 & PLX9052_INTCSR_LI1STAT_MASK)
264 == PLX9052_INTCSR_LI1STAT_INACTIVE) {
6a5c8664
IA
265 retval = 0;
266 } else {
267 /* Clear interrupt and keep it enabled. */
268 outl(PCI236_INTR_ENABLE,
0a85b6f0 269 devpriv->lcr_iobase + PLX9052_INTCSR);
6a5c8664
IA
270 }
271 }
6a5c8664 272 }
5f74ea14 273 spin_unlock_irqrestore(&dev->spinlock, flags);
6a5c8664
IA
274
275 return retval;
276}
277
278/*
279 * Input from subdevice 1.
280 * Copied from the comedi_parport driver.
281 */
0a85b6f0
MT
282static int pc236_intr_insn(struct comedi_device *dev,
283 struct comedi_subdevice *s, struct comedi_insn *insn,
284 unsigned int *data)
6a5c8664
IA
285{
286 data[1] = 0;
a2714e3e 287 return insn->n;
6a5c8664
IA
288}
289
290/*
291 * Subdevice 1 command test.
292 * Copied from the comedi_parport driver.
293 */
0a85b6f0
MT
294static int pc236_intr_cmdtest(struct comedi_device *dev,
295 struct comedi_subdevice *s,
296 struct comedi_cmd *cmd)
6a5c8664
IA
297{
298 int err = 0;
299 int tmp;
300
301 /* step 1 */
302
303 tmp = cmd->start_src;
304 cmd->start_src &= TRIG_NOW;
305 if (!cmd->start_src || tmp != cmd->start_src)
306 err++;
307
308 tmp = cmd->scan_begin_src;
309 cmd->scan_begin_src &= TRIG_EXT;
310 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
311 err++;
312
313 tmp = cmd->convert_src;
314 cmd->convert_src &= TRIG_FOLLOW;
315 if (!cmd->convert_src || tmp != cmd->convert_src)
316 err++;
317
318 tmp = cmd->scan_end_src;
319 cmd->scan_end_src &= TRIG_COUNT;
320 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
321 err++;
322
323 tmp = cmd->stop_src;
324 cmd->stop_src &= TRIG_NONE;
325 if (!cmd->stop_src || tmp != cmd->stop_src)
326 err++;
327
328 if (err)
329 return 1;
330
331 /* step 2: ignored */
332
333 if (err)
334 return 2;
335
336 /* step 3: */
337
338 if (cmd->start_arg != 0) {
339 cmd->start_arg = 0;
340 err++;
341 }
342 if (cmd->scan_begin_arg != 0) {
343 cmd->scan_begin_arg = 0;
344 err++;
345 }
346 if (cmd->convert_arg != 0) {
347 cmd->convert_arg = 0;
348 err++;
349 }
350 if (cmd->scan_end_arg != 1) {
351 cmd->scan_end_arg = 1;
352 err++;
353 }
354 if (cmd->stop_arg != 0) {
355 cmd->stop_arg = 0;
356 err++;
357 }
358
359 if (err)
360 return 3;
361
362 /* step 4: ignored */
363
364 if (err)
365 return 4;
366
367 return 0;
368}
369
370/*
371 * Subdevice 1 command.
372 */
da91b269 373static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
6a5c8664
IA
374{
375 pc236_intr_enable(dev);
376
377 return 0;
378}
379
380/*
381 * Subdevice 1 cancel command.
382 */
0a85b6f0
MT
383static int pc236_intr_cancel(struct comedi_device *dev,
384 struct comedi_subdevice *s)
6a5c8664
IA
385{
386 pc236_intr_disable(dev);
387
388 return 0;
389}
390
391/*
392 * Interrupt service routine.
393 * Based on the comedi_parport driver.
394 */
70265d24 395static irqreturn_t pc236_interrupt(int irq, void *d)
6a5c8664 396{
71b5f4f1 397 struct comedi_device *dev = d;
34c43922 398 struct comedi_subdevice *s = dev->subdevices + 1;
6a5c8664
IA
399 int handled;
400
401 handled = pc236_intr_check(dev);
402 if (dev->attached && handled) {
403 comedi_buf_put(s->async, 0);
404 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
405 comedi_event(dev, s);
406 }
407 return IRQ_RETVAL(handled);
408}
90f703d3 409
62bc23d1
IA
410static void pc236_report_attach(struct comedi_device *dev, unsigned int irq)
411{
24ffe74c 412 const struct pc236_board *thisboard = comedi_board(dev);
b6d446b5 413 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
62bc23d1
IA
414 char tmpbuf[60];
415 int tmplen;
416
a46e759f
IA
417 if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA) &&
418 thisboard->bustype == isa_bustype)
62bc23d1
IA
419 tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
420 "(base %#lx) ", dev->iobase);
a46e759f
IA
421 else if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI) &&
422 thisboard->bustype == pci_bustype) {
a46e759f 423 tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
b6d446b5 424 "(pci %s) ", pci_name(pcidev));
a46e759f 425 } else
62bc23d1 426 tmplen = 0;
62bc23d1
IA
427 if (irq)
428 tmplen += scnprintf(&tmpbuf[tmplen], sizeof(tmpbuf) - tmplen,
429 "(irq %u%s) ", irq,
430 (dev->irq ? "" : " UNAVAILABLE"));
431 else
432 tmplen += scnprintf(&tmpbuf[tmplen], sizeof(tmpbuf) - tmplen,
433 "(no irq) ");
434 dev_info(dev->class_dev, "%s %sattached\n",
435 dev->board_name, tmpbuf);
436}
437
18e41de0
IA
438static int pc236_common_attach(struct comedi_device *dev, unsigned long iobase,
439 unsigned int irq, unsigned long req_irq_flags)
7bd06f69 440{
24ffe74c 441 const struct pc236_board *thisboard = comedi_board(dev);
7bd06f69 442 struct comedi_subdevice *s;
7bd06f69
IA
443 int ret;
444
7bd06f69 445 dev->board_name = thisboard->name;
7bd06f69
IA
446 dev->iobase = iobase;
447
2f0b9d08 448 ret = comedi_alloc_subdevices(dev, 2);
8b6c5694 449 if (ret)
7bd06f69 450 return ret;
7bd06f69
IA
451
452 s = dev->subdevices + 0;
453 /* digital i/o subdevice (8255) */
454 ret = subdev_8255_init(dev, s, NULL, iobase);
455 if (ret < 0) {
62bc23d1 456 dev_err(dev->class_dev, "error! out of memory!\n");
7bd06f69
IA
457 return ret;
458 }
459 s = dev->subdevices + 1;
460 dev->read_subdev = s;
461 s->type = COMEDI_SUBD_UNUSED;
462 pc236_intr_disable(dev);
463 if (irq) {
18e41de0 464 if (request_irq(irq, pc236_interrupt, req_irq_flags,
7bd06f69
IA
465 PC236_DRIVER_NAME, dev) >= 0) {
466 dev->irq = irq;
467 s->type = COMEDI_SUBD_DI;
468 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
469 s->n_chan = 1;
470 s->maxdata = 1;
471 s->range_table = &range_digital;
472 s->insn_bits = pc236_intr_insn;
473 s->do_cmdtest = pc236_intr_cmdtest;
474 s->do_cmd = pc236_intr_cmd;
475 s->cancel = pc236_intr_cancel;
476 }
477 }
62bc23d1 478 pc236_report_attach(dev, irq);
7bd06f69
IA
479 return 1;
480}
481
18e41de0
IA
482static int pc236_pci_common_attach(struct comedi_device *dev,
483 struct pci_dev *pci_dev)
484{
485 struct pc236_private *devpriv = dev->private;
486 unsigned long iobase;
487 int ret;
488
b6d446b5
HS
489 comedi_set_hw_dev(dev, &pci_dev->dev);
490
18e41de0
IA
491 ret = comedi_pci_enable(pci_dev, PC236_DRIVER_NAME);
492 if (ret < 0) {
493 dev_err(dev->class_dev,
494 "error! cannot enable PCI device and request regions!\n");
495 return ret;
496 }
497 devpriv->lcr_iobase = pci_resource_start(pci_dev, 1);
498 iobase = pci_resource_start(pci_dev, 2);
499 return pc236_common_attach(dev, iobase, pci_dev->irq, IRQF_SHARED);
500}
18e41de0
IA
501
502/*
503 * Attach is called by the Comedi core to configure the driver
504 * for a particular board. If you specified a board_name array
505 * in the driver structure, dev->board_ptr contains that
506 * address.
507 */
508static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it)
509{
510 const struct pc236_board *thisboard = comedi_board(dev);
511 int ret;
512
513 dev_info(dev->class_dev, PC236_DRIVER_NAME ": attach\n");
514 ret = alloc_private(dev, sizeof(struct pc236_private));
515 if (ret < 0) {
516 dev_err(dev->class_dev, "error! out of memory!\n");
517 return ret;
518 }
519 /* Process options according to bus type. */
a46e759f
IA
520 if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA) &&
521 thisboard->bustype == isa_bustype) {
522 unsigned long iobase = it->options[0];
523 unsigned int irq = it->options[1];
524 ret = pc236_request_region(dev, iobase, PC236_IO_SIZE);
525 if (ret < 0)
526 return ret;
527 return pc236_common_attach(dev, iobase, irq, 0);
528 } else if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI) &&
529 thisboard->bustype == pci_bustype) {
a46e759f
IA
530 struct pci_dev *pci_dev;
531
ed7fd225
HS
532 pci_dev = pc236_find_pci_dev(dev, it);
533 if (!pci_dev)
a46e759f
IA
534 return -EIO;
535 return pc236_pci_common_attach(dev, pci_dev);
536 } else {
18e41de0
IA
537 dev_err(dev->class_dev, PC236_DRIVER_NAME
538 ": BUG! cannot determine board type!\n");
a46e759f 539 return -EINVAL;
18e41de0 540 }
18e41de0
IA
541}
542
543/*
544 * The attach_pci hook (if non-NULL) is called at PCI probe time in preference
545 * to the "manual" attach hook. dev->board_ptr is NULL on entry. There should
546 * be a board entry matching the supplied PCI device.
547 */
18e41de0
IA
548static int __devinit pc236_attach_pci(struct comedi_device *dev,
549 struct pci_dev *pci_dev)
550{
551 int ret;
552
a46e759f
IA
553 if (!IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI))
554 return -EINVAL;
555
18e41de0
IA
556 dev_info(dev->class_dev, PC236_DRIVER_NAME ": attach pci %s\n",
557 pci_name(pci_dev));
558 ret = alloc_private(dev, sizeof(struct pc236_private));
559 if (ret < 0) {
560 dev_err(dev->class_dev, "error! out of memory!\n");
561 return ret;
562 }
563 dev->board_ptr = pc236_find_pci_board(pci_dev);
564 if (dev->board_ptr == NULL) {
565 dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
566 return -EINVAL;
567 }
3abcfe0e
IA
568 /*
569 * Need to 'get' the PCI device to match the 'put' in pc236_detach().
570 * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
571 * support for manual attachment of PCI devices via pc236_attach()
572 * has been removed.
573 */
574 pci_dev_get(pci_dev);
18e41de0
IA
575 return pc236_pci_common_attach(dev, pci_dev);
576}
18e41de0 577
7bd06f69
IA
578static void pc236_detach(struct comedi_device *dev)
579{
24ffe74c 580 struct pc236_private *devpriv = dev->private;
b6d446b5 581 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
24ffe74c 582
7bd06f69
IA
583 if (devpriv)
584 pc236_intr_disable(dev);
585 if (dev->irq)
586 free_irq(dev->irq, dev);
587 if (dev->subdevices)
588 subdev_8255_cleanup(dev, dev->subdevices + 0);
b6d446b5
HS
589 if (pcidev) {
590 if (dev->iobase)
591 comedi_pci_disable(pcidev);
592 pci_dev_put(pcidev);
593 } else {
594 if (dev->iobase)
595 release_region(dev->iobase, PC236_IO_SIZE);
7bd06f69
IA
596 }
597}
598
599/*
600 * The struct comedi_driver structure tells the Comedi core module
601 * which functions to call to configure/deconfigure (attach/detach)
602 * the board, and also about the kernel module that contains
603 * the device code.
604 */
605static struct comedi_driver amplc_pc236_driver = {
606 .driver_name = PC236_DRIVER_NAME,
607 .module = THIS_MODULE,
608 .attach = pc236_attach,
18e41de0 609 .attach_pci = pc236_attach_pci,
7bd06f69
IA
610 .detach = pc236_detach,
611 .board_name = &pc236_boards[0].name,
612 .offset = sizeof(struct pc236_board),
613 .num_names = ARRAY_SIZE(pc236_boards),
614};
615
616#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
617static DEFINE_PCI_DEVICE_TABLE(pc236_pci_table) = {
618 { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236) },
619 {0}
620};
621
622MODULE_DEVICE_TABLE(pci, pc236_pci_table);
623
624static int __devinit amplc_pc236_pci_probe(struct pci_dev *dev,
625 const struct pci_device_id *ent)
626{
627 return comedi_pci_auto_config(dev, &amplc_pc236_driver);
628}
629
630static void __devexit amplc_pc236_pci_remove(struct pci_dev *dev)
631{
632 comedi_pci_auto_unconfig(dev);
633}
634
635static struct pci_driver amplc_pc236_pci_driver = {
636 .name = PC236_DRIVER_NAME,
637 .id_table = pc236_pci_table,
638 .probe = &amplc_pc236_pci_probe,
639 .remove = __devexit_p(&amplc_pc236_pci_remove)
640};
641
642module_comedi_pci_driver(amplc_pc236_driver, amplc_pc236_pci_driver);
643#else
644module_comedi_driver(amplc_pc236_driver);
645#endif
646
90f703d3
AT
647MODULE_AUTHOR("Comedi http://www.comedi.org");
648MODULE_DESCRIPTION("Comedi low-level driver");
649MODULE_LICENSE("GPL");
This page took 0.38366 seconds and 5 git commands to generate.