Got rid of unnecessary casts when initializing comedi_driver.board_name
[comedi.git] / comedi / drivers / amplc_pc236.c
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 /*
26 Driver: amplc_pc236.o
27 Description: Amplicon PC36AT, PCI236
28 Author: Ian Abbott <abbotti@mev.co.uk>
29 Devices: [Amplicon] PC36AT (pc36at), PCI236 (pci236)
30 Updated: Fri, 23 Aug 2002 11:41:11 +0100
31 Status: works
32
33 Configuration options - PC36AT:
34   [0] - I/O port base address
35   [1] - IRQ (optional)
36
37 Configuration 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
43 The PC36AT ISA board and PCI236 PCI board have a single 8255 appearing
44 as subdevice 0.
45
46 Subdevice 1 pretends to be a digital input device, but it always returns
47 0 when read. However, if you run a command with scan_begin_src=TRIG_EXT,
48 a rising edge on port C bit 7 acts as an external trigger, which can be
49 used to wake up tasks.  This is like the comedi_parport device, but the
50 only way to physically disable the interrupt on the PC36AT is to remove
51 the IRQ jumper.  If no interrupt is connected, then subdevice 1 is
52 unused.
53 */
54
55 #include <linux/comedidev.h>
56
57 #include <linux/pci.h>
58
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
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 */
78 #define PCI236_INTR_DISABLE ( PLX9052_INTCSR_LI1ENAB_DISABLED \
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 )
84 /* Enable interrupt, also clear any interrupt there. */
85 #define PCI236_INTR_ENABLE ( PLX9052_INTCSR_LI1ENAB_ENABLED \
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 )
91
92 /*
93  * Board descriptions for Amplicon PC36AT and PCI236.
94  */
95
96 enum pc236_bustype {isa_bustype, pci_bustype};
97 enum pc236_model {pc36at_model, pci236_model};
98
99 typedef struct pc236_board_struct{
100         const char *name;
101         char *fancy_name;
102         enum pc236_bustype bustype;
103         enum pc236_model model;
104 }pc236_board;
105 static pc236_board pc236_boards[] = {
106         {
107         name:           "pc36at",
108         fancy_name:     "PC36AT",
109         bustype:        isa_bustype,
110         model:          pc36at_model,
111         },
112         {
113         name:           "pci236",
114         fancy_name:     "PCI236",
115         bustype:        pci_bustype,
116         model:          pci236_model,
117         },
118 };
119
120 static struct pci_device_id pc236_pci_table[] __devinitdata = {
121         { PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pci236_model },
122         { 0 }
123 };
124 MODULE_DEVICE_TABLE(pci, pc236_pci_table);
125
126 /*
127  * Useful for shorthand access to the particular board structure
128  */
129 #define thisboard ((pc236_board *)dev->board_ptr)
130
131 /* this structure is for data unique to this hardware driver.  If
132    several hardware drivers keep similar information in this structure,
133    feel free to suggest moving the variable to the comedi_device struct.  */
134 typedef struct{
135         /* PCI device */
136         struct pci_dev *pci_dev;
137         unsigned long lcr_iobase;       /* PLX PCI9052 config registers in PCIBAR1 */
138         int enable_irq;
139 }pc236_private;
140
141 #define devpriv ((pc236_private *)dev->private)
142
143 /*
144  * The comedi_driver structure tells the Comedi core module
145  * which functions to call to configure/deconfigure (attach/detach)
146  * the board, and also about the kernel module that contains
147  * the device code.
148  */
149 static int pc236_attach(comedi_device *dev,comedi_devconfig *it);
150 static int pc236_detach(comedi_device *dev);
151 static comedi_driver driver_amplc_pc236={
152         driver_name:    PC236_DRIVER_NAME,
153         module:         THIS_MODULE,
154         attach:         pc236_attach,
155         detach:         pc236_detach,
156         board_name:     &pc236_boards[0].name,
157         offset:         sizeof(pc236_board),
158         num_names:      sizeof(pc236_boards) / sizeof(pc236_board),
159 };
160 COMEDI_INITCLEANUP(driver_amplc_pc236);
161
162
163 static int pc236_request_region(unsigned long from, unsigned long extent);
164 static void pc236_intr_disable(comedi_device *dev);
165 static void pc236_intr_enable(comedi_device *dev);
166 static int pc236_intr_check(comedi_device *dev);
167 static int pc236_intr_insn(comedi_device *dev,comedi_subdevice *s,
168         comedi_insn *insn,lsampl_t *data);
169 static int pc236_intr_cmdtest(comedi_device *dev,comedi_subdevice *s,
170         comedi_cmd *cmd);
171 static int pc236_intr_cmd(comedi_device *dev,comedi_subdevice *s);
172 static int pc236_intr_cancel(comedi_device *dev,comedi_subdevice *s);
173 static irqreturn_t pc236_interrupt(int irq,void *d PT_REGS_ARG);
174
175 /*
176  * Attach is called by the Comedi core to configure the driver
177  * for a particular board.  If you specified a board_name array
178  * in the driver structure, dev->board_ptr contains that
179  * address.
180  */
181 static int pc236_attach(comedi_device *dev,comedi_devconfig *it)
182 {
183         comedi_subdevice *s;
184         struct pci_dev *pci_dev = NULL;
185         unsigned long iobase = 0;
186         unsigned int irq = 0;
187         int bus = 0, slot = 0;
188         struct pci_device_id *pci_id;
189         int share_irq = 0;
190         int ret;
191
192         printk("comedi%d: %s: ",dev->minor, PC236_DRIVER_NAME);
193 /*
194  * Allocate the private structure area.  alloc_private() is a
195  * convenient macro defined in comedidev.h.
196  */
197         if ((ret=alloc_private(dev,sizeof(pc236_private))) < 0) {
198                 printk("out of memory!\n");
199                 return ret;
200         }
201         /* Process options. */
202         switch (thisboard->bustype) {
203         case isa_bustype:
204                 iobase = it->options[0];
205                 irq = it->options[1];
206                 share_irq = 0;
207                 break;
208         case pci_bustype:
209                 bus = it->options[0];
210                 slot = it->options[1];
211                 share_irq = 1;
212
213                 /* Look for PCI table entry for this model. */
214                 for (pci_id = pc236_pci_table; pci_id->vendor != 0; pci_id++) {
215                         if (pci_id->driver_data == thisboard->model)
216                                 break;
217                 }
218                 if (pci_id->vendor == 0) {
219                         printk("bug! cannot determine board type!\n");
220                         return -EINVAL;
221                 }
222
223                 /* Look for matching PCI device. */
224                 for(pci_dev = pci_get_device(pci_id->vendor, pci_id->device,
225                                         NULL); pci_dev != NULL;
226                                 pci_dev = pci_get_device(pci_id->vendor,
227                                         pci_id->device, pci_dev)) {
228                         /* If bus/slot specified, check them. */
229                         if (bus || slot) {
230                                 if (bus != pci_dev->bus->number
231                                                 || slot != PCI_SLOT(pci_dev->devfn))
232                                         continue;
233                         }
234 #if 0
235                         if (pci_id->subvendor != PCI_ANY_ID) {
236                                 if (pci_dev->subsystem_vendor != pci_id->subvendor)
237                                         continue;
238                         }
239                         if (pci_id->subdevice != PCI_ANY_ID) {
240                                 if (pci_dev->subsystem_device != pci_id->subdevice)
241                                         continue;
242                         }
243 #endif
244                         if (((pci_dev->class ^ pci_id->class) & pci_id->class_mask) != 0)
245                                 continue;
246                         /* Found a match. */
247                         devpriv->pci_dev = pci_dev;
248                         break;
249                 }
250                 if (!pci_dev) {
251                         printk("no %s found!\n", thisboard->fancy_name);
252                         return -EIO;
253                 }
254                 break;
255         default:
256                 printk("bug! cannot determine board type!\n");
257                 return -EINVAL;
258                 break;
259         }
260
261 /*
262  * Initialize dev->board_name.
263  */
264         dev->board_name = thisboard->name;
265         printk("%s ", dev->board_name);
266
267         /* Enable device and reserve I/O spaces. */
268         if (pci_dev) {
269                 if ((ret=pci_enable_device(pci_dev)) < 0) {
270                         printk("error enabling PCI device!\n");
271                         return ret;
272                 }
273                 if ((ret=pci_request_regions(pci_dev, PC236_DRIVER_NAME)) < 0) {
274                         printk("I/O port conflict (PCI)!\n");
275                         return ret;
276                 }
277                 devpriv->lcr_iobase = pci_resource_start(pci_dev, 1);
278                 iobase = pci_resource_start(pci_dev, 2);
279                 irq = pci_dev->irq;
280         } else {
281                 if ((ret=pc236_request_region(iobase, PC236_IO_SIZE)) < 0) {
282                         return ret;
283                 }
284         }
285         dev->iobase = iobase;
286
287 /*
288  * Allocate the subdevice structures.  alloc_subdevice() is a
289  * convenient macro defined in comedidev.h.
290  */
291         if ((ret=alloc_subdevices(dev, 2)) < 0) {
292                 printk("out of memory!\n");
293                 return ret;
294         }
295
296         s = dev->subdevices+0;
297         /* digital i/o subdevice (8255) */
298         if ((ret=subdev_8255_init(dev, s, NULL, iobase)) < 0) {
299                 printk("out of memory!\n");
300                 return ret;
301         }
302         s = dev->subdevices+1;
303         dev->read_subdev = s;
304         s->type = COMEDI_SUBD_UNUSED;
305         pc236_intr_disable(dev);
306         if (irq) {
307                 unsigned long flags = share_irq ? IRQF_SHARED : 0;
308
309                 if (comedi_request_irq(irq, pc236_interrupt, flags,
310                                         PC236_DRIVER_NAME, dev) >= 0) {
311                         dev->irq = irq;
312                         s->type = COMEDI_SUBD_DI;
313                         s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
314                         s->n_chan = 1;
315                         s->maxdata = 1;
316                         s->range_table = &range_digital;
317                         s->insn_bits = pc236_intr_insn;
318                         s->do_cmdtest = pc236_intr_cmdtest;
319                         s->do_cmd = pc236_intr_cmd;
320                         s->cancel = pc236_intr_cancel;
321                 }
322         }
323         if (thisboard->bustype == isa_bustype) {
324                 printk("(base %#lx) ", iobase);
325         } else {
326                 printk("(pci %s) ", pci_name(pci_dev));
327         }
328         if (irq) {
329                 printk("(irq %u%s) ", irq, (dev->irq ? "" : " UNAVAILABLE"));
330         } else {
331                 printk("(no irq) ");
332         }
333
334         printk("attached\n");
335
336         return 1;
337 }
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 pc236_detach(comedi_device *dev)
349 {
350         printk("comedi%d: %s: remove\n", dev->minor, PC236_DRIVER_NAME);
351         if (devpriv) {
352                 pc236_intr_disable(dev);
353         }
354         if (dev->irq) comedi_free_irq(dev->irq, dev);
355         if (dev->subdevices) {
356                 subdev_8255_cleanup(dev, dev->subdevices+0);
357         }
358         if (devpriv) {
359                 if (devpriv->pci_dev) {
360                         if(dev->iobase)
361                         {
362                                 pci_release_regions(devpriv->pci_dev);
363                                 pci_disable_device(devpriv->pci_dev);
364                         }
365                         pci_dev_put(devpriv->pci_dev);
366                 } else if (dev->iobase) {
367                         release_region(dev->iobase, PC236_IO_SIZE);
368                 }
369         }
370         return 0;
371 }
372
373 /*
374  * This function checks and requests an I/O region, reporting an error
375  * if there is a conflict.
376  */
377 static int pc236_request_region(unsigned long from, unsigned long extent)
378 {
379         if (!from || !request_region(from, extent, PC236_DRIVER_NAME)) {
380                 printk("I/O port conflict (%#lx,%lu)!\n", from, extent);
381                 return -EIO;
382         }
383         return 0;
384 }
385
386 /*
387  * This function is called to mark the interrupt as disabled (no command
388  * configured on subdevice 1) and to physically disable the interrupt
389  * (not possible on the PC36AT, except by removing the IRQ jumper!).
390  */
391 static void pc236_intr_disable(comedi_device *dev)
392 {
393         unsigned long flags;
394
395         comedi_spin_lock_irqsave(&dev->spinlock, flags);
396         devpriv->enable_irq = 0;
397         if (devpriv->lcr_iobase)
398                 outl(PCI236_INTR_DISABLE, devpriv->lcr_iobase+PLX9052_INTCSR);
399         comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
400 }
401
402 /*
403  * This function is called to mark the interrupt as enabled (a command
404  * configured on subdevice 1) and to physically enable the interrupt
405  * (not possible on the PC36AT, except by (re)connecting the IRQ jumper!).
406  */
407 static void pc236_intr_enable(comedi_device *dev)
408 {
409         unsigned long flags;
410
411         comedi_spin_lock_irqsave(&dev->spinlock, flags);
412         devpriv->enable_irq = 1;
413         if (devpriv->lcr_iobase)
414                 outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase+PLX9052_INTCSR);
415         comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
416 }
417
418 /*
419  * This function is called when an interrupt occurs to check whether
420  * the interrupt has been marked as enabled and was generated by the
421  * board.  If so, the function prepares the hardware for the next
422  * interrupt.
423  * Returns 0 if the interrupt should be ignored.
424  */
425 static int pc236_intr_check(comedi_device *dev)
426 {
427         int retval = 0;
428         unsigned long flags;
429
430         comedi_spin_lock_irqsave(&dev->spinlock, flags);
431         if (devpriv->enable_irq) {
432                 retval = 1;
433                 if (devpriv->lcr_iobase) {
434                         if ((inl(devpriv->lcr_iobase+PLX9052_INTCSR)
435                                                 & PLX9052_INTCSR_LI1STAT_MASK)
436                                         == PLX9052_INTCSR_LI1STAT_INACTIVE) {
437                                 retval = 0;
438                         } else {
439                                 /* Clear interrupt and keep it enabled. */
440                                 outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase+PLX9052_INTCSR);
441                         }
442                 }
443         }
444         comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
445
446         return retval;
447 }
448
449 /*
450  * Input from subdevice 1.
451  * Copied from the comedi_parport driver.
452  */
453 static int pc236_intr_insn(comedi_device *dev,comedi_subdevice *s,
454         comedi_insn *insn,lsampl_t *data)
455 {
456         data[1] = 0;
457         return 2;
458 }
459
460 /*
461  * Subdevice 1 command test.
462  * Copied from the comedi_parport driver.
463  */
464 static int pc236_intr_cmdtest(comedi_device *dev,comedi_subdevice *s,
465         comedi_cmd *cmd)
466 {
467         int err=0;
468         int tmp;
469
470         /* step 1 */
471
472         tmp=cmd->start_src;
473         cmd->start_src &= TRIG_NOW;
474         if(!cmd->start_src || tmp!=cmd->start_src)err++;
475
476         tmp=cmd->scan_begin_src;
477         cmd->scan_begin_src &= TRIG_EXT;
478         if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++;
479
480         tmp=cmd->convert_src;
481         cmd->convert_src &= TRIG_FOLLOW;
482         if(!cmd->convert_src || tmp!=cmd->convert_src)err++;
483
484         tmp=cmd->scan_end_src;
485         cmd->scan_end_src &= TRIG_COUNT;
486         if(!cmd->scan_end_src || tmp!=cmd->scan_end_src)err++;
487
488         tmp=cmd->stop_src;
489         cmd->stop_src &= TRIG_NONE;
490         if(!cmd->stop_src || tmp!=cmd->stop_src)err++;
491
492         if(err)return 1;
493
494         /* step 2: ignored */
495
496         if(err)return 2;
497
498         /* step 3: */
499
500         if(cmd->start_arg!=0){
501                 cmd->start_arg = 0;
502                 err++;
503         }
504         if(cmd->scan_begin_arg!=0){
505                 cmd->scan_begin_arg = 0;
506                 err++;
507         }
508         if(cmd->convert_arg!=0){
509                 cmd->convert_arg = 0;
510                 err++;
511         }
512         if(cmd->scan_end_arg!=1){
513                 cmd->scan_end_arg = 1;
514                 err++;
515         }
516         if(cmd->stop_arg!=0){
517                 cmd->stop_arg = 0;
518                 err++;
519         }
520
521         if(err)return 3;
522
523         /* step 4: ignored */
524
525         if(err)return 4;
526
527         return 0;
528 }
529
530 /*
531  * Subdevice 1 command.
532  */
533 static int pc236_intr_cmd(comedi_device *dev,comedi_subdevice *s)
534 {
535         pc236_intr_enable(dev);
536
537         return 0;
538 }
539
540 /*
541  * Subdevice 1 cancel command.
542  */
543 static int pc236_intr_cancel(comedi_device *dev,comedi_subdevice *s)
544 {
545         pc236_intr_disable(dev);
546
547         return 0;
548 }
549
550 /*
551  * Interrupt service routine.
552  * Based on the comedi_parport driver.
553  */
554 static irqreturn_t pc236_interrupt(int irq,void *d PT_REGS_ARG)
555 {
556         comedi_device *dev=d;
557         comedi_subdevice *s=dev->subdevices+1;
558         int handled;
559
560         handled = pc236_intr_check(dev);
561         if (handled) {
562                 comedi_buf_put(s->async,0);
563                 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
564                 comedi_event(dev,s,s->async->events);
565         }
566         return IRQ_RETVAL(handled);
567 }
568