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