Commit | Line | Data |
---|---|---|
f0922ec5 DS |
1 | /* |
2 | comedi/drivers/das16cs.c | |
3 | Driver for Computer Boards PC-CARD DAS16/16. | |
4 | ||
5 | COMEDI - Linux Control and Measurement Device Interface | |
6 | Copyright (C) 2000, 2001, 2002 David A. Schleef <ds@schleef.org> | |
7 | ||
8 | This program is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 2 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | This program is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
cd1a76fb HS |
18 | PCMCIA support code for this driver is adapted from the dummy_cs.c |
19 | driver of the Linux PCMCIA Card Services package. | |
1b19f53e HS |
20 | |
21 | The initial developer of the original code is David A. Hinds | |
22 | <dahinds@users.sourceforge.net>. Portions created by David A. Hinds | |
23 | are Copyright (C) 1999 David A. Hinds. All Rights Reserved. | |
24 | ||
f0922ec5 DS |
25 | */ |
26 | /* | |
27 | Driver: cb_das16_cs | |
28 | Description: Computer Boards PC-CARD DAS16/16 | |
29 | Devices: [ComputerBoards] PC-CARD DAS16/16 (cb_das16_cs), PC-CARD DAS16/16-AO | |
30 | Author: ds | |
31 | Updated: Mon, 04 Nov 2002 20:04:21 -0800 | |
32 | Status: experimental | |
33 | ||
34 | ||
35 | */ | |
36 | ||
ce157f80 | 37 | #include <linux/module.h> |
25436dc9 | 38 | #include <linux/interrupt.h> |
f0922ec5 | 39 | #include <linux/delay.h> |
f0922ec5 | 40 | |
2bdaef1a HS |
41 | #include "../comedidev.h" |
42 | ||
f0922ec5 DS |
43 | #include <pcmcia/cistpl.h> |
44 | #include <pcmcia/ds.h> | |
45 | ||
27020ffe | 46 | #include "comedi_fc.h" |
f0922ec5 DS |
47 | #include "8253.h" |
48 | ||
f0922ec5 DS |
49 | #define DAS16CS_ADC_DATA 0 |
50 | #define DAS16CS_DIO_MUX 2 | |
51 | #define DAS16CS_MISC1 4 | |
52 | #define DAS16CS_MISC2 6 | |
53 | #define DAS16CS_CTR0 8 | |
54 | #define DAS16CS_CTR1 10 | |
55 | #define DAS16CS_CTR2 12 | |
56 | #define DAS16CS_CTR_CONTROL 14 | |
57 | #define DAS16CS_DIO 16 | |
58 | ||
3281a63d | 59 | struct das16cs_board { |
f0922ec5 DS |
60 | const char *name; |
61 | int device_id; | |
62 | int n_ao_chans; | |
3281a63d | 63 | }; |
13ab179c | 64 | |
3281a63d | 65 | static const struct das16cs_board das16cs_boards[] = { |
f0922ec5 | 66 | { |
13ab179c HS |
67 | .name = "PC-CARD DAS16/16-AO", |
68 | .device_id = 0x0039, | |
69 | .n_ao_chans = 2, | |
70 | }, { | |
71 | .name = "PCM-DAS16s/16", | |
72 | .device_id = 0x4009, | |
73 | .n_ao_chans = 0, | |
74 | }, { | |
75 | .name = "PC-CARD DAS16/16", | |
76 | .device_id = 0x0000, /* unknown */ | |
77 | .n_ao_chans = 0, | |
78 | }, | |
f0922ec5 DS |
79 | }; |
80 | ||
bfae362a | 81 | struct das16cs_private { |
790c5541 | 82 | unsigned int ao_readback[2]; |
f0922ec5 DS |
83 | unsigned short status1; |
84 | unsigned short status2; | |
bfae362a | 85 | }; |
f0922ec5 | 86 | |
443bbedb HS |
87 | static const struct comedi_lrange das16cs_ai_range = { |
88 | 4, { | |
89 | BIP_RANGE(10), | |
90 | BIP_RANGE(5), | |
91 | BIP_RANGE(2.5), | |
92 | BIP_RANGE(1.25), | |
93 | } | |
f0922ec5 DS |
94 | }; |
95 | ||
e467bc37 HS |
96 | static int das16cs_ai_eoc(struct comedi_device *dev, |
97 | struct comedi_subdevice *s, | |
98 | struct comedi_insn *insn, | |
99 | unsigned long context) | |
100 | { | |
101 | unsigned int status; | |
102 | ||
103 | status = inw(dev->iobase + DAS16CS_MISC1); | |
104 | if (status & 0x0080) | |
105 | return 0; | |
106 | return -EBUSY; | |
107 | } | |
108 | ||
0a85b6f0 MT |
109 | static int das16cs_ai_rinsn(struct comedi_device *dev, |
110 | struct comedi_subdevice *s, | |
111 | struct comedi_insn *insn, unsigned int *data) | |
f0922ec5 | 112 | { |
16a8cdfc | 113 | struct das16cs_private *devpriv = dev->private; |
e05b98b6 HS |
114 | int chan = CR_CHAN(insn->chanspec); |
115 | int range = CR_RANGE(insn->chanspec); | |
116 | int aref = CR_AREF(insn->chanspec); | |
e467bc37 | 117 | int ret; |
f0922ec5 | 118 | int i; |
f0922ec5 | 119 | |
ae1a4000 | 120 | outw(chan, dev->iobase + DAS16CS_DIO_MUX); |
f0922ec5 DS |
121 | |
122 | devpriv->status1 &= ~0xf320; | |
123 | devpriv->status1 |= (aref == AREF_DIFF) ? 0 : 0x0020; | |
ae1a4000 | 124 | outw(devpriv->status1, dev->iobase + DAS16CS_MISC1); |
f0922ec5 DS |
125 | |
126 | devpriv->status2 &= ~0xff00; | |
e05b98b6 HS |
127 | switch (range) { |
128 | case 0: | |
129 | devpriv->status2 |= 0x800; | |
130 | break; | |
131 | case 1: | |
132 | devpriv->status2 |= 0x000; | |
133 | break; | |
134 | case 2: | |
135 | devpriv->status2 |= 0x100; | |
136 | break; | |
137 | case 3: | |
138 | devpriv->status2 |= 0x200; | |
139 | break; | |
e05b98b6 | 140 | } |
ae1a4000 | 141 | outw(devpriv->status2, dev->iobase + DAS16CS_MISC2); |
f0922ec5 DS |
142 | |
143 | for (i = 0; i < insn->n; i++) { | |
ae1a4000 | 144 | outw(0, dev->iobase + DAS16CS_ADC_DATA); |
f0922ec5 | 145 | |
e467bc37 | 146 | ret = comedi_timeout(dev, s, insn, das16cs_ai_eoc, 0); |
22ca19d9 | 147 | if (ret) |
e467bc37 | 148 | return ret; |
e467bc37 | 149 | |
ae1a4000 | 150 | data[i] = inw(dev->iobase + DAS16CS_ADC_DATA); |
f0922ec5 DS |
151 | } |
152 | ||
153 | return i; | |
154 | } | |
155 | ||
0a85b6f0 MT |
156 | static int das16cs_ao_winsn(struct comedi_device *dev, |
157 | struct comedi_subdevice *s, | |
158 | struct comedi_insn *insn, unsigned int *data) | |
f0922ec5 | 159 | { |
16a8cdfc | 160 | struct das16cs_private *devpriv = dev->private; |
f0922ec5 DS |
161 | int i; |
162 | int chan = CR_CHAN(insn->chanspec); | |
163 | unsigned short status1; | |
164 | unsigned short d; | |
165 | int bit; | |
166 | ||
167 | for (i = 0; i < insn->n; i++) { | |
168 | devpriv->ao_readback[chan] = data[i]; | |
169 | d = data[i]; | |
170 | ||
ae1a4000 | 171 | outw(devpriv->status1, dev->iobase + DAS16CS_MISC1); |
5f74ea14 | 172 | udelay(1); |
f0922ec5 DS |
173 | |
174 | status1 = devpriv->status1 & ~0xf; | |
175 | if (chan) | |
176 | status1 |= 0x0001; | |
177 | else | |
178 | status1 |= 0x0008; | |
179 | ||
ae1a4000 | 180 | outw(status1, dev->iobase + DAS16CS_MISC1); |
5f74ea14 | 181 | udelay(1); |
f0922ec5 DS |
182 | |
183 | for (bit = 15; bit >= 0; bit--) { | |
184 | int b = (d >> bit) & 0x1; | |
995e13b9 | 185 | |
f0922ec5 | 186 | b <<= 1; |
ae1a4000 | 187 | outw(status1 | b | 0x0000, dev->iobase + DAS16CS_MISC1); |
5f74ea14 | 188 | udelay(1); |
ae1a4000 | 189 | outw(status1 | b | 0x0004, dev->iobase + DAS16CS_MISC1); |
5f74ea14 | 190 | udelay(1); |
f0922ec5 | 191 | } |
0a77678c HS |
192 | /* |
193 | * Make both DAC0CS and DAC1CS high to load | |
194 | * the new data and update analog the output | |
195 | */ | |
ae1a4000 | 196 | outw(status1 | 0x9, dev->iobase + DAS16CS_MISC1); |
f0922ec5 DS |
197 | } |
198 | ||
199 | return i; | |
200 | } | |
201 | ||
0a85b6f0 MT |
202 | static int das16cs_ao_rinsn(struct comedi_device *dev, |
203 | struct comedi_subdevice *s, | |
204 | struct comedi_insn *insn, unsigned int *data) | |
f0922ec5 | 205 | { |
16a8cdfc | 206 | struct das16cs_private *devpriv = dev->private; |
f0922ec5 DS |
207 | int i; |
208 | int chan = CR_CHAN(insn->chanspec); | |
209 | ||
210 | for (i = 0; i < insn->n; i++) | |
211 | data[i] = devpriv->ao_readback[chan]; | |
212 | ||
213 | return i; | |
214 | } | |
215 | ||
0a85b6f0 MT |
216 | static int das16cs_dio_insn_bits(struct comedi_device *dev, |
217 | struct comedi_subdevice *s, | |
97f4289a HS |
218 | struct comedi_insn *insn, |
219 | unsigned int *data) | |
f0922ec5 | 220 | { |
97f4289a | 221 | if (comedi_dio_update_state(s, data)) |
ae1a4000 | 222 | outw(s->state, dev->iobase + DAS16CS_DIO); |
f0922ec5 | 223 | |
ae1a4000 | 224 | data[1] = inw(dev->iobase + DAS16CS_DIO); |
f0922ec5 | 225 | |
a2714e3e | 226 | return insn->n; |
f0922ec5 DS |
227 | } |
228 | ||
0a85b6f0 MT |
229 | static int das16cs_dio_insn_config(struct comedi_device *dev, |
230 | struct comedi_subdevice *s, | |
5dacadcc HS |
231 | struct comedi_insn *insn, |
232 | unsigned int *data) | |
f0922ec5 | 233 | { |
16a8cdfc | 234 | struct das16cs_private *devpriv = dev->private; |
5dacadcc HS |
235 | unsigned int chan = CR_CHAN(insn->chanspec); |
236 | unsigned int mask; | |
237 | int ret; | |
f0922ec5 DS |
238 | |
239 | if (chan < 4) | |
5dacadcc | 240 | mask = 0x0f; |
f0922ec5 | 241 | else |
5dacadcc | 242 | mask = 0xf0; |
f0922ec5 | 243 | |
5dacadcc HS |
244 | ret = comedi_dio_insn_config(dev, s, insn, data, mask); |
245 | if (ret) | |
246 | return ret; | |
f0922ec5 DS |
247 | |
248 | devpriv->status2 &= ~0x00c0; | |
249 | devpriv->status2 |= (s->io_bits & 0xf0) ? 0x0080 : 0; | |
250 | devpriv->status2 |= (s->io_bits & 0x0f) ? 0x0040 : 0; | |
251 | ||
ae1a4000 | 252 | outw(devpriv->status2, dev->iobase + DAS16CS_MISC2); |
f0922ec5 DS |
253 | |
254 | return insn->n; | |
255 | } | |
256 | ||
2bdaef1a HS |
257 | static const void *das16cs_find_boardinfo(struct comedi_device *dev, |
258 | struct pcmcia_device *link) | |
ae7df434 | 259 | { |
2bdaef1a | 260 | const struct das16cs_board *board; |
ae7df434 HS |
261 | int i; |
262 | ||
1efc5d53 | 263 | for (i = 0; i < ARRAY_SIZE(das16cs_boards); i++) { |
2bdaef1a HS |
264 | board = &das16cs_boards[i]; |
265 | if (board->device_id == link->card_id) | |
266 | return board; | |
ae7df434 HS |
267 | } |
268 | ||
ae7df434 HS |
269 | return NULL; |
270 | } | |
271 | ||
2bdaef1a HS |
272 | static int das16cs_auto_attach(struct comedi_device *dev, |
273 | unsigned long context) | |
ae7df434 | 274 | { |
2bdaef1a HS |
275 | struct pcmcia_device *link = comedi_to_pcmcia_dev(dev); |
276 | const struct das16cs_board *board; | |
9a1a6cf8 | 277 | struct das16cs_private *devpriv; |
ae7df434 HS |
278 | struct comedi_subdevice *s; |
279 | int ret; | |
ae7df434 | 280 | |
2bdaef1a HS |
281 | board = das16cs_find_boardinfo(dev, link); |
282 | if (!board) | |
283 | return -ENODEV; | |
284 | dev->board_ptr = board; | |
285 | dev->board_name = board->name; | |
ae7df434 | 286 | |
9e29ce10 | 287 | link->config_flags |= CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; |
a3ac9519 | 288 | ret = comedi_pcmcia_enable(dev, NULL); |
2bdaef1a HS |
289 | if (ret) |
290 | return ret; | |
9e29ce10 | 291 | dev->iobase = link->resource[0]->start; |
2bdaef1a | 292 | |
bcd22e8e | 293 | link->priv = dev; |
ae7df434 | 294 | |
0bdab509 | 295 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
c34fa261 HS |
296 | if (!devpriv) |
297 | return -ENOMEM; | |
ae7df434 | 298 | |
5c416ef3 | 299 | ret = comedi_alloc_subdevices(dev, 3); |
ae7df434 HS |
300 | if (ret) |
301 | return ret; | |
302 | ||
3d69288a | 303 | s = &dev->subdevices[0]; |
ae7df434 | 304 | /* analog input subdevice */ |
cf2592d0 HS |
305 | s->type = COMEDI_SUBD_AI; |
306 | s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ; | |
307 | s->n_chan = 16; | |
308 | s->maxdata = 0xffff; | |
309 | s->range_table = &das16cs_ai_range; | |
310 | s->len_chanlist = 16; | |
311 | s->insn_read = das16cs_ai_rinsn; | |
ae7df434 | 312 | |
3d69288a | 313 | s = &dev->subdevices[1]; |
ae7df434 | 314 | /* analog output subdevice */ |
2bdaef1a | 315 | if (board->n_ao_chans) { |
cf2592d0 HS |
316 | s->type = COMEDI_SUBD_AO; |
317 | s->subdev_flags = SDF_WRITABLE; | |
2bdaef1a | 318 | s->n_chan = board->n_ao_chans; |
cf2592d0 HS |
319 | s->maxdata = 0xffff; |
320 | s->range_table = &range_bipolar10; | |
321 | s->insn_write = &das16cs_ao_winsn; | |
322 | s->insn_read = &das16cs_ao_rinsn; | |
da7fde2b | 323 | } else { |
cf2592d0 | 324 | s->type = COMEDI_SUBD_UNUSED; |
ae7df434 HS |
325 | } |
326 | ||
3d69288a | 327 | s = &dev->subdevices[2]; |
ae7df434 | 328 | /* digital i/o subdevice */ |
cf2592d0 HS |
329 | s->type = COMEDI_SUBD_DIO; |
330 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
331 | s->n_chan = 8; | |
332 | s->maxdata = 1; | |
333 | s->range_table = &range_digital; | |
334 | s->insn_bits = das16cs_dio_insn_bits; | |
335 | s->insn_config = das16cs_dio_insn_config; | |
ae7df434 | 336 | |
a659643a | 337 | return 0; |
ae7df434 HS |
338 | } |
339 | ||
2f018267 HS |
340 | static struct comedi_driver driver_das16cs = { |
341 | .driver_name = "cb_das16_cs", | |
342 | .module = THIS_MODULE, | |
2bdaef1a | 343 | .auto_attach = das16cs_auto_attach, |
7fadd565 | 344 | .detach = comedi_pcmcia_disable, |
2f018267 HS |
345 | }; |
346 | ||
c2107ff4 | 347 | static int das16cs_pcmcia_attach(struct pcmcia_device *link) |
55a19b39 | 348 | { |
2bdaef1a | 349 | return comedi_pcmcia_auto_config(link, &driver_das16cs); |
1b19f53e | 350 | } |
f0922ec5 | 351 | |
2202a5a7 | 352 | static const struct pcmcia_device_id das16cs_id_table[] = { |
f0922ec5 DS |
353 | PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0039), |
354 | PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4009), | |
355 | PCMCIA_DEVICE_NULL | |
356 | }; | |
f0922ec5 DS |
357 | MODULE_DEVICE_TABLE(pcmcia, das16cs_id_table); |
358 | ||
e8bed9c6 HS |
359 | static struct pcmcia_driver das16cs_driver = { |
360 | .name = "cb_das16_cs", | |
361 | .owner = THIS_MODULE, | |
e8bed9c6 | 362 | .id_table = das16cs_id_table, |
2bdaef1a HS |
363 | .probe = das16cs_pcmcia_attach, |
364 | .remove = comedi_pcmcia_auto_unconfig, | |
f0922ec5 | 365 | }; |
66bf1ed6 | 366 | module_comedi_pcmcia_driver(driver_das16cs, das16cs_driver); |
16920671 HS |
367 | |
368 | MODULE_AUTHOR("David A. Schleef <ds@schleef.org>"); | |
369 | MODULE_DESCRIPTION("Comedi driver for Computer Boards PC-CARD DAS16/16"); | |
370 | MODULE_LICENSE("GPL"); |