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