staging: comedi: das08: Rearrange AO write functions
[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 58#include "8255.h"
c952e019 59#include "8253.h"
0882eaa6
DS
60#include "das08.h"
61
62#define DRV_NAME "das08"
63
4bfa9b2e
IA
64#define DO_COMEDI_DRIVER_REGISTER \
65 (IS_ENABLED(CONFIG_COMEDI_DAS08_ISA) || \
66 IS_ENABLED(CONFIG_COMEDI_DAS08_PCI))
2e3c024d 67
0882eaa6
DS
68#define PCI_VENDOR_ID_COMPUTERBOARDS 0x1307
69#define PCI_DEVICE_ID_PCIDAS08 0x29
70#define PCIDAS08_SIZE 0x54
71
2696fb57 72/* pci configuration registers */
0882eaa6
DS
73#define INTCSR 0x4c
74#define INTR1_ENABLE 0x1
75#define INTR1_HIGH_POLARITY 0x2
76#define PCI_INTR_ENABLE 0x40
2696fb57 77#define INTR1_EDGE_TRIG 0x100 /* requires high polarity */
0882eaa6
DS
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
b4ae23ce
GS
129#define DAS08JR_AO_LSB(x) ((x) ? 6 : 4)
130#define DAS08JR_AO_MSB(x) ((x) ? 7 : 5)
0882eaa6
DS
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
b4ae23ce
GS
155#define DAS08AO_AO_LSB(x) ((x) ? 0xa : 8)
156#define DAS08AO_AO_MSB(x) ((x) ? 0xb : 9)
0882eaa6
DS
157#define DAS08AO_AO_UPDATE 8
158
159/* gainlist same as _pgx_ below */
160
9ced1de6 161static const struct comedi_lrange range_das08_pgl = { 9, {
0a85b6f0
MT
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 }
0882eaa6 172};
0a85b6f0 173
9ced1de6 174static const struct comedi_lrange range_das08_pgh = { 12, {
0a85b6f0
MT
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 }
0882eaa6 188};
0a85b6f0 189
9ced1de6 190static const struct comedi_lrange range_das08_pgm = { 9, {
0a85b6f0
MT
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 }
0882eaa6
DS
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
9ced1de6 217static const struct comedi_lrange *const das08_ai_lranges[] = {
0882eaa6
DS
218 &range_unknown,
219 &range_bipolar5,
220 &range_das08_pgh,
221 &range_das08_pgl,
222 &range_das08_pgm,
223};
224
b4ae23ce
GS
225static const int das08_pgh_gainlist[] = {
226 8, 0, 10, 2, 12, 4, 14, 6, 1, 3, 5, 7
227};
0882eaa6
DS
228static const int das08_pgl_gainlist[] = { 8, 0, 2, 4, 6, 1, 3, 5, 7 };
229static const int das08_pgm_gainlist[] = { 8, 0, 10, 12, 14, 9, 11, 13, 15 };
230
231static const int *const das08_gainlists[] = {
232 NULL,
233 NULL,
234 das08_pgh_gainlist,
235 das08_pgl_gainlist,
236 das08_pgm_gainlist,
237};
238
0882eaa6
DS
239#define TIMEOUT 100000
240
da91b269 241static int das08_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 242 struct comedi_insn *insn, unsigned int *data)
0882eaa6 243{
282f3528
IA
244 const struct das08_board_struct *thisboard = comedi_board(dev);
245 struct das08_private_struct *devpriv = dev->private;
0882eaa6
DS
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 */
b4ae23ce
GS
259 /* lock to prevent race with digital output */
260 spin_lock(&dev->spinlock);
0882eaa6
DS
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],
0a85b6f0 270 dev->iobase + DAS08AO_GAIN_CONTROL);
0882eaa6
DS
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)
33fba3b7 277 dev_info(dev->class_dev, "over-range\n");
0882eaa6
DS
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) {
33fba3b7 287 dev_err(dev->class_dev, "timeout\n");
0882eaa6
DS
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
da91b269 311static int das08_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 312 struct comedi_insn *insn, unsigned int *data)
0882eaa6
DS
313{
314 data[0] = 0;
315 data[1] = DAS08_IP(inb(dev->iobase + DAS08_STATUS));
316
a2714e3e 317 return insn->n;
0882eaa6
DS
318}
319
da91b269 320static int das08_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 321 struct comedi_insn *insn, unsigned int *data)
0882eaa6 322{
282f3528 323 struct das08_private_struct *devpriv = dev->private;
0882eaa6
DS
324 int wbits;
325
2696fb57 326 /* get current settings of digital output lines */
0882eaa6 327 wbits = (devpriv->do_mux_bits >> 4) & 0xf;
2696fb57 328 /* null bits we are going to set */
0882eaa6 329 wbits &= ~data[0];
2696fb57 330 /* set new bit values */
0882eaa6 331 wbits |= data[0] & data[1];
2696fb57 332 /* remember digital output bits */
b4ae23ce
GS
333 /* prevent race with setting of analog input mux */
334 spin_lock(&dev->spinlock);
0882eaa6
DS
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
a2714e3e 342 return insn->n;
0882eaa6
DS
343}
344
624fcb26
IA
345static int das08jr_di_rbits(struct comedi_device *dev,
346 struct comedi_subdevice *s,
347 struct comedi_insn *insn, unsigned int *data)
0882eaa6
DS
348{
349 data[0] = 0;
350 data[1] = inb(dev->iobase + DAS08JR_DIO);
351
a2714e3e 352 return insn->n;
0882eaa6
DS
353}
354
624fcb26
IA
355static int das08jr_do_wbits(struct comedi_device *dev,
356 struct comedi_subdevice *s,
357 struct comedi_insn *insn, unsigned int *data)
0882eaa6 358{
282f3528
IA
359 struct das08_private_struct *devpriv = dev->private;
360
2696fb57 361 /* null bits we are going to set */
0882eaa6 362 devpriv->do_bits &= ~data[0];
2696fb57 363 /* set new bit values */
0882eaa6
DS
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
a2714e3e 369 return insn->n;
0882eaa6
DS
370}
371
3b85aa63
IA
372static void das08_ao_set_data(struct comedi_device *dev,
373 unsigned int chan, unsigned int data)
0882eaa6 374{
3b85aa63
IA
375 const struct das08_board_struct *thisboard = comedi_board(dev);
376 unsigned char lsb;
377 unsigned char msb;
0882eaa6 378
3b85aa63
IA
379 lsb = data & 0xff;
380 msb = (data >> 8) & 0xff;
381 if (thisboard->is_jr) {
0882eaa6
DS
382 outb(lsb, dev->iobase + DAS08JR_AO_LSB(chan));
383 outb(msb, dev->iobase + DAS08JR_AO_MSB(chan));
0882eaa6
DS
384 /* load DACs */
385 inb(dev->iobase + DAS08JR_DIO);
3b85aa63
IA
386 } else {
387 outb(lsb, dev->iobase + DAS08AO_AO_LSB(chan));
388 outb(msb, dev->iobase + DAS08AO_AO_MSB(chan));
389 /* load DACs */
390 inb(dev->iobase + DAS08AO_AO_UPDATE);
0882eaa6 391 }
0882eaa6
DS
392}
393
3b85aa63
IA
394static int das08_ao_winsn(struct comedi_device *dev,
395 struct comedi_subdevice *s,
396 struct comedi_insn *insn, unsigned int *data)
0882eaa6 397{
3b85aa63
IA
398 unsigned int n;
399 unsigned int chan;
0882eaa6
DS
400
401 chan = CR_CHAN(insn->chanspec);
402
3b85aa63
IA
403 for (n = 0; n < insn->n; n++)
404 das08_ao_set_data(dev, chan, *data);
0882eaa6
DS
405
406 return n;
407}
408
80727146 409static void i8254_initialize(struct comedi_device *dev)
d8fdaea5 410{
4d31848a
IA
411 const struct das08_board_struct *thisboard = comedi_board(dev);
412 unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
cba9d4aa 413 unsigned int mode = I8254_MODE0 | I8254_BINARY;
d8fdaea5 414 int i;
afdd107c 415
d8fdaea5 416 for (i = 0; i < 3; ++i)
4d31848a 417 i8254_set_mode(i8254_iobase, 0, i, mode);
d8fdaea5
IA
418}
419
0a85b6f0
MT
420static int das08_counter_read(struct comedi_device *dev,
421 struct comedi_subdevice *s,
422 struct comedi_insn *insn, unsigned int *data)
0882eaa6 423{
4d31848a
IA
424 const struct das08_board_struct *thisboard = comedi_board(dev);
425 unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
6daf1e05 426 int chan = insn->chanspec;
afdd107c 427
4d31848a 428 data[0] = i8254_read(i8254_iobase, 0, chan);
0882eaa6
DS
429 return 1;
430}
431
0a85b6f0
MT
432static int das08_counter_write(struct comedi_device *dev,
433 struct comedi_subdevice *s,
434 struct comedi_insn *insn, unsigned int *data)
0882eaa6 435{
4d31848a
IA
436 const struct das08_board_struct *thisboard = comedi_board(dev);
437 unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
6daf1e05 438 int chan = insn->chanspec;
afdd107c 439
4d31848a 440 i8254_write(i8254_iobase, 0, chan, data[0]);
0882eaa6
DS
441 return 1;
442}
443
0a85b6f0
MT
444static int das08_counter_config(struct comedi_device *dev,
445 struct comedi_subdevice *s,
446 struct comedi_insn *insn, unsigned int *data)
0882eaa6 447{
4d31848a
IA
448 const struct das08_board_struct *thisboard = comedi_board(dev);
449 unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
6daf1e05 450 int chan = insn->chanspec;
0882eaa6 451
0882eaa6
DS
452 switch (data[0]) {
453 case INSN_CONFIG_SET_COUNTER_MODE:
4d31848a 454 i8254_set_mode(i8254_iobase, 0, chan, data[1]);
0882eaa6
DS
455 break;
456 case INSN_CONFIG_8254_READ_STATUS:
4d31848a 457 data[1] = i8254_status(i8254_iobase, 0, chan);
0882eaa6
DS
458 break;
459 default:
460 return -EINVAL;
461 break;
462 }
463 return 2;
464}
465
4bfa9b2e 466#if DO_COMEDI_DRIVER_REGISTER
d8fdaea5
IA
467static const struct das08_board_struct das08_boards[] = {
468#if IS_ENABLED(CONFIG_COMEDI_DAS08_ISA)
469 {
470 .name = "isa-das08", /* cio-das08.pdf */
471 .bustype = isa,
d8fdaea5
IA
472 .ai_nbits = 12,
473 .ai_pg = das08_pg_none,
474 .ai_encoding = das08_encode12,
ffd76b32 475 .di_nchan = 3,
d8fdaea5
IA
476 .do_nchan = 4,
477 .i8255_offset = 8,
478 .i8254_offset = 4,
479 .iosize = 16, /* unchecked */
480 },
481 {
482 .name = "das08-pgm", /* cio-das08pgx.pdf */
483 .bustype = isa,
d8fdaea5
IA
484 .ai_nbits = 12,
485 .ai_pg = das08_pgm,
486 .ai_encoding = das08_encode12,
ffd76b32 487 .di_nchan = 3,
d8fdaea5
IA
488 .do_nchan = 4,
489 .i8255_offset = 0,
490 .i8254_offset = 0x04,
491 .iosize = 16, /* unchecked */
492 },
493 {
494 .name = "das08-pgh", /* cio-das08pgx.pdf */
495 .bustype = isa,
d8fdaea5
IA
496 .ai_nbits = 12,
497 .ai_pg = das08_pgh,
498 .ai_encoding = das08_encode12,
ffd76b32 499 .di_nchan = 3,
d8fdaea5 500 .do_nchan = 4,
d8fdaea5
IA
501 .i8254_offset = 0x04,
502 .iosize = 16, /* unchecked */
503 },
504 {
505 .name = "das08-pgl", /* cio-das08pgx.pdf */
506 .bustype = isa,
d8fdaea5
IA
507 .ai_nbits = 12,
508 .ai_pg = das08_pgl,
509 .ai_encoding = das08_encode12,
ffd76b32 510 .di_nchan = 3,
d8fdaea5 511 .do_nchan = 4,
d8fdaea5
IA
512 .i8254_offset = 0x04,
513 .iosize = 16, /* unchecked */
514 },
515 {
516 .name = "das08-aoh", /* cio-das08_aox.pdf */
517 .bustype = isa,
d8fdaea5
IA
518 .ai_nbits = 12,
519 .ai_pg = das08_pgh,
520 .ai_encoding = das08_encode12,
d8fdaea5 521 .ao_nbits = 12,
ffd76b32 522 .di_nchan = 3,
d8fdaea5
IA
523 .do_nchan = 4,
524 .i8255_offset = 0x0c,
525 .i8254_offset = 0x04,
526 .iosize = 16, /* unchecked */
527 },
528 {
529 .name = "das08-aol", /* cio-das08_aox.pdf */
530 .bustype = isa,
d8fdaea5
IA
531 .ai_nbits = 12,
532 .ai_pg = das08_pgl,
533 .ai_encoding = das08_encode12,
d8fdaea5 534 .ao_nbits = 12,
ffd76b32 535 .di_nchan = 3,
d8fdaea5
IA
536 .do_nchan = 4,
537 .i8255_offset = 0x0c,
538 .i8254_offset = 0x04,
539 .iosize = 16, /* unchecked */
540 },
541 {
542 .name = "das08-aom", /* cio-das08_aox.pdf */
543 .bustype = isa,
d8fdaea5
IA
544 .ai_nbits = 12,
545 .ai_pg = das08_pgm,
546 .ai_encoding = das08_encode12,
d8fdaea5 547 .ao_nbits = 12,
ffd76b32 548 .di_nchan = 3,
d8fdaea5
IA
549 .do_nchan = 4,
550 .i8255_offset = 0x0c,
551 .i8254_offset = 0x04,
552 .iosize = 16, /* unchecked */
553 },
554 {
555 .name = "das08/jr-ao", /* cio-das08-jr-ao.pdf */
556 .bustype = isa,
1effe42d 557 .is_jr = true,
d8fdaea5
IA
558 .ai_nbits = 12,
559 .ai_pg = das08_pg_none,
560 .ai_encoding = das08_encode12,
d8fdaea5 561 .ao_nbits = 12,
ffd76b32 562 .di_nchan = 8,
d8fdaea5 563 .do_nchan = 8,
d8fdaea5
IA
564 .iosize = 16, /* unchecked */
565 },
566 {
567 .name = "das08jr-16-ao", /* cio-das08jr-16-ao.pdf */
568 .bustype = isa,
1effe42d 569 .is_jr = true,
d8fdaea5
IA
570 .ai_nbits = 16,
571 .ai_pg = das08_pg_none,
e6391a18 572 .ai_encoding = das08_encode16,
d8fdaea5 573 .ao_nbits = 16,
ffd76b32 574 .di_nchan = 8,
d8fdaea5 575 .do_nchan = 8,
d8fdaea5
IA
576 .i8254_offset = 0x04,
577 .iosize = 16, /* unchecked */
578 },
579 {
580 .name = "pc104-das08",
4865a47f 581 .bustype = isa,
d8fdaea5
IA
582 .ai_nbits = 12,
583 .ai_pg = das08_pg_none,
584 .ai_encoding = das08_encode12,
ffd76b32 585 .di_nchan = 3,
d8fdaea5 586 .do_nchan = 4,
d8fdaea5
IA
587 .i8254_offset = 4,
588 .iosize = 16, /* unchecked */
589 },
d8fdaea5
IA
590 {
591 .name = "das08jr/16",
592 .bustype = isa,
1effe42d 593 .is_jr = true,
d8fdaea5
IA
594 .ai_nbits = 16,
595 .ai_pg = das08_pg_none,
596 .ai_encoding = das08_encode16,
ffd76b32 597 .di_nchan = 8,
d8fdaea5 598 .do_nchan = 8,
d8fdaea5
IA
599 .iosize = 16, /* unchecked */
600 },
d8fdaea5
IA
601#endif /* IS_ENABLED(CONFIG_COMEDI_DAS08_ISA) */
602#if IS_ENABLED(CONFIG_COMEDI_DAS08_PCI)
603 {
f86b0d7d 604 .name = "pci-das08", /* pci-das08 */
d8fdaea5
IA
605 .id = PCI_DEVICE_ID_PCIDAS08,
606 .bustype = pci,
d8fdaea5
IA
607 .ai_nbits = 12,
608 .ai_pg = das08_bipolar5,
609 .ai_encoding = das08_encode12,
ffd76b32 610 .di_nchan = 3,
d8fdaea5 611 .do_nchan = 4,
d8fdaea5
IA
612 .i8254_offset = 4,
613 .iosize = 8,
614 },
f86b0d7d
IA
615 { /* wildcard entry matches any supported PCI device */
616 .name = DRV_NAME,
617 .id = PCI_ANY_ID,
618 .bustype = pci,
619 },
d8fdaea5
IA
620#endif /* IS_ENABLED(CONFIG_COMEDI_DAS08_PCI) */
621};
622#endif /* DO_COMEDI_DRIVER_REGISTER */
0882eaa6 623
da91b269 624int das08_common_attach(struct comedi_device *dev, unsigned long iobase)
0882eaa6 625{
282f3528
IA
626 const struct das08_board_struct *thisboard = comedi_board(dev);
627 struct das08_private_struct *devpriv = dev->private;
34c43922 628 struct comedi_subdevice *s;
0882eaa6
DS
629 int ret;
630
0882eaa6
DS
631 dev->iobase = iobase;
632
633 dev->board_name = thisboard->name;
634
2f0b9d08 635 ret = comedi_alloc_subdevices(dev, 6);
8b6c5694 636 if (ret)
0882eaa6
DS
637 return ret;
638
639 s = dev->subdevices + 0;
640 /* ai */
dd2ac5d4 641 if (thisboard->ai_nbits) {
0882eaa6 642 s->type = COMEDI_SUBD_AI;
b4ae23ce
GS
643 /* XXX some boards actually have differential
644 * inputs instead of single ended.
645 * The driver does nothing with arefs though,
646 * so it's no big deal.
647 */
0882eaa6
DS
648 s->subdev_flags = SDF_READABLE | SDF_GROUND;
649 s->n_chan = 8;
650 s->maxdata = (1 << thisboard->ai_nbits) - 1;
651 s->range_table = das08_ai_lranges[thisboard->ai_pg];
dd2ac5d4 652 s->insn_read = das08_ai_rinsn;
0882eaa6
DS
653 devpriv->pg_gainlist = das08_gainlists[thisboard->ai_pg];
654 } else {
655 s->type = COMEDI_SUBD_UNUSED;
656 }
657
658 s = dev->subdevices + 1;
659 /* ao */
dd2ac5d4 660 if (thisboard->ao_nbits) {
0882eaa6 661 s->type = COMEDI_SUBD_AO;
2696fb57 662/* XXX lacks read-back insn */
0882eaa6
DS
663 s->subdev_flags = SDF_WRITABLE;
664 s->n_chan = 2;
665 s->maxdata = (1 << thisboard->ao_nbits) - 1;
666 s->range_table = &range_bipolar5;
3b85aa63 667 s->insn_write = das08_ao_winsn;
0882eaa6
DS
668 } else {
669 s->type = COMEDI_SUBD_UNUSED;
670 }
671
672 s = dev->subdevices + 2;
673 /* di */
dd2ac5d4 674 if (thisboard->di_nchan) {
0882eaa6
DS
675 s->type = COMEDI_SUBD_DI;
676 s->subdev_flags = SDF_READABLE;
ffd76b32 677 s->n_chan = thisboard->di_nchan;
0882eaa6
DS
678 s->maxdata = 1;
679 s->range_table = &range_digital;
dd2ac5d4
IA
680 s->insn_bits =
681 thisboard->is_jr ? das08jr_di_rbits : das08_di_rbits;
0882eaa6
DS
682 } else {
683 s->type = COMEDI_SUBD_UNUSED;
684 }
685
686 s = dev->subdevices + 3;
687 /* do */
dd2ac5d4 688 if (thisboard->do_nchan) {
0882eaa6
DS
689 s->type = COMEDI_SUBD_DO;
690 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
691 s->n_chan = thisboard->do_nchan;
692 s->maxdata = 1;
693 s->range_table = &range_digital;
dd2ac5d4
IA
694 s->insn_bits =
695 thisboard->is_jr ? das08jr_do_wbits : das08_do_wbits;
0882eaa6
DS
696 } else {
697 s->type = COMEDI_SUBD_UNUSED;
698 }
699
700 s = dev->subdevices + 4;
701 /* 8255 */
702 if (thisboard->i8255_offset != 0) {
703 subdev_8255_init(dev, s, NULL, (unsigned long)(dev->iobase +
0a85b6f0
MT
704 thisboard->
705 i8255_offset));
0882eaa6
DS
706 } else {
707 s->type = COMEDI_SUBD_UNUSED;
708 }
709
710 s = dev->subdevices + 5;
711 /* 8254 */
712 if (thisboard->i8254_offset != 0) {
713 s->type = COMEDI_SUBD_COUNTER;
714 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
715 s->n_chan = 3;
716 s->maxdata = 0xFFFF;
717 s->insn_read = das08_counter_read;
718 s->insn_write = das08_counter_write;
719 s->insn_config = das08_counter_config;
80727146 720 i8254_initialize(dev);
0882eaa6
DS
721 } else {
722 s->type = COMEDI_SUBD_UNUSED;
723 }
724
725 return 0;
726}
b4ae23ce 727EXPORT_SYMBOL_GPL(das08_common_attach);
0882eaa6 728
8b7d417c
IA
729static int das08_pci_attach_common(struct comedi_device *dev,
730 struct pci_dev *pdev)
731{
732 unsigned long iobase;
8b7d417c
IA
733 struct das08_private_struct *devpriv = dev->private;
734
4bfa9b2e
IA
735 if (!IS_ENABLED(CONFIG_COMEDI_DAS08_PCI))
736 return -EINVAL;
737
8b7d417c
IA
738 devpriv->pdev = pdev;
739 /* enable PCI device and reserve I/O spaces */
740 if (comedi_pci_enable(pdev, dev->driver->driver_name)) {
741 dev_err(dev->class_dev,
742 "Error enabling PCI device and requesting regions\n");
743 return -EIO;
744 }
745 /* read base addresses */
8b7d417c 746 iobase = pci_resource_start(pdev, 2);
fb8ff5d5 747 dev_info(dev->class_dev, "iobase 0x%lx\n", iobase);
8b7d417c
IA
748 return das08_common_attach(dev, iobase);
749}
8b7d417c 750
8b7d417c
IA
751static const struct das08_board_struct *
752das08_find_pci_board(struct pci_dev *pdev)
753{
4bfa9b2e 754#if DO_COMEDI_DRIVER_REGISTER
8b7d417c
IA
755 unsigned int i;
756 for (i = 0; i < ARRAY_SIZE(das08_boards); i++)
757 if (das08_boards[i].bustype == pci &&
758 pdev->device == das08_boards[i].id)
759 return &das08_boards[i];
4bfa9b2e 760#endif
8b7d417c
IA
761 return NULL;
762}
8b7d417c 763
8b7d417c 764/* only called in the PCI probe path, via comedi_pci_auto_config() */
4bfa9b2e
IA
765static int __devinit __maybe_unused
766das08_attach_pci(struct comedi_device *dev, struct pci_dev *pdev)
8b7d417c
IA
767{
768 int ret;
769
4bfa9b2e
IA
770 if (!IS_ENABLED(CONFIG_COMEDI_DAS08_PCI))
771 return -EINVAL;
8b7d417c
IA
772 ret = alloc_private(dev, sizeof(struct das08_private_struct));
773 if (ret < 0)
774 return ret;
775 dev_info(dev->class_dev, "attach pci %s\n", pci_name(pdev));
776 dev->board_ptr = das08_find_pci_board(pdev);
777 if (dev->board_ptr == NULL) {
778 dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
779 return -EINVAL;
780 }
4a7a4f95
IA
781 /*
782 * Need to 'get' the PCI device to match the 'put' in das08_detach().
783 * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
784 * support for manual attachment of PCI devices via das08_attach()
785 * has been removed.
786 */
787 pci_dev_get(pdev);
8b7d417c
IA
788 return das08_pci_attach_common(dev, pdev);
789}
8b7d417c 790
f86b0d7d
IA
791static struct pci_dev *das08_find_pci(struct comedi_device *dev,
792 int bus, int slot)
793{
794 const struct das08_board_struct *thisboard = comedi_board(dev);
795 struct pci_dev *pdev;
796 unsigned int matchid;
797
798 if (bus || slot)
799 dev_dbg(dev->class_dev, "Looking for %s at PCI %02X:%02X\n",
800 thisboard->name, bus, slot);
801 else
802 dev_dbg(dev->class_dev, "Looking for %s on PCI buses\n",
803 thisboard->name);
804
805 matchid = thisboard->id;
806 pdev = NULL;
807 for_each_pci_dev(pdev) {
808 if ((bus || slot) &&
809 (bus != pdev->bus->number || slot != PCI_SLOT(pdev->devfn)))
810 continue;
811 if (pdev->vendor != PCI_VENDOR_ID_COMPUTERBOARDS)
812 continue;
813 if (matchid == PCI_ANY_ID) {
814 /* wildcard board matches any supported PCI board */
8b7d417c
IA
815 const struct das08_board_struct *foundboard;
816 foundboard = das08_find_pci_board(pdev);
817 if (foundboard == NULL)
f86b0d7d 818 continue;
8b7d417c
IA
819 /* replace wildcard board_ptr */
820 dev->board_ptr = thisboard = foundboard;
f86b0d7d
IA
821 } else {
822 /* match specific PCI board */
823 if (pdev->device != matchid)
824 continue;
825 }
826 /* found a match */
827 dev_info(dev->class_dev, "Found %s at PCI %s\n",
828 thisboard->name, pci_name(pdev));
829 return pdev;
830 }
831 /* no match found */
832 if (bus || slot)
833 dev_err(dev->class_dev,
834 "No %s cards found at PCI %02X:%02X\n",
835 thisboard->name, bus, slot);
836 else
837 dev_err(dev->class_dev, "No %s cards found on PCI buses\n",
838 thisboard->name);
839 return NULL;
840}
f86b0d7d 841
4bfa9b2e
IA
842static int __maybe_unused
843das08_attach(struct comedi_device *dev, struct comedi_devconfig *it)
0882eaa6 844{
282f3528
IA
845 const struct das08_board_struct *thisboard = comedi_board(dev);
846 struct das08_private_struct *devpriv;
0882eaa6
DS
847 int ret;
848 unsigned long iobase;
0882eaa6 849
c3744138
BP
850 ret = alloc_private(dev, sizeof(struct das08_private_struct));
851 if (ret < 0)
0882eaa6 852 return ret;
282f3528 853 devpriv = dev->private;
0882eaa6 854
33fba3b7 855 dev_info(dev->class_dev, "attach\n");
d60d9f34 856 if (IS_ENABLED(CONFIG_COMEDI_DAS08_PCI) && thisboard->bustype == pci) {
f86b0d7d
IA
857 struct pci_dev *pdev;
858 pdev = das08_find_pci(dev, it->options[0], it->options[1]);
859 if (pdev == NULL)
0882eaa6 860 return -EIO;
8b7d417c 861 return das08_pci_attach_common(dev, pdev);
d60d9f34 862 } else if (IS_ENABLED(CONFIG_COMEDI_DAS08_ISA) &&
4865a47f 863 thisboard->bustype == isa) {
0882eaa6 864 iobase = it->options[0];
33fba3b7 865 dev_info(dev->class_dev, "iobase 0x%lx\n", iobase);
d60d9f34 866 if (!request_region(iobase, thisboard->iosize, DRV_NAME)) {
33fba3b7 867 dev_err(dev->class_dev, "I/O port conflict\n");
d60d9f34
IA
868 return -EIO;
869 }
8b7d417c 870 return das08_common_attach(dev, iobase);
d60d9f34
IA
871 } else
872 return -EIO;
0882eaa6
DS
873}
874
484ecc95 875void das08_common_detach(struct comedi_device *dev)
0882eaa6 876{
0882eaa6
DS
877 if (dev->subdevices)
878 subdev_8255_cleanup(dev, dev->subdevices + 4);
d60d9f34
IA
879}
880EXPORT_SYMBOL_GPL(das08_common_detach);
881
4bfa9b2e 882static void __maybe_unused das08_detach(struct comedi_device *dev)
d60d9f34 883{
282f3528
IA
884 const struct das08_board_struct *thisboard = comedi_board(dev);
885 struct das08_private_struct *devpriv = dev->private;
886
d60d9f34 887 das08_common_detach(dev);
4865a47f 888 if (IS_ENABLED(CONFIG_COMEDI_DAS08_ISA) && thisboard->bustype == isa) {
0882eaa6
DS
889 if (dev->iobase)
890 release_region(dev->iobase, thisboard->iosize);
d60d9f34
IA
891 } else if (IS_ENABLED(CONFIG_COMEDI_DAS08_PCI) &&
892 thisboard->bustype == pci) {
893 if (devpriv && devpriv->pdev) {
fb8ff5d5 894 if (dev->iobase)
0882eaa6 895 comedi_pci_disable(devpriv->pdev);
0882eaa6
DS
896 pci_dev_put(devpriv->pdev);
897 }
898 }
0882eaa6
DS
899}
900
4bfa9b2e 901#if DO_COMEDI_DRIVER_REGISTER
d8fdaea5
IA
902static struct comedi_driver das08_driver = {
903 .driver_name = DRV_NAME,
904 .module = THIS_MODULE,
905 .attach = das08_attach,
8b7d417c 906 .attach_pci = das08_attach_pci,
d8fdaea5
IA
907 .detach = das08_detach,
908 .board_name = &das08_boards[0].name,
909 .num_names = sizeof(das08_boards) / sizeof(struct das08_board_struct),
910 .offset = sizeof(struct das08_board_struct),
911};
912#endif
913
4b5c0f10 914#if IS_ENABLED(CONFIG_COMEDI_DAS08_PCI)
d8fdaea5
IA
915static DEFINE_PCI_DEVICE_TABLE(das08_pci_table) = {
916 { PCI_DEVICE(PCI_VENDOR_ID_COMPUTERBOARDS, PCI_DEVICE_ID_PCIDAS08) },
917 {0}
918};
919
920MODULE_DEVICE_TABLE(pci, das08_pci_table);
921
1e576a57 922static int __devinit das08_pci_probe(struct pci_dev *dev,
727b286b
AT
923 const struct pci_device_id *ent)
924{
1e576a57 925 return comedi_pci_auto_config(dev, &das08_driver);
727b286b
AT
926}
927
1e576a57 928static void __devexit das08_pci_remove(struct pci_dev *dev)
727b286b
AT
929{
930 comedi_pci_auto_unconfig(dev);
931}
932
1e576a57 933static struct pci_driver das08_pci_driver = {
727b286b 934 .id_table = das08_pci_table,
1e576a57
IA
935 .name = DRV_NAME,
936 .probe = &das08_pci_probe,
937 .remove = __devexit_p(&das08_pci_remove)
727b286b 938};
2e3c024d 939#endif /* CONFIG_COMEDI_DAS08_PCI */
727b286b 940
4bfa9b2e 941#if DO_COMEDI_DRIVER_REGISTER
4b5c0f10 942#if IS_ENABLED(CONFIG_COMEDI_DAS08_PCI)
1e576a57
IA
943module_comedi_pci_driver(das08_driver, das08_pci_driver);
944#else
945module_comedi_driver(das08_driver);
2e3c024d 946#endif
1e576a57
IA
947#else /* DO_COMEDI_DRIVER_REGISTER */
948static int __init das08_init(void)
949{
950 return 0;
727b286b
AT
951}
952
1e576a57 953static void __exit das08_exit(void)
727b286b 954{
727b286b
AT
955}
956
1e576a57
IA
957module_init(das08_init);
958module_exit(das08_exit);
959#endif /* DO_COMEDI_DRIVER_REGISTER */
0882eaa6 960
90f703d3
AT
961MODULE_AUTHOR("Comedi http://www.comedi.org");
962MODULE_DESCRIPTION("Comedi low-level driver");
963MODULE_LICENSE("GPL");