3 * Driver for Intelligent Instruments PCI-20001C carrier board and modules.
5 * Copyright (C) 2000 Markus Kempf <kempf@matsci.uni-sb.de>
6 * with suggestions from David Schleef 16.06.2000
11 * Description: Intelligent Instruments PCI-20001C carrier board
12 * Devices: (Intelligent Instrumentation) PCI-20001C [ii_pci20kc]
13 * Author: Markus Kempf <kempf@matsci.uni-sb.de>
16 * Supports the PCI-20001C-1a and PCI-20001C-2a carrier boards. The
17 * -2a version has 32 on-board DIO channels. Three add-on modules
18 * can be added to the carrier board for additional functionality.
20 * Supported add-on modules:
21 * PCI-20006M-1 1 channel, 16-bit analog output module
22 * PCI-20006M-2 2 channel, 16-bit analog output module
23 * PCI-20341M-1A 4 channel, 16-bit analog input module
26 * 0 Board base address
30 #include <linux/module.h>
31 #include "../comedidev.h"
36 #define II20K_MOD_OFFSET 0x100
37 #define II20K_ID_REG 0x00
38 #define II20K_ID_MOD1_EMPTY (1 << 7)
39 #define II20K_ID_MOD2_EMPTY (1 << 6)
40 #define II20K_ID_MOD3_EMPTY (1 << 5)
41 #define II20K_ID_MASK 0x1f
42 #define II20K_ID_PCI20001C_1A 0x1b /* no on-board DIO */
43 #define II20K_ID_PCI20001C_2A 0x1d /* on-board DIO */
44 #define II20K_MOD_STATUS_REG 0x40
45 #define II20K_MOD_STATUS_IRQ_MOD1 (1 << 7)
46 #define II20K_MOD_STATUS_IRQ_MOD2 (1 << 6)
47 #define II20K_MOD_STATUS_IRQ_MOD3 (1 << 5)
48 #define II20K_DIO0_REG 0x80
49 #define II20K_DIO1_REG 0x81
50 #define II20K_DIR_ENA_REG 0x82
51 #define II20K_DIR_DIO3_OUT (1 << 7)
52 #define II20K_DIR_DIO2_OUT (1 << 6)
53 #define II20K_BUF_DISAB_DIO3 (1 << 5)
54 #define II20K_BUF_DISAB_DIO2 (1 << 4)
55 #define II20K_DIR_DIO1_OUT (1 << 3)
56 #define II20K_DIR_DIO0_OUT (1 << 2)
57 #define II20K_BUF_DISAB_DIO1 (1 << 1)
58 #define II20K_BUF_DISAB_DIO0 (1 << 0)
59 #define II20K_CTRL01_REG 0x83
60 #define II20K_CTRL01_SET (1 << 7)
61 #define II20K_CTRL01_DIO0_IN (1 << 4)
62 #define II20K_CTRL01_DIO1_IN (1 << 1)
63 #define II20K_DIO2_REG 0xc0
64 #define II20K_DIO3_REG 0xc1
65 #define II20K_CTRL23_REG 0xc3
66 #define II20K_CTRL23_SET (1 << 7)
67 #define II20K_CTRL23_DIO2_IN (1 << 4)
68 #define II20K_CTRL23_DIO3_IN (1 << 1)
70 #define II20K_ID_PCI20006M_1 0xe2 /* 1 AO channels */
71 #define II20K_ID_PCI20006M_2 0xe3 /* 2 AO channels */
72 #define II20K_AO_STRB_REG(x) (0x0b + ((x) * 0x08))
73 #define II20K_AO_LSB_REG(x) (0x0d + ((x) * 0x08))
74 #define II20K_AO_MSB_REG(x) (0x0e + ((x) * 0x08))
75 #define II20K_AO_STRB_BOTH_REG 0x1b
77 #define II20K_ID_PCI20341M_1 0x77 /* 4 AI channels */
78 #define II20K_AI_STATUS_CMD_REG 0x01
79 #define II20K_AI_STATUS_CMD_BUSY (1 << 7)
80 #define II20K_AI_STATUS_CMD_HW_ENA (1 << 1)
81 #define II20K_AI_STATUS_CMD_EXT_START (1 << 0)
82 #define II20K_AI_LSB_REG 0x02
83 #define II20K_AI_MSB_REG 0x03
84 #define II20K_AI_PACER_RESET_REG 0x04
85 #define II20K_AI_16BIT_DATA_REG 0x06
86 #define II20K_AI_CONF_REG 0x10
87 #define II20K_AI_CONF_ENA (1 << 2)
88 #define II20K_AI_OPT_REG 0x11
89 #define II20K_AI_OPT_TRIG_ENA (1 << 5)
90 #define II20K_AI_OPT_TRIG_INV (1 << 4)
91 #define II20K_AI_OPT_TIMEBASE(x) (((x) & 0x3) << 1)
92 #define II20K_AI_OPT_BURST_MODE (1 << 0)
93 #define II20K_AI_STATUS_REG 0x12
94 #define II20K_AI_STATUS_INT (1 << 7)
95 #define II20K_AI_STATUS_TRIG (1 << 6)
96 #define II20K_AI_STATUS_TRIG_ENA (1 << 5)
97 #define II20K_AI_STATUS_PACER_ERR (1 << 2)
98 #define II20K_AI_STATUS_DATA_ERR (1 << 1)
99 #define II20K_AI_STATUS_SET_TIME_ERR (1 << 0)
100 #define II20K_AI_LAST_CHAN_ADDR_REG 0x13
101 #define II20K_AI_CUR_ADDR_REG 0x14
102 #define II20K_AI_SET_TIME_REG 0x15
103 #define II20K_AI_DELAY_LSB_REG 0x16
104 #define II20K_AI_DELAY_MSB_REG 0x17
105 #define II20K_AI_CHAN_ADV_REG 0x18
106 #define II20K_AI_CHAN_RESET_REG 0x19
107 #define II20K_AI_START_TRIG_REG 0x1a
108 #define II20K_AI_COUNT_RESET_REG 0x1b
109 #define II20K_AI_CHANLIST_REG 0x80
110 #define II20K_AI_CHANLIST_ONBOARD_ONLY (1 << 5)
111 #define II20K_AI_CHANLIST_GAIN(x) (((x) & 0x3) << 3)
112 #define II20K_AI_CHANLIST_MUX_ENA (1 << 2)
113 #define II20K_AI_CHANLIST_CHAN(x) (((x) & 0x3) << 0)
114 #define II20K_AI_CHANLIST_LEN 0x80
116 /* the AO range is set by jumpers on the 20006M module */
117 static const struct comedi_lrange ii20k_ao_ranges = {
119 BIP_RANGE(5), /* Chan 0 - W1/W3 in Chan 1 - W2/W4 in */
120 UNI_RANGE(10), /* Chan 0 - W1/W3 out Chan 1 - W2/W4 in */
121 BIP_RANGE(10) /* Chan 0 - W1/W3 in Chan 1 - W2/W4 out */
125 static const struct comedi_lrange ii20k_ai_ranges = {
127 BIP_RANGE(5), /* gain 1 */
128 BIP_RANGE(0.5), /* gain 10 */
129 BIP_RANGE(0.05), /* gain 100 */
130 BIP_RANGE(0.025) /* gain 200 */
134 struct ii20k_ao_private {
135 unsigned int last_data[2];
138 struct ii20k_private {
139 void __iomem *ioaddr;
142 static void __iomem *ii20k_module_iobase(struct comedi_device *dev,
143 struct comedi_subdevice *s)
145 struct ii20k_private *devpriv = dev->private;
147 return devpriv->ioaddr + (s->index + 1) * II20K_MOD_OFFSET;
150 static int ii20k_ao_insn_read(struct comedi_device *dev,
151 struct comedi_subdevice *s,
152 struct comedi_insn *insn,
155 struct ii20k_ao_private *ao_spriv = s->private;
156 unsigned int chan = CR_CHAN(insn->chanspec);
159 for (i = 0; i < insn->n; i++)
160 data[i] = ao_spriv->last_data[chan];
165 static int ii20k_ao_insn_write(struct comedi_device *dev,
166 struct comedi_subdevice *s,
167 struct comedi_insn *insn,
170 struct ii20k_ao_private *ao_spriv = s->private;
171 void __iomem *iobase = ii20k_module_iobase(dev, s);
172 unsigned int chan = CR_CHAN(insn->chanspec);
173 unsigned int val = ao_spriv->last_data[chan];
176 for (i = 0; i < insn->n; i++) {
180 val += ((s->maxdata + 1) >> 1);
183 writeb(val & 0xff, iobase + II20K_AO_LSB_REG(chan));
184 writeb((val >> 8) & 0xff, iobase + II20K_AO_MSB_REG(chan));
185 writeb(0x00, iobase + II20K_AO_STRB_REG(chan));
188 ao_spriv->last_data[chan] = val;
193 static int ii20k_ai_wait_eoc(struct comedi_device *dev,
194 struct comedi_subdevice *s,
197 void __iomem *iobase = ii20k_module_iobase(dev, s);
198 unsigned char status;
201 status = readb(iobase + II20K_AI_STATUS_REG);
202 if ((status & II20K_AI_STATUS_INT) == 0)
209 static void ii20k_ai_setup(struct comedi_device *dev,
210 struct comedi_subdevice *s,
211 unsigned int chanspec)
213 void __iomem *iobase = ii20k_module_iobase(dev, s);
214 unsigned int chan = CR_CHAN(chanspec);
215 unsigned int range = CR_RANGE(chanspec);
218 /* initialize module */
219 writeb(II20K_AI_CONF_ENA, iobase + II20K_AI_CONF_REG);
221 /* software conversion */
222 writeb(0, iobase + II20K_AI_STATUS_CMD_REG);
224 /* set the time base for the settling time counter based on the gain */
225 val = (range < 3) ? II20K_AI_OPT_TIMEBASE(0) : II20K_AI_OPT_TIMEBASE(2);
226 writeb(val, iobase + II20K_AI_OPT_REG);
228 /* set the settling time counter based on the gain */
229 val = (range < 2) ? 0x58 : (range < 3) ? 0x93 : 0x99;
230 writeb(val, iobase + II20K_AI_SET_TIME_REG);
232 /* set number of input channels */
233 writeb(1, iobase + II20K_AI_LAST_CHAN_ADDR_REG);
235 /* set the channel list byte */
236 val = II20K_AI_CHANLIST_ONBOARD_ONLY |
237 II20K_AI_CHANLIST_MUX_ENA |
238 II20K_AI_CHANLIST_GAIN(range) |
239 II20K_AI_CHANLIST_CHAN(chan);
240 writeb(val, iobase + II20K_AI_CHANLIST_REG);
242 /* reset settling time counter and trigger delay counter */
243 writeb(0, iobase + II20K_AI_COUNT_RESET_REG);
245 /* reset channel scanner */
246 writeb(0, iobase + II20K_AI_CHAN_RESET_REG);
249 static int ii20k_ai_insn_read(struct comedi_device *dev,
250 struct comedi_subdevice *s,
251 struct comedi_insn *insn,
254 void __iomem *iobase = ii20k_module_iobase(dev, s);
258 ii20k_ai_setup(dev, s, insn->chanspec);
260 for (i = 0; i < insn->n; i++) {
263 /* generate a software start convert signal */
264 readb(iobase + II20K_AI_PACER_RESET_REG);
266 ret = ii20k_ai_wait_eoc(dev, s, 100);
270 val = readb(iobase + II20K_AI_LSB_REG);
271 val |= (readb(iobase + II20K_AI_MSB_REG) << 8);
273 /* munge two's complement data */
274 val += ((s->maxdata + 1) >> 1);
283 static void ii20k_dio_config(struct comedi_device *dev,
284 struct comedi_subdevice *s)
286 struct ii20k_private *devpriv = dev->private;
287 unsigned char ctrl01 = 0;
288 unsigned char ctrl23 = 0;
289 unsigned char dir_ena = 0;
291 /* port 0 - channels 0-7 */
292 if (s->io_bits & 0x000000ff) {
294 ctrl01 &= ~II20K_CTRL01_DIO0_IN;
295 dir_ena &= ~II20K_BUF_DISAB_DIO0;
296 dir_ena |= II20K_DIR_DIO0_OUT;
299 ctrl01 |= II20K_CTRL01_DIO0_IN;
300 dir_ena &= ~II20K_DIR_DIO0_OUT;
303 /* port 1 - channels 8-15 */
304 if (s->io_bits & 0x0000ff00) {
306 ctrl01 &= ~II20K_CTRL01_DIO1_IN;
307 dir_ena &= ~II20K_BUF_DISAB_DIO1;
308 dir_ena |= II20K_DIR_DIO1_OUT;
311 ctrl01 |= II20K_CTRL01_DIO1_IN;
312 dir_ena &= ~II20K_DIR_DIO1_OUT;
315 /* port 2 - channels 16-23 */
316 if (s->io_bits & 0x00ff0000) {
318 ctrl23 &= ~II20K_CTRL23_DIO2_IN;
319 dir_ena &= ~II20K_BUF_DISAB_DIO2;
320 dir_ena |= II20K_DIR_DIO2_OUT;
323 ctrl23 |= II20K_CTRL23_DIO2_IN;
324 dir_ena &= ~II20K_DIR_DIO2_OUT;
327 /* port 3 - channels 24-31 */
328 if (s->io_bits & 0xff000000) {
330 ctrl23 &= ~II20K_CTRL23_DIO3_IN;
331 dir_ena &= ~II20K_BUF_DISAB_DIO3;
332 dir_ena |= II20K_DIR_DIO3_OUT;
335 ctrl23 |= II20K_CTRL23_DIO3_IN;
336 dir_ena &= ~II20K_DIR_DIO3_OUT;
339 ctrl23 |= II20K_CTRL01_SET;
340 ctrl23 |= II20K_CTRL23_SET;
342 /* order is important */
343 writeb(ctrl01, devpriv->ioaddr + II20K_CTRL01_REG);
344 writeb(ctrl23, devpriv->ioaddr + II20K_CTRL23_REG);
345 writeb(dir_ena, devpriv->ioaddr + II20K_DIR_ENA_REG);
348 static int ii20k_dio_insn_config(struct comedi_device *dev,
349 struct comedi_subdevice *s,
350 struct comedi_insn *insn,
353 unsigned int chan = CR_CHAN(insn->chanspec);
366 ret = comedi_dio_insn_config(dev, s, insn, data, mask);
370 ii20k_dio_config(dev, s);
375 static int ii20k_dio_insn_bits(struct comedi_device *dev,
376 struct comedi_subdevice *s,
377 struct comedi_insn *insn,
380 struct ii20k_private *devpriv = dev->private;
381 unsigned int mask = data[0] & s->io_bits; /* outputs only */
382 unsigned int bits = data[1];
386 s->state |= (bits & mask);
388 if (mask & 0x000000ff)
389 writeb((s->state >> 0) & 0xff,
390 devpriv->ioaddr + II20K_DIO0_REG);
391 if (mask & 0x0000ff00)
392 writeb((s->state >> 8) & 0xff,
393 devpriv->ioaddr + II20K_DIO1_REG);
394 if (mask & 0x00ff0000)
395 writeb((s->state >> 16) & 0xff,
396 devpriv->ioaddr + II20K_DIO2_REG);
397 if (mask & 0xff000000)
398 writeb((s->state >> 24) & 0xff,
399 devpriv->ioaddr + II20K_DIO3_REG);
402 data[1] = readb(devpriv->ioaddr + II20K_DIO0_REG);
403 data[1] |= readb(devpriv->ioaddr + II20K_DIO1_REG) << 8;
404 data[1] |= readb(devpriv->ioaddr + II20K_DIO2_REG) << 16;
405 data[1] |= readb(devpriv->ioaddr + II20K_DIO3_REG) << 24;
410 static int ii20k_init_module(struct comedi_device *dev,
411 struct comedi_subdevice *s)
413 struct ii20k_ao_private *ao_spriv;
414 void __iomem *iobase = ii20k_module_iobase(dev, s);
417 id = readb(iobase + II20K_ID_REG);
419 case II20K_ID_PCI20006M_1:
420 case II20K_ID_PCI20006M_2:
421 ao_spriv = comedi_alloc_spriv(s, sizeof(*ao_spriv));
425 /* Analog Output subdevice */
426 s->type = COMEDI_SUBD_AO;
427 s->subdev_flags = SDF_WRITABLE;
428 s->n_chan = (id == II20K_ID_PCI20006M_2) ? 2 : 1;
430 s->range_table = &ii20k_ao_ranges;
431 s->insn_read = ii20k_ao_insn_read;
432 s->insn_write = ii20k_ao_insn_write;
434 case II20K_ID_PCI20341M_1:
435 /* Analog Input subdevice */
436 s->type = COMEDI_SUBD_AI;
437 s->subdev_flags = SDF_READABLE | SDF_DIFF;
440 s->range_table = &ii20k_ai_ranges;
441 s->insn_read = ii20k_ai_insn_read;
444 s->type = COMEDI_SUBD_UNUSED;
451 static int ii20k_attach(struct comedi_device *dev,
452 struct comedi_devconfig *it)
454 struct ii20k_private *devpriv;
455 struct comedi_subdevice *s;
460 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
464 devpriv->ioaddr = (void __iomem *)(unsigned long)it->options[0];
466 id = readb(devpriv->ioaddr + II20K_ID_REG);
467 switch (id & II20K_ID_MASK) {
468 case II20K_ID_PCI20001C_1A:
470 case II20K_ID_PCI20001C_2A:
477 ret = comedi_alloc_subdevices(dev, 4);
481 s = &dev->subdevices[0];
482 if (id & II20K_ID_MOD1_EMPTY) {
483 s->type = COMEDI_SUBD_UNUSED;
485 ret = ii20k_init_module(dev, s);
490 s = &dev->subdevices[1];
491 if (id & II20K_ID_MOD2_EMPTY) {
492 s->type = COMEDI_SUBD_UNUSED;
494 ret = ii20k_init_module(dev, s);
499 s = &dev->subdevices[2];
500 if (id & II20K_ID_MOD3_EMPTY) {
501 s->type = COMEDI_SUBD_UNUSED;
503 ret = ii20k_init_module(dev, s);
508 /* Digital I/O subdevice */
509 s = &dev->subdevices[3];
511 s->type = COMEDI_SUBD_DIO;
512 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
515 s->range_table = &range_digital;
516 s->insn_bits = ii20k_dio_insn_bits;
517 s->insn_config = ii20k_dio_insn_config;
519 /* default all channels to input */
520 ii20k_dio_config(dev, s);
522 s->type = COMEDI_SUBD_UNUSED;
528 static struct comedi_driver ii20k_driver = {
529 .driver_name = "ii_pci20kc",
530 .module = THIS_MODULE,
531 .attach = ii20k_attach,
532 .detach = comedi_legacy_detach,
534 module_comedi_driver(ii20k_driver);
536 MODULE_AUTHOR("Comedi http://www.comedi.org");
537 MODULE_DESCRIPTION("Comedi low-level driver");
538 MODULE_LICENSE("GPL");