Commit | Line | Data |
---|---|---|
eb3a3c1b MK |
1 | /* |
2 | * comedi/drivers/ii_pci20kc.c | |
3 | * Driver for Intelligent Instruments PCI-20001C carrier board | |
4 | * and modules. | |
5 | * | |
6 | * Copyright (C) 2000 Markus Kempf <kempf@matsci.uni-sb.de> | |
7 | * with suggestions from David Schleef | |
8 | * 16.06.2000 | |
9 | * | |
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 | |
14 | * - differential | |
15 | * - range (-5V - +5V) | |
16 | * - 16 bit | |
17 | * PCI-20006 M-2 16-Bit analog output module | |
18 | * - ranges (-10V - +10V) (0V - +10V) (-5V - +5V) | |
19 | * - 16 bit | |
20 | * | |
21 | * only ONE PCI-20341 module possible | |
22 | * only ONE PCI-20006 module possible | |
23 | * no extern trigger implemented | |
24 | * | |
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 instead of 4 digital ports | |
27 | * di-port == Port 0 | |
28 | * do-port == Port 1 | |
29 | * | |
30 | * The state of this driver is only a starting point for a complete | |
31 | * COMEDI-driver. The final driver should support all features of the | |
32 | * carrier board and modules. | |
33 | * | |
34 | * The test configuration: | |
35 | * | |
36 | * kernel 2.2.14 with RTAI v1.2 and patch-2.2.14rthal2 | |
37 | * COMEDI 0.7.45 | |
38 | * COMEDILIB 0.7.9 | |
39 | * | |
40 | */ | |
41 | /* | |
42 | Driver: ii_pci20kc | |
43 | Description: Intelligent Instruments PCI-20001C carrier board | |
44 | Author: Markus Kempf <kempf@matsci.uni-sb.de> | |
45 | Devices: [Intelligent Instrumentation] PCI-20001C (ii_pci20kc) | |
46 | Status: works | |
47 | ||
48 | Supports the PCI-20001 C-2a Carrier board, and could probably support | |
49 | the other carrier boards with small modifications. Modules supported | |
50 | are: | |
51 | PCI-20006 M-2 16-bit analog output module | |
52 | PCI-20341 M-1A 16-bit analog input module | |
53 | ||
54 | Options: | |
55 | 0 Board base address | |
56 | 1 IRQ | |
57 | 2 first option for module 1 | |
58 | 3 second option for module 1 | |
59 | 4 first option for module 2 | |
60 | 5 second option for module 2 | |
61 | 6 first option for module 3 | |
62 | 7 second option for module 3 | |
63 | ||
64 | options for PCI-20006M: | |
65 | first: Analog output channel 0 range configuration | |
66 | 0 bipolar 10 (-10V -- +10V) | |
67 | 1 unipolar 10 (0V -- +10V) | |
68 | 2 bipolar 5 (-5V -- 5V) | |
69 | second: Analog output channel 1 range configuration | |
70 | ||
71 | options for PCI-20341M: | |
72 | first: Analog input gain configuration | |
73 | 0 1 | |
74 | 1 10 | |
75 | 2 100 | |
76 | 3 200 | |
77 | */ | |
78 | ||
79 | /* XXX needs to use ioremap() for compatibility with 2.4 kernels. Should also | |
80 | * check_mem_region() etc. - fmhess */ | |
81 | ||
82 | #include "../comedidev.h" | |
83 | ||
84 | #define PCI20000_ID 0x1d | |
85 | #define PCI20341_ID 0x77 | |
86 | #define PCI20006_ID 0xe3 | |
87 | #define PCI20xxx_EMPTY_ID 0xff | |
88 | ||
89 | #define PCI20000_OFFSET 0x100 | |
90 | #define PCI20000_MODULES 3 | |
91 | ||
92 | #define PCI20000_DIO_0 0x80 | |
93 | #define PCI20000_DIO_1 0x81 | |
94 | #define PCI20000_DIO_2 0xc0 | |
95 | #define PCI20000_DIO_3 0xc1 | |
96 | #define PCI20000_DIO_CONTROL_01 0x83 /* port 0, 1 control */ | |
97 | #define PCI20000_DIO_CONTROL_23 0xc3 /* port 2, 3 control */ | |
98 | #define PCI20000_DIO_BUFFER 0x82 /* buffer direction and enable */ | |
99 | #define PCI20000_DIO_EOC 0xef /* even port, control output */ | |
100 | #define PCI20000_DIO_OOC 0xfd /* odd port, control output */ | |
101 | #define PCI20000_DIO_EIC 0x90 /* even port, control input */ | |
102 | #define PCI20000_DIO_OIC 0x82 /* odd port, control input */ | |
103 | #define DIO_CAND 0x12 /* and bit 1, bit 4 of control */ | |
104 | #define DIO_BE 0x01 /* buffer: port enable */ | |
105 | #define DIO_BO 0x04 /* buffer: output */ | |
106 | #define DIO_BI 0x05 /* buffer: input */ | |
107 | #define DIO_PS_0 0x00 /* buffer: port shift 0 */ | |
108 | #define DIO_PS_1 0x01 /* buffer: port shift 1 */ | |
109 | #define DIO_PS_2 0x04 /* buffer: port shift 2 */ | |
110 | #define DIO_PS_3 0x05 /* buffer: port shift 3 */ | |
111 | ||
112 | #define PCI20006_LCHAN0 0x0d | |
113 | #define PCI20006_STROBE0 0x0b | |
114 | #define PCI20006_LCHAN1 0x15 | |
115 | #define PCI20006_STROBE1 0x13 | |
116 | ||
117 | #define PCI20341_INIT 0x04 | |
118 | #define PCI20341_REPMODE 0x00 /* single shot mode */ | |
119 | #define PCI20341_PACER 0x00 /* Hardware Pacer disabled */ | |
120 | #define PCI20341_CHAN_NR 0x04 /* number of input channels */ | |
121 | #define PCI20341_CONFIG_REG 0x10 | |
122 | #define PCI20341_MOD_STATUS 0x01 | |
123 | #define PCI20341_OPT_REG 0x11 | |
124 | #define PCI20341_SET_TIME_REG 0x15 | |
125 | #define PCI20341_LCHAN_ADDR_REG 0x13 | |
126 | #define PCI20341_CHAN_LIST 0x80 | |
127 | #define PCI20341_CC_RESET 0x1b | |
128 | #define PCI20341_CHAN_RESET 0x19 | |
129 | #define PCI20341_SOFT_PACER 0x04 | |
130 | #define PCI20341_STATUS_REG 0x12 | |
131 | #define PCI20341_LDATA 0x02 | |
132 | #define PCI20341_DAISY_CHAIN 0x20 /* On-board inputs only */ | |
133 | #define PCI20341_MUX 0x04 /* Enable on-board MUX */ | |
134 | #define PCI20341_SCANLIST 0x80 /* Channel/Gain Scan List */ | |
135 | ||
97baaedf | 136 | union pci20xxx_subdev_private { |
eb3a3c1b MK |
137 | void *iobase; |
138 | struct { | |
139 | void *iobase; | |
9ced1de6 | 140 | const struct comedi_lrange *ao_range_list[2]; /* range of channels of ao module */ |
790c5541 | 141 | unsigned int last_data[2]; |
eb3a3c1b MK |
142 | } pci20006; |
143 | struct { | |
144 | void *iobase; | |
145 | int timebase; | |
146 | int settling_time; | |
147 | int ai_gain; | |
148 | } pci20341; | |
97baaedf | 149 | }; |
eb3a3c1b | 150 | |
0e99a2b9 BP |
151 | struct pci20xxx_private { |
152 | ||
eb3a3c1b | 153 | void *ioaddr; |
97baaedf | 154 | union pci20xxx_subdev_private subdev_private[PCI20000_MODULES]; |
0e99a2b9 BP |
155 | }; |
156 | ||
eb3a3c1b | 157 | |
0e99a2b9 | 158 | #define devpriv ((struct pci20xxx_private *)dev->private) |
eb3a3c1b MK |
159 | #define CHAN (CR_CHAN(it->chanlist[0])) |
160 | ||
0707bb04 | 161 | static int pci20xxx_attach(struct comedi_device * dev, struct comedi_devconfig * it); |
71b5f4f1 | 162 | static int pci20xxx_detach(struct comedi_device * dev); |
eb3a3c1b | 163 | |
139dfbdf | 164 | static struct comedi_driver driver_pci20xxx = { |
eb3a3c1b MK |
165 | driver_name:"ii_pci20kc", |
166 | module:THIS_MODULE, | |
167 | attach:pci20xxx_attach, | |
168 | detach:pci20xxx_detach, | |
169 | }; | |
170 | ||
34c43922 | 171 | static int pci20006_init(struct comedi_device * dev, struct comedi_subdevice * s, |
eb3a3c1b | 172 | int opt0, int opt1); |
34c43922 | 173 | static int pci20341_init(struct comedi_device * dev, struct comedi_subdevice * s, |
eb3a3c1b | 174 | int opt0, int opt1); |
34c43922 | 175 | static int pci20xxx_dio_init(struct comedi_device * dev, struct comedi_subdevice * s); |
eb3a3c1b MK |
176 | |
177 | /* | |
178 | options[0] Board base address | |
179 | options[1] IRQ | |
180 | options[2] first option for module 1 | |
181 | options[3] second option for module 1 | |
182 | options[4] first option for module 2 | |
183 | options[5] second option for module 2 | |
184 | options[6] first option for module 3 | |
185 | options[7] second option for module 3 | |
186 | ||
187 | options for PCI-20341M: | |
188 | first Analog input gain configuration | |
189 | 0 == 1 | |
190 | 1 == 10 | |
191 | 2 == 100 | |
192 | 3 == 200 | |
193 | ||
194 | options for PCI-20006M: | |
195 | first Analog output channel 0 range configuration | |
196 | 0 == bipolar 10 (-10V -- +10V) | |
197 | 1 == unipolar 10V (0V -- +10V) | |
198 | 2 == bipolar 5V (-5V -- +5V) | |
199 | second Analog output channel 1 range configuration | |
200 | 0 == bipolar 10 (-10V -- +10V) | |
201 | 1 == unipolar 10V (0V -- +10V) | |
202 | 2 == bipolar 5V (-5V -- +5V) | |
203 | */ | |
0707bb04 | 204 | static int pci20xxx_attach(struct comedi_device * dev, struct comedi_devconfig * it) |
eb3a3c1b MK |
205 | { |
206 | unsigned char i; | |
207 | int ret; | |
208 | int id; | |
34c43922 | 209 | struct comedi_subdevice *s; |
97baaedf | 210 | union pci20xxx_subdev_private *sdp; |
eb3a3c1b MK |
211 | |
212 | if ((ret = alloc_subdevices(dev, 1 + PCI20000_MODULES)) < 0) | |
213 | return ret; | |
0e99a2b9 | 214 | if ((ret = alloc_private(dev, sizeof(struct pci20xxx_private))) < 0) |
eb3a3c1b MK |
215 | return ret; |
216 | ||
217 | devpriv->ioaddr = (void *)(unsigned long)it->options[0]; | |
218 | dev->board_name = "pci20kc"; | |
219 | ||
220 | /* Check PCI-20001 C-2A Carrier Board ID */ | |
221 | if ((readb(devpriv->ioaddr) & PCI20000_ID) != PCI20000_ID) { | |
222 | printk("comedi%d: ii_pci20kc", dev->minor); | |
223 | printk(" PCI-20001 C-2A Carrier Board at base=0x%p not found !\n", devpriv->ioaddr); | |
224 | return -EINVAL; | |
225 | } | |
226 | printk("comedi%d:\n", dev->minor); | |
227 | printk("ii_pci20kc: PCI-20001 C-2A at base=0x%p\n", devpriv->ioaddr); | |
228 | ||
229 | for (i = 0; i < PCI20000_MODULES; i++) { | |
230 | s = dev->subdevices + i; | |
231 | id = readb(devpriv->ioaddr + (i + 1) * PCI20000_OFFSET); | |
232 | s->private = devpriv->subdev_private + i; | |
233 | sdp = s->private; | |
234 | switch (id) { | |
235 | case PCI20006_ID: | |
236 | sdp->pci20006.iobase = | |
237 | devpriv->ioaddr + (i + 1) * PCI20000_OFFSET; | |
238 | pci20006_init(dev, s, it->options[2 * i + 2], | |
239 | it->options[2 * i + 3]); | |
240 | printk("comedi%d: ii_pci20kc", dev->minor); | |
241 | printk(" PCI-20006 module in slot %d \n", i + 1); | |
242 | break; | |
243 | case PCI20341_ID: | |
244 | sdp->pci20341.iobase = | |
245 | devpriv->ioaddr + (i + 1) * PCI20000_OFFSET; | |
246 | pci20341_init(dev, s, it->options[2 * i + 2], | |
247 | it->options[2 * i + 3]); | |
248 | printk("comedi%d: ii_pci20kc", dev->minor); | |
249 | printk(" PCI-20341 module in slot %d \n", i + 1); | |
250 | break; | |
251 | default: | |
252 | printk("ii_pci20kc: unknown module code 0x%02x in slot %d: module disabled\n", id, i); | |
253 | /* fall through */ | |
254 | case PCI20xxx_EMPTY_ID: | |
255 | s->type = COMEDI_SUBD_UNUSED; | |
256 | break; | |
257 | } | |
258 | } | |
259 | ||
0e99a2b9 | 260 | /* initialize struct pci20xxx_private */ |
eb3a3c1b MK |
261 | pci20xxx_dio_init(dev, dev->subdevices + PCI20000_MODULES); |
262 | ||
263 | return 1; | |
264 | } | |
265 | ||
71b5f4f1 | 266 | static int pci20xxx_detach(struct comedi_device * dev) |
eb3a3c1b MK |
267 | { |
268 | printk("comedi%d: pci20xxx: remove\n", dev->minor); | |
269 | ||
270 | return 0; | |
271 | } | |
272 | ||
273 | /* pci20006m */ | |
274 | ||
34c43922 | 275 | static int pci20006_insn_read(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 276 | struct comedi_insn * insn, unsigned int * data); |
34c43922 | 277 | static int pci20006_insn_write(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 278 | struct comedi_insn * insn, unsigned int * data); |
eb3a3c1b | 279 | |
9ced1de6 | 280 | static const struct comedi_lrange *pci20006_range_list[] = { |
eb3a3c1b MK |
281 | &range_bipolar10, |
282 | &range_unipolar10, | |
283 | &range_bipolar5, | |
284 | }; | |
285 | ||
34c43922 | 286 | static int pci20006_init(struct comedi_device * dev, struct comedi_subdevice * s, |
eb3a3c1b MK |
287 | int opt0, int opt1) |
288 | { | |
97baaedf | 289 | union pci20xxx_subdev_private *sdp = s->private; |
eb3a3c1b MK |
290 | |
291 | if (opt0 < 0 || opt0 > 2) | |
292 | opt0 = 0; | |
293 | if (opt1 < 0 || opt1 > 2) | |
294 | opt1 = 0; | |
295 | ||
296 | sdp->pci20006.ao_range_list[0] = pci20006_range_list[opt0]; | |
297 | sdp->pci20006.ao_range_list[1] = pci20006_range_list[opt1]; | |
298 | ||
299 | /* ao subdevice */ | |
300 | s->type = COMEDI_SUBD_AO; | |
301 | s->subdev_flags = SDF_WRITABLE; | |
302 | s->n_chan = 2; | |
303 | s->len_chanlist = 2; | |
304 | s->insn_read = pci20006_insn_read; | |
305 | s->insn_write = pci20006_insn_write; | |
306 | s->maxdata = 0xffff; | |
307 | s->range_table_list = sdp->pci20006.ao_range_list; | |
308 | return 0; | |
309 | } | |
310 | ||
34c43922 | 311 | static int pci20006_insn_read(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 312 | struct comedi_insn * insn, unsigned int * data) |
eb3a3c1b | 313 | { |
97baaedf | 314 | union pci20xxx_subdev_private *sdp = s->private; |
eb3a3c1b MK |
315 | |
316 | data[0] = sdp->pci20006.last_data[CR_CHAN(insn->chanspec)]; | |
317 | ||
318 | return 1; | |
319 | } | |
320 | ||
34c43922 | 321 | static int pci20006_insn_write(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 322 | struct comedi_insn * insn, unsigned int * data) |
eb3a3c1b | 323 | { |
97baaedf | 324 | union pci20xxx_subdev_private *sdp = s->private; |
eb3a3c1b MK |
325 | int hi, lo; |
326 | unsigned int boarddata; | |
327 | ||
328 | sdp->pci20006.last_data[CR_CHAN(insn->chanspec)] = data[0]; | |
329 | boarddata = (((unsigned int)data[0] + 0x8000) & 0xffff); /* comedi-data -> board-data */ | |
330 | lo = (boarddata & 0xff); | |
331 | hi = ((boarddata >> 8) & 0xff); | |
332 | ||
333 | switch (CR_CHAN(insn->chanspec)) { | |
334 | case 0: | |
335 | writeb(lo, sdp->iobase + PCI20006_LCHAN0); | |
336 | writeb(hi, sdp->iobase + PCI20006_LCHAN0 + 1); | |
337 | writeb(0x00, sdp->iobase + PCI20006_STROBE0); | |
338 | break; | |
339 | case 1: | |
340 | writeb(lo, sdp->iobase + PCI20006_LCHAN1); | |
341 | writeb(hi, sdp->iobase + PCI20006_LCHAN1 + 1); | |
342 | writeb(0x00, sdp->iobase + PCI20006_STROBE1); | |
343 | break; | |
344 | default: | |
345 | printk(" comedi%d: pci20xxx: ao channel Error!\n", dev->minor); | |
346 | return -EINVAL; | |
347 | } | |
348 | ||
349 | return 1; | |
350 | } | |
351 | ||
352 | /* PCI20341M */ | |
353 | ||
34c43922 | 354 | static int pci20341_insn_read(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 355 | struct comedi_insn * insn, unsigned int * data); |
eb3a3c1b MK |
356 | |
357 | static const int pci20341_timebase[] = { 0x00, 0x00, 0x00, 0x04 }; | |
358 | static const int pci20341_settling_time[] = { 0x58, 0x58, 0x93, 0x99 }; | |
359 | ||
9ced1de6 BP |
360 | static const struct comedi_lrange range_bipolar0_5 = { 1, {BIP_RANGE(0.5)} }; |
361 | static const struct comedi_lrange range_bipolar0_05 = { 1, {BIP_RANGE(0.05)} }; | |
362 | static const struct comedi_lrange range_bipolar0_025 = { 1, {BIP_RANGE(0.025)} }; | |
eb3a3c1b | 363 | |
9ced1de6 | 364 | static const struct comedi_lrange *const pci20341_ranges[] = { |
eb3a3c1b MK |
365 | &range_bipolar5, |
366 | &range_bipolar0_5, | |
367 | &range_bipolar0_05, | |
368 | &range_bipolar0_025, | |
369 | }; | |
370 | ||
34c43922 | 371 | static int pci20341_init(struct comedi_device * dev, struct comedi_subdevice * s, |
eb3a3c1b MK |
372 | int opt0, int opt1) |
373 | { | |
97baaedf | 374 | union pci20xxx_subdev_private *sdp = s->private; |
eb3a3c1b MK |
375 | int option; |
376 | ||
377 | /* options handling */ | |
378 | if (opt0 < 0 || opt0 > 3) | |
379 | opt0 = 0; | |
380 | sdp->pci20341.timebase = pci20341_timebase[opt0]; | |
381 | sdp->pci20341.settling_time = pci20341_settling_time[opt0]; | |
382 | ||
383 | /* ai subdevice */ | |
384 | s->type = COMEDI_SUBD_AI; | |
385 | s->subdev_flags = SDF_READABLE; | |
386 | s->n_chan = PCI20341_CHAN_NR; | |
387 | s->len_chanlist = PCI20341_SCANLIST; | |
388 | s->insn_read = pci20341_insn_read; | |
389 | s->maxdata = 0xffff; | |
390 | s->range_table = pci20341_ranges[opt0]; | |
391 | ||
392 | option = sdp->pci20341.timebase | PCI20341_REPMODE; /* depends on gain, trigger, repetition mode */ | |
393 | ||
394 | writeb(PCI20341_INIT, sdp->iobase + PCI20341_CONFIG_REG); /* initialize Module */ | |
395 | writeb(PCI20341_PACER, sdp->iobase + PCI20341_MOD_STATUS); /* set Pacer */ | |
396 | writeb(option, sdp->iobase + PCI20341_OPT_REG); /* option register */ | |
397 | writeb(sdp->pci20341.settling_time, sdp->iobase + PCI20341_SET_TIME_REG); /* settling time counter */ | |
398 | /* trigger not implemented */ | |
399 | return 0; | |
400 | } | |
401 | ||
34c43922 | 402 | static int pci20341_insn_read(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 403 | struct comedi_insn * insn, unsigned int * data) |
eb3a3c1b | 404 | { |
97baaedf | 405 | union pci20xxx_subdev_private *sdp = s->private; |
eb3a3c1b MK |
406 | unsigned int i = 0, j = 0; |
407 | int lo, hi; | |
408 | unsigned char eoc; /* end of conversion */ | |
409 | unsigned int clb; /* channel list byte */ | |
410 | unsigned int boarddata; | |
411 | ||
412 | writeb(1, sdp->iobase + PCI20341_LCHAN_ADDR_REG); /* write number of input channels */ | |
413 | clb = PCI20341_DAISY_CHAIN | PCI20341_MUX | (sdp->pci20341.ai_gain << 3) | |
414 | | CR_CHAN(insn->chanspec); | |
415 | writeb(clb, sdp->iobase + PCI20341_CHAN_LIST); | |
416 | writeb(0x00, sdp->iobase + PCI20341_CC_RESET); /* reset settling time counter and trigger delay counter */ | |
417 | writeb(0x00, sdp->iobase + PCI20341_CHAN_RESET); | |
418 | ||
419 | /* generate Pacer */ | |
420 | ||
421 | for (i = 0; i < insn->n; i++) { | |
422 | /* data polling isn't the niciest way to get the data, I know, | |
423 | * but there are only 6 cycles (mean) and it is easier than | |
424 | * the whole interrupt stuff | |
425 | */ | |
426 | j = 0; | |
427 | readb(sdp->iobase + PCI20341_SOFT_PACER); /* generate Pacer */ | |
428 | eoc = readb(sdp->iobase + PCI20341_STATUS_REG); | |
429 | while ((eoc < 0x80) && j < 100) { /* poll Interrupt Flag */ | |
430 | j++; | |
431 | eoc = readb(sdp->iobase + PCI20341_STATUS_REG); | |
432 | } | |
433 | if (j >= 100) { | |
434 | printk("comedi%d: pci20xxx: AI interrupt channel %i polling exit !\n", dev->minor, i); | |
435 | return -EINVAL; | |
436 | } | |
437 | lo = readb(sdp->iobase + PCI20341_LDATA); | |
438 | hi = readb(sdp->iobase + PCI20341_LDATA + 1); | |
439 | boarddata = lo + 0x100 * hi; | |
790c5541 | 440 | data[i] = (short) ((boarddata + 0x8000) & 0xffff); /* board-data -> comedi-data */ |
eb3a3c1b MK |
441 | } |
442 | ||
443 | return i; | |
444 | } | |
445 | ||
446 | /* native DIO */ | |
447 | ||
34c43922 BP |
448 | static void pci20xxx_dio_config(struct comedi_device * dev, struct comedi_subdevice * s); |
449 | static int pci20xxx_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s, | |
90035c08 | 450 | struct comedi_insn * insn, unsigned int * data); |
34c43922 | 451 | static int pci20xxx_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 452 | struct comedi_insn * insn, unsigned int * data); |
eb3a3c1b | 453 | |
0e99a2b9 | 454 | /* initialize struct pci20xxx_private */ |
34c43922 | 455 | static int pci20xxx_dio_init(struct comedi_device * dev, struct comedi_subdevice * s) |
eb3a3c1b MK |
456 | { |
457 | ||
458 | s->type = COMEDI_SUBD_DIO; | |
459 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
460 | s->n_chan = 32; | |
461 | s->insn_bits = pci20xxx_dio_insn_bits; | |
462 | s->insn_config = pci20xxx_dio_insn_config; | |
463 | s->maxdata = 1; | |
464 | s->len_chanlist = 32; | |
465 | s->range_table = &range_digital; | |
466 | s->io_bits = 0; | |
467 | ||
468 | /* digital I/O lines default to input on board reset. */ | |
469 | pci20xxx_dio_config(dev, s); | |
470 | ||
471 | return 0; | |
472 | } | |
473 | ||
34c43922 | 474 | static int pci20xxx_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 475 | struct comedi_insn * insn, unsigned int * data) |
eb3a3c1b MK |
476 | { |
477 | int mask, bits; | |
478 | ||
479 | mask = 1 << CR_CHAN(insn->chanspec); | |
480 | if (mask & 0x000000ff) { | |
481 | bits = 0x000000ff; | |
482 | } else if (mask & 0x0000ff00) { | |
483 | bits = 0x0000ff00; | |
484 | } else if (mask & 0x00ff0000) { | |
485 | bits = 0x00ff0000; | |
486 | } else { | |
487 | bits = 0xff000000; | |
488 | } | |
489 | if (data[0]) { | |
490 | s->io_bits |= bits; | |
491 | } else { | |
492 | s->io_bits &= ~bits; | |
493 | } | |
494 | pci20xxx_dio_config(dev, s); | |
495 | ||
496 | return 1; | |
497 | } | |
498 | ||
34c43922 | 499 | static int pci20xxx_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 500 | struct comedi_insn * insn, unsigned int * data) |
eb3a3c1b MK |
501 | { |
502 | unsigned int mask = data[0]; | |
503 | ||
504 | s->state &= ~mask; | |
505 | s->state |= (mask & data[1]); | |
506 | ||
507 | mask &= s->io_bits; | |
508 | if (mask & 0x000000ff) | |
509 | writeb((s->state >> 0) & 0xff, | |
510 | devpriv->ioaddr + PCI20000_DIO_0); | |
511 | if (mask & 0x0000ff00) | |
512 | writeb((s->state >> 8) & 0xff, | |
513 | devpriv->ioaddr + PCI20000_DIO_1); | |
514 | if (mask & 0x00ff0000) | |
515 | writeb((s->state >> 16) & 0xff, | |
516 | devpriv->ioaddr + PCI20000_DIO_2); | |
517 | if (mask & 0xff000000) | |
518 | writeb((s->state >> 24) & 0xff, | |
519 | devpriv->ioaddr + PCI20000_DIO_3); | |
520 | ||
521 | data[1] = readb(devpriv->ioaddr + PCI20000_DIO_0); | |
522 | data[1] |= readb(devpriv->ioaddr + PCI20000_DIO_1) << 8; | |
523 | data[1] |= readb(devpriv->ioaddr + PCI20000_DIO_2) << 16; | |
524 | data[1] |= readb(devpriv->ioaddr + PCI20000_DIO_3) << 24; | |
525 | ||
526 | return 2; | |
527 | } | |
528 | ||
34c43922 | 529 | static void pci20xxx_dio_config(struct comedi_device * dev, struct comedi_subdevice * s) |
eb3a3c1b MK |
530 | { |
531 | unsigned char control_01; | |
532 | unsigned char control_23; | |
533 | unsigned char buffer; | |
534 | ||
535 | control_01 = readb(devpriv->ioaddr + PCI20000_DIO_CONTROL_01); | |
536 | control_23 = readb(devpriv->ioaddr + PCI20000_DIO_CONTROL_23); | |
537 | buffer = readb(devpriv->ioaddr + PCI20000_DIO_BUFFER); | |
538 | ||
539 | if (s->io_bits & 0x000000ff) { | |
540 | /* output port 0 */ | |
541 | control_01 &= PCI20000_DIO_EOC; | |
542 | buffer = (buffer & (~(DIO_BE << DIO_PS_0))) | (DIO_BO << | |
543 | DIO_PS_0); | |
544 | } else { | |
545 | /* input port 0 */ | |
546 | control_01 = (control_01 & DIO_CAND) | PCI20000_DIO_EIC; | |
547 | buffer = (buffer & (~(DIO_BI << DIO_PS_0))); | |
548 | } | |
549 | if (s->io_bits & 0x0000ff00) { | |
550 | /* output port 1 */ | |
551 | control_01 &= PCI20000_DIO_OOC; | |
552 | buffer = (buffer & (~(DIO_BE << DIO_PS_1))) | (DIO_BO << | |
553 | DIO_PS_1); | |
554 | } else { | |
555 | /* input port 1 */ | |
556 | control_01 = (control_01 & DIO_CAND) | PCI20000_DIO_OIC; | |
557 | buffer = (buffer & (~(DIO_BI << DIO_PS_1))); | |
558 | } | |
559 | if (s->io_bits & 0x00ff0000) { | |
560 | /* output port 2 */ | |
561 | control_23 &= PCI20000_DIO_EOC; | |
562 | buffer = (buffer & (~(DIO_BE << DIO_PS_2))) | (DIO_BO << | |
563 | DIO_PS_2); | |
564 | } else { | |
565 | /* input port 2 */ | |
566 | control_23 = (control_23 & DIO_CAND) | PCI20000_DIO_EIC; | |
567 | buffer = (buffer & (~(DIO_BI << DIO_PS_2))); | |
568 | } | |
569 | if (s->io_bits & 0xff000000) { | |
570 | /* output port 3 */ | |
571 | control_23 &= PCI20000_DIO_OOC; | |
572 | buffer = (buffer & (~(DIO_BE << DIO_PS_3))) | (DIO_BO << | |
573 | DIO_PS_3); | |
574 | } else { | |
575 | /* input port 3 */ | |
576 | control_23 = (control_23 & DIO_CAND) | PCI20000_DIO_OIC; | |
577 | buffer = (buffer & (~(DIO_BI << DIO_PS_3))); | |
578 | } | |
579 | writeb(control_01, devpriv->ioaddr + PCI20000_DIO_CONTROL_01); | |
580 | writeb(control_23, devpriv->ioaddr + PCI20000_DIO_CONTROL_23); | |
581 | writeb(buffer, devpriv->ioaddr + PCI20000_DIO_BUFFER); | |
582 | } | |
583 | ||
584 | #if 0 | |
34c43922 | 585 | static void pci20xxx_do(struct comedi_device * dev, struct comedi_subdevice * s) |
eb3a3c1b MK |
586 | { |
587 | /* XXX if the channel is configured for input, does this | |
588 | do bad things? */ | |
589 | /* XXX it would be a good idea to only update the registers | |
590 | that _need_ to be updated. This requires changes to | |
591 | comedi, however. */ | |
592 | writeb((s->state >> 0) & 0xff, devpriv->ioaddr + PCI20000_DIO_0); | |
593 | writeb((s->state >> 8) & 0xff, devpriv->ioaddr + PCI20000_DIO_1); | |
594 | writeb((s->state >> 16) & 0xff, devpriv->ioaddr + PCI20000_DIO_2); | |
595 | writeb((s->state >> 24) & 0xff, devpriv->ioaddr + PCI20000_DIO_3); | |
596 | } | |
597 | ||
34c43922 | 598 | static unsigned int pci20xxx_di(struct comedi_device * dev, struct comedi_subdevice * s) |
eb3a3c1b MK |
599 | { |
600 | /* XXX same note as above */ | |
601 | unsigned int bits; | |
602 | ||
603 | bits = readb(devpriv->ioaddr + PCI20000_DIO_0); | |
604 | bits |= readb(devpriv->ioaddr + PCI20000_DIO_1) << 8; | |
605 | bits |= readb(devpriv->ioaddr + PCI20000_DIO_2) << 16; | |
606 | bits |= readb(devpriv->ioaddr + PCI20000_DIO_3) << 24; | |
607 | ||
608 | return bits; | |
609 | } | |
610 | #endif | |
611 | ||
612 | COMEDI_INITCLEANUP(driver_pci20xxx); |