Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
[sfrench/cifs-2.6.git] / drivers / staging / comedi / drivers / das08.c
1 /*
2  *  comedi/drivers/das08.c
3  *  DAS08 driver
4  *
5  *  COMEDI - Linux Control and Measurement Device Interface
6  *  Copyright (C) 2000 David A. Schleef <ds@schleef.org>
7  *  Copyright (C) 2001,2002,2003 Frank Mori Hess <fmhess@users.sourceforge.net>
8  *  Copyright (C) 2004 Salvador E. Tropea <set@users.sf.net> <set@ieee.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
27 /*
28  * Driver: das08
29  * Description: DAS-08 compatible boards
30  * Author: Warren Jasper, ds, Frank Hess
31  * Devices: [Keithley Metrabyte] DAS08 (isa-das08),
32  * [ComputerBoards] DAS08 (isa-das08), DAS08-PGM (das08-pgm),
33  * DAS08-PGH (das08-pgh), DAS08-PGL (das08-pgl), DAS08-AOH (das08-aoh),
34  * DAS08-AOL (das08-aol), DAS08-AOM (das08-aom), DAS08/JR-AO (das08/jr-ao),
35  * DAS08/JR-16-AO (das08jr-16-ao), PCI-DAS08 (das08),
36  * PC104-DAS08 (pc104-das08), DAS08/JR/16 (das08jr/16)
37  * Status: works
38  *
39  * This is a rewrite of the das08 and das08jr drivers.
40  *
41  * Options (for ISA cards):
42  *              [0] - base io address
43  *
44  * Options (for pci-das08):
45  *              [0] - bus  (optional)
46  *              [1] = slot (optional)
47  *
48  * The das08 driver doesn't support asynchronous commands, since
49  * the cheap das08 hardware doesn't really support them.  The
50  * comedi_rt_timer driver can be used to emulate commands for this
51  * driver.
52  */
53
54 #include "../comedidev.h"
55
56 #include <linux/delay.h>
57
58 #include "comedi_pci.h"
59 #include "8255.h"
60 #include "das08.h"
61
62 #define DRV_NAME "das08"
63
64 #define PCI_VENDOR_ID_COMPUTERBOARDS 0x1307
65 #define PCI_DEVICE_ID_PCIDAS08 0x29
66 #define PCIDAS08_SIZE 0x54
67
68 /* pci configuration registers */
69 #define INTCSR               0x4c
70 #define   INTR1_ENABLE         0x1
71 #define   INTR1_HIGH_POLARITY  0x2
72 #define   PCI_INTR_ENABLE      0x40
73 #define   INTR1_EDGE_TRIG      0x100    /*  requires high polarity */
74 #define CNTRL                0x50
75 #define   CNTRL_DIR            0x2
76 #define   CNTRL_INTR           0x4
77
78 /*
79     cio-das08.pdf
80
81   "isa-das08"
82
83   0     a/d bits 0-3            start 8 bit
84   1     a/d bits 4-11           start 12 bit
85   2     eoc, ip1-3, irq, mux    op1-4, inte, mux
86   3     unused                  unused
87   4567  8254
88   89ab  8255
89
90   requires hard-wiring for async ai
91
92 */
93
94 #define DAS08_LSB               0
95 #define DAS08_MSB               1
96 #define DAS08_TRIG_12BIT        1
97 #define DAS08_STATUS            2
98 #define   DAS08_EOC                     (1<<7)
99 #define   DAS08_IRQ                     (1<<3)
100 #define   DAS08_IP(x)                   (((x)>>4)&0x7)
101 #define DAS08_CONTROL           2
102 #define   DAS08_MUX_MASK        0x7
103 #define   DAS08_MUX(x)          ((x) & DAS08_MUX_MASK)
104 #define   DAS08_INTE                    (1<<3)
105 #define   DAS08_DO_MASK         0xf0
106 #define   DAS08_OP(x)           (((x) << 4) & DAS08_DO_MASK)
107
108 /*
109     cio-das08jr.pdf
110
111   "das08/jr-ao"
112
113   0     a/d bits 0-3            unused
114   1     a/d bits 4-11           start 12 bit
115   2     eoc, mux                mux
116   3     di                      do
117   4     unused                  ao0_lsb
118   5     unused                  ao0_msb
119   6     unused                  ao1_lsb
120   7     unused                  ao1_msb
121
122 */
123
124 #define DAS08JR_DIO             3
125 #define DAS08JR_AO_LSB(x)       ((x) ? 6 : 4)
126 #define DAS08JR_AO_MSB(x)       ((x) ? 7 : 5)
127
128 /*
129     cio-das08_aox.pdf
130
131   "das08-aoh"
132   "das08-aol"
133   "das08-aom"
134
135   0     a/d bits 0-3            start 8 bit
136   1     a/d bits 4-11           start 12 bit
137   2     eoc, ip1-3, irq, mux    op1-4, inte, mux
138   3     mux, gain status        gain control
139   4567  8254
140   8     unused                  ao0_lsb
141   9     unused                  ao0_msb
142   a     unused                  ao1_lsb
143   b     unused                  ao1_msb
144   89ab
145   cdef  8255
146 */
147
148 #define DAS08AO_GAIN_CONTROL    3
149 #define DAS08AO_GAIN_STATUS     3
150
151 #define DAS08AO_AO_LSB(x)       ((x) ? 0xa : 8)
152 #define DAS08AO_AO_MSB(x)       ((x) ? 0xb : 9)
153 #define DAS08AO_AO_UPDATE       8
154
155 /* gainlist same as _pgx_ below */
156
157 static int das08_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
158                           struct comedi_insn *insn, unsigned int *data);
159 static int das08_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
160                           struct comedi_insn *insn, unsigned int *data);
161 static int das08_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s,
162                           struct comedi_insn *insn, unsigned int *data);
163 static int das08jr_di_rbits(struct comedi_device *dev,
164                             struct comedi_subdevice *s,
165                             struct comedi_insn *insn, unsigned int *data);
166 static int das08jr_do_wbits(struct comedi_device *dev,
167                             struct comedi_subdevice *s,
168                             struct comedi_insn *insn, unsigned int *data);
169 static int das08jr_ao_winsn(struct comedi_device *dev,
170                             struct comedi_subdevice *s,
171                             struct comedi_insn *insn, unsigned int *data);
172 static int das08ao_ao_winsn(struct comedi_device *dev,
173                             struct comedi_subdevice *s,
174                             struct comedi_insn *insn, unsigned int *data);
175 static void i8254_set_mode_low(unsigned int base, int channel,
176                                unsigned int mode);
177
178 static const struct comedi_lrange range_das08_pgl = { 9, {
179                                                           BIP_RANGE(10),
180                                                           BIP_RANGE(5),
181                                                           BIP_RANGE(2.5),
182                                                           BIP_RANGE(1.25),
183                                                           BIP_RANGE(0.625),
184                                                           UNI_RANGE(10),
185                                                           UNI_RANGE(5),
186                                                           UNI_RANGE(2.5),
187                                                           UNI_RANGE(1.25)
188                                                           }
189 };
190
191 static const struct comedi_lrange range_das08_pgh = { 12, {
192                                                            BIP_RANGE(10),
193                                                            BIP_RANGE(5),
194                                                            BIP_RANGE(1),
195                                                            BIP_RANGE(0.5),
196                                                            BIP_RANGE(0.1),
197                                                            BIP_RANGE(0.05),
198                                                            BIP_RANGE(0.01),
199                                                            BIP_RANGE(0.005),
200                                                            UNI_RANGE(10),
201                                                            UNI_RANGE(1),
202                                                            UNI_RANGE(0.1),
203                                                            UNI_RANGE(0.01),
204                                                            }
205 };
206
207 static const struct comedi_lrange range_das08_pgm = { 9, {
208                                                           BIP_RANGE(10),
209                                                           BIP_RANGE(5),
210                                                           BIP_RANGE(0.5),
211                                                           BIP_RANGE(0.05),
212                                                           BIP_RANGE(0.01),
213                                                           UNI_RANGE(10),
214                                                           UNI_RANGE(1),
215                                                           UNI_RANGE(0.1),
216                                                           UNI_RANGE(0.01)
217                                                           }
218 };                              /*
219                                    cio-das08jr.pdf
220
221                                    "das08/jr-ao"
222
223                                    0 a/d bits 0-3            unused
224                                    1 a/d bits 4-11           start 12 bit
225                                    2 eoc, mux                mux
226                                    3 di                      do
227                                    4 unused                  ao0_lsb
228                                    5 unused                  ao0_msb
229                                    6 unused                  ao1_lsb
230                                    7 unused                  ao1_msb
231
232                                  */
233
234 static const struct comedi_lrange *const das08_ai_lranges[] = {
235         &range_unknown,
236         &range_bipolar5,
237         &range_das08_pgh,
238         &range_das08_pgl,
239         &range_das08_pgm,
240 };
241
242 static const int das08_pgh_gainlist[] = {
243         8, 0, 10, 2, 12, 4, 14, 6, 1, 3, 5, 7
244 };
245 static const int das08_pgl_gainlist[] = { 8, 0, 2, 4, 6, 1, 3, 5, 7 };
246 static const int das08_pgm_gainlist[] = { 8, 0, 10, 12, 14, 9, 11, 13, 15 };
247
248 static const int *const das08_gainlists[] = {
249         NULL,
250         NULL,
251         das08_pgh_gainlist,
252         das08_pgl_gainlist,
253         das08_pgm_gainlist,
254 };
255
256 static const struct das08_board_struct das08_boards[] = {
257         {
258          .name = "isa-das08",   /*  cio-das08.pdf */
259          .bustype = isa,
260          .ai = das08_ai_rinsn,
261          .ai_nbits = 12,
262          .ai_pg = das08_pg_none,
263          .ai_encoding = das08_encode12,
264          .ao = NULL,
265          .ao_nbits = 12,
266          .di = das08_di_rbits,
267          .do_ = das08_do_wbits,
268          .do_nchan = 4,
269          .i8255_offset = 8,
270          .i8254_offset = 4,
271          .iosize = 16,          /*  unchecked */
272          },
273         {
274          .name = "das08-pgm",   /*  cio-das08pgx.pdf */
275          .bustype = isa,
276          .ai = das08_ai_rinsn,
277          .ai_nbits = 12,
278          .ai_pg = das08_pgm,
279          .ai_encoding = das08_encode12,
280          .ao = NULL,
281          .di = das08_di_rbits,
282          .do_ = das08_do_wbits,
283          .do_nchan = 4,
284          .i8255_offset = 0,
285          .i8254_offset = 0x04,
286          .iosize = 16,          /*  unchecked */
287          },
288         {
289          .name = "das08-pgh",   /*  cio-das08pgx.pdf */
290          .bustype = isa,
291          .ai = das08_ai_rinsn,
292          .ai_nbits = 12,
293          .ai_pg = das08_pgh,
294          .ai_encoding = das08_encode12,
295          .ao = NULL,
296          .di = das08_di_rbits,
297          .do_ = das08_do_wbits,
298          .do_nchan = 4,
299          .i8255_offset = 0,
300          .i8254_offset = 0x04,
301          .iosize = 16,          /*  unchecked */
302          },
303         {
304          .name = "das08-pgl",   /*  cio-das08pgx.pdf */
305          .bustype = isa,
306          .ai = das08_ai_rinsn,
307          .ai_nbits = 12,
308          .ai_pg = das08_pgl,
309          .ai_encoding = das08_encode12,
310          .ao = NULL,
311          .di = das08_di_rbits,
312          .do_ = das08_do_wbits,
313          .do_nchan = 4,
314          .i8255_offset = 0,
315          .i8254_offset = 0x04,
316          .iosize = 16,          /*  unchecked */
317          },
318         {
319          .name = "das08-aoh",   /*  cio-das08_aox.pdf */
320          .bustype = isa,
321          .ai = das08_ai_rinsn,
322          .ai_nbits = 12,
323          .ai_pg = das08_pgh,
324          .ai_encoding = das08_encode12,
325          .ao = das08ao_ao_winsn,        /*  8 */
326          .ao_nbits = 12,
327          .di = das08_di_rbits,
328          .do_ = das08_do_wbits,
329          .do_nchan = 4,
330          .i8255_offset = 0x0c,
331          .i8254_offset = 0x04,
332          .iosize = 16,          /*  unchecked */
333          },
334         {
335          .name = "das08-aol",   /*  cio-das08_aox.pdf */
336          .bustype = isa,
337          .ai = das08_ai_rinsn,
338          .ai_nbits = 12,
339          .ai_pg = das08_pgl,
340          .ai_encoding = das08_encode12,
341          .ao = das08ao_ao_winsn,        /*  8 */
342          .ao_nbits = 12,
343          .di = das08_di_rbits,
344          .do_ = das08_do_wbits,
345          .do_nchan = 4,
346          .i8255_offset = 0x0c,
347          .i8254_offset = 0x04,
348          .iosize = 16,          /*  unchecked */
349          },
350         {
351          .name = "das08-aom",   /*  cio-das08_aox.pdf */
352          .bustype = isa,
353          .ai = das08_ai_rinsn,
354          .ai_nbits = 12,
355          .ai_pg = das08_pgm,
356          .ai_encoding = das08_encode12,
357          .ao = das08ao_ao_winsn,        /*  8 */
358          .ao_nbits = 12,
359          .di = das08_di_rbits,
360          .do_ = das08_do_wbits,
361          .do_nchan = 4,
362          .i8255_offset = 0x0c,
363          .i8254_offset = 0x04,
364          .iosize = 16,          /*  unchecked */
365          },
366         {
367          .name = "das08/jr-ao", /*  cio-das08-jr-ao.pdf */
368          .bustype = isa,
369          .ai = das08_ai_rinsn,
370          .ai_nbits = 12,
371          .ai_pg = das08_pg_none,
372          .ai_encoding = das08_encode12,
373          .ao = das08jr_ao_winsn,
374          .ao_nbits = 12,
375          .di = das08jr_di_rbits,
376          .do_ = das08jr_do_wbits,
377          .do_nchan = 8,
378          .i8255_offset = 0,
379          .i8254_offset = 0,
380          .iosize = 16,          /*  unchecked */
381          },
382         {
383          .name = "das08jr-16-ao",       /*  cio-das08jr-16-ao.pdf */
384          .bustype = isa,
385          .ai = das08_ai_rinsn,
386          .ai_nbits = 16,
387          .ai_pg = das08_pg_none,
388          .ai_encoding = das08_encode12,
389          .ao = das08jr_ao_winsn,
390          .ao_nbits = 16,
391          .di = das08jr_di_rbits,
392          .do_ = das08jr_do_wbits,
393          .do_nchan = 8,
394          .i8255_offset = 0,
395          .i8254_offset = 0x04,
396          .iosize = 16,          /*  unchecked */
397          },
398 #ifdef CONFIG_COMEDI_PCI
399         {
400          .name = "das08",       /*  pci-das08 */
401          .id = PCI_DEVICE_ID_PCIDAS08,
402          .bustype = pci,
403          .ai = das08_ai_rinsn,
404          .ai_nbits = 12,
405          .ai_pg = das08_bipolar5,
406          .ai_encoding = das08_encode12,
407          .ao = NULL,
408          .ao_nbits = 0,
409          .di = das08_di_rbits,
410          .do_ = das08_do_wbits,
411          .do_nchan = 4,
412          .i8255_offset = 0,
413          .i8254_offset = 4,
414          .iosize = 8,
415          },
416 #endif
417         {
418          .name = "pc104-das08",
419          .bustype = pc104,
420          .ai = das08_ai_rinsn,
421          .ai_nbits = 12,
422          .ai_pg = das08_pg_none,
423          .ai_encoding = das08_encode12,
424          .ao = NULL,
425          .ao_nbits = 0,
426          .di = das08_di_rbits,
427          .do_ = das08_do_wbits,
428          .do_nchan = 4,
429          .i8255_offset = 0,
430          .i8254_offset = 4,
431          .iosize = 16,          /*  unchecked */
432          },
433 #if 0
434         {
435          .name = "das08/f",
436          },
437         {
438          .name = "das08jr",
439          },
440 #endif
441         {
442          .name = "das08jr/16",
443          .bustype = isa,
444          .ai = das08_ai_rinsn,
445          .ai_nbits = 16,
446          .ai_pg = das08_pg_none,
447          .ai_encoding = das08_encode16,
448          .ao = NULL,
449          .ao_nbits = 0,
450          .di = das08jr_di_rbits,
451          .do_ = das08jr_do_wbits,
452          .do_nchan = 8,
453          .i8255_offset = 0,
454          .i8254_offset = 0,
455          .iosize = 16,          /*  unchecked */
456          },
457 #if 0
458         {
459          .name = "das48-pga",   /*  cio-das48-pga.pdf */
460          },
461         {
462          .name = "das08-pga-g2",        /*  a KM board */
463          },
464 #endif
465 };
466
467 #ifdef CONFIG_COMEDI_PCMCIA
468 struct das08_board_struct das08_cs_boards[NUM_DAS08_CS_BOARDS] = {
469         {
470          .name = "pcm-das08",
471          .id = 0x0,             /*  XXX */
472          .bustype = pcmcia,
473          .ai = das08_ai_rinsn,
474          .ai_nbits = 12,
475          .ai_pg = das08_bipolar5,
476          .ai_encoding = das08_pcm_encode12,
477          .ao = NULL,
478          .ao_nbits = 0,
479          .di = das08_di_rbits,
480          .do_ = das08_do_wbits,
481          .do_nchan = 3,
482          .i8255_offset = 0,
483          .i8254_offset = 0,
484          .iosize = 16,
485          },
486         /*  duplicate so driver name can be used also */
487         {
488          .name = "das08_cs",
489          .id = 0x0,             /*  XXX */
490          .bustype = pcmcia,
491          .ai = das08_ai_rinsn,
492          .ai_nbits = 12,
493          .ai_pg = das08_bipolar5,
494          .ai_encoding = das08_pcm_encode12,
495          .ao = NULL,
496          .ao_nbits = 0,
497          .di = das08_di_rbits,
498          .do_ = das08_do_wbits,
499          .do_nchan = 3,
500          .i8255_offset = 0,
501          .i8254_offset = 0,
502          .iosize = 16,
503          },
504 };
505 #endif
506
507 #ifdef CONFIG_COMEDI_PCI
508 static DEFINE_PCI_DEVICE_TABLE(das08_pci_table) = {
509         {
510         PCI_VENDOR_ID_COMPUTERBOARDS, PCI_DEVICE_ID_PCIDAS08,
511                     PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
512         0}
513 };
514
515 MODULE_DEVICE_TABLE(pci, das08_pci_table);
516 #endif
517
518 #define devpriv ((struct das08_private_struct *)dev->private)
519 #define thisboard ((const struct das08_board_struct *)dev->board_ptr)
520
521 #define TIMEOUT 100000
522
523 static int das08_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
524                           struct comedi_insn *insn, unsigned int *data)
525 {
526         int i, n;
527         int chan;
528         int range;
529         int lsb, msb;
530
531         chan = CR_CHAN(insn->chanspec);
532         range = CR_RANGE(insn->chanspec);
533
534         /* clear crap */
535         inb(dev->iobase + DAS08_LSB);
536         inb(dev->iobase + DAS08_MSB);
537
538         /* set multiplexer */
539         /*  lock to prevent race with digital output */
540         spin_lock(&dev->spinlock);
541         devpriv->do_mux_bits &= ~DAS08_MUX_MASK;
542         devpriv->do_mux_bits |= DAS08_MUX(chan);
543         outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL);
544         spin_unlock(&dev->spinlock);
545
546         if (s->range_table->length > 1) {
547                 /* set gain/range */
548                 range = CR_RANGE(insn->chanspec);
549                 outb(devpriv->pg_gainlist[range],
550                      dev->iobase + DAS08AO_GAIN_CONTROL);
551         }
552
553         for (n = 0; n < insn->n; n++) {
554                 /* clear over-range bits for 16-bit boards */
555                 if (thisboard->ai_nbits == 16)
556                         if (inb(dev->iobase + DAS08_MSB) & 0x80)
557                                 printk(KERN_INFO "das08: over-range\n");
558
559                 /* trigger conversion */
560                 outb_p(0, dev->iobase + DAS08_TRIG_12BIT);
561
562                 for (i = 0; i < TIMEOUT; i++) {
563                         if (!(inb(dev->iobase + DAS08_STATUS) & DAS08_EOC))
564                                 break;
565                 }
566                 if (i == TIMEOUT) {
567                         printk(KERN_ERR "das08: timeout\n");
568                         return -ETIME;
569                 }
570                 msb = inb(dev->iobase + DAS08_MSB);
571                 lsb = inb(dev->iobase + DAS08_LSB);
572                 if (thisboard->ai_encoding == das08_encode12) {
573                         data[n] = (lsb >> 4) | (msb << 4);
574                 } else if (thisboard->ai_encoding == das08_pcm_encode12) {
575                         data[n] = (msb << 8) + lsb;
576                 } else if (thisboard->ai_encoding == das08_encode16) {
577                         /* FPOS 16-bit boards are sign-magnitude */
578                         if (msb & 0x80)
579                                 data[n] = (1 << 15) | lsb | ((msb & 0x7f) << 8);
580                         else
581                                 data[n] = (1 << 15) - (lsb | (msb & 0x7f) << 8);
582                 } else {
583                         comedi_error(dev, "bug! unknown ai encoding");
584                         return -1;
585                 }
586         }
587
588         return n;
589 }
590
591 static int das08_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
592                           struct comedi_insn *insn, unsigned int *data)
593 {
594         data[0] = 0;
595         data[1] = DAS08_IP(inb(dev->iobase + DAS08_STATUS));
596
597         return 2;
598 }
599
600 static int das08_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s,
601                           struct comedi_insn *insn, unsigned int *data)
602 {
603         int wbits;
604
605         /*  get current settings of digital output lines */
606         wbits = (devpriv->do_mux_bits >> 4) & 0xf;
607         /*  null bits we are going to set */
608         wbits &= ~data[0];
609         /*  set new bit values */
610         wbits |= data[0] & data[1];
611         /*  remember digital output bits */
612         /*  prevent race with setting of analog input mux */
613         spin_lock(&dev->spinlock);
614         devpriv->do_mux_bits &= ~DAS08_DO_MASK;
615         devpriv->do_mux_bits |= DAS08_OP(wbits);
616         outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL);
617         spin_unlock(&dev->spinlock);
618
619         data[1] = wbits;
620
621         return 2;
622 }
623
624 static int das08jr_di_rbits(struct comedi_device *dev,
625                             struct comedi_subdevice *s,
626                             struct comedi_insn *insn, unsigned int *data)
627 {
628         data[0] = 0;
629         data[1] = inb(dev->iobase + DAS08JR_DIO);
630
631         return 2;
632 }
633
634 static int das08jr_do_wbits(struct comedi_device *dev,
635                             struct comedi_subdevice *s,
636                             struct comedi_insn *insn, unsigned int *data)
637 {
638         /*  null bits we are going to set */
639         devpriv->do_bits &= ~data[0];
640         /*  set new bit values */
641         devpriv->do_bits |= data[0] & data[1];
642         outb(devpriv->do_bits, dev->iobase + DAS08JR_DIO);
643
644         data[1] = devpriv->do_bits;
645
646         return 2;
647 }
648
649 static int das08jr_ao_winsn(struct comedi_device *dev,
650                             struct comedi_subdevice *s,
651                             struct comedi_insn *insn, unsigned int *data)
652 {
653         int n;
654         int lsb, msb;
655         int chan;
656
657         lsb = data[0] & 0xff;
658         msb = (data[0] >> 8) & 0xf;
659
660         chan = CR_CHAN(insn->chanspec);
661
662         for (n = 0; n < insn->n; n++) {
663 #if 0
664                 outb(lsb, dev->iobase + devpriv->ao_offset_lsb[chan]);
665                 outb(msb, dev->iobase + devpriv->ao_offset_msb[chan]);
666 #else
667                 outb(lsb, dev->iobase + DAS08JR_AO_LSB(chan));
668                 outb(msb, dev->iobase + DAS08JR_AO_MSB(chan));
669 #endif
670
671                 /* load DACs */
672                 inb(dev->iobase + DAS08JR_DIO);
673         }
674
675         return n;
676 }
677
678 /*
679  *
680  * The -aox boards have the DACs at a different offset and use
681  * a different method to force an update.
682  *
683  */
684 static int das08ao_ao_winsn(struct comedi_device *dev,
685                             struct comedi_subdevice *s,
686                             struct comedi_insn *insn, unsigned int *data)
687 {
688         int n;
689         int lsb, msb;
690         int chan;
691
692         lsb = data[0] & 0xff;
693         msb = (data[0] >> 8) & 0xf;
694
695         chan = CR_CHAN(insn->chanspec);
696
697         for (n = 0; n < insn->n; n++) {
698 #if 0
699                 outb(lsb, dev->iobase + devpriv->ao_offset_lsb[chan]);
700                 outb(msb, dev->iobase + devpriv->ao_offset_msb[chan]);
701 #else
702                 outb(lsb, dev->iobase + DAS08AO_AO_LSB(chan));
703                 outb(msb, dev->iobase + DAS08AO_AO_MSB(chan));
704 #endif
705
706                 /* load DACs */
707                 inb(dev->iobase + DAS08AO_AO_UPDATE);
708         }
709
710         return n;
711 }
712
713 static unsigned int i8254_read_channel_low(unsigned int base, int chan)
714 {
715         unsigned int msb, lsb;
716
717         /* The following instructions must be in order.
718            We must avoid other process reading the counter's value in the
719            middle.
720            The spin_lock isn't needed since ioctl calls grab the big kernel
721            lock automatically */
722         /*spin_lock(sp); */
723         outb(chan << 6, base + I8254_CTRL);
724         base += chan;
725         lsb = inb(base);
726         msb = inb(base);
727         /*spin_unlock(sp); */
728
729         return lsb | (msb << 8);
730 }
731
732 static void i8254_write_channel_low(unsigned int base, int chan,
733                                     unsigned int value)
734 {
735         unsigned int msb, lsb;
736
737         lsb = value & 0xFF;
738         msb = value >> 8;
739
740         /* write lsb, then msb */
741         base += chan;
742         /* See comments in i8254_read_channel_low */
743         /*spin_lock(sp); */
744         outb(lsb, base);
745         outb(msb, base);
746         /*spin_unlock(sp); */
747 }
748
749 static unsigned int i8254_read_channel(struct i8254_struct *st, int channel)
750 {
751         int chan = st->logic2phys[channel];
752
753         return i8254_read_channel_low(st->iobase, chan);
754 }
755
756 static void i8254_write_channel(struct i8254_struct *st, int channel,
757                                 unsigned int value)
758 {
759         int chan = st->logic2phys[channel];
760
761         i8254_write_channel_low(st->iobase, chan, value);
762 }
763
764 static void i8254_initialize(struct i8254_struct *st)
765 {
766         int i;
767         for (i = 0; i < 3; ++i)
768                 i8254_set_mode_low(st->iobase, i, st->mode[i]);
769 }
770
771 static void i8254_set_mode_low(unsigned int base, int channel,
772                                unsigned int mode)
773 {
774         outb((channel << 6) | 0x30 | (mode & 0x0F), base + I8254_CTRL);
775 }
776
777 static void i8254_set_mode(struct i8254_struct *st, int channel,
778                            unsigned int mode)
779 {
780         int chan = st->logic2phys[channel];
781
782         st->mode[chan] = mode;
783         return i8254_set_mode_low(st->iobase, chan, mode);
784 }
785
786 static unsigned int i8254_read_status_low(unsigned int base, int channel)
787 {
788         outb(0xE0 | (2 << channel), base + I8254_CTRL);
789         return inb(base + channel);
790 }
791
792 static unsigned int i8254_read_status(struct i8254_struct *st, int channel)
793 {
794         int chan = st->logic2phys[channel];
795
796         return i8254_read_status_low(st->iobase, chan);
797 }
798
799 static int das08_counter_read(struct comedi_device *dev,
800                               struct comedi_subdevice *s,
801                               struct comedi_insn *insn, unsigned int *data)
802 {
803         int chan = insn->chanspec;
804
805         /* printk("Reading counter channel %d ",chan); */
806         data[0] = i8254_read_channel(&devpriv->i8254, chan);
807         /* printk("=> 0x%08X\n",data[0]); */
808
809         return 1;
810 }
811
812 static int das08_counter_write(struct comedi_device *dev,
813                                struct comedi_subdevice *s,
814                                struct comedi_insn *insn, unsigned int *data)
815 {
816         int chan = insn->chanspec;
817
818         /* printk("Writing counter channel %d with 0x%04X\n",chan,data[0]); */
819         i8254_write_channel(&devpriv->i8254, chan, data[0]);
820
821         return 1;
822 }
823
824 static int das08_counter_config(struct comedi_device *dev,
825                                 struct comedi_subdevice *s,
826                                 struct comedi_insn *insn, unsigned int *data)
827 {
828         int chan = insn->chanspec;
829
830         if (insn->n != 2)
831                 return -EINVAL;
832
833         switch (data[0]) {
834         case INSN_CONFIG_SET_COUNTER_MODE:
835                 i8254_set_mode(&devpriv->i8254, chan, data[1]);
836                 break;
837         case INSN_CONFIG_8254_READ_STATUS:
838                 data[1] = i8254_read_status(&devpriv->i8254, chan);
839                 break;
840         default:
841                 return -EINVAL;
842                 break;
843         }
844         return 2;
845 }
846
847 static int das08_attach(struct comedi_device *dev, struct comedi_devconfig *it);
848
849 static struct comedi_driver driver_das08 = {
850         .driver_name = DRV_NAME,
851         .module = THIS_MODULE,
852         .attach = das08_attach,
853         .detach = das08_common_detach,
854         .board_name = &das08_boards[0].name,
855         .num_names = sizeof(das08_boards) / sizeof(struct das08_board_struct),
856         .offset = sizeof(struct das08_board_struct),
857 };
858
859 int das08_common_attach(struct comedi_device *dev, unsigned long iobase)
860 {
861         struct comedi_subdevice *s;
862         int ret;
863
864         /*  allocate ioports for non-pcmcia, non-pci boards */
865         if ((thisboard->bustype != pcmcia) && (thisboard->bustype != pci)) {
866                 printk(KERN_INFO " iobase 0x%lx\n", iobase);
867                 if (!request_region(iobase, thisboard->iosize, DRV_NAME)) {
868                         printk(KERN_ERR " I/O port conflict\n");
869                         return -EIO;
870                 }
871         }
872         dev->iobase = iobase;
873
874         dev->board_name = thisboard->name;
875
876         ret = alloc_subdevices(dev, 6);
877         if (ret < 0)
878                 return ret;
879
880         s = dev->subdevices + 0;
881         /* ai */
882         if (thisboard->ai) {
883                 s->type = COMEDI_SUBD_AI;
884                 /* XXX some boards actually have differential
885                  * inputs instead of single ended.
886                  * The driver does nothing with arefs though,
887                  * so it's no big deal.
888                  */
889                 s->subdev_flags = SDF_READABLE | SDF_GROUND;
890                 s->n_chan = 8;
891                 s->maxdata = (1 << thisboard->ai_nbits) - 1;
892                 s->range_table = das08_ai_lranges[thisboard->ai_pg];
893                 s->insn_read = thisboard->ai;
894                 devpriv->pg_gainlist = das08_gainlists[thisboard->ai_pg];
895         } else {
896                 s->type = COMEDI_SUBD_UNUSED;
897         }
898
899         s = dev->subdevices + 1;
900         /* ao */
901         if (thisboard->ao) {
902                 s->type = COMEDI_SUBD_AO;
903 /* XXX lacks read-back insn */
904                 s->subdev_flags = SDF_WRITABLE;
905                 s->n_chan = 2;
906                 s->maxdata = (1 << thisboard->ao_nbits) - 1;
907                 s->range_table = &range_bipolar5;
908                 s->insn_write = thisboard->ao;
909         } else {
910                 s->type = COMEDI_SUBD_UNUSED;
911         }
912
913         s = dev->subdevices + 2;
914         /* di */
915         if (thisboard->di) {
916                 s->type = COMEDI_SUBD_DI;
917                 s->subdev_flags = SDF_READABLE;
918                 s->n_chan = (thisboard->di == das08_di_rbits) ? 3 : 8;
919                 s->maxdata = 1;
920                 s->range_table = &range_digital;
921                 s->insn_bits = thisboard->di;
922         } else {
923                 s->type = COMEDI_SUBD_UNUSED;
924         }
925
926         s = dev->subdevices + 3;
927         /* do */
928         if (thisboard->do_) {
929                 s->type = COMEDI_SUBD_DO;
930                 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
931                 s->n_chan = thisboard->do_nchan;
932                 s->maxdata = 1;
933                 s->range_table = &range_digital;
934                 s->insn_bits = thisboard->do_;
935         } else {
936                 s->type = COMEDI_SUBD_UNUSED;
937         }
938
939         s = dev->subdevices + 4;
940         /* 8255 */
941         if (thisboard->i8255_offset != 0) {
942                 subdev_8255_init(dev, s, NULL, (unsigned long)(dev->iobase +
943                                                                thisboard->
944                                                                i8255_offset));
945         } else {
946                 s->type = COMEDI_SUBD_UNUSED;
947         }
948
949         s = dev->subdevices + 5;
950         /* 8254 */
951         if (thisboard->i8254_offset != 0) {
952                 s->type = COMEDI_SUBD_COUNTER;
953                 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
954                 s->n_chan = 3;
955                 s->maxdata = 0xFFFF;
956                 s->insn_read = das08_counter_read;
957                 s->insn_write = das08_counter_write;
958                 s->insn_config = das08_counter_config;
959                 /* Set-up the 8254 structure */
960                 devpriv->i8254.channels = 3;
961                 devpriv->i8254.logic2phys[0] = 0;
962                 devpriv->i8254.logic2phys[1] = 1;
963                 devpriv->i8254.logic2phys[2] = 2;
964                 devpriv->i8254.iobase = iobase + thisboard->i8254_offset;
965                 devpriv->i8254.mode[0] =
966                     devpriv->i8254.mode[1] =
967                     devpriv->i8254.mode[2] = I8254_MODE0 | I8254_BINARY;
968                 i8254_initialize(&devpriv->i8254);
969         } else {
970                 s->type = COMEDI_SUBD_UNUSED;
971         }
972
973         return 0;
974 }
975 EXPORT_SYMBOL_GPL(das08_common_attach);
976
977 static int das08_attach(struct comedi_device *dev, struct comedi_devconfig *it)
978 {
979         int ret;
980         unsigned long iobase;
981 #ifdef CONFIG_COMEDI_PCI
982         unsigned long pci_iobase = 0;
983         struct pci_dev *pdev;
984 #endif
985
986         ret = alloc_private(dev, sizeof(struct das08_private_struct));
987         if (ret < 0)
988                 return ret;
989
990         printk(KERN_INFO "comedi%d: das08: ", dev->minor);
991         /*  deal with a pci board */
992         if (thisboard->bustype == pci) {
993 #ifdef CONFIG_COMEDI_PCI
994                 if (it->options[0] || it->options[1]) {
995                         printk("bus %i slot %i ",
996                                it->options[0], it->options[1]);
997                 }
998                 printk("\n");
999                 /*  find card */
1000                 for (pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
1001                      pdev != NULL;
1002                      pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) {
1003                         if (pdev->vendor == PCI_VENDOR_ID_COMPUTERBOARDS
1004                             && pdev->device == PCI_DEVICE_ID_PCIDAS08) {
1005                                 if (it->options[0] || it->options[1]) {
1006                                         if (pdev->bus->number == it->options[0]
1007                                             && PCI_SLOT(pdev->devfn) ==
1008                                             it->options[1]) {
1009                                                 break;
1010                                         }
1011                                 } else {
1012                                         break;
1013                                 }
1014                         }
1015                 }
1016                 if (!pdev) {
1017                         printk(KERN_ERR "No pci das08 cards found\n");
1018                         return -EIO;
1019                 }
1020                 devpriv->pdev = pdev;
1021                 /*  enable PCI device and reserve I/O spaces */
1022                 if (comedi_pci_enable(pdev, DRV_NAME)) {
1023                         printk(KERN_ERR " Error enabling PCI device and "
1024                                                 "requesting regions\n");
1025                         return -EIO;
1026                 }
1027                 /*  read base addresses */
1028                 pci_iobase = pci_resource_start(pdev, 1);
1029                 iobase = pci_resource_start(pdev, 2);
1030                 printk(KERN_INFO "pcibase 0x%lx  iobase 0x%lx\n",
1031                                                         pci_iobase, iobase);
1032                 devpriv->pci_iobase = pci_iobase;
1033 #if 0
1034 /* We could enable to pci-das08's interrupt here to make it possible
1035  * to do timed input in this driver, but there is little point since
1036  * conversions would have to be started by the interrupt handler
1037  * so you might as well use comedi_rt_timer to emulate commands
1038  */
1039                 /* set source of interrupt trigger to counter2 output */
1040                 outb(CNTRL_INTR | CNTRL_DIR, pci_iobase + CNTRL);
1041                 /* Enable local interrupt 1 and pci interrupt */
1042                 outw(INTR1_ENABLE | PCI_INTR_ENABLE, pci_iobase + INTCSR);
1043 #endif
1044 #else /* CONFIG_COMEDI_PCI */
1045                 printk(KERN_ERR "this driver has not been built with PCI support.\n");
1046                 return -EINVAL;
1047 #endif /* CONFIG_COMEDI_PCI */
1048         } else {
1049                 iobase = it->options[0];
1050         }
1051         printk(KERN_INFO "\n");
1052
1053         return das08_common_attach(dev, iobase);
1054 }
1055
1056
1057 int das08_common_detach(struct comedi_device *dev)
1058 {
1059         printk(KERN_INFO "comedi%d: das08: remove\n", dev->minor);
1060
1061         if (dev->subdevices)
1062                 subdev_8255_cleanup(dev, dev->subdevices + 4);
1063
1064         /*  deallocate ioports for non-pcmcia, non-pci boards */
1065         if ((thisboard->bustype != pcmcia) && (thisboard->bustype != pci)) {
1066                 if (dev->iobase)
1067                         release_region(dev->iobase, thisboard->iosize);
1068         }
1069 #ifdef CONFIG_COMEDI_PCI
1070         if (devpriv) {
1071                 if (devpriv->pdev) {
1072                         if (devpriv->pci_iobase)
1073                                 comedi_pci_disable(devpriv->pdev);
1074
1075                         pci_dev_put(devpriv->pdev);
1076                 }
1077         }
1078 #endif
1079
1080         return 0;
1081 }
1082 EXPORT_SYMBOL_GPL(das08_common_detach);
1083
1084 #ifdef CONFIG_COMEDI_PCI
1085 COMEDI_PCI_INITCLEANUP(driver_das08, das08_pci_table);
1086 #else
1087 COMEDI_INITCLEANUP(driver_das08);
1088 #endif
1089
1090 #ifdef CONFIG_COMEDI_PCMCIA
1091 EXPORT_SYMBOL_GPL(das08_cs_boards);
1092 #endif