1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29#include <linux/module.h>
30#include <linux/delay.h>
31#include <linux/interrupt.h>
32#include "../comedidev.h"
33
34#include "8255.h"
35
36
37#define DMM32AT_AI_START_CONV_REG 0x00
38#define DMM32AT_AI_LSB_REG 0x00
39#define DMM32AT_AUX_DOUT_REG 0x01
40#define DMM32AT_AUX_DOUT2 BIT(2)
41#define DMM32AT_AUX_DOUT1 BIT(1)
42#define DMM32AT_AUX_DOUT0 BIT(0)
43#define DMM32AT_AI_MSB_REG 0x01
44#define DMM32AT_AI_LO_CHAN_REG 0x02
45#define DMM32AT_AI_HI_CHAN_REG 0x03
46#define DMM32AT_AUX_DI_REG 0x04
47#define DMM32AT_AUX_DI_DACBUSY BIT(7)
48#define DMM32AT_AUX_DI_CALBUSY BIT(6)
49#define DMM32AT_AUX_DI3 BIT(3)
50#define DMM32AT_AUX_DI2 BIT(2)
51#define DMM32AT_AUX_DI1 BIT(1)
52#define DMM32AT_AUX_DI0 BIT(0)
53#define DMM32AT_AO_LSB_REG 0x04
54#define DMM32AT_AO_MSB_REG 0x05
55#define DMM32AT_AO_MSB_DACH(x) ((x) << 6)
56#define DMM32AT_FIFO_DEPTH_REG 0x06
57#define DMM32AT_FIFO_CTRL_REG 0x07
58#define DMM32AT_FIFO_CTRL_FIFOEN BIT(3)
59#define DMM32AT_FIFO_CTRL_SCANEN BIT(2)
60#define DMM32AT_FIFO_CTRL_FIFORST BIT(1)
61#define DMM32AT_FIFO_STATUS_REG 0x07
62#define DMM32AT_FIFO_STATUS_EF BIT(7)
63#define DMM32AT_FIFO_STATUS_HF BIT(6)
64#define DMM32AT_FIFO_STATUS_FF BIT(5)
65#define DMM32AT_FIFO_STATUS_OVF BIT(4)
66#define DMM32AT_FIFO_STATUS_FIFOEN BIT(3)
67#define DMM32AT_FIFO_STATUS_SCANEN BIT(2)
68#define DMM32AT_FIFO_STATUS_PAGE_MASK (3 << 0)
69#define DMM32AT_CTRL_REG 0x08
70#define DMM32AT_CTRL_RESETA BIT(5)
71#define DMM32AT_CTRL_RESETD BIT(4)
72#define DMM32AT_CTRL_INTRST BIT(3)
73#define DMM32AT_CTRL_PAGE(x) ((x) << 0)
74#define DMM32AT_CTRL_PAGE_8254 DMM32AT_CTRL_PAGE(0)
75#define DMM32AT_CTRL_PAGE_8255 DMM32AT_CTRL_PAGE(1)
76#define DMM32AT_CTRL_PAGE_CALIB DMM32AT_CTRL_PAGE(3)
77#define DMM32AT_AI_STATUS_REG 0x08
78#define DMM32AT_AI_STATUS_STS BIT(7)
79#define DMM32AT_AI_STATUS_SD1 BIT(6)
80#define DMM32AT_AI_STATUS_SD0 BIT(5)
81#define DMM32AT_AI_STATUS_ADCH_MASK (0x1f << 0)
82#define DMM32AT_INTCLK_REG 0x09
83#define DMM32AT_INTCLK_ADINT BIT(7)
84#define DMM32AT_INTCLK_DINT BIT(6)
85#define DMM32AT_INTCLK_TINT BIT(5)
86#define DMM32AT_INTCLK_CLKEN BIT(1)
87#define DMM32AT_INTCLK_CLKSEL BIT(0)
88#define DMM32AT_CTRDIO_CFG_REG 0x0a
89#define DMM32AT_CTRDIO_CFG_FREQ12 BIT(7)
90#define DMM32AT_CTRDIO_CFG_FREQ0 BIT(6)
91#define DMM32AT_CTRDIO_CFG_OUT2EN BIT(5)
92#define DMM32AT_CTRDIO_CFG_OUT0EN BIT(4)
93#define DMM32AT_CTRDIO_CFG_GT0EN BIT(2)
94#define DMM32AT_CTRDIO_CFG_SRC0 BIT(1)
95#define DMM32AT_CTRDIO_CFG_GT12EN BIT(0)
96#define DMM32AT_AI_CFG_REG 0x0b
97#define DMM32AT_AI_CFG_SCINT(x) ((x) << 4)
98#define DMM32AT_AI_CFG_SCINT_20US DMM32AT_AI_CFG_SCINT(0)
99#define DMM32AT_AI_CFG_SCINT_15US DMM32AT_AI_CFG_SCINT(1)
100#define DMM32AT_AI_CFG_SCINT_10US DMM32AT_AI_CFG_SCINT(2)
101#define DMM32AT_AI_CFG_SCINT_5US DMM32AT_AI_CFG_SCINT(3)
102#define DMM32AT_AI_CFG_RANGE BIT(3)
103#define DMM32AT_AI_CFG_ADBU BIT(2)
104#define DMM32AT_AI_CFG_GAIN(x) ((x) << 0)
105#define DMM32AT_AI_READBACK_REG 0x0b
106#define DMM32AT_AI_READBACK_WAIT BIT(7)
107#define DMM32AT_AI_READBACK_RANGE BIT(3)
108#define DMM32AT_AI_READBACK_ADBU BIT(2)
109#define DMM32AT_AI_READBACK_GAIN_MASK (3 << 0)
110
111#define DMM32AT_CLK1 0x0d
112#define DMM32AT_CLK2 0x0e
113#define DMM32AT_CLKCT 0x0f
114
115#define DMM32AT_8255_IOBASE 0x0c
116
117
118
119
120#define DMM32AT_RANGE_U10 0x0c
121#define DMM32AT_RANGE_U5 0x0d
122#define DMM32AT_RANGE_B10 0x08
123#define DMM32AT_RANGE_B5 0x00
124
125
126#define DMM32AT_CLKCT1 0x56
127#define DMM32AT_CLKCT2 0xb6
128
129
130static const struct comedi_lrange dmm32at_airanges = {
131 4, {
132 UNI_RANGE(10),
133 UNI_RANGE(5),
134 BIP_RANGE(10),
135 BIP_RANGE(5)
136 }
137};
138
139
140static const unsigned char dmm32at_rangebits[] = {
141 DMM32AT_RANGE_U10,
142 DMM32AT_RANGE_U5,
143 DMM32AT_RANGE_B10,
144 DMM32AT_RANGE_B5,
145};
146
147
148
149
150static const struct comedi_lrange dmm32at_aoranges = {
151 4, {
152 UNI_RANGE(10),
153 UNI_RANGE(5),
154 BIP_RANGE(10),
155 BIP_RANGE(5)
156 }
157};
158
159static void dmm32at_ai_set_chanspec(struct comedi_device *dev,
160 struct comedi_subdevice *s,
161 unsigned int chanspec, int nchan)
162{
163 unsigned int chan = CR_CHAN(chanspec);
164 unsigned int range = CR_RANGE(chanspec);
165 unsigned int last_chan = (chan + nchan - 1) % s->n_chan;
166
167 outb(DMM32AT_FIFO_CTRL_FIFORST, dev->iobase + DMM32AT_FIFO_CTRL_REG);
168
169 if (nchan > 1)
170 outb(DMM32AT_FIFO_CTRL_SCANEN,
171 dev->iobase + DMM32AT_FIFO_CTRL_REG);
172
173 outb(chan, dev->iobase + DMM32AT_AI_LO_CHAN_REG);
174 outb(last_chan, dev->iobase + DMM32AT_AI_HI_CHAN_REG);
175 outb(dmm32at_rangebits[range], dev->iobase + DMM32AT_AI_CFG_REG);
176}
177
178static unsigned int dmm32at_ai_get_sample(struct comedi_device *dev,
179 struct comedi_subdevice *s)
180{
181 unsigned int val;
182
183 val = inb(dev->iobase + DMM32AT_AI_LSB_REG);
184 val |= (inb(dev->iobase + DMM32AT_AI_MSB_REG) << 8);
185
186
187 return comedi_offset_munge(s, val);
188}
189
190static int dmm32at_ai_status(struct comedi_device *dev,
191 struct comedi_subdevice *s,
192 struct comedi_insn *insn,
193 unsigned long context)
194{
195 unsigned char status;
196
197 status = inb(dev->iobase + context);
198 if ((status & DMM32AT_AI_STATUS_STS) == 0)
199 return 0;
200 return -EBUSY;
201}
202
203static int dmm32at_ai_insn_read(struct comedi_device *dev,
204 struct comedi_subdevice *s,
205 struct comedi_insn *insn,
206 unsigned int *data)
207{
208 int ret;
209 int i;
210
211 dmm32at_ai_set_chanspec(dev, s, insn->chanspec, 1);
212
213
214 ret = comedi_timeout(dev, s, insn, dmm32at_ai_status,
215 DMM32AT_AI_READBACK_REG);
216 if (ret)
217 return ret;
218
219 for (i = 0; i < insn->n; i++) {
220 outb(0xff, dev->iobase + DMM32AT_AI_START_CONV_REG);
221
222 ret = comedi_timeout(dev, s, insn, dmm32at_ai_status,
223 DMM32AT_AI_STATUS_REG);
224 if (ret)
225 return ret;
226
227 data[i] = dmm32at_ai_get_sample(dev, s);
228 }
229
230 return insn->n;
231}
232
233static int dmm32at_ai_check_chanlist(struct comedi_device *dev,
234 struct comedi_subdevice *s,
235 struct comedi_cmd *cmd)
236{
237 unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
238 unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
239 int i;
240
241 for (i = 1; i < cmd->chanlist_len; i++) {
242 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
243 unsigned int range = CR_RANGE(cmd->chanlist[i]);
244
245 if (chan != (chan0 + i) % s->n_chan) {
246 dev_dbg(dev->class_dev,
247 "entries in chanlist must be consecutive channels, counting upwards\n");
248 return -EINVAL;
249 }
250 if (range != range0) {
251 dev_dbg(dev->class_dev,
252 "entries in chanlist must all have the same gain\n");
253 return -EINVAL;
254 }
255 }
256
257 return 0;
258}
259
260static int dmm32at_ai_cmdtest(struct comedi_device *dev,
261 struct comedi_subdevice *s,
262 struct comedi_cmd *cmd)
263{
264 int err = 0;
265 unsigned int arg;
266
267
268
269 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
270 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
271 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
272 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
273 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
274
275 if (err)
276 return 1;
277
278
279
280 err |= comedi_check_trigger_is_unique(cmd->stop_src);
281
282
283
284 if (err)
285 return 2;
286
287
288
289 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
290
291 err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, 1000000);
292 err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, 1000000000);
293
294 if (cmd->convert_arg >= 17500)
295 cmd->convert_arg = 20000;
296 else if (cmd->convert_arg >= 12500)
297 cmd->convert_arg = 15000;
298 else if (cmd->convert_arg >= 7500)
299 cmd->convert_arg = 10000;
300 else
301 cmd->convert_arg = 5000;
302
303 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
304 cmd->chanlist_len);
305
306 if (cmd->stop_src == TRIG_COUNT)
307 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
308 else
309 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
310
311 if (err)
312 return 3;
313
314
315
316 arg = cmd->convert_arg * cmd->scan_end_arg;
317 err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, arg);
318
319 if (err)
320 return 4;
321
322
323 if (cmd->chanlist && cmd->chanlist_len > 0)
324 err |= dmm32at_ai_check_chanlist(dev, s, cmd);
325
326 if (err)
327 return 5;
328
329 return 0;
330}
331
332static void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec)
333{
334 unsigned char lo1, lo2, hi2;
335 unsigned short both2;
336
337
338 lo1 = 200;
339 both2 = nansec / 20000;
340 hi2 = (both2 & 0xff00) >> 8;
341 lo2 = both2 & 0x00ff;
342
343
344 outb(0, dev->iobase + DMM32AT_CTRDIO_CFG_REG);
345
346
347 outb(DMM32AT_CTRL_PAGE_8254, dev->iobase + DMM32AT_CTRL_REG);
348
349
350 outb(DMM32AT_CLKCT1, dev->iobase + DMM32AT_CLKCT);
351 outb(lo1, dev->iobase + DMM32AT_CLK1);
352
353
354 outb(DMM32AT_CLKCT2, dev->iobase + DMM32AT_CLKCT);
355 outb(lo2, dev->iobase + DMM32AT_CLK2);
356 outb(hi2, dev->iobase + DMM32AT_CLK2);
357
358
359 outb(DMM32AT_INTCLK_ADINT |
360 DMM32AT_INTCLK_CLKEN | DMM32AT_INTCLK_CLKSEL,
361 dev->iobase + DMM32AT_INTCLK_REG);
362}
363
364static int dmm32at_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
365{
366 struct comedi_cmd *cmd = &s->async->cmd;
367 int ret;
368
369 dmm32at_ai_set_chanspec(dev, s, cmd->chanlist[0], cmd->chanlist_len);
370
371
372 outb(DMM32AT_CTRL_INTRST, dev->iobase + DMM32AT_CTRL_REG);
373
374
375
376
377
378 ret = comedi_timeout(dev, s, NULL, dmm32at_ai_status,
379 DMM32AT_AI_READBACK_REG);
380 if (ret)
381 return ret;
382
383 if (cmd->stop_src == TRIG_NONE || cmd->stop_arg > 1) {
384
385 dmm32at_setaitimer(dev, cmd->scan_begin_arg);
386 } else {
387
388 outb(DMM32AT_INTCLK_ADINT, dev->iobase + DMM32AT_INTCLK_REG);
389 outb(0xff, dev->iobase + DMM32AT_AI_START_CONV_REG);
390 }
391
392 return 0;
393}
394
395static int dmm32at_ai_cancel(struct comedi_device *dev,
396 struct comedi_subdevice *s)
397{
398
399 outb(0x0, dev->iobase + DMM32AT_INTCLK_REG);
400 return 0;
401}
402
403static irqreturn_t dmm32at_isr(int irq, void *d)
404{
405 struct comedi_device *dev = d;
406 unsigned char intstat;
407 unsigned int val;
408 int i;
409
410 if (!dev->attached) {
411 dev_err(dev->class_dev, "spurious interrupt\n");
412 return IRQ_HANDLED;
413 }
414
415 intstat = inb(dev->iobase + DMM32AT_INTCLK_REG);
416
417 if (intstat & DMM32AT_INTCLK_ADINT) {
418 struct comedi_subdevice *s = dev->read_subdev;
419 struct comedi_cmd *cmd = &s->async->cmd;
420
421 for (i = 0; i < cmd->chanlist_len; i++) {
422 val = dmm32at_ai_get_sample(dev, s);
423 comedi_buf_write_samples(s, &val, 1);
424 }
425
426 if (cmd->stop_src == TRIG_COUNT &&
427 s->async->scans_done >= cmd->stop_arg)
428 s->async->events |= COMEDI_CB_EOA;
429
430 comedi_handle_events(dev, s);
431 }
432
433
434 outb(DMM32AT_CTRL_INTRST, dev->iobase + DMM32AT_CTRL_REG);
435 return IRQ_HANDLED;
436}
437
438static int dmm32at_ao_eoc(struct comedi_device *dev,
439 struct comedi_subdevice *s,
440 struct comedi_insn *insn,
441 unsigned long context)
442{
443 unsigned char status;
444
445 status = inb(dev->iobase + DMM32AT_AUX_DI_REG);
446 if ((status & DMM32AT_AUX_DI_DACBUSY) == 0)
447 return 0;
448 return -EBUSY;
449}
450
451static int dmm32at_ao_insn_write(struct comedi_device *dev,
452 struct comedi_subdevice *s,
453 struct comedi_insn *insn,
454 unsigned int *data)
455{
456 unsigned int chan = CR_CHAN(insn->chanspec);
457 int i;
458
459 for (i = 0; i < insn->n; i++) {
460 unsigned int val = data[i];
461 int ret;
462
463
464 outb(val & 0xff, dev->iobase + DMM32AT_AO_LSB_REG);
465 outb((val >> 8) | DMM32AT_AO_MSB_DACH(chan),
466 dev->iobase + DMM32AT_AO_MSB_REG);
467
468
469 ret = comedi_timeout(dev, s, insn, dmm32at_ao_eoc, 0);
470 if (ret)
471 return ret;
472
473
474 inb(dev->iobase + DMM32AT_AO_MSB_REG);
475
476 s->readback[chan] = val;
477 }
478
479 return insn->n;
480}
481
482static int dmm32at_8255_io(struct comedi_device *dev,
483 int dir, int port, int data, unsigned long regbase)
484{
485
486 outb(DMM32AT_CTRL_PAGE_8255, dev->iobase + DMM32AT_CTRL_REG);
487
488 if (dir) {
489 outb(data, dev->iobase + regbase + port);
490 return 0;
491 }
492 return inb(dev->iobase + regbase + port);
493}
494
495
496static int dmm32at_reset(struct comedi_device *dev)
497{
498 unsigned char aihi, ailo, fifostat, aistat, intstat, airback;
499
500
501 outb(DMM32AT_CTRL_RESETA, dev->iobase + DMM32AT_CTRL_REG);
502
503
504 usleep_range(1000, 3000);
505
506
507 outb(0x0, dev->iobase + DMM32AT_FIFO_CTRL_REG);
508
509
510 outb(0x0, dev->iobase + DMM32AT_INTCLK_REG);
511
512
513 outb(0x80, dev->iobase + DMM32AT_AI_LO_CHAN_REG);
514 outb(0xff, dev->iobase + DMM32AT_AI_HI_CHAN_REG);
515
516
517 outb(DMM32AT_RANGE_U10, dev->iobase + DMM32AT_AI_CFG_REG);
518
519
520 usleep_range(100, 200);
521
522
523 ailo = inb(dev->iobase + DMM32AT_AI_LO_CHAN_REG);
524 aihi = inb(dev->iobase + DMM32AT_AI_HI_CHAN_REG);
525 fifostat = inb(dev->iobase + DMM32AT_FIFO_STATUS_REG);
526 aistat = inb(dev->iobase + DMM32AT_AI_STATUS_REG);
527 intstat = inb(dev->iobase + DMM32AT_INTCLK_REG);
528 airback = inb(dev->iobase + DMM32AT_AI_READBACK_REG);
529
530
531
532
533
534
535 if (ailo != 0x00 || aihi != 0x1f ||
536 fifostat != DMM32AT_FIFO_STATUS_EF ||
537 aistat != (DMM32AT_AI_STATUS_SD1 | DMM32AT_AI_STATUS_SD0) ||
538 intstat != 0x00 || airback != 0x0c)
539 return -EIO;
540
541 return 0;
542}
543
544static int dmm32at_attach(struct comedi_device *dev,
545 struct comedi_devconfig *it)
546{
547 struct comedi_subdevice *s;
548 int ret;
549
550 ret = comedi_request_region(dev, it->options[0], 0x10);
551 if (ret)
552 return ret;
553
554 ret = dmm32at_reset(dev);
555 if (ret) {
556 dev_err(dev->class_dev, "board detection failed\n");
557 return ret;
558 }
559
560 if (it->options[1]) {
561 ret = request_irq(it->options[1], dmm32at_isr, 0,
562 dev->board_name, dev);
563 if (ret == 0)
564 dev->irq = it->options[1];
565 }
566
567 ret = comedi_alloc_subdevices(dev, 3);
568 if (ret)
569 return ret;
570
571
572 s = &dev->subdevices[0];
573 s->type = COMEDI_SUBD_AI;
574 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
575 s->n_chan = 32;
576 s->maxdata = 0xffff;
577 s->range_table = &dmm32at_airanges;
578 s->insn_read = dmm32at_ai_insn_read;
579 if (dev->irq) {
580 dev->read_subdev = s;
581 s->subdev_flags |= SDF_CMD_READ;
582 s->len_chanlist = s->n_chan;
583 s->do_cmd = dmm32at_ai_cmd;
584 s->do_cmdtest = dmm32at_ai_cmdtest;
585 s->cancel = dmm32at_ai_cancel;
586 }
587
588
589 s = &dev->subdevices[1];
590 s->type = COMEDI_SUBD_AO;
591 s->subdev_flags = SDF_WRITABLE;
592 s->n_chan = 4;
593 s->maxdata = 0x0fff;
594 s->range_table = &dmm32at_aoranges;
595 s->insn_write = dmm32at_ao_insn_write;
596
597 ret = comedi_alloc_subdev_readback(s);
598 if (ret)
599 return ret;
600
601
602 s = &dev->subdevices[2];
603 return subdev_8255_init(dev, s, dmm32at_8255_io, DMM32AT_8255_IOBASE);
604}
605
606static struct comedi_driver dmm32at_driver = {
607 .driver_name = "dmm32at",
608 .module = THIS_MODULE,
609 .attach = dmm32at_attach,
610 .detach = comedi_legacy_detach,
611};
612module_comedi_driver(dmm32at_driver);
613
614MODULE_AUTHOR("Comedi http://www.comedi.org");
615MODULE_DESCRIPTION("Comedi: Diamond Systems Diamond-MM-32-AT");
616MODULE_LICENSE("GPL");
617