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 | ||
136 | typedef union { | |
137 | void *iobase; | |
138 | struct { | |
139 | void *iobase; | |
140 | const 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; | |
149 | } pci20xxx_subdev_private; | |
150 | ||
151 | typedef struct { | |
152 | void *ioaddr; | |
153 | pci20xxx_subdev_private subdev_private[PCI20000_MODULES]; | |
154 | } pci20xxx_private; | |
155 | ||
156 | #define devpriv ((pci20xxx_private *)dev->private) | |
157 | #define CHAN (CR_CHAN(it->chanlist[0])) | |
158 | ||
159 | static int pci20xxx_attach(comedi_device * dev, comedi_devconfig * it); | |
160 | static int pci20xxx_detach(comedi_device * dev); | |
161 | ||
162 | static comedi_driver driver_pci20xxx = { | |
163 | driver_name:"ii_pci20kc", | |
164 | module:THIS_MODULE, | |
165 | attach:pci20xxx_attach, | |
166 | detach:pci20xxx_detach, | |
167 | }; | |
168 | ||
169 | static int pci20006_init(comedi_device * dev, comedi_subdevice * s, | |
170 | int opt0, int opt1); | |
171 | static int pci20341_init(comedi_device * dev, comedi_subdevice * s, | |
172 | int opt0, int opt1); | |
173 | static int pci20xxx_dio_init(comedi_device * dev, comedi_subdevice * s); | |
174 | ||
175 | /* | |
176 | options[0] Board base address | |
177 | options[1] IRQ | |
178 | options[2] first option for module 1 | |
179 | options[3] second option for module 1 | |
180 | options[4] first option for module 2 | |
181 | options[5] second option for module 2 | |
182 | options[6] first option for module 3 | |
183 | options[7] second option for module 3 | |
184 | ||
185 | options for PCI-20341M: | |
186 | first Analog input gain configuration | |
187 | 0 == 1 | |
188 | 1 == 10 | |
189 | 2 == 100 | |
190 | 3 == 200 | |
191 | ||
192 | options for PCI-20006M: | |
193 | first Analog output channel 0 range configuration | |
194 | 0 == bipolar 10 (-10V -- +10V) | |
195 | 1 == unipolar 10V (0V -- +10V) | |
196 | 2 == bipolar 5V (-5V -- +5V) | |
197 | second Analog output channel 1 range configuration | |
198 | 0 == bipolar 10 (-10V -- +10V) | |
199 | 1 == unipolar 10V (0V -- +10V) | |
200 | 2 == bipolar 5V (-5V -- +5V) | |
201 | */ | |
202 | static int pci20xxx_attach(comedi_device * dev, comedi_devconfig * it) | |
203 | { | |
204 | unsigned char i; | |
205 | int ret; | |
206 | int id; | |
207 | comedi_subdevice *s; | |
208 | pci20xxx_subdev_private *sdp; | |
209 | ||
210 | if ((ret = alloc_subdevices(dev, 1 + PCI20000_MODULES)) < 0) | |
211 | return ret; | |
212 | if ((ret = alloc_private(dev, sizeof(pci20xxx_private))) < 0) | |
213 | return ret; | |
214 | ||
215 | devpriv->ioaddr = (void *)(unsigned long)it->options[0]; | |
216 | dev->board_name = "pci20kc"; | |
217 | ||
218 | /* Check PCI-20001 C-2A Carrier Board ID */ | |
219 | if ((readb(devpriv->ioaddr) & PCI20000_ID) != PCI20000_ID) { | |
220 | printk("comedi%d: ii_pci20kc", dev->minor); | |
221 | printk(" PCI-20001 C-2A Carrier Board at base=0x%p not found !\n", devpriv->ioaddr); | |
222 | return -EINVAL; | |
223 | } | |
224 | printk("comedi%d:\n", dev->minor); | |
225 | printk("ii_pci20kc: PCI-20001 C-2A at base=0x%p\n", devpriv->ioaddr); | |
226 | ||
227 | for (i = 0; i < PCI20000_MODULES; i++) { | |
228 | s = dev->subdevices + i; | |
229 | id = readb(devpriv->ioaddr + (i + 1) * PCI20000_OFFSET); | |
230 | s->private = devpriv->subdev_private + i; | |
231 | sdp = s->private; | |
232 | switch (id) { | |
233 | case PCI20006_ID: | |
234 | sdp->pci20006.iobase = | |
235 | devpriv->ioaddr + (i + 1) * PCI20000_OFFSET; | |
236 | pci20006_init(dev, s, it->options[2 * i + 2], | |
237 | it->options[2 * i + 3]); | |
238 | printk("comedi%d: ii_pci20kc", dev->minor); | |
239 | printk(" PCI-20006 module in slot %d \n", i + 1); | |
240 | break; | |
241 | case PCI20341_ID: | |
242 | sdp->pci20341.iobase = | |
243 | devpriv->ioaddr + (i + 1) * PCI20000_OFFSET; | |
244 | pci20341_init(dev, s, it->options[2 * i + 2], | |
245 | it->options[2 * i + 3]); | |
246 | printk("comedi%d: ii_pci20kc", dev->minor); | |
247 | printk(" PCI-20341 module in slot %d \n", i + 1); | |
248 | break; | |
249 | default: | |
250 | printk("ii_pci20kc: unknown module code 0x%02x in slot %d: module disabled\n", id, i); | |
251 | /* fall through */ | |
252 | case PCI20xxx_EMPTY_ID: | |
253 | s->type = COMEDI_SUBD_UNUSED; | |
254 | break; | |
255 | } | |
256 | } | |
257 | ||
258 | /* initialize pci20xxx_private */ | |
259 | pci20xxx_dio_init(dev, dev->subdevices + PCI20000_MODULES); | |
260 | ||
261 | return 1; | |
262 | } | |
263 | ||
264 | static int pci20xxx_detach(comedi_device * dev) | |
265 | { | |
266 | printk("comedi%d: pci20xxx: remove\n", dev->minor); | |
267 | ||
268 | return 0; | |
269 | } | |
270 | ||
271 | /* pci20006m */ | |
272 | ||
273 | static int pci20006_insn_read(comedi_device * dev, comedi_subdevice * s, | |
790c5541 | 274 | comedi_insn * insn, unsigned int * data); |
eb3a3c1b | 275 | static int pci20006_insn_write(comedi_device * dev, comedi_subdevice * s, |
790c5541 | 276 | comedi_insn * insn, unsigned int * data); |
eb3a3c1b MK |
277 | |
278 | static const comedi_lrange *pci20006_range_list[] = { | |
279 | &range_bipolar10, | |
280 | &range_unipolar10, | |
281 | &range_bipolar5, | |
282 | }; | |
283 | ||
284 | static int pci20006_init(comedi_device * dev, comedi_subdevice * s, | |
285 | int opt0, int opt1) | |
286 | { | |
287 | pci20xxx_subdev_private *sdp = s->private; | |
288 | ||
289 | if (opt0 < 0 || opt0 > 2) | |
290 | opt0 = 0; | |
291 | if (opt1 < 0 || opt1 > 2) | |
292 | opt1 = 0; | |
293 | ||
294 | sdp->pci20006.ao_range_list[0] = pci20006_range_list[opt0]; | |
295 | sdp->pci20006.ao_range_list[1] = pci20006_range_list[opt1]; | |
296 | ||
297 | /* ao subdevice */ | |
298 | s->type = COMEDI_SUBD_AO; | |
299 | s->subdev_flags = SDF_WRITABLE; | |
300 | s->n_chan = 2; | |
301 | s->len_chanlist = 2; | |
302 | s->insn_read = pci20006_insn_read; | |
303 | s->insn_write = pci20006_insn_write; | |
304 | s->maxdata = 0xffff; | |
305 | s->range_table_list = sdp->pci20006.ao_range_list; | |
306 | return 0; | |
307 | } | |
308 | ||
309 | static int pci20006_insn_read(comedi_device * dev, comedi_subdevice * s, | |
790c5541 | 310 | comedi_insn * insn, unsigned int * data) |
eb3a3c1b MK |
311 | { |
312 | pci20xxx_subdev_private *sdp = s->private; | |
313 | ||
314 | data[0] = sdp->pci20006.last_data[CR_CHAN(insn->chanspec)]; | |
315 | ||
316 | return 1; | |
317 | } | |
318 | ||
319 | static int pci20006_insn_write(comedi_device * dev, comedi_subdevice * s, | |
790c5541 | 320 | comedi_insn * insn, unsigned int * data) |
eb3a3c1b MK |
321 | { |
322 | pci20xxx_subdev_private *sdp = s->private; | |
323 | int hi, lo; | |
324 | unsigned int boarddata; | |
325 | ||
326 | sdp->pci20006.last_data[CR_CHAN(insn->chanspec)] = data[0]; | |
327 | boarddata = (((unsigned int)data[0] + 0x8000) & 0xffff); /* comedi-data -> board-data */ | |
328 | lo = (boarddata & 0xff); | |
329 | hi = ((boarddata >> 8) & 0xff); | |
330 | ||
331 | switch (CR_CHAN(insn->chanspec)) { | |
332 | case 0: | |
333 | writeb(lo, sdp->iobase + PCI20006_LCHAN0); | |
334 | writeb(hi, sdp->iobase + PCI20006_LCHAN0 + 1); | |
335 | writeb(0x00, sdp->iobase + PCI20006_STROBE0); | |
336 | break; | |
337 | case 1: | |
338 | writeb(lo, sdp->iobase + PCI20006_LCHAN1); | |
339 | writeb(hi, sdp->iobase + PCI20006_LCHAN1 + 1); | |
340 | writeb(0x00, sdp->iobase + PCI20006_STROBE1); | |
341 | break; | |
342 | default: | |
343 | printk(" comedi%d: pci20xxx: ao channel Error!\n", dev->minor); | |
344 | return -EINVAL; | |
345 | } | |
346 | ||
347 | return 1; | |
348 | } | |
349 | ||
350 | /* PCI20341M */ | |
351 | ||
352 | static int pci20341_insn_read(comedi_device * dev, comedi_subdevice * s, | |
790c5541 | 353 | comedi_insn * insn, unsigned int * data); |
eb3a3c1b MK |
354 | |
355 | static const int pci20341_timebase[] = { 0x00, 0x00, 0x00, 0x04 }; | |
356 | static const int pci20341_settling_time[] = { 0x58, 0x58, 0x93, 0x99 }; | |
357 | ||
358 | static const comedi_lrange range_bipolar0_5 = { 1, {BIP_RANGE(0.5)} }; | |
359 | static const comedi_lrange range_bipolar0_05 = { 1, {BIP_RANGE(0.05)} }; | |
360 | static const comedi_lrange range_bipolar0_025 = { 1, {BIP_RANGE(0.025)} }; | |
361 | ||
362 | static const comedi_lrange *const pci20341_ranges[] = { | |
363 | &range_bipolar5, | |
364 | &range_bipolar0_5, | |
365 | &range_bipolar0_05, | |
366 | &range_bipolar0_025, | |
367 | }; | |
368 | ||
369 | static int pci20341_init(comedi_device * dev, comedi_subdevice * s, | |
370 | int opt0, int opt1) | |
371 | { | |
372 | pci20xxx_subdev_private *sdp = s->private; | |
373 | int option; | |
374 | ||
375 | /* options handling */ | |
376 | if (opt0 < 0 || opt0 > 3) | |
377 | opt0 = 0; | |
378 | sdp->pci20341.timebase = pci20341_timebase[opt0]; | |
379 | sdp->pci20341.settling_time = pci20341_settling_time[opt0]; | |
380 | ||
381 | /* ai subdevice */ | |
382 | s->type = COMEDI_SUBD_AI; | |
383 | s->subdev_flags = SDF_READABLE; | |
384 | s->n_chan = PCI20341_CHAN_NR; | |
385 | s->len_chanlist = PCI20341_SCANLIST; | |
386 | s->insn_read = pci20341_insn_read; | |
387 | s->maxdata = 0xffff; | |
388 | s->range_table = pci20341_ranges[opt0]; | |
389 | ||
390 | option = sdp->pci20341.timebase | PCI20341_REPMODE; /* depends on gain, trigger, repetition mode */ | |
391 | ||
392 | writeb(PCI20341_INIT, sdp->iobase + PCI20341_CONFIG_REG); /* initialize Module */ | |
393 | writeb(PCI20341_PACER, sdp->iobase + PCI20341_MOD_STATUS); /* set Pacer */ | |
394 | writeb(option, sdp->iobase + PCI20341_OPT_REG); /* option register */ | |
395 | writeb(sdp->pci20341.settling_time, sdp->iobase + PCI20341_SET_TIME_REG); /* settling time counter */ | |
396 | /* trigger not implemented */ | |
397 | return 0; | |
398 | } | |
399 | ||
400 | static int pci20341_insn_read(comedi_device * dev, comedi_subdevice * s, | |
790c5541 | 401 | comedi_insn * insn, unsigned int * data) |
eb3a3c1b MK |
402 | { |
403 | pci20xxx_subdev_private *sdp = s->private; | |
404 | unsigned int i = 0, j = 0; | |
405 | int lo, hi; | |
406 | unsigned char eoc; /* end of conversion */ | |
407 | unsigned int clb; /* channel list byte */ | |
408 | unsigned int boarddata; | |
409 | ||
410 | writeb(1, sdp->iobase + PCI20341_LCHAN_ADDR_REG); /* write number of input channels */ | |
411 | clb = PCI20341_DAISY_CHAIN | PCI20341_MUX | (sdp->pci20341.ai_gain << 3) | |
412 | | CR_CHAN(insn->chanspec); | |
413 | writeb(clb, sdp->iobase + PCI20341_CHAN_LIST); | |
414 | writeb(0x00, sdp->iobase + PCI20341_CC_RESET); /* reset settling time counter and trigger delay counter */ | |
415 | writeb(0x00, sdp->iobase + PCI20341_CHAN_RESET); | |
416 | ||
417 | /* generate Pacer */ | |
418 | ||
419 | for (i = 0; i < insn->n; i++) { | |
420 | /* data polling isn't the niciest way to get the data, I know, | |
421 | * but there are only 6 cycles (mean) and it is easier than | |
422 | * the whole interrupt stuff | |
423 | */ | |
424 | j = 0; | |
425 | readb(sdp->iobase + PCI20341_SOFT_PACER); /* generate Pacer */ | |
426 | eoc = readb(sdp->iobase + PCI20341_STATUS_REG); | |
427 | while ((eoc < 0x80) && j < 100) { /* poll Interrupt Flag */ | |
428 | j++; | |
429 | eoc = readb(sdp->iobase + PCI20341_STATUS_REG); | |
430 | } | |
431 | if (j >= 100) { | |
432 | printk("comedi%d: pci20xxx: AI interrupt channel %i polling exit !\n", dev->minor, i); | |
433 | return -EINVAL; | |
434 | } | |
435 | lo = readb(sdp->iobase + PCI20341_LDATA); | |
436 | hi = readb(sdp->iobase + PCI20341_LDATA + 1); | |
437 | boarddata = lo + 0x100 * hi; | |
790c5541 | 438 | data[i] = (short) ((boarddata + 0x8000) & 0xffff); /* board-data -> comedi-data */ |
eb3a3c1b MK |
439 | } |
440 | ||
441 | return i; | |
442 | } | |
443 | ||
444 | /* native DIO */ | |
445 | ||
446 | static void pci20xxx_dio_config(comedi_device * dev, comedi_subdevice * s); | |
447 | static int pci20xxx_dio_insn_bits(comedi_device * dev, comedi_subdevice * s, | |
790c5541 | 448 | comedi_insn * insn, unsigned int * data); |
eb3a3c1b | 449 | static int pci20xxx_dio_insn_config(comedi_device * dev, comedi_subdevice * s, |
790c5541 | 450 | comedi_insn * insn, unsigned int * data); |
eb3a3c1b MK |
451 | |
452 | /* initialize pci20xxx_private */ | |
453 | static int pci20xxx_dio_init(comedi_device * dev, comedi_subdevice * s) | |
454 | { | |
455 | ||
456 | s->type = COMEDI_SUBD_DIO; | |
457 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
458 | s->n_chan = 32; | |
459 | s->insn_bits = pci20xxx_dio_insn_bits; | |
460 | s->insn_config = pci20xxx_dio_insn_config; | |
461 | s->maxdata = 1; | |
462 | s->len_chanlist = 32; | |
463 | s->range_table = &range_digital; | |
464 | s->io_bits = 0; | |
465 | ||
466 | /* digital I/O lines default to input on board reset. */ | |
467 | pci20xxx_dio_config(dev, s); | |
468 | ||
469 | return 0; | |
470 | } | |
471 | ||
472 | static int pci20xxx_dio_insn_config(comedi_device * dev, comedi_subdevice * s, | |
790c5541 | 473 | comedi_insn * insn, unsigned int * data) |
eb3a3c1b MK |
474 | { |
475 | int mask, bits; | |
476 | ||
477 | mask = 1 << CR_CHAN(insn->chanspec); | |
478 | if (mask & 0x000000ff) { | |
479 | bits = 0x000000ff; | |
480 | } else if (mask & 0x0000ff00) { | |
481 | bits = 0x0000ff00; | |
482 | } else if (mask & 0x00ff0000) { | |
483 | bits = 0x00ff0000; | |
484 | } else { | |
485 | bits = 0xff000000; | |
486 | } | |
487 | if (data[0]) { | |
488 | s->io_bits |= bits; | |
489 | } else { | |
490 | s->io_bits &= ~bits; | |
491 | } | |
492 | pci20xxx_dio_config(dev, s); | |
493 | ||
494 | return 1; | |
495 | } | |
496 | ||
497 | static int pci20xxx_dio_insn_bits(comedi_device * dev, comedi_subdevice * s, | |
790c5541 | 498 | comedi_insn * insn, unsigned int * data) |
eb3a3c1b MK |
499 | { |
500 | unsigned int mask = data[0]; | |
501 | ||
502 | s->state &= ~mask; | |
503 | s->state |= (mask & data[1]); | |
504 | ||
505 | mask &= s->io_bits; | |
506 | if (mask & 0x000000ff) | |
507 | writeb((s->state >> 0) & 0xff, | |
508 | devpriv->ioaddr + PCI20000_DIO_0); | |
509 | if (mask & 0x0000ff00) | |
510 | writeb((s->state >> 8) & 0xff, | |
511 | devpriv->ioaddr + PCI20000_DIO_1); | |
512 | if (mask & 0x00ff0000) | |
513 | writeb((s->state >> 16) & 0xff, | |
514 | devpriv->ioaddr + PCI20000_DIO_2); | |
515 | if (mask & 0xff000000) | |
516 | writeb((s->state >> 24) & 0xff, | |
517 | devpriv->ioaddr + PCI20000_DIO_3); | |
518 | ||
519 | data[1] = readb(devpriv->ioaddr + PCI20000_DIO_0); | |
520 | data[1] |= readb(devpriv->ioaddr + PCI20000_DIO_1) << 8; | |
521 | data[1] |= readb(devpriv->ioaddr + PCI20000_DIO_2) << 16; | |
522 | data[1] |= readb(devpriv->ioaddr + PCI20000_DIO_3) << 24; | |
523 | ||
524 | return 2; | |
525 | } | |
526 | ||
527 | static void pci20xxx_dio_config(comedi_device * dev, comedi_subdevice * s) | |
528 | { | |
529 | unsigned char control_01; | |
530 | unsigned char control_23; | |
531 | unsigned char buffer; | |
532 | ||
533 | control_01 = readb(devpriv->ioaddr + PCI20000_DIO_CONTROL_01); | |
534 | control_23 = readb(devpriv->ioaddr + PCI20000_DIO_CONTROL_23); | |
535 | buffer = readb(devpriv->ioaddr + PCI20000_DIO_BUFFER); | |
536 | ||
537 | if (s->io_bits & 0x000000ff) { | |
538 | /* output port 0 */ | |
539 | control_01 &= PCI20000_DIO_EOC; | |
540 | buffer = (buffer & (~(DIO_BE << DIO_PS_0))) | (DIO_BO << | |
541 | DIO_PS_0); | |
542 | } else { | |
543 | /* input port 0 */ | |
544 | control_01 = (control_01 & DIO_CAND) | PCI20000_DIO_EIC; | |
545 | buffer = (buffer & (~(DIO_BI << DIO_PS_0))); | |
546 | } | |
547 | if (s->io_bits & 0x0000ff00) { | |
548 | /* output port 1 */ | |
549 | control_01 &= PCI20000_DIO_OOC; | |
550 | buffer = (buffer & (~(DIO_BE << DIO_PS_1))) | (DIO_BO << | |
551 | DIO_PS_1); | |
552 | } else { | |
553 | /* input port 1 */ | |
554 | control_01 = (control_01 & DIO_CAND) | PCI20000_DIO_OIC; | |
555 | buffer = (buffer & (~(DIO_BI << DIO_PS_1))); | |
556 | } | |
557 | if (s->io_bits & 0x00ff0000) { | |
558 | /* output port 2 */ | |
559 | control_23 &= PCI20000_DIO_EOC; | |
560 | buffer = (buffer & (~(DIO_BE << DIO_PS_2))) | (DIO_BO << | |
561 | DIO_PS_2); | |
562 | } else { | |
563 | /* input port 2 */ | |
564 | control_23 = (control_23 & DIO_CAND) | PCI20000_DIO_EIC; | |
565 | buffer = (buffer & (~(DIO_BI << DIO_PS_2))); | |
566 | } | |
567 | if (s->io_bits & 0xff000000) { | |
568 | /* output port 3 */ | |
569 | control_23 &= PCI20000_DIO_OOC; | |
570 | buffer = (buffer & (~(DIO_BE << DIO_PS_3))) | (DIO_BO << | |
571 | DIO_PS_3); | |
572 | } else { | |
573 | /* input port 3 */ | |
574 | control_23 = (control_23 & DIO_CAND) | PCI20000_DIO_OIC; | |
575 | buffer = (buffer & (~(DIO_BI << DIO_PS_3))); | |
576 | } | |
577 | writeb(control_01, devpriv->ioaddr + PCI20000_DIO_CONTROL_01); | |
578 | writeb(control_23, devpriv->ioaddr + PCI20000_DIO_CONTROL_23); | |
579 | writeb(buffer, devpriv->ioaddr + PCI20000_DIO_BUFFER); | |
580 | } | |
581 | ||
582 | #if 0 | |
583 | static void pci20xxx_do(comedi_device * dev, comedi_subdevice * s) | |
584 | { | |
585 | /* XXX if the channel is configured for input, does this | |
586 | do bad things? */ | |
587 | /* XXX it would be a good idea to only update the registers | |
588 | that _need_ to be updated. This requires changes to | |
589 | comedi, however. */ | |
590 | writeb((s->state >> 0) & 0xff, devpriv->ioaddr + PCI20000_DIO_0); | |
591 | writeb((s->state >> 8) & 0xff, devpriv->ioaddr + PCI20000_DIO_1); | |
592 | writeb((s->state >> 16) & 0xff, devpriv->ioaddr + PCI20000_DIO_2); | |
593 | writeb((s->state >> 24) & 0xff, devpriv->ioaddr + PCI20000_DIO_3); | |
594 | } | |
595 | ||
596 | static unsigned int pci20xxx_di(comedi_device * dev, comedi_subdevice * s) | |
597 | { | |
598 | /* XXX same note as above */ | |
599 | unsigned int bits; | |
600 | ||
601 | bits = readb(devpriv->ioaddr + PCI20000_DIO_0); | |
602 | bits |= readb(devpriv->ioaddr + PCI20000_DIO_1) << 8; | |
603 | bits |= readb(devpriv->ioaddr + PCI20000_DIO_2) << 16; | |
604 | bits |= readb(devpriv->ioaddr + PCI20000_DIO_3) << 24; | |
605 | ||
606 | return bits; | |
607 | } | |
608 | #endif | |
609 | ||
610 | COMEDI_INITCLEANUP(driver_pci20xxx); |