Staging: comedi: Remove comedi_devconfig typedef
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / staging / comedi / drivers / dt3000.c
CommitLineData
9a21297d
DS
1/*
2 comedi/drivers/dt3000.c
3 Data Translation DT3000 series driver
4
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 1999 David A. Schleef <ds@schleef.org>
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22*/
23/*
24Driver: dt3000
25Description: Data Translation DT3000 series
26Author: ds
27Devices: [Data Translation] DT3001 (dt3000), DT3001-PGL, DT3002, DT3003,
28 DT3003-PGL, DT3004, DT3005, DT3004-200
29Updated: Mon, 14 Apr 2008 15:41:24 +0100
30Status: works
31
32Configuration Options:
33 [0] - PCI bus of device (optional)
34 [1] - PCI slot of device (optional)
35 If bus/slot is not specified, the first supported
36 PCI device found will be used.
37
38There is code to support AI commands, but it may not work.
39
40AO commands are not supported.
41*/
42
43/*
44 The DT3000 series is Data Translation's attempt to make a PCI
45 data acquisition board. The design of this series is very nice,
46 since each board has an on-board DSP (Texas Instruments TMS320C52).
47 However, a few details are a little annoying. The boards lack
48 bus-mastering DMA, which eliminates them from serious work.
49 They also are not capable of autocalibration, which is a common
50 feature in modern hardware. The default firmware is pretty bad,
51 making it nearly impossible to write an RT compatible driver.
52 It would make an interesting project to write a decent firmware
53 for these boards.
54
55 Data Translation originally wanted an NDA for the documentation
56 for the 3k series. However, if you ask nicely, they might send
57 you the docs without one, also.
58*/
59
60#define DEBUG 1
61
62#include "../comedidev.h"
63#include <linux/delay.h>
64
65#include "comedi_pci.h"
66
67#define PCI_VENDOR_ID_DT 0x1116
68
9ced1de6 69static const struct comedi_lrange range_dt3000_ai = { 4, {
9a21297d
DS
70 RANGE(-10, 10),
71 RANGE(-5, 5),
72 RANGE(-2.5, 2.5),
73 RANGE(-1.25, 1.25)
74 }
75};
9ced1de6 76static const struct comedi_lrange range_dt3000_ai_pgl = { 4, {
9a21297d
DS
77 RANGE(-10, 10),
78 RANGE(-1, 1),
79 RANGE(-0.1, 0.1),
80 RANGE(-0.02, 0.02)
81 }
82};
83
84typedef struct {
85 const char *name;
86 unsigned int device_id;
87 int adchan;
88 int adbits;
89 int ai_speed;
9ced1de6 90 const struct comedi_lrange *adrange;
9a21297d
DS
91 int dachan;
92 int dabits;
93} dt3k_boardtype;
94
95static const dt3k_boardtype dt3k_boardtypes[] = {
96 {name:"dt3001",
97 device_id:0x22,
98 adchan: 16,
99 adbits: 12,
100 adrange: &range_dt3000_ai,
101 ai_speed:3000,
102 dachan: 2,
103 dabits: 12,
104 },
105 {name:"dt3001-pgl",
106 device_id:0x27,
107 adchan: 16,
108 adbits: 12,
109 adrange: &range_dt3000_ai_pgl,
110 ai_speed:3000,
111 dachan: 2,
112 dabits: 12,
113 },
114 {name:"dt3002",
115 device_id:0x23,
116 adchan: 32,
117 adbits: 12,
118 adrange: &range_dt3000_ai,
119 ai_speed:3000,
120 dachan: 0,
121 dabits: 0,
122 },
123 {name:"dt3003",
124 device_id:0x24,
125 adchan: 64,
126 adbits: 12,
127 adrange: &range_dt3000_ai,
128 ai_speed:3000,
129 dachan: 2,
130 dabits: 12,
131 },
132 {name:"dt3003-pgl",
133 device_id:0x28,
134 adchan: 64,
135 adbits: 12,
136 adrange: &range_dt3000_ai_pgl,
137 ai_speed:3000,
138 dachan: 2,
139 dabits: 12,
140 },
141 {name:"dt3004",
142 device_id:0x25,
143 adchan: 16,
144 adbits: 16,
145 adrange: &range_dt3000_ai,
146 ai_speed:10000,
147 dachan: 2,
148 dabits: 12,
149 },
150 {name:"dt3005", /* a.k.a. 3004-200 */
151 device_id:0x26,
152 adchan: 16,
153 adbits: 16,
154 adrange: &range_dt3000_ai,
155 ai_speed:5000,
156 dachan: 2,
157 dabits: 12,
158 },
159};
160
161#define n_dt3k_boards sizeof(dt3k_boardtypes)/sizeof(dt3k_boardtype)
162#define this_board ((const dt3k_boardtype *)dev->board_ptr)
163
164static DEFINE_PCI_DEVICE_TABLE(dt3k_pci_table) = {
165 {PCI_VENDOR_ID_DT, 0x0022, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
166 {PCI_VENDOR_ID_DT, 0x0027, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
167 {PCI_VENDOR_ID_DT, 0x0023, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
168 {PCI_VENDOR_ID_DT, 0x0024, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
169 {PCI_VENDOR_ID_DT, 0x0028, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
170 {PCI_VENDOR_ID_DT, 0x0025, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
171 {PCI_VENDOR_ID_DT, 0x0026, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
172 {0}
173};
174
175MODULE_DEVICE_TABLE(pci, dt3k_pci_table);
176
177#define DT3000_SIZE (4*0x1000)
178
179/* dual-ported RAM location definitions */
180
181#define DPR_DAC_buffer (4*0x000)
182#define DPR_ADC_buffer (4*0x800)
183#define DPR_Command (4*0xfd3)
184#define DPR_SubSys (4*0xfd3)
185#define DPR_Encode (4*0xfd4)
186#define DPR_Params(a) (4*(0xfd5+(a)))
187#define DPR_Tick_Reg_Lo (4*0xff5)
188#define DPR_Tick_Reg_Hi (4*0xff6)
189#define DPR_DA_Buf_Front (4*0xff7)
190#define DPR_DA_Buf_Rear (4*0xff8)
191#define DPR_AD_Buf_Front (4*0xff9)
192#define DPR_AD_Buf_Rear (4*0xffa)
193#define DPR_Int_Mask (4*0xffb)
194#define DPR_Intr_Flag (4*0xffc)
195#define DPR_Response_Mbx (4*0xffe)
196#define DPR_Command_Mbx (4*0xfff)
197
198#define AI_FIFO_DEPTH 2003
199#define AO_FIFO_DEPTH 2048
200
201/* command list */
202
203#define CMD_GETBRDINFO 0
204#define CMD_CONFIG 1
205#define CMD_GETCONFIG 2
206#define CMD_START 3
207#define CMD_STOP 4
208#define CMD_READSINGLE 5
209#define CMD_WRITESINGLE 6
210#define CMD_CALCCLOCK 7
211#define CMD_READEVENTS 8
212#define CMD_WRITECTCTRL 16
213#define CMD_READCTCTRL 17
214#define CMD_WRITECT 18
215#define CMD_READCT 19
216#define CMD_WRITEDATA 32
217#define CMD_READDATA 33
218#define CMD_WRITEIO 34
219#define CMD_READIO 35
220#define CMD_WRITECODE 36
221#define CMD_READCODE 37
222#define CMD_EXECUTE 38
223#define CMD_HALT 48
224
225#define SUBS_AI 0
226#define SUBS_AO 1
227#define SUBS_DIN 2
228#define SUBS_DOUT 3
229#define SUBS_MEM 4
230#define SUBS_CT 5
231
232/* interrupt flags */
233#define DT3000_CMDONE 0x80
234#define DT3000_CTDONE 0x40
235#define DT3000_DAHWERR 0x20
236#define DT3000_DASWERR 0x10
237#define DT3000_DAEMPTY 0x08
238#define DT3000_ADHWERR 0x04
239#define DT3000_ADSWERR 0x02
240#define DT3000_ADFULL 0x01
241
242#define DT3000_COMPLETION_MASK 0xff00
243#define DT3000_COMMAND_MASK 0x00ff
244#define DT3000_NOTPROCESSED 0x0000
245#define DT3000_NOERROR 0x5500
246#define DT3000_ERROR 0xaa00
247#define DT3000_NOTSUPPORTED 0xff00
248
249#define DT3000_EXTERNAL_CLOCK 1
250#define DT3000_RISING_EDGE 2
251
252#define TMODE_MASK 0x1c
253
254#define DT3000_AD_TRIG_INTERNAL (0<<2)
255#define DT3000_AD_TRIG_EXTERNAL (1<<2)
256#define DT3000_AD_RETRIG_INTERNAL (2<<2)
257#define DT3000_AD_RETRIG_EXTERNAL (3<<2)
258#define DT3000_AD_EXTRETRIG (4<<2)
259
260#define DT3000_CHANNEL_MODE_SE 0
261#define DT3000_CHANNEL_MODE_DI 1
262
263typedef struct {
264 struct pci_dev *pci_dev;
265 resource_size_t phys_addr;
266 void *io_addr;
267 unsigned int lock;
790c5541 268 unsigned int ao_readback[2];
9a21297d
DS
269 unsigned int ai_front;
270 unsigned int ai_rear;
271} dt3k_private;
272#define devpriv ((dt3k_private *)dev->private)
273
0707bb04 274static int dt3000_attach(struct comedi_device * dev, struct comedi_devconfig * it);
71b5f4f1 275static int dt3000_detach(struct comedi_device * dev);
139dfbdf 276static struct comedi_driver driver_dt3000 = {
9a21297d
DS
277 driver_name:"dt3000",
278 module:THIS_MODULE,
279 attach:dt3000_attach,
280 detach:dt3000_detach,
281};
282
283COMEDI_PCI_INITCLEANUP(driver_dt3000, dt3k_pci_table);
284
34c43922 285static void dt3k_ai_empty_fifo(struct comedi_device * dev, struct comedi_subdevice * s);
9a21297d
DS
286static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *arg,
287 unsigned int round_mode);
34c43922 288static int dt3k_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
9a21297d
DS
289#ifdef DEBUG
290static void debug_intr_flags(unsigned int flags);
291#endif
292
293#define TIMEOUT 100
294
71b5f4f1 295static int dt3k_send_cmd(struct comedi_device * dev, unsigned int cmd)
9a21297d
DS
296{
297 int i;
298 unsigned int status = 0;
299
300 writew(cmd, devpriv->io_addr + DPR_Command_Mbx);
301
302 for (i = 0; i < TIMEOUT; i++) {
303 status = readw(devpriv->io_addr + DPR_Command_Mbx);
304 if ((status & DT3000_COMPLETION_MASK) != DT3000_NOTPROCESSED)
305 break;
306 comedi_udelay(1);
307 }
308 if ((status & DT3000_COMPLETION_MASK) == DT3000_NOERROR) {
309 return 0;
310 }
311
312 printk("dt3k_send_cmd() timeout/error status=0x%04x\n", status);
313
314 return -ETIME;
315}
316
71b5f4f1 317static unsigned int dt3k_readsingle(struct comedi_device * dev, unsigned int subsys,
9a21297d
DS
318 unsigned int chan, unsigned int gain)
319{
320 writew(subsys, devpriv->io_addr + DPR_SubSys);
321
322 writew(chan, devpriv->io_addr + DPR_Params(0));
323 writew(gain, devpriv->io_addr + DPR_Params(1));
324
325 dt3k_send_cmd(dev, CMD_READSINGLE);
326
327 return readw(devpriv->io_addr + DPR_Params(2));
328}
329
71b5f4f1 330static void dt3k_writesingle(struct comedi_device * dev, unsigned int subsys,
9a21297d
DS
331 unsigned int chan, unsigned int data)
332{
333 writew(subsys, devpriv->io_addr + DPR_SubSys);
334
335 writew(chan, devpriv->io_addr + DPR_Params(0));
336 writew(0, devpriv->io_addr + DPR_Params(1));
337 writew(data, devpriv->io_addr + DPR_Params(2));
338
339 dt3k_send_cmd(dev, CMD_WRITESINGLE);
340}
341
342static int debug_n_ints = 0;
343
344// FIXME! Assumes shared interrupt is for this card.
345// What's this debug_n_ints stuff? Obviously needs some work...
346static irqreturn_t dt3k_interrupt(int irq, void *d PT_REGS_ARG)
347{
71b5f4f1 348 struct comedi_device *dev = d;
34c43922 349 struct comedi_subdevice *s;
9a21297d
DS
350 unsigned int status;
351
352 if (!dev->attached) {
353 return IRQ_NONE;
354 }
355
356 s = dev->subdevices + 0;
357 status = readw(devpriv->io_addr + DPR_Intr_Flag);
358#ifdef DEBUG
359 debug_intr_flags(status);
360#endif
361
362 if (status & DT3000_ADFULL) {
363 dt3k_ai_empty_fifo(dev, s);
364 s->async->events |= COMEDI_CB_BLOCK;
365 }
366
367 if (status & (DT3000_ADSWERR | DT3000_ADHWERR)) {
368 s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
369 }
370
371 debug_n_ints++;
372 if (debug_n_ints >= 10) {
373 dt3k_ai_cancel(dev, s);
374 s->async->events |= COMEDI_CB_EOA;
375 }
376
377 comedi_event(dev, s);
378 return IRQ_HANDLED;
379}
380
381#ifdef DEBUG
382static char *intr_flags[] = {
383 "AdFull", "AdSwError", "AdHwError", "DaEmpty",
384 "DaSwError", "DaHwError", "CtDone", "CmDone",
385};
386static void debug_intr_flags(unsigned int flags)
387{
388 int i;
389 printk("dt3k: intr_flags:");
390 for (i = 0; i < 8; i++) {
391 if (flags & (1 << i)) {
392 printk(" %s", intr_flags[i]);
393 }
394 }
395 printk("\n");
396}
397#endif
398
34c43922 399static void dt3k_ai_empty_fifo(struct comedi_device * dev, struct comedi_subdevice * s)
9a21297d
DS
400{
401 int front;
402 int rear;
403 int count;
404 int i;
790c5541 405 short data;
9a21297d
DS
406
407 front = readw(devpriv->io_addr + DPR_AD_Buf_Front);
408 count = front - devpriv->ai_front;
409 if (count < 0)
410 count += AI_FIFO_DEPTH;
411
412 printk("reading %d samples\n", count);
413
414 rear = devpriv->ai_rear;
415
416 for (i = 0; i < count; i++) {
417 data = readw(devpriv->io_addr + DPR_ADC_buffer + rear);
418 comedi_buf_put(s->async, data);
419 rear++;
420 if (rear >= AI_FIFO_DEPTH)
421 rear = 0;
422 }
423
424 devpriv->ai_rear = rear;
425 writew(rear, devpriv->io_addr + DPR_AD_Buf_Rear);
426}
427
34c43922 428static int dt3k_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
ea6d0d4c 429 struct comedi_cmd * cmd)
9a21297d
DS
430{
431 int err = 0;
432 int tmp;
433
434 /* step 1: make sure trigger sources are trivially valid */
435
436 tmp = cmd->start_src;
437 cmd->start_src &= TRIG_NOW;
438 if (!cmd->start_src || tmp != cmd->start_src)
439 err++;
440
441 tmp = cmd->scan_begin_src;
442 cmd->scan_begin_src &= TRIG_TIMER;
443 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
444 err++;
445
446 tmp = cmd->convert_src;
447 cmd->convert_src &= TRIG_TIMER;
448 if (!cmd->convert_src || tmp != cmd->convert_src)
449 err++;
450
451 tmp = cmd->scan_end_src;
452 cmd->scan_end_src &= TRIG_COUNT;
453 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
454 err++;
455
456 tmp = cmd->stop_src;
457 cmd->stop_src &= TRIG_COUNT;
458 if (!cmd->stop_src || tmp != cmd->stop_src)
459 err++;
460
461 if (err)
462 return 1;
463
464 /* step 2: make sure trigger sources are unique and mutually compatible */
465
466 if (err)
467 return 2;
468
469 /* step 3: make sure arguments are trivially compatible */
470
471 if (cmd->start_arg != 0) {
472 cmd->start_arg = 0;
473 err++;
474 }
475
476 if (cmd->scan_begin_src == TRIG_TIMER) {
477 if (cmd->scan_begin_arg < this_board->ai_speed) {
478 cmd->scan_begin_arg = this_board->ai_speed;
479 err++;
480 }
481 if (cmd->scan_begin_arg > 100 * 16 * 65535) {
482 cmd->scan_begin_arg = 100 * 16 * 65535;
483 err++;
484 }
485 } else {
486 /* not supported */
487 }
488 if (cmd->convert_src == TRIG_TIMER) {
489 if (cmd->convert_arg < this_board->ai_speed) {
490 cmd->convert_arg = this_board->ai_speed;
491 err++;
492 }
493 if (cmd->convert_arg > 50 * 16 * 65535) {
494 cmd->convert_arg = 50 * 16 * 65535;
495 err++;
496 }
497 } else {
498 /* not supported */
499 }
500
501 if (cmd->scan_end_arg != cmd->chanlist_len) {
502 cmd->scan_end_arg = cmd->chanlist_len;
503 err++;
504 }
505 if (cmd->stop_src == TRIG_COUNT) {
506 if (cmd->stop_arg > 0x00ffffff) {
507 cmd->stop_arg = 0x00ffffff;
508 err++;
509 }
510 } else {
511 /* TRIG_NONE */
512 if (cmd->stop_arg != 0) {
513 cmd->stop_arg = 0;
514 err++;
515 }
516 }
517
518 if (err)
519 return 3;
520
521 /* step 4: fix up any arguments */
522
523 if (cmd->scan_begin_src == TRIG_TIMER) {
524 tmp = cmd->scan_begin_arg;
525 dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
526 cmd->flags & TRIG_ROUND_MASK);
527 if (tmp != cmd->scan_begin_arg)
528 err++;
529 } else {
530 /* not supported */
531 }
532 if (cmd->convert_src == TRIG_TIMER) {
533 tmp = cmd->convert_arg;
534 dt3k_ns_to_timer(50, &cmd->convert_arg,
535 cmd->flags & TRIG_ROUND_MASK);
536 if (tmp != cmd->convert_arg)
537 err++;
538 if (cmd->scan_begin_src == TRIG_TIMER &&
539 cmd->scan_begin_arg <
540 cmd->convert_arg * cmd->scan_end_arg) {
541 cmd->scan_begin_arg =
542 cmd->convert_arg * cmd->scan_end_arg;
543 err++;
544 }
545 } else {
546 /* not supported */
547 }
548
549 if (err)
550 return 4;
551
552 return 0;
553}
554
555static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *nanosec,
556 unsigned int round_mode)
557{
558 int divider, base, prescale;
559
560 /* This function needs improvment */
561 /* Don't know if divider==0 works. */
562
563 for (prescale = 0; prescale < 16; prescale++) {
564 base = timer_base * (prescale + 1);
565 switch (round_mode) {
566 case TRIG_ROUND_NEAREST:
567 default:
568 divider = (*nanosec + base / 2) / base;
569 break;
570 case TRIG_ROUND_DOWN:
571 divider = (*nanosec) / base;
572 break;
573 case TRIG_ROUND_UP:
574 divider = (*nanosec) / base;
575 break;
576 }
577 if (divider < 65536) {
578 *nanosec = divider * base;
579 return (prescale << 16) | (divider);
580 }
581 }
582
583 prescale = 15;
584 base = timer_base * (1 << prescale);
585 divider = 65535;
586 *nanosec = divider * base;
587 return (prescale << 16) | (divider);
588}
589
34c43922 590static int dt3k_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
9a21297d 591{
ea6d0d4c 592 struct comedi_cmd *cmd = &s->async->cmd;
9a21297d
DS
593 int i;
594 unsigned int chan, range, aref;
595 unsigned int divider;
596 unsigned int tscandiv;
597 int ret;
598 unsigned int mode;
599
600 printk("dt3k_ai_cmd:\n");
601 for (i = 0; i < cmd->chanlist_len; i++) {
602 chan = CR_CHAN(cmd->chanlist[i]);
603 range = CR_RANGE(cmd->chanlist[i]);
604
605 writew((range << 6) | chan,
606 devpriv->io_addr + DPR_ADC_buffer + i);
607 }
608 aref = CR_AREF(cmd->chanlist[0]);
609
610 writew(cmd->scan_end_arg, devpriv->io_addr + DPR_Params(0));
611 printk("param[0]=0x%04x\n", cmd->scan_end_arg);
612
613 if (cmd->convert_src == TRIG_TIMER) {
614 divider = dt3k_ns_to_timer(50, &cmd->convert_arg,
615 cmd->flags & TRIG_ROUND_MASK);
616 writew((divider >> 16), devpriv->io_addr + DPR_Params(1));
617 printk("param[1]=0x%04x\n", divider >> 16);
618 writew((divider & 0xffff), devpriv->io_addr + DPR_Params(2));
619 printk("param[2]=0x%04x\n", divider & 0xffff);
620 } else {
621 /* not supported */
622 }
623
624 if (cmd->scan_begin_src == TRIG_TIMER) {
625 tscandiv = dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
626 cmd->flags & TRIG_ROUND_MASK);
627 writew((tscandiv >> 16), devpriv->io_addr + DPR_Params(3));
628 printk("param[3]=0x%04x\n", tscandiv >> 16);
629 writew((tscandiv & 0xffff), devpriv->io_addr + DPR_Params(4));
630 printk("param[4]=0x%04x\n", tscandiv & 0xffff);
631 } else {
632 /* not supported */
633 }
634
635 mode = DT3000_AD_RETRIG_INTERNAL | 0 | 0;
636 writew(mode, devpriv->io_addr + DPR_Params(5));
637 printk("param[5]=0x%04x\n", mode);
638 writew(aref == AREF_DIFF, devpriv->io_addr + DPR_Params(6));
639 printk("param[6]=0x%04x\n", aref == AREF_DIFF);
640
641 writew(AI_FIFO_DEPTH / 2, devpriv->io_addr + DPR_Params(7));
642 printk("param[7]=0x%04x\n", AI_FIFO_DEPTH / 2);
643
644 writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
645 ret = dt3k_send_cmd(dev, CMD_CONFIG);
646
647 writew(DT3000_ADFULL | DT3000_ADSWERR | DT3000_ADHWERR,
648 devpriv->io_addr + DPR_Int_Mask);
649
650 debug_n_ints = 0;
651
652 writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
653 ret = dt3k_send_cmd(dev, CMD_START);
654
655 return 0;
656}
657
34c43922 658static int dt3k_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
9a21297d
DS
659{
660 int ret;
661
662 writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
663 ret = dt3k_send_cmd(dev, CMD_STOP);
664
665 writew(0, devpriv->io_addr + DPR_Int_Mask);
666
667 return 0;
668}
669
34c43922 670static int dt3k_ai_insn(struct comedi_device * dev, struct comedi_subdevice * s,
90035c08 671 struct comedi_insn * insn, unsigned int * data)
9a21297d
DS
672{
673 int i;
674 unsigned int chan, gain, aref;
675
676 chan = CR_CHAN(insn->chanspec);
677 gain = CR_RANGE(insn->chanspec);
678 /* XXX docs don't explain how to select aref */
679 aref = CR_AREF(insn->chanspec);
680
681 for (i = 0; i < insn->n; i++) {
682 data[i] = dt3k_readsingle(dev, SUBS_AI, chan, gain);
683 }
684
685 return i;
686}
687
34c43922 688static int dt3k_ao_insn(struct comedi_device * dev, struct comedi_subdevice * s,
90035c08 689 struct comedi_insn * insn, unsigned int * data)
9a21297d
DS
690{
691 int i;
692 unsigned int chan;
693
694 chan = CR_CHAN(insn->chanspec);
695 for (i = 0; i < insn->n; i++) {
696 dt3k_writesingle(dev, SUBS_AO, chan, data[i]);
697 devpriv->ao_readback[chan] = data[i];
698 }
699
700 return i;
701}
702
34c43922 703static int dt3k_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
90035c08 704 struct comedi_insn * insn, unsigned int * data)
9a21297d
DS
705{
706 int i;
707 unsigned int chan;
708
709 chan = CR_CHAN(insn->chanspec);
710 for (i = 0; i < insn->n; i++) {
711 data[i] = devpriv->ao_readback[chan];
712 }
713
714 return i;
715}
716
71b5f4f1 717static void dt3k_dio_config(struct comedi_device * dev, int bits)
9a21297d
DS
718{
719 /* XXX */
720 writew(SUBS_DOUT, devpriv->io_addr + DPR_SubSys);
721
722 writew(bits, devpriv->io_addr + DPR_Params(0));
723#if 0
724 /* don't know */
725 writew(0, devpriv->io_addr + DPR_Params(1));
726 writew(0, devpriv->io_addr + DPR_Params(2));
727#endif
728
729 dt3k_send_cmd(dev, CMD_CONFIG);
730}
731
34c43922 732static int dt3k_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
90035c08 733 struct comedi_insn * insn, unsigned int * data)
9a21297d
DS
734{
735 int mask;
736
737 mask = (CR_CHAN(insn->chanspec) < 4) ? 0x0f : 0xf0;
738
739 switch (data[0]) {
740 case INSN_CONFIG_DIO_OUTPUT:
741 s->io_bits |= mask;
742 break;
743 case INSN_CONFIG_DIO_INPUT:
744 s->io_bits &= ~mask;
745 break;
746 case INSN_CONFIG_DIO_QUERY:
747 data[1] =
748 (s->io_bits & (1 << CR_CHAN(insn->
749 chanspec))) ? COMEDI_OUTPUT :
750 COMEDI_INPUT;
751 return insn->n;
752 break;
753 default:
754 return -EINVAL;
755 break;
756 }
757 mask = (s->io_bits & 0x01) | ((s->io_bits & 0x10) >> 3);
758 dt3k_dio_config(dev, mask);
759
760 return insn->n;
761}
762
34c43922 763static int dt3k_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
90035c08 764 struct comedi_insn * insn, unsigned int * data)
9a21297d
DS
765{
766 if (insn->n != 2)
767 return -EINVAL;
768
769 if (data[0]) {
770 s->state &= ~data[0];
771 s->state |= data[1] & data[0];
772 dt3k_writesingle(dev, SUBS_DOUT, 0, s->state);
773 }
774 data[1] = dt3k_readsingle(dev, SUBS_DIN, 0, 0);
775
776 return 2;
777}
778
34c43922 779static int dt3k_mem_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
90035c08 780 struct comedi_insn * insn, unsigned int * data)
9a21297d
DS
781{
782 unsigned int addr = CR_CHAN(insn->chanspec);
783 int i;
784
785 for (i = 0; i < insn->n; i++) {
786 writew(SUBS_MEM, devpriv->io_addr + DPR_SubSys);
787 writew(addr, devpriv->io_addr + DPR_Params(0));
788 writew(1, devpriv->io_addr + DPR_Params(1));
789
790 dt3k_send_cmd(dev, CMD_READCODE);
791
792 data[i] = readw(devpriv->io_addr + DPR_Params(2));
793 }
794
795 return i;
796}
797
71b5f4f1 798static int dt_pci_probe(struct comedi_device * dev, int bus, int slot);
9a21297d 799
0707bb04 800static int dt3000_attach(struct comedi_device * dev, struct comedi_devconfig * it)
9a21297d 801{
34c43922 802 struct comedi_subdevice *s;
9a21297d
DS
803 int bus, slot;
804 int ret = 0;
805
806 printk("dt3000:");
807 bus = it->options[0];
808 slot = it->options[1];
809
810 if ((ret = alloc_private(dev, sizeof(dt3k_private))) < 0)
811 return ret;
812
813 ret = dt_pci_probe(dev, bus, slot);
814 if (ret < 0)
815 return ret;
816 if (ret == 0) {
817 printk(" no DT board found\n");
818 return -ENODEV;
819 }
820
821 dev->board_name = this_board->name;
822
823 if (comedi_request_irq(devpriv->pci_dev->irq, dt3k_interrupt,
824 IRQF_SHARED, "dt3000", dev)) {
825 printk(" unable to allocate IRQ %u\n", devpriv->pci_dev->irq);
826 return -EINVAL;
827 }
828 dev->irq = devpriv->pci_dev->irq;
829
830 if ((ret = alloc_subdevices(dev, 4)) < 0)
831 return ret;
832
833 s = dev->subdevices;
834 dev->read_subdev = s;
835
836 /* ai subdevice */
837 s->type = COMEDI_SUBD_AI;
838 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
839 s->n_chan = this_board->adchan;
840 s->insn_read = dt3k_ai_insn;
841 s->maxdata = (1 << this_board->adbits) - 1;
842 s->len_chanlist = 512;
843 s->range_table = &range_dt3000_ai; /* XXX */
844 s->do_cmd = dt3k_ai_cmd;
845 s->do_cmdtest = dt3k_ai_cmdtest;
846 s->cancel = dt3k_ai_cancel;
847
848 s++;
849 /* ao subsystem */
850 s->type = COMEDI_SUBD_AO;
851 s->subdev_flags = SDF_WRITABLE;
852 s->n_chan = 2;
853 s->insn_read = dt3k_ao_insn_read;
854 s->insn_write = dt3k_ao_insn;
855 s->maxdata = (1 << this_board->dabits) - 1;
856 s->len_chanlist = 1;
857 s->range_table = &range_bipolar10;
858
859 s++;
860 /* dio subsystem */
861 s->type = COMEDI_SUBD_DIO;
862 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
863 s->n_chan = 8;
864 s->insn_config = dt3k_dio_insn_config;
865 s->insn_bits = dt3k_dio_insn_bits;
866 s->maxdata = 1;
867 s->len_chanlist = 8;
868 s->range_table = &range_digital;
869
870 s++;
871 /* mem subsystem */
872 s->type = COMEDI_SUBD_MEMORY;
873 s->subdev_flags = SDF_READABLE;
874 s->n_chan = 0x1000;
875 s->insn_read = dt3k_mem_insn_read;
876 s->maxdata = 0xff;
877 s->len_chanlist = 1;
878 s->range_table = &range_unknown;
879
880#if 0
881 s++;
882 /* proc subsystem */
883 s->type = COMEDI_SUBD_PROC;
884#endif
885
886 return 0;
887}
888
71b5f4f1 889static int dt3000_detach(struct comedi_device * dev)
9a21297d
DS
890{
891 if (dev->irq)
892 comedi_free_irq(dev->irq, dev);
893
894 if (devpriv) {
895 if (devpriv->pci_dev) {
896 if (devpriv->phys_addr) {
897 comedi_pci_disable(devpriv->pci_dev);
898 }
899 pci_dev_put(devpriv->pci_dev);
900 }
901 if (devpriv->io_addr)
902 iounmap(devpriv->io_addr);
903 }
904 /* XXX */
905
906 return 0;
907}
908
909static struct pci_dev *dt_pci_find_device(struct pci_dev *from, int *board);
71b5f4f1 910static int setup_pci(struct comedi_device * dev);
9a21297d 911
71b5f4f1 912static int dt_pci_probe(struct comedi_device * dev, int bus, int slot)
9a21297d
DS
913{
914 int board;
915 int ret;
916 struct pci_dev *pcidev;
917
918 pcidev = NULL;
919 while ((pcidev = dt_pci_find_device(pcidev, &board)) != NULL) {
920 if ((bus == 0 && slot == 0) ||
921 (pcidev->bus->number == bus &&
922 PCI_SLOT(pcidev->devfn) == slot)) {
923 break;
924 }
925 }
926 devpriv->pci_dev = pcidev;
927
928 if (board >= 0)
929 dev->board_ptr = dt3k_boardtypes + board;
930
931 if (!devpriv->pci_dev)
932 return 0;
933
934 if ((ret = setup_pci(dev)) < 0)
935 return ret;
936
937 return 1;
938}
939
71b5f4f1 940static int setup_pci(struct comedi_device * dev)
9a21297d
DS
941{
942 resource_size_t addr;
943 int ret;
944
945 ret = comedi_pci_enable(devpriv->pci_dev, "dt3000");
946 if (ret < 0)
947 return ret;
948
949 addr = pci_resource_start(devpriv->pci_dev, 0);
950 devpriv->phys_addr = addr;
951 devpriv->io_addr = ioremap(devpriv->phys_addr, DT3000_SIZE);
952 if (!devpriv->io_addr)
953 return -ENOMEM;
954#if DEBUG
955 printk("0x%08llx mapped to %p, ",
956 (unsigned long long)devpriv->phys_addr, devpriv->io_addr);
957#endif
958
959 return 0;
960}
961
962static struct pci_dev *dt_pci_find_device(struct pci_dev *from, int *board)
963{
964 int i;
965
966 for (from = pci_get_device(PCI_VENDOR_ID_DT, PCI_ANY_ID, from);
967 from != NULL;
968 from = pci_get_device(PCI_VENDOR_ID_DT, PCI_ANY_ID, from)) {
969 for (i = 0; i < n_dt3k_boards; i++) {
970 if (from->device == dt3k_boardtypes[i].device_id) {
971 *board = i;
972 return from;
973 }
974 }
975 printk("unknown Data Translation PCI device found with device_id=0x%04x\n", from->device);
976 }
977 *board = -1;
978 return from;
979}