2 * comedi/drivers/ii_pci20kc.c
3 * Driver for Intelligent Instruments PCI-20001C carrier board
6 * Copyright (C) 2000 Markus Kempf <kempf@matsci.uni-sb.de>
7 * with suggestions from David Schleef
10 * Linux device driver for COMEDI
11 * Intelligent Instrumentation
12 * PCI-20001 C-2A Carrier Board
13 * PCI-20341 M-1A 16-Bit analog input module
17 * PCI-20006 M-2 16-Bit analog output module
18 * - ranges (-10V - +10V) (0V - +10V) (-5V - +5V)
21 * only ONE PCI-20341 module possible
22 * only ONE PCI-20006 module possible
23 * no extern trigger implemented
25 * NOT WORKING (but soon) only 4 on-board differential channels supported
26 * NOT WORKING (but soon) only ONE di-port and ONE do-port supported
27 * instead of 4 digital ports
31 * The state of this driver is only a starting point for a complete
32 * COMEDI-driver. The final driver should support all features of the
33 * carrier board and modules.
35 * The test configuration:
37 * kernel 2.2.14 with RTAI v1.2 and patch-2.2.14rthal2
44 Description: Intelligent Instruments PCI-20001C carrier board
45 Author: Markus Kempf <kempf@matsci.uni-sb.de>
46 Devices: [Intelligent Instrumentation] PCI-20001C (ii_pci20kc)
49 Supports the PCI-20001 C-2a Carrier board, and could probably support
50 the other carrier boards with small modifications. Modules supported
52 PCI-20006 M-2 16-bit analog output module
53 PCI-20341 M-1A 16-bit analog input module
58 2 first option for module 1
59 3 second option for module 1
60 4 first option for module 2
61 5 second option for module 2
62 6 first option for module 3
63 7 second option for module 3
65 options for PCI-20006M:
66 first: Analog output channel 0 range configuration
67 0 bipolar 10 (-10V -- +10V)
68 1 unipolar 10 (0V -- +10V)
69 2 bipolar 5 (-5V -- 5V)
70 second: Analog output channel 1 range configuration
72 options for PCI-20341M:
73 first: Analog input gain configuration
81 #include "../comedidev.h"
83 #define PCI20000_ID 0x1d
84 #define PCI20341_ID 0x77
85 #define PCI20006_ID 0xe3
86 #define PCI20xxx_EMPTY_ID 0xff
88 #define PCI20000_OFFSET 0x100
89 #define PCI20000_MODULES 3
91 #define PCI20000_DIO_0 0x80
92 #define PCI20000_DIO_1 0x81
93 #define PCI20000_DIO_2 0xc0
94 #define PCI20000_DIO_3 0xc1
95 #define PCI20000_DIO_CONTROL_01 0x83 /* port 0, 1 control */
96 #define PCI20000_DIO_CONTROL_23 0xc3 /* port 2, 3 control */
97 #define PCI20000_DIO_BUFFER 0x82 /* buffer direction & enable */
98 #define PCI20000_DIO_EOC 0xef /* even port, control output */
99 #define PCI20000_DIO_OOC 0xfd /* odd port, control output */
100 #define PCI20000_DIO_EIC 0x90 /* even port, control input */
101 #define PCI20000_DIO_OIC 0x82 /* odd port, control input */
102 #define DIO_CAND 0x12 /* and bit 1 & 4 of control */
103 #define DIO_BE 0x01 /* buffer: port enable */
104 #define DIO_BO 0x04 /* buffer: output */
105 #define DIO_BI 0x05 /* buffer: input */
106 #define DIO_PS_0 0x00 /* buffer: port shift 0 */
107 #define DIO_PS_1 0x01 /* buffer: port shift 1 */
108 #define DIO_PS_2 0x04 /* buffer: port shift 2 */
109 #define DIO_PS_3 0x05 /* buffer: port shift 3 */
111 #define PCI20006_LCHAN0 0x0d
112 #define PCI20006_STROBE0 0x0b
113 #define PCI20006_LCHAN1 0x15
114 #define PCI20006_STROBE1 0x13
116 #define PCI20341_INIT 0x04
117 #define PCI20341_REPMODE 0x00 /* single shot mode */
118 #define PCI20341_PACER 0x00 /* Hardware Pacer disabled */
119 #define PCI20341_CHAN_NR 0x04 /* number of input channels */
120 #define PCI20341_CONFIG_REG 0x10
121 #define PCI20341_MOD_STATUS 0x01
122 #define PCI20341_OPT_REG 0x11
123 #define PCI20341_SET_TIME_REG 0x15
124 #define PCI20341_LCHAN_ADDR_REG 0x13
125 #define PCI20341_CHAN_LIST 0x80
126 #define PCI20341_CC_RESET 0x1b
127 #define PCI20341_CHAN_RESET 0x19
128 #define PCI20341_SOFT_PACER 0x04
129 #define PCI20341_STATUS_REG 0x12
130 #define PCI20341_LDATA 0x02
131 #define PCI20341_DAISY_CHAIN 0x20 /* On-board inputs only */
132 #define PCI20341_MUX 0x04 /* Enable on-board MUX */
133 #define PCI20341_SCANLIST 0x80 /* Channel/Gain Scan List */
135 union pci20xxx_subdev_private
{
139 const struct comedi_lrange
*ao_range_list
[2];
140 /* range of channels of ao module */
141 unsigned int last_data
[2];
151 struct pci20xxx_private
{
154 union pci20xxx_subdev_private subdev_private
[PCI20000_MODULES
];
157 #define devpriv ((struct pci20xxx_private *)dev->private)
158 #define CHAN (CR_CHAN(it->chanlist[0]))
160 static int pci20xxx_attach(struct comedi_device
*dev
,
161 struct comedi_devconfig
*it
);
162 static int pci20xxx_detach(struct comedi_device
*dev
);
164 static struct comedi_driver driver_pci20xxx
= {
165 .driver_name
= "ii_pci20kc",
166 .module
= THIS_MODULE
,
167 .attach
= pci20xxx_attach
,
168 .detach
= pci20xxx_detach
,
171 static int pci20006_init(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
173 static int pci20341_init(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
175 static int pci20xxx_dio_init(struct comedi_device
*dev
,
176 struct comedi_subdevice
*s
);
179 options[0] Board base address
181 options[2] first option for module 1
182 options[3] second option for module 1
183 options[4] first option for module 2
184 options[5] second option for module 2
185 options[6] first option for module 3
186 options[7] second option for module 3
188 options for PCI-20341M:
189 first Analog input gain configuration
195 options for PCI-20006M:
196 first Analog output channel 0 range configuration
197 0 == bipolar 10 (-10V -- +10V)
198 1 == unipolar 10V (0V -- +10V)
199 2 == bipolar 5V (-5V -- +5V)
200 second Analog output channel 1 range configuration
201 0 == bipolar 10 (-10V -- +10V)
202 1 == unipolar 10V (0V -- +10V)
203 2 == bipolar 5V (-5V -- +5V)
205 static int pci20xxx_attach(struct comedi_device
*dev
,
206 struct comedi_devconfig
*it
)
211 struct comedi_subdevice
*s
;
212 union pci20xxx_subdev_private
*sdp
;
214 ret
= alloc_subdevices(dev
, 1 + PCI20000_MODULES
);
218 ret
= alloc_private(dev
, sizeof(struct pci20xxx_private
));
222 devpriv
->ioaddr
= (void *)(unsigned long)it
->options
[0];
223 dev
->board_name
= "pci20kc";
225 /* Check PCI-20001 C-2A Carrier Board ID */
226 if ((readb(devpriv
->ioaddr
) & PCI20000_ID
) != PCI20000_ID
) {
227 printk(KERN_WARNING
"comedi%d: ii_pci20kc PCI-20001"
228 " C-2A Carrier Board at base=0x%p not found !\n",
229 dev
->minor
, devpriv
->ioaddr
);
232 printk(KERN_INFO
"comedi%d: ii_pci20kc: PCI-20001 C-2A at base=0x%p\n",
233 dev
->minor
, devpriv
->ioaddr
);
235 for (i
= 0; i
< PCI20000_MODULES
; i
++) {
236 s
= dev
->subdevices
+ i
;
237 id
= readb(devpriv
->ioaddr
+ (i
+ 1) * PCI20000_OFFSET
);
238 s
->private = devpriv
->subdev_private
+ i
;
242 sdp
->pci20006
.iobase
=
243 devpriv
->ioaddr
+ (i
+ 1) * PCI20000_OFFSET
;
244 pci20006_init(dev
, s
, it
->options
[2 * i
+ 2],
245 it
->options
[2 * i
+ 3]);
246 printk(KERN_INFO
"comedi%d: "
247 "ii_pci20kc PCI-20006 module in slot %d \n",
251 sdp
->pci20341
.iobase
=
252 devpriv
->ioaddr
+ (i
+ 1) * PCI20000_OFFSET
;
253 pci20341_init(dev
, s
, it
->options
[2 * i
+ 2],
254 it
->options
[2 * i
+ 3]);
255 printk(KERN_INFO
"comedi%d: "
256 "ii_pci20kc PCI-20341 module in slot %d \n",
260 printk(KERN_WARNING
"ii_pci20kc: unknown module "
261 "code 0x%02x in slot %d: module disabled\n",
264 case PCI20xxx_EMPTY_ID
:
265 s
->type
= COMEDI_SUBD_UNUSED
;
270 /* initialize struct pci20xxx_private */
271 pci20xxx_dio_init(dev
, dev
->subdevices
+ PCI20000_MODULES
);
276 static int pci20xxx_detach(struct comedi_device
*dev
)
278 printk(KERN_INFO
"comedi%d: pci20xxx: remove\n", dev
->minor
);
285 static int pci20006_insn_read(struct comedi_device
*dev
,
286 struct comedi_subdevice
*s
,
287 struct comedi_insn
*insn
, unsigned int *data
);
288 static int pci20006_insn_write(struct comedi_device
*dev
,
289 struct comedi_subdevice
*s
,
290 struct comedi_insn
*insn
, unsigned int *data
);
292 static const struct comedi_lrange
*pci20006_range_list
[] = {
298 static int pci20006_init(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
301 union pci20xxx_subdev_private
*sdp
= s
->private;
303 if (opt0
< 0 || opt0
> 2)
305 if (opt1
< 0 || opt1
> 2)
308 sdp
->pci20006
.ao_range_list
[0] = pci20006_range_list
[opt0
];
309 sdp
->pci20006
.ao_range_list
[1] = pci20006_range_list
[opt1
];
312 s
->type
= COMEDI_SUBD_AO
;
313 s
->subdev_flags
= SDF_WRITABLE
;
316 s
->insn_read
= pci20006_insn_read
;
317 s
->insn_write
= pci20006_insn_write
;
319 s
->range_table_list
= sdp
->pci20006
.ao_range_list
;
323 static int pci20006_insn_read(struct comedi_device
*dev
,
324 struct comedi_subdevice
*s
,
325 struct comedi_insn
*insn
, unsigned int *data
)
327 union pci20xxx_subdev_private
*sdp
= s
->private;
329 data
[0] = sdp
->pci20006
.last_data
[CR_CHAN(insn
->chanspec
)];
334 static int pci20006_insn_write(struct comedi_device
*dev
,
335 struct comedi_subdevice
*s
,
336 struct comedi_insn
*insn
, unsigned int *data
)
338 union pci20xxx_subdev_private
*sdp
= s
->private;
340 unsigned int boarddata
;
342 sdp
->pci20006
.last_data
[CR_CHAN(insn
->chanspec
)] = data
[0];
343 boarddata
= (((unsigned int)data
[0] + 0x8000) & 0xffff);
344 /* comedi-data -> board-data */
345 lo
= (boarddata
& 0xff);
346 hi
= ((boarddata
>> 8) & 0xff);
348 switch (CR_CHAN(insn
->chanspec
)) {
350 writeb(lo
, sdp
->iobase
+ PCI20006_LCHAN0
);
351 writeb(hi
, sdp
->iobase
+ PCI20006_LCHAN0
+ 1);
352 writeb(0x00, sdp
->iobase
+ PCI20006_STROBE0
);
355 writeb(lo
, sdp
->iobase
+ PCI20006_LCHAN1
);
356 writeb(hi
, sdp
->iobase
+ PCI20006_LCHAN1
+ 1);
357 writeb(0x00, sdp
->iobase
+ PCI20006_STROBE1
);
361 " comedi%d: pci20xxx: ao channel Error!\n", dev
->minor
);
370 static int pci20341_insn_read(struct comedi_device
*dev
,
371 struct comedi_subdevice
*s
,
372 struct comedi_insn
*insn
, unsigned int *data
);
374 static const int pci20341_timebase
[] = { 0x00, 0x00, 0x00, 0x04 };
375 static const int pci20341_settling_time
[] = { 0x58, 0x58, 0x93, 0x99 };
377 static const struct comedi_lrange range_bipolar0_5
= { 1, {BIP_RANGE(0.5)} };
378 static const struct comedi_lrange range_bipolar0_05
= { 1, {BIP_RANGE(0.05)} };
379 static const struct comedi_lrange range_bipolar0_025
= { 1, {BIP_RANGE(0.025)} };
381 static const struct comedi_lrange
*const pci20341_ranges
[] = {
388 static int pci20341_init(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
391 union pci20xxx_subdev_private
*sdp
= s
->private;
394 /* options handling */
395 if (opt0
< 0 || opt0
> 3)
397 sdp
->pci20341
.timebase
= pci20341_timebase
[opt0
];
398 sdp
->pci20341
.settling_time
= pci20341_settling_time
[opt0
];
401 s
->type
= COMEDI_SUBD_AI
;
402 s
->subdev_flags
= SDF_READABLE
;
403 s
->n_chan
= PCI20341_CHAN_NR
;
404 s
->len_chanlist
= PCI20341_SCANLIST
;
405 s
->insn_read
= pci20341_insn_read
;
407 s
->range_table
= pci20341_ranges
[opt0
];
409 option
= sdp
->pci20341
.timebase
| PCI20341_REPMODE
; /* depends on gain, trigger, repetition mode */
411 writeb(PCI20341_INIT
, sdp
->iobase
+ PCI20341_CONFIG_REG
); /* initialize Module */
412 writeb(PCI20341_PACER
, sdp
->iobase
+ PCI20341_MOD_STATUS
); /* set Pacer */
413 writeb(option
, sdp
->iobase
+ PCI20341_OPT_REG
); /* option register */
414 writeb(sdp
->pci20341
.settling_time
, sdp
->iobase
+ PCI20341_SET_TIME_REG
); /* settling time counter */
415 /* trigger not implemented */
419 static int pci20341_insn_read(struct comedi_device
*dev
,
420 struct comedi_subdevice
*s
,
421 struct comedi_insn
*insn
, unsigned int *data
)
423 union pci20xxx_subdev_private
*sdp
= s
->private;
424 unsigned int i
= 0, j
= 0;
426 unsigned char eoc
; /* end of conversion */
427 unsigned int clb
; /* channel list byte */
428 unsigned int boarddata
;
430 writeb(1, sdp
->iobase
+ PCI20341_LCHAN_ADDR_REG
); /* write number of input channels */
431 clb
= PCI20341_DAISY_CHAIN
| PCI20341_MUX
| (sdp
->pci20341
.ai_gain
<< 3)
432 | CR_CHAN(insn
->chanspec
);
433 writeb(clb
, sdp
->iobase
+ PCI20341_CHAN_LIST
);
434 writeb(0x00, sdp
->iobase
+ PCI20341_CC_RESET
); /* reset settling time counter and trigger delay counter */
435 writeb(0x00, sdp
->iobase
+ PCI20341_CHAN_RESET
);
439 for (i
= 0; i
< insn
->n
; i
++) {
440 /* data polling isn't the niciest way to get the data, I know,
441 * but there are only 6 cycles (mean) and it is easier than
442 * the whole interrupt stuff
445 readb(sdp
->iobase
+ PCI20341_SOFT_PACER
); /* generate Pacer */
446 eoc
= readb(sdp
->iobase
+ PCI20341_STATUS_REG
);
447 while ((eoc
< 0x80) && j
< 100) { /* poll Interrupt Flag */
449 eoc
= readb(sdp
->iobase
+ PCI20341_STATUS_REG
);
453 "comedi%d: pci20xxx: "
454 "AI interrupt channel %i polling exit !\n",
458 lo
= readb(sdp
->iobase
+ PCI20341_LDATA
);
459 hi
= readb(sdp
->iobase
+ PCI20341_LDATA
+ 1);
460 boarddata
= lo
+ 0x100 * hi
;
461 data
[i
] = (short)((boarddata
+ 0x8000) & 0xffff); /* board-data -> comedi-data */
469 static void pci20xxx_dio_config(struct comedi_device
*dev
,
470 struct comedi_subdevice
*s
);
471 static int pci20xxx_dio_insn_bits(struct comedi_device
*dev
,
472 struct comedi_subdevice
*s
,
473 struct comedi_insn
*insn
, unsigned int *data
);
474 static int pci20xxx_dio_insn_config(struct comedi_device
*dev
,
475 struct comedi_subdevice
*s
,
476 struct comedi_insn
*insn
,
479 /* initialize struct pci20xxx_private */
480 static int pci20xxx_dio_init(struct comedi_device
*dev
,
481 struct comedi_subdevice
*s
)
484 s
->type
= COMEDI_SUBD_DIO
;
485 s
->subdev_flags
= SDF_READABLE
| SDF_WRITABLE
;
487 s
->insn_bits
= pci20xxx_dio_insn_bits
;
488 s
->insn_config
= pci20xxx_dio_insn_config
;
490 s
->len_chanlist
= 32;
491 s
->range_table
= &range_digital
;
494 /* digital I/O lines default to input on board reset. */
495 pci20xxx_dio_config(dev
, s
);
500 static int pci20xxx_dio_insn_config(struct comedi_device
*dev
,
501 struct comedi_subdevice
*s
,
502 struct comedi_insn
*insn
,
507 mask
= 1 << CR_CHAN(insn
->chanspec
);
508 if (mask
& 0x000000ff)
510 else if (mask
& 0x0000ff00)
512 else if (mask
& 0x00ff0000)
520 pci20xxx_dio_config(dev
, s
);
525 static int pci20xxx_dio_insn_bits(struct comedi_device
*dev
,
526 struct comedi_subdevice
*s
,
527 struct comedi_insn
*insn
, unsigned int *data
)
529 unsigned int mask
= data
[0];
532 s
->state
|= (mask
& data
[1]);
535 if (mask
& 0x000000ff)
536 writeb((s
->state
>> 0) & 0xff,
537 devpriv
->ioaddr
+ PCI20000_DIO_0
);
538 if (mask
& 0x0000ff00)
539 writeb((s
->state
>> 8) & 0xff,
540 devpriv
->ioaddr
+ PCI20000_DIO_1
);
541 if (mask
& 0x00ff0000)
542 writeb((s
->state
>> 16) & 0xff,
543 devpriv
->ioaddr
+ PCI20000_DIO_2
);
544 if (mask
& 0xff000000)
545 writeb((s
->state
>> 24) & 0xff,
546 devpriv
->ioaddr
+ PCI20000_DIO_3
);
548 data
[1] = readb(devpriv
->ioaddr
+ PCI20000_DIO_0
);
549 data
[1] |= readb(devpriv
->ioaddr
+ PCI20000_DIO_1
) << 8;
550 data
[1] |= readb(devpriv
->ioaddr
+ PCI20000_DIO_2
) << 16;
551 data
[1] |= readb(devpriv
->ioaddr
+ PCI20000_DIO_3
) << 24;
556 static void pci20xxx_dio_config(struct comedi_device
*dev
,
557 struct comedi_subdevice
*s
)
559 unsigned char control_01
;
560 unsigned char control_23
;
561 unsigned char buffer
;
563 control_01
= readb(devpriv
->ioaddr
+ PCI20000_DIO_CONTROL_01
);
564 control_23
= readb(devpriv
->ioaddr
+ PCI20000_DIO_CONTROL_23
);
565 buffer
= readb(devpriv
->ioaddr
+ PCI20000_DIO_BUFFER
);
567 if (s
->io_bits
& 0x000000ff) {
569 control_01
&= PCI20000_DIO_EOC
;
570 buffer
= (buffer
& (~(DIO_BE
<< DIO_PS_0
))) | (DIO_BO
<<
574 control_01
= (control_01
& DIO_CAND
) | PCI20000_DIO_EIC
;
575 buffer
= (buffer
& (~(DIO_BI
<< DIO_PS_0
)));
577 if (s
->io_bits
& 0x0000ff00) {
579 control_01
&= PCI20000_DIO_OOC
;
580 buffer
= (buffer
& (~(DIO_BE
<< DIO_PS_1
))) | (DIO_BO
<<
584 control_01
= (control_01
& DIO_CAND
) | PCI20000_DIO_OIC
;
585 buffer
= (buffer
& (~(DIO_BI
<< DIO_PS_1
)));
587 if (s
->io_bits
& 0x00ff0000) {
589 control_23
&= PCI20000_DIO_EOC
;
590 buffer
= (buffer
& (~(DIO_BE
<< DIO_PS_2
))) | (DIO_BO
<<
594 control_23
= (control_23
& DIO_CAND
) | PCI20000_DIO_EIC
;
595 buffer
= (buffer
& (~(DIO_BI
<< DIO_PS_2
)));
597 if (s
->io_bits
& 0xff000000) {
599 control_23
&= PCI20000_DIO_OOC
;
600 buffer
= (buffer
& (~(DIO_BE
<< DIO_PS_3
))) | (DIO_BO
<<
604 control_23
= (control_23
& DIO_CAND
) | PCI20000_DIO_OIC
;
605 buffer
= (buffer
& (~(DIO_BI
<< DIO_PS_3
)));
607 writeb(control_01
, devpriv
->ioaddr
+ PCI20000_DIO_CONTROL_01
);
608 writeb(control_23
, devpriv
->ioaddr
+ PCI20000_DIO_CONTROL_23
);
609 writeb(buffer
, devpriv
->ioaddr
+ PCI20000_DIO_BUFFER
);
613 static int __init
driver_pci20xxx_init_module(void)
615 return comedi_driver_register(&driver_pci20xxx
);
618 static void __exit
driver_pci20xxx_cleanup_module(void)
620 comedi_driver_unregister(&driver_pci20xxx
);
623 module_init(driver_pci20xxx_init_module
);
624 module_exit(driver_pci20xxx_cleanup_module
);
626 MODULE_AUTHOR("Comedi http://www.comedi.org");
627 MODULE_DESCRIPTION("Comedi low-level driver");
628 MODULE_LICENSE("GPL");