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
80 #include <linux/module.h>
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 {
136 void __iomem *iobase;
138 void __iomem *iobase;
139 const struct comedi_lrange *ao_range_list[2];
140 /* range of channels of ao module */
141 unsigned int last_data[2];
144 void __iomem *iobase;
151 struct pci20xxx_private {
152 void __iomem *ioaddr;
155 #define CHAN (CR_CHAN(it->chanlist[0]))
159 static int pci20006_insn_read(struct comedi_device *dev,
160 struct comedi_subdevice *s,
161 struct comedi_insn *insn, unsigned int *data);
162 static int pci20006_insn_write(struct comedi_device *dev,
163 struct comedi_subdevice *s,
164 struct comedi_insn *insn, unsigned int *data);
166 static const struct comedi_lrange *pci20006_range_list[] = {
172 static int pci20006_init(struct comedi_device *dev, struct comedi_subdevice *s,
175 union pci20xxx_subdev_private *sdp = s->private;
177 if (opt0 < 0 || opt0 > 2)
179 if (opt1 < 0 || opt1 > 2)
182 sdp->pci20006.ao_range_list[0] = pci20006_range_list[opt0];
183 sdp->pci20006.ao_range_list[1] = pci20006_range_list[opt1];
186 s->type = COMEDI_SUBD_AO;
187 s->subdev_flags = SDF_WRITABLE;
190 s->insn_read = pci20006_insn_read;
191 s->insn_write = pci20006_insn_write;
193 s->range_table_list = sdp->pci20006.ao_range_list;
197 static int pci20006_insn_read(struct comedi_device *dev,
198 struct comedi_subdevice *s,
199 struct comedi_insn *insn, unsigned int *data)
201 union pci20xxx_subdev_private *sdp = s->private;
203 data[0] = sdp->pci20006.last_data[CR_CHAN(insn->chanspec)];
208 static int pci20006_insn_write(struct comedi_device *dev,
209 struct comedi_subdevice *s,
210 struct comedi_insn *insn, unsigned int *data)
212 union pci20xxx_subdev_private *sdp = s->private;
214 unsigned int boarddata;
216 sdp->pci20006.last_data[CR_CHAN(insn->chanspec)] = data[0];
217 boarddata = (((unsigned int)data[0] + 0x8000) & 0xffff);
218 /* comedi-data -> board-data */
219 lo = (boarddata & 0xff);
220 hi = ((boarddata >> 8) & 0xff);
222 switch (CR_CHAN(insn->chanspec)) {
224 writeb(lo, sdp->iobase + PCI20006_LCHAN0);
225 writeb(hi, sdp->iobase + PCI20006_LCHAN0 + 1);
226 writeb(0x00, sdp->iobase + PCI20006_STROBE0);
229 writeb(lo, sdp->iobase + PCI20006_LCHAN1);
230 writeb(hi, sdp->iobase + PCI20006_LCHAN1 + 1);
231 writeb(0x00, sdp->iobase + PCI20006_STROBE1);
234 dev_warn(dev->class_dev, "ao channel Error!\n");
243 static int pci20341_insn_read(struct comedi_device *dev,
244 struct comedi_subdevice *s,
245 struct comedi_insn *insn, unsigned int *data);
247 static const int pci20341_timebase[] = { 0x00, 0x00, 0x00, 0x04 };
248 static const int pci20341_settling_time[] = { 0x58, 0x58, 0x93, 0x99 };
250 static const struct comedi_lrange range_bipolar0_5 = {
255 static const struct comedi_lrange range_bipolar0_05 = {
260 static const struct comedi_lrange range_bipolar0_025 = {
265 static const struct comedi_lrange *const pci20341_ranges[] = {
272 static int pci20341_init(struct comedi_device *dev, struct comedi_subdevice *s,
275 union pci20xxx_subdev_private *sdp = s->private;
278 /* options handling */
279 if (opt0 < 0 || opt0 > 3)
281 sdp->pci20341.timebase = pci20341_timebase[opt0];
282 sdp->pci20341.settling_time = pci20341_settling_time[opt0];
285 s->type = COMEDI_SUBD_AI;
286 s->subdev_flags = SDF_READABLE;
287 s->n_chan = PCI20341_CHAN_NR;
288 s->len_chanlist = PCI20341_SCANLIST;
289 s->insn_read = pci20341_insn_read;
291 s->range_table = pci20341_ranges[opt0];
293 /* depends on gain, trigger, repetition mode */
294 option = sdp->pci20341.timebase | PCI20341_REPMODE;
296 /* initialize Module */
297 writeb(PCI20341_INIT, sdp->iobase + PCI20341_CONFIG_REG);
299 writeb(PCI20341_PACER, sdp->iobase + PCI20341_MOD_STATUS);
300 /* option register */
301 writeb(option, sdp->iobase + PCI20341_OPT_REG);
302 /* settling time counter */
303 writeb(sdp->pci20341.settling_time,
304 sdp->iobase + PCI20341_SET_TIME_REG);
305 /* trigger not implemented */
309 static int pci20341_insn_read(struct comedi_device *dev,
310 struct comedi_subdevice *s,
311 struct comedi_insn *insn, unsigned int *data)
313 union pci20xxx_subdev_private *sdp = s->private;
314 unsigned int i = 0, j = 0;
316 unsigned char eoc; /* end of conversion */
317 unsigned int clb; /* channel list byte */
318 unsigned int boarddata;
320 /* write number of input channels */
321 writeb(1, sdp->iobase + PCI20341_LCHAN_ADDR_REG);
322 clb = PCI20341_DAISY_CHAIN | PCI20341_MUX | (sdp->pci20341.ai_gain << 3)
323 | CR_CHAN(insn->chanspec);
324 writeb(clb, sdp->iobase + PCI20341_CHAN_LIST);
326 /* reset settling time counter and trigger delay counter */
327 writeb(0x00, sdp->iobase + PCI20341_CC_RESET);
329 writeb(0x00, sdp->iobase + PCI20341_CHAN_RESET);
333 for (i = 0; i < insn->n; i++) {
334 /* data polling isn't the niciest way to get the data, I know,
335 * but there are only 6 cycles (mean) and it is easier than
336 * the whole interrupt stuff
340 readb(sdp->iobase + PCI20341_SOFT_PACER);
342 eoc = readb(sdp->iobase + PCI20341_STATUS_REG);
343 /* poll Interrupt Flag */
344 while ((eoc < 0x80) && j < 100) {
346 eoc = readb(sdp->iobase + PCI20341_STATUS_REG);
349 dev_warn(dev->class_dev,
350 "AI interrupt channel %i polling exit !\n", i);
353 lo = readb(sdp->iobase + PCI20341_LDATA);
354 hi = readb(sdp->iobase + PCI20341_LDATA + 1);
355 boarddata = lo + 0x100 * hi;
357 /* board-data -> comedi-data */
358 data[i] = (short)((boarddata + 0x8000) & 0xffff);
365 static void pci20xxx_do(struct comedi_device *dev, struct comedi_subdevice *s)
367 struct pci20xxx_private *devpriv = dev->private;
369 /* XXX if the channel is configured for input, does this
371 /* XXX it would be a good idea to only update the registers
372 that _need_ to be updated. This requires changes to
374 writeb((s->state >> 0) & 0xff, devpriv->ioaddr + PCI20000_DIO_0);
375 writeb((s->state >> 8) & 0xff, devpriv->ioaddr + PCI20000_DIO_1);
376 writeb((s->state >> 16) & 0xff, devpriv->ioaddr + PCI20000_DIO_2);
377 writeb((s->state >> 24) & 0xff, devpriv->ioaddr + PCI20000_DIO_3);
380 static unsigned int pci20xxx_di(struct comedi_device *dev,
381 struct comedi_subdevice *s)
383 struct pci20xxx_private *devpriv = dev->private;
386 /* XXX same note as above */
387 bits = readb(devpriv->ioaddr + PCI20000_DIO_0);
388 bits |= readb(devpriv->ioaddr + PCI20000_DIO_1) << 8;
389 bits |= readb(devpriv->ioaddr + PCI20000_DIO_2) << 16;
390 bits |= readb(devpriv->ioaddr + PCI20000_DIO_3) << 24;
396 static void pci20xxx_dio_config(struct comedi_device *dev,
397 struct comedi_subdevice *s)
399 struct pci20xxx_private *devpriv = dev->private;
400 unsigned char control_01;
401 unsigned char control_23;
402 unsigned char buffer;
404 control_01 = readb(devpriv->ioaddr + PCI20000_DIO_CONTROL_01);
405 control_23 = readb(devpriv->ioaddr + PCI20000_DIO_CONTROL_23);
406 buffer = readb(devpriv->ioaddr + PCI20000_DIO_BUFFER);
408 if (s->io_bits & 0x000000ff) {
410 control_01 &= PCI20000_DIO_EOC;
411 buffer = (buffer & (~(DIO_BE << DIO_PS_0))) | (DIO_BO <<
415 control_01 = (control_01 & DIO_CAND) | PCI20000_DIO_EIC;
416 buffer = (buffer & (~(DIO_BI << DIO_PS_0)));
418 if (s->io_bits & 0x0000ff00) {
420 control_01 &= PCI20000_DIO_OOC;
421 buffer = (buffer & (~(DIO_BE << DIO_PS_1))) | (DIO_BO <<
425 control_01 = (control_01 & DIO_CAND) | PCI20000_DIO_OIC;
426 buffer = (buffer & (~(DIO_BI << DIO_PS_1)));
428 if (s->io_bits & 0x00ff0000) {
430 control_23 &= PCI20000_DIO_EOC;
431 buffer = (buffer & (~(DIO_BE << DIO_PS_2))) | (DIO_BO <<
435 control_23 = (control_23 & DIO_CAND) | PCI20000_DIO_EIC;
436 buffer = (buffer & (~(DIO_BI << DIO_PS_2)));
438 if (s->io_bits & 0xff000000) {
440 control_23 &= PCI20000_DIO_OOC;
441 buffer = (buffer & (~(DIO_BE << DIO_PS_3))) | (DIO_BO <<
445 control_23 = (control_23 & DIO_CAND) | PCI20000_DIO_OIC;
446 buffer = (buffer & (~(DIO_BI << DIO_PS_3)));
448 writeb(control_01, devpriv->ioaddr + PCI20000_DIO_CONTROL_01);
449 writeb(control_23, devpriv->ioaddr + PCI20000_DIO_CONTROL_23);
450 writeb(buffer, devpriv->ioaddr + PCI20000_DIO_BUFFER);
453 static int pci20xxx_dio_insn_config(struct comedi_device *dev,
454 struct comedi_subdevice *s,
455 struct comedi_insn *insn,
460 mask = 1 << CR_CHAN(insn->chanspec);
461 if (mask & 0x000000ff)
463 else if (mask & 0x0000ff00)
465 else if (mask & 0x00ff0000)
473 pci20xxx_dio_config(dev, s);
478 static int pci20xxx_dio_insn_bits(struct comedi_device *dev,
479 struct comedi_subdevice *s,
480 struct comedi_insn *insn, unsigned int *data)
482 struct pci20xxx_private *devpriv = dev->private;
483 unsigned int mask = data[0];
486 s->state |= (mask & data[1]);
489 if (mask & 0x000000ff)
490 writeb((s->state >> 0) & 0xff,
491 devpriv->ioaddr + PCI20000_DIO_0);
492 if (mask & 0x0000ff00)
493 writeb((s->state >> 8) & 0xff,
494 devpriv->ioaddr + PCI20000_DIO_1);
495 if (mask & 0x00ff0000)
496 writeb((s->state >> 16) & 0xff,
497 devpriv->ioaddr + PCI20000_DIO_2);
498 if (mask & 0xff000000)
499 writeb((s->state >> 24) & 0xff,
500 devpriv->ioaddr + PCI20000_DIO_3);
502 data[1] = readb(devpriv->ioaddr + PCI20000_DIO_0);
503 data[1] |= readb(devpriv->ioaddr + PCI20000_DIO_1) << 8;
504 data[1] |= readb(devpriv->ioaddr + PCI20000_DIO_2) << 16;
505 data[1] |= readb(devpriv->ioaddr + PCI20000_DIO_3) << 24;
510 static int pci20xxx_dio_init(struct comedi_device *dev,
511 struct comedi_subdevice *s)
513 s->type = COMEDI_SUBD_DIO;
514 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
516 s->insn_bits = pci20xxx_dio_insn_bits;
517 s->insn_config = pci20xxx_dio_insn_config;
519 s->len_chanlist = 32;
520 s->range_table = &range_digital;
523 /* digital I/O lines default to input on board reset. */
524 pci20xxx_dio_config(dev, s);
529 static int pci20xxx_attach(struct comedi_device *dev,
530 struct comedi_devconfig *it)
532 struct pci20xxx_private *devpriv;
536 struct comedi_subdevice *s;
537 union pci20xxx_subdev_private *sdp;
539 ret = comedi_alloc_subdevices(dev, 1 + PCI20000_MODULES);
543 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
547 devpriv->ioaddr = (void __iomem *)(unsigned long)it->options[0];
549 /* Check PCI-20001 C-2A Carrier Board ID */
550 if ((readb(devpriv->ioaddr) & PCI20000_ID) != PCI20000_ID) {
551 dev_warn(dev->class_dev,
552 "PCI-20001 C-2A Carrier Board at base=0x%p not found !\n",
556 dev_info(dev->class_dev, "PCI-20001 C-2A at base=0x%p\n",
559 for (i = 0; i < PCI20000_MODULES; i++) {
560 s = &dev->subdevices[i];
561 sdp = comedi_alloc_spriv(s, sizeof(*sdp));
564 id = readb(devpriv->ioaddr + (i + 1) * PCI20000_OFFSET);
567 sdp->pci20006.iobase =
568 devpriv->ioaddr + (i + 1) * PCI20000_OFFSET;
569 pci20006_init(dev, s, it->options[2 * i + 2],
570 it->options[2 * i + 3]);
571 dev_info(dev->class_dev,
572 "PCI-20006 module in slot %d\n", i + 1);
575 sdp->pci20341.iobase =
576 devpriv->ioaddr + (i + 1) * PCI20000_OFFSET;
577 pci20341_init(dev, s, it->options[2 * i + 2],
578 it->options[2 * i + 3]);
579 dev_info(dev->class_dev,
580 "PCI-20341 module in slot %d\n", i + 1);
583 dev_warn(dev->class_dev,
584 "unknown module code 0x%02x in slot %d: module disabled\n",
585 id, i); /* XXX this looks like a bug! i + 1 ?? */
587 case PCI20xxx_EMPTY_ID:
588 s->type = COMEDI_SUBD_UNUSED;
593 /* initialize struct pci20xxx_private */
594 pci20xxx_dio_init(dev, &dev->subdevices[PCI20000_MODULES]);
599 static void pci20xxx_detach(struct comedi_device *dev)
601 /* Nothing to cleanup */
604 static struct comedi_driver pci20xxx_driver = {
605 .driver_name = "ii_pci20kc",
606 .module = THIS_MODULE,
607 .attach = pci20xxx_attach,
608 .detach = pci20xxx_detach,
610 module_comedi_driver(pci20xxx_driver);
612 MODULE_AUTHOR("Comedi http://www.comedi.org");
613 MODULE_DESCRIPTION("Comedi low-level driver");
614 MODULE_LICENSE("GPL");