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