1 /*
2 * comedi/drivers/das08.c
3 * comedi module for common DAS08 support (used by ISA/PCI/PCMCIA drivers)
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
21 #include <linux/module.h>
22
23 #include "../comedidev.h"
24
25 #include "8255.h"
26 #include "comedi_8254.h"
27 #include "das08.h"
28
29 /*
30 cio-das08.pdf
31
32 "isa-das08"
33
34 0 a/d bits 0-3 start 8 bit
35 1 a/d bits 4-11 start 12 bit
36 2 eoc, ip1-3, irq, mux op1-4, inte, mux
37 3 unused unused
38 4567 8254
39 89ab 8255
40
41 requires hard-wiring for async ai
42
43 */
44
45 #define DAS08_LSB 0
46 #define DAS08_MSB 1
47 #define DAS08_TRIG_12BIT 1
48 #define DAS08_STATUS 2
49 #define DAS08_EOC (1<<7)
50 #define DAS08_IRQ (1<<3)
51 #define DAS08_IP(x) (((x)>>4)&0x7)
52 #define DAS08_CONTROL 2
53 #define DAS08_MUX_MASK 0x7
54 #define DAS08_MUX(x) ((x) & DAS08_MUX_MASK)
55 #define DAS08_INTE (1<<3)
56 #define DAS08_DO_MASK 0xf0
57 #define DAS08_OP(x) (((x) << 4) & DAS08_DO_MASK)
58
59 /*
60 cio-das08jr.pdf
61
62 "das08/jr-ao"
63
64 0 a/d bits 0-3 unused
65 1 a/d bits 4-11 start 12 bit
66 2 eoc, mux mux
67 3 di do
68 4 unused ao0_lsb
69 5 unused ao0_msb
70 6 unused ao1_lsb
71 7 unused ao1_msb
72
73 */
74
75 #define DAS08JR_DIO 3
76 #define DAS08JR_AO_LSB(x) ((x) ? 6 : 4)
77 #define DAS08JR_AO_MSB(x) ((x) ? 7 : 5)
78
79 /*
80 cio-das08_aox.pdf
81
82 "das08-aoh"
83 "das08-aol"
84 "das08-aom"
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 mux, gain status gain control
90 4567 8254
91 8 unused ao0_lsb
92 9 unused ao0_msb
93 a unused ao1_lsb
94 b unused ao1_msb
95 89ab
96 cdef 8255
97 */
98
99 #define DAS08AO_GAIN_CONTROL 3
100 #define DAS08AO_GAIN_STATUS 3
101
102 #define DAS08AO_AO_LSB(x) ((x) ? 0xa : 8)
103 #define DAS08AO_AO_MSB(x) ((x) ? 0xb : 9)
104 #define DAS08AO_AO_UPDATE 8
105
106 /* gainlist same as _pgx_ below */
107
108 static const struct comedi_lrange range_das08_pgl = {
109 9, {
110 BIP_RANGE(10),
111 BIP_RANGE(5),
112 BIP_RANGE(2.5),
113 BIP_RANGE(1.25),
114 BIP_RANGE(0.625),
115 UNI_RANGE(10),
116 UNI_RANGE(5),
117 UNI_RANGE(2.5),
118 UNI_RANGE(1.25)
119 }
120 };
121
122 static const struct comedi_lrange range_das08_pgh = {
123 12, {
124 BIP_RANGE(10),
125 BIP_RANGE(5),
126 BIP_RANGE(1),
127 BIP_RANGE(0.5),
128 BIP_RANGE(0.1),
129 BIP_RANGE(0.05),
130 BIP_RANGE(0.01),
131 BIP_RANGE(0.005),
132 UNI_RANGE(10),
133 UNI_RANGE(1),
134 UNI_RANGE(0.1),
135 UNI_RANGE(0.01)
136 }
137 };
138
139 static const struct comedi_lrange range_das08_pgm = {
140 9, {
141 BIP_RANGE(10),
142 BIP_RANGE(5),
143 BIP_RANGE(0.5),
144 BIP_RANGE(0.05),
145 BIP_RANGE(0.01),
146 UNI_RANGE(10),
147 UNI_RANGE(1),
148 UNI_RANGE(0.1),
149 UNI_RANGE(0.01)
150 }
151 }; /*
152 cio-das08jr.pdf
153
154 "das08/jr-ao"
155
156 0 a/d bits 0-3 unused
157 1 a/d bits 4-11 start 12 bit
158 2 eoc, mux mux
159 3 di do
160 4 unused ao0_lsb
161 5 unused ao0_msb
162 6 unused ao1_lsb
163 7 unused ao1_msb
164
165 */
166
167 static const struct comedi_lrange *const das08_ai_lranges[] = {
168 &range_unknown,
169 &range_bipolar5,
170 &range_das08_pgh,
171 &range_das08_pgl,
172 &range_das08_pgm,
173 };
174
175 static const int das08_pgh_gainlist[] = {
176 8, 0, 10, 2, 12, 4, 14, 6, 1, 3, 5, 7
177 };
178 static const int das08_pgl_gainlist[] = { 8, 0, 2, 4, 6, 1, 3, 5, 7 };
179 static const int das08_pgm_gainlist[] = { 8, 0, 10, 12, 14, 9, 11, 13, 15 };
180
181 static const int *const das08_gainlists[] = {
182 NULL,
183 NULL,
184 das08_pgh_gainlist,
185 das08_pgl_gainlist,
186 das08_pgm_gainlist,
187 };
188
das08_ai_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)189 static int das08_ai_eoc(struct comedi_device *dev,
190 struct comedi_subdevice *s,
191 struct comedi_insn *insn,
192 unsigned long context)
193 {
194 unsigned int status;
195
196 status = inb(dev->iobase + DAS08_STATUS);
197 if ((status & DAS08_EOC) == 0)
198 return 0;
199 return -EBUSY;
200 }
201
das08_ai_rinsn(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)202 static int das08_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
203 struct comedi_insn *insn, unsigned int *data)
204 {
205 const struct das08_board_struct *thisboard = dev->board_ptr;
206 struct das08_private_struct *devpriv = dev->private;
207 int n;
208 int chan;
209 int range;
210 int lsb, msb;
211 int ret;
212
213 chan = CR_CHAN(insn->chanspec);
214 range = CR_RANGE(insn->chanspec);
215
216 /* clear crap */
217 inb(dev->iobase + DAS08_LSB);
218 inb(dev->iobase + DAS08_MSB);
219
220 /* set multiplexer */
221 /* lock to prevent race with digital output */
222 spin_lock(&dev->spinlock);
223 devpriv->do_mux_bits &= ~DAS08_MUX_MASK;
224 devpriv->do_mux_bits |= DAS08_MUX(chan);
225 outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL);
226 spin_unlock(&dev->spinlock);
227
228 if (s->range_table->length > 1) {
229 /* set gain/range */
230 range = CR_RANGE(insn->chanspec);
231 outb(devpriv->pg_gainlist[range],
232 dev->iobase + DAS08AO_GAIN_CONTROL);
233 }
234
235 for (n = 0; n < insn->n; n++) {
236 /* clear over-range bits for 16-bit boards */
237 if (thisboard->ai_nbits == 16)
238 if (inb(dev->iobase + DAS08_MSB) & 0x80)
239 dev_info(dev->class_dev, "over-range\n");
240
241 /* trigger conversion */
242 outb_p(0, dev->iobase + DAS08_TRIG_12BIT);
243
244 ret = comedi_timeout(dev, s, insn, das08_ai_eoc, 0);
245 if (ret)
246 return ret;
247
248 msb = inb(dev->iobase + DAS08_MSB);
249 lsb = inb(dev->iobase + DAS08_LSB);
250 if (thisboard->ai_encoding == das08_encode12) {
251 data[n] = (lsb >> 4) | (msb << 4);
252 } else if (thisboard->ai_encoding == das08_pcm_encode12) {
253 data[n] = (msb << 8) + lsb;
254 } else if (thisboard->ai_encoding == das08_encode16) {
255 /* FPOS 16-bit boards are sign-magnitude */
256 if (msb & 0x80)
257 data[n] = (1 << 15) | lsb | ((msb & 0x7f) << 8);
258 else
259 data[n] = (1 << 15) - (lsb | (msb & 0x7f) << 8);
260 } else {
261 dev_err(dev->class_dev, "bug! unknown ai encoding\n");
262 return -1;
263 }
264 }
265
266 return n;
267 }
268
das08_di_rbits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)269 static int das08_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
270 struct comedi_insn *insn, unsigned int *data)
271 {
272 data[0] = 0;
273 data[1] = DAS08_IP(inb(dev->iobase + DAS08_STATUS));
274
275 return insn->n;
276 }
277
das08_do_wbits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)278 static int das08_do_wbits(struct comedi_device *dev,
279 struct comedi_subdevice *s,
280 struct comedi_insn *insn,
281 unsigned int *data)
282 {
283 struct das08_private_struct *devpriv = dev->private;
284
285 if (comedi_dio_update_state(s, data)) {
286 /* prevent race with setting of analog input mux */
287 spin_lock(&dev->spinlock);
288 devpriv->do_mux_bits &= ~DAS08_DO_MASK;
289 devpriv->do_mux_bits |= DAS08_OP(s->state);
290 outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL);
291 spin_unlock(&dev->spinlock);
292 }
293
294 data[1] = s->state;
295
296 return insn->n;
297 }
298
das08jr_di_rbits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)299 static int das08jr_di_rbits(struct comedi_device *dev,
300 struct comedi_subdevice *s,
301 struct comedi_insn *insn, unsigned int *data)
302 {
303 data[0] = 0;
304 data[1] = inb(dev->iobase + DAS08JR_DIO);
305
306 return insn->n;
307 }
308
das08jr_do_wbits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)309 static int das08jr_do_wbits(struct comedi_device *dev,
310 struct comedi_subdevice *s,
311 struct comedi_insn *insn,
312 unsigned int *data)
313 {
314 if (comedi_dio_update_state(s, data))
315 outb(s->state, dev->iobase + DAS08JR_DIO);
316
317 data[1] = s->state;
318
319 return insn->n;
320 }
321
das08_ao_set_data(struct comedi_device * dev,unsigned int chan,unsigned int data)322 static void das08_ao_set_data(struct comedi_device *dev,
323 unsigned int chan, unsigned int data)
324 {
325 const struct das08_board_struct *thisboard = dev->board_ptr;
326 unsigned char lsb;
327 unsigned char msb;
328
329 lsb = data & 0xff;
330 msb = (data >> 8) & 0xff;
331 if (thisboard->is_jr) {
332 outb(lsb, dev->iobase + DAS08JR_AO_LSB(chan));
333 outb(msb, dev->iobase + DAS08JR_AO_MSB(chan));
334 /* load DACs */
335 inb(dev->iobase + DAS08JR_DIO);
336 } else {
337 outb(lsb, dev->iobase + DAS08AO_AO_LSB(chan));
338 outb(msb, dev->iobase + DAS08AO_AO_MSB(chan));
339 /* load DACs */
340 inb(dev->iobase + DAS08AO_AO_UPDATE);
341 }
342 }
343
das08_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)344 static int das08_ao_insn_write(struct comedi_device *dev,
345 struct comedi_subdevice *s,
346 struct comedi_insn *insn,
347 unsigned int *data)
348 {
349 unsigned int chan = CR_CHAN(insn->chanspec);
350 unsigned int val = s->readback[chan];
351 int i;
352
353 for (i = 0; i < insn->n; i++) {
354 val = data[i];
355 das08_ao_set_data(dev, chan, val);
356 }
357 s->readback[chan] = val;
358
359 return insn->n;
360 }
361
das08_common_attach(struct comedi_device * dev,unsigned long iobase)362 int das08_common_attach(struct comedi_device *dev, unsigned long iobase)
363 {
364 const struct das08_board_struct *thisboard = dev->board_ptr;
365 struct das08_private_struct *devpriv = dev->private;
366 struct comedi_subdevice *s;
367 int ret;
368 int i;
369
370 dev->iobase = iobase;
371
372 dev->board_name = thisboard->name;
373
374 ret = comedi_alloc_subdevices(dev, 6);
375 if (ret)
376 return ret;
377
378 s = &dev->subdevices[0];
379 /* ai */
380 if (thisboard->ai_nbits) {
381 s->type = COMEDI_SUBD_AI;
382 /* XXX some boards actually have differential
383 * inputs instead of single ended.
384 * The driver does nothing with arefs though,
385 * so it's no big deal.
386 */
387 s->subdev_flags = SDF_READABLE | SDF_GROUND;
388 s->n_chan = 8;
389 s->maxdata = (1 << thisboard->ai_nbits) - 1;
390 s->range_table = das08_ai_lranges[thisboard->ai_pg];
391 s->insn_read = das08_ai_rinsn;
392 devpriv->pg_gainlist = das08_gainlists[thisboard->ai_pg];
393 } else {
394 s->type = COMEDI_SUBD_UNUSED;
395 }
396
397 s = &dev->subdevices[1];
398 /* ao */
399 if (thisboard->ao_nbits) {
400 s->type = COMEDI_SUBD_AO;
401 s->subdev_flags = SDF_WRITABLE;
402 s->n_chan = 2;
403 s->maxdata = (1 << thisboard->ao_nbits) - 1;
404 s->range_table = &range_bipolar5;
405 s->insn_write = das08_ao_insn_write;
406
407 ret = comedi_alloc_subdev_readback(s);
408 if (ret)
409 return ret;
410
411 /* initialize all channels to 0V */
412 for (i = 0; i < s->n_chan; i++) {
413 s->readback[i] = s->maxdata / 2;
414 das08_ao_set_data(dev, i, s->readback[i]);
415 }
416 } else {
417 s->type = COMEDI_SUBD_UNUSED;
418 }
419
420 s = &dev->subdevices[2];
421 /* di */
422 if (thisboard->di_nchan) {
423 s->type = COMEDI_SUBD_DI;
424 s->subdev_flags = SDF_READABLE;
425 s->n_chan = thisboard->di_nchan;
426 s->maxdata = 1;
427 s->range_table = &range_digital;
428 s->insn_bits =
429 thisboard->is_jr ? das08jr_di_rbits : das08_di_rbits;
430 } else {
431 s->type = COMEDI_SUBD_UNUSED;
432 }
433
434 s = &dev->subdevices[3];
435 /* do */
436 if (thisboard->do_nchan) {
437 s->type = COMEDI_SUBD_DO;
438 s->subdev_flags = SDF_WRITABLE;
439 s->n_chan = thisboard->do_nchan;
440 s->maxdata = 1;
441 s->range_table = &range_digital;
442 s->insn_bits =
443 thisboard->is_jr ? das08jr_do_wbits : das08_do_wbits;
444 } else {
445 s->type = COMEDI_SUBD_UNUSED;
446 }
447
448 s = &dev->subdevices[4];
449 /* 8255 */
450 if (thisboard->i8255_offset != 0) {
451 ret = subdev_8255_init(dev, s, NULL, thisboard->i8255_offset);
452 if (ret)
453 return ret;
454 } else {
455 s->type = COMEDI_SUBD_UNUSED;
456 }
457
458 /* Counter subdevice (8254) */
459 s = &dev->subdevices[5];
460 if (thisboard->i8254_offset) {
461 dev->pacer = comedi_8254_init(dev->iobase +
462 thisboard->i8254_offset,
463 0, I8254_IO8, 0);
464 if (!dev->pacer)
465 return -ENOMEM;
466
467 comedi_8254_subdevice_init(s, dev->pacer);
468 } else {
469 s->type = COMEDI_SUBD_UNUSED;
470 }
471
472 return 0;
473 }
474 EXPORT_SYMBOL_GPL(das08_common_attach);
475
das08_init(void)476 static int __init das08_init(void)
477 {
478 return 0;
479 }
480 module_init(das08_init);
481
das08_exit(void)482 static void __exit das08_exit(void)
483 {
484 }
485 module_exit(das08_exit);
486
487 MODULE_AUTHOR("Comedi http://www.comedi.org");
488 MODULE_DESCRIPTION("Comedi low-level driver");
489 MODULE_LICENSE("GPL");
490