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 | |
f0922ec5 DS |
33 | */ |
34 | ||
ce157f80 | 35 | #include <linux/module.h> |
25436dc9 | 36 | #include <linux/interrupt.h> |
f0922ec5 | 37 | #include <linux/delay.h> |
f0922ec5 | 38 | |
5df18b6c | 39 | #include "../comedi_pcmcia.h" |
f0922ec5 | 40 | |
a90feb7f | 41 | #include "comedi_8254.h" |
f0922ec5 | 42 | |
f0922ec5 DS |
43 | #define DAS16CS_ADC_DATA 0 |
44 | #define DAS16CS_DIO_MUX 2 | |
45 | #define DAS16CS_MISC1 4 | |
46 | #define DAS16CS_MISC2 6 | |
a90feb7f | 47 | #define DAS16CS_TIMER_BASE 8 |
f0922ec5 DS |
48 | #define DAS16CS_DIO 16 |
49 | ||
3281a63d | 50 | struct das16cs_board { |
f0922ec5 DS |
51 | const char *name; |
52 | int device_id; | |
53 | int n_ao_chans; | |
3281a63d | 54 | }; |
13ab179c | 55 | |
3281a63d | 56 | static const struct das16cs_board das16cs_boards[] = { |
f0922ec5 | 57 | { |
13ab179c HS |
58 | .name = "PC-CARD DAS16/16-AO", |
59 | .device_id = 0x0039, | |
60 | .n_ao_chans = 2, | |
61 | }, { | |
62 | .name = "PCM-DAS16s/16", | |
63 | .device_id = 0x4009, | |
64 | .n_ao_chans = 0, | |
65 | }, { | |
66 | .name = "PC-CARD DAS16/16", | |
67 | .device_id = 0x0000, /* unknown */ | |
68 | .n_ao_chans = 0, | |
69 | }, | |
f0922ec5 DS |
70 | }; |
71 | ||
bfae362a | 72 | struct das16cs_private { |
f0922ec5 DS |
73 | unsigned short status1; |
74 | unsigned short status2; | |
bfae362a | 75 | }; |
f0922ec5 | 76 | |
443bbedb HS |
77 | static const struct comedi_lrange das16cs_ai_range = { |
78 | 4, { | |
79 | BIP_RANGE(10), | |
80 | BIP_RANGE(5), | |
81 | BIP_RANGE(2.5), | |
82 | BIP_RANGE(1.25), | |
83 | } | |
f0922ec5 DS |
84 | }; |
85 | ||
e467bc37 HS |
86 | static int das16cs_ai_eoc(struct comedi_device *dev, |
87 | struct comedi_subdevice *s, | |
88 | struct comedi_insn *insn, | |
89 | unsigned long context) | |
90 | { | |
91 | unsigned int status; | |
92 | ||
93 | status = inw(dev->iobase + DAS16CS_MISC1); | |
94 | if (status & 0x0080) | |
95 | return 0; | |
96 | return -EBUSY; | |
97 | } | |
98 | ||
0a85b6f0 MT |
99 | static int das16cs_ai_rinsn(struct comedi_device *dev, |
100 | struct comedi_subdevice *s, | |
101 | struct comedi_insn *insn, unsigned int *data) | |
f0922ec5 | 102 | { |
16a8cdfc | 103 | struct das16cs_private *devpriv = dev->private; |
e05b98b6 HS |
104 | int chan = CR_CHAN(insn->chanspec); |
105 | int range = CR_RANGE(insn->chanspec); | |
106 | int aref = CR_AREF(insn->chanspec); | |
e467bc37 | 107 | int ret; |
f0922ec5 | 108 | int i; |
f0922ec5 | 109 | |
ae1a4000 | 110 | outw(chan, dev->iobase + DAS16CS_DIO_MUX); |
f0922ec5 DS |
111 | |
112 | devpriv->status1 &= ~0xf320; | |
113 | devpriv->status1 |= (aref == AREF_DIFF) ? 0 : 0x0020; | |
ae1a4000 | 114 | outw(devpriv->status1, dev->iobase + DAS16CS_MISC1); |
f0922ec5 DS |
115 | |
116 | devpriv->status2 &= ~0xff00; | |
e05b98b6 HS |
117 | switch (range) { |
118 | case 0: | |
119 | devpriv->status2 |= 0x800; | |
120 | break; | |
121 | case 1: | |
122 | devpriv->status2 |= 0x000; | |
123 | break; | |
124 | case 2: | |
125 | devpriv->status2 |= 0x100; | |
126 | break; | |
127 | case 3: | |
128 | devpriv->status2 |= 0x200; | |
129 | break; | |
e05b98b6 | 130 | } |
ae1a4000 | 131 | outw(devpriv->status2, dev->iobase + DAS16CS_MISC2); |
f0922ec5 DS |
132 | |
133 | for (i = 0; i < insn->n; i++) { | |
ae1a4000 | 134 | outw(0, dev->iobase + DAS16CS_ADC_DATA); |
f0922ec5 | 135 | |
e467bc37 | 136 | ret = comedi_timeout(dev, s, insn, das16cs_ai_eoc, 0); |
22ca19d9 | 137 | if (ret) |
e467bc37 | 138 | return ret; |
e467bc37 | 139 | |
ae1a4000 | 140 | data[i] = inw(dev->iobase + DAS16CS_ADC_DATA); |
f0922ec5 DS |
141 | } |
142 | ||
143 | return i; | |
144 | } | |
145 | ||
6b00f53e HS |
146 | static int das16cs_ao_insn_write(struct comedi_device *dev, |
147 | struct comedi_subdevice *s, | |
148 | struct comedi_insn *insn, | |
149 | unsigned int *data) | |
f0922ec5 | 150 | { |
16a8cdfc | 151 | struct das16cs_private *devpriv = dev->private; |
6b00f53e HS |
152 | unsigned int chan = CR_CHAN(insn->chanspec); |
153 | unsigned int val = s->readback[chan]; | |
f0922ec5 | 154 | unsigned short status1; |
f0922ec5 | 155 | int bit; |
6b00f53e | 156 | int i; |
f0922ec5 DS |
157 | |
158 | for (i = 0; i < insn->n; i++) { | |
6b00f53e | 159 | val = data[i]; |
f0922ec5 | 160 | |
ae1a4000 | 161 | outw(devpriv->status1, dev->iobase + DAS16CS_MISC1); |
5f74ea14 | 162 | udelay(1); |
f0922ec5 DS |
163 | |
164 | status1 = devpriv->status1 & ~0xf; | |
165 | if (chan) | |
166 | status1 |= 0x0001; | |
167 | else | |
168 | status1 |= 0x0008; | |
169 | ||
ae1a4000 | 170 | outw(status1, dev->iobase + DAS16CS_MISC1); |
5f74ea14 | 171 | udelay(1); |
f0922ec5 DS |
172 | |
173 | for (bit = 15; bit >= 0; bit--) { | |
6b00f53e | 174 | int b = (val >> bit) & 0x1; |
995e13b9 | 175 | |
f0922ec5 | 176 | b <<= 1; |
ae1a4000 | 177 | outw(status1 | b | 0x0000, dev->iobase + DAS16CS_MISC1); |
5f74ea14 | 178 | udelay(1); |
ae1a4000 | 179 | outw(status1 | b | 0x0004, dev->iobase + DAS16CS_MISC1); |
5f74ea14 | 180 | udelay(1); |
f0922ec5 | 181 | } |
0a77678c HS |
182 | /* |
183 | * Make both DAC0CS and DAC1CS high to load | |
184 | * the new data and update analog the output | |
185 | */ | |
ae1a4000 | 186 | outw(status1 | 0x9, dev->iobase + DAS16CS_MISC1); |
f0922ec5 | 187 | } |
6b00f53e | 188 | s->readback[chan] = val; |
f0922ec5 | 189 | |
6b00f53e | 190 | return insn->n; |
f0922ec5 DS |
191 | } |
192 | ||
0a85b6f0 MT |
193 | static int das16cs_dio_insn_bits(struct comedi_device *dev, |
194 | struct comedi_subdevice *s, | |
97f4289a HS |
195 | struct comedi_insn *insn, |
196 | unsigned int *data) | |
f0922ec5 | 197 | { |
97f4289a | 198 | if (comedi_dio_update_state(s, data)) |
ae1a4000 | 199 | outw(s->state, dev->iobase + DAS16CS_DIO); |
f0922ec5 | 200 | |
ae1a4000 | 201 | data[1] = inw(dev->iobase + DAS16CS_DIO); |
f0922ec5 | 202 | |
a2714e3e | 203 | return insn->n; |
f0922ec5 DS |
204 | } |
205 | ||
0a85b6f0 MT |
206 | static int das16cs_dio_insn_config(struct comedi_device *dev, |
207 | struct comedi_subdevice *s, | |
5dacadcc HS |
208 | struct comedi_insn *insn, |
209 | unsigned int *data) | |
f0922ec5 | 210 | { |
16a8cdfc | 211 | struct das16cs_private *devpriv = dev->private; |
5dacadcc HS |
212 | unsigned int chan = CR_CHAN(insn->chanspec); |
213 | unsigned int mask; | |
214 | int ret; | |
f0922ec5 DS |
215 | |
216 | if (chan < 4) | |
5dacadcc | 217 | mask = 0x0f; |
f0922ec5 | 218 | else |
5dacadcc | 219 | mask = 0xf0; |
f0922ec5 | 220 | |
5dacadcc HS |
221 | ret = comedi_dio_insn_config(dev, s, insn, data, mask); |
222 | if (ret) | |
223 | return ret; | |
f0922ec5 DS |
224 | |
225 | devpriv->status2 &= ~0x00c0; | |
226 | devpriv->status2 |= (s->io_bits & 0xf0) ? 0x0080 : 0; | |
227 | devpriv->status2 |= (s->io_bits & 0x0f) ? 0x0040 : 0; | |
228 | ||
ae1a4000 | 229 | outw(devpriv->status2, dev->iobase + DAS16CS_MISC2); |
f0922ec5 DS |
230 | |
231 | return insn->n; | |
232 | } | |
233 | ||
2bdaef1a HS |
234 | static const void *das16cs_find_boardinfo(struct comedi_device *dev, |
235 | struct pcmcia_device *link) | |
ae7df434 | 236 | { |
2bdaef1a | 237 | const struct das16cs_board *board; |
ae7df434 HS |
238 | int i; |
239 | ||
1efc5d53 | 240 | for (i = 0; i < ARRAY_SIZE(das16cs_boards); i++) { |
2bdaef1a HS |
241 | board = &das16cs_boards[i]; |
242 | if (board->device_id == link->card_id) | |
243 | return board; | |
ae7df434 HS |
244 | } |
245 | ||
ae7df434 HS |
246 | return NULL; |
247 | } | |
248 | ||
2bdaef1a HS |
249 | static int das16cs_auto_attach(struct comedi_device *dev, |
250 | unsigned long context) | |
ae7df434 | 251 | { |
2bdaef1a HS |
252 | struct pcmcia_device *link = comedi_to_pcmcia_dev(dev); |
253 | const struct das16cs_board *board; | |
9a1a6cf8 | 254 | struct das16cs_private *devpriv; |
ae7df434 HS |
255 | struct comedi_subdevice *s; |
256 | int ret; | |
ae7df434 | 257 | |
2bdaef1a HS |
258 | board = das16cs_find_boardinfo(dev, link); |
259 | if (!board) | |
260 | return -ENODEV; | |
261 | dev->board_ptr = board; | |
262 | dev->board_name = board->name; | |
ae7df434 | 263 | |
9e29ce10 | 264 | link->config_flags |= CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; |
a3ac9519 | 265 | ret = comedi_pcmcia_enable(dev, NULL); |
2bdaef1a HS |
266 | if (ret) |
267 | return ret; | |
9e29ce10 | 268 | dev->iobase = link->resource[0]->start; |
2bdaef1a | 269 | |
bcd22e8e | 270 | link->priv = dev; |
ae7df434 | 271 | |
0bdab509 | 272 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
c34fa261 HS |
273 | if (!devpriv) |
274 | return -ENOMEM; | |
ae7df434 | 275 | |
a90feb7f HS |
276 | dev->pacer = comedi_8254_init(dev->iobase + DAS16CS_TIMER_BASE, |
277 | I8254_OSC_BASE_10MHZ, I8254_IO16, 0); | |
278 | if (!dev->pacer) | |
279 | return -ENOMEM; | |
280 | ||
5c416ef3 | 281 | ret = comedi_alloc_subdevices(dev, 3); |
ae7df434 HS |
282 | if (ret) |
283 | return ret; | |
284 | ||
3d69288a | 285 | s = &dev->subdevices[0]; |
ae7df434 | 286 | /* analog input subdevice */ |
cf2592d0 HS |
287 | s->type = COMEDI_SUBD_AI; |
288 | s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ; | |
289 | s->n_chan = 16; | |
290 | s->maxdata = 0xffff; | |
291 | s->range_table = &das16cs_ai_range; | |
292 | s->len_chanlist = 16; | |
293 | s->insn_read = das16cs_ai_rinsn; | |
ae7df434 | 294 | |
3d69288a | 295 | s = &dev->subdevices[1]; |
ae7df434 | 296 | /* analog output subdevice */ |
2bdaef1a | 297 | if (board->n_ao_chans) { |
cf2592d0 HS |
298 | s->type = COMEDI_SUBD_AO; |
299 | s->subdev_flags = SDF_WRITABLE; | |
2bdaef1a | 300 | s->n_chan = board->n_ao_chans; |
cf2592d0 HS |
301 | s->maxdata = 0xffff; |
302 | s->range_table = &range_bipolar10; | |
6b00f53e | 303 | s->insn_write = &das16cs_ao_insn_write; |
6b00f53e HS |
304 | |
305 | ret = comedi_alloc_subdev_readback(s); | |
306 | if (ret) | |
307 | return ret; | |
da7fde2b | 308 | } else { |
cf2592d0 | 309 | s->type = COMEDI_SUBD_UNUSED; |
ae7df434 HS |
310 | } |
311 | ||
3d69288a | 312 | s = &dev->subdevices[2]; |
ae7df434 | 313 | /* digital i/o subdevice */ |
cf2592d0 HS |
314 | s->type = COMEDI_SUBD_DIO; |
315 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
316 | s->n_chan = 8; | |
317 | s->maxdata = 1; | |
318 | s->range_table = &range_digital; | |
319 | s->insn_bits = das16cs_dio_insn_bits; | |
320 | s->insn_config = das16cs_dio_insn_config; | |
ae7df434 | 321 | |
a659643a | 322 | return 0; |
ae7df434 HS |
323 | } |
324 | ||
2f018267 HS |
325 | static struct comedi_driver driver_das16cs = { |
326 | .driver_name = "cb_das16_cs", | |
327 | .module = THIS_MODULE, | |
2bdaef1a | 328 | .auto_attach = das16cs_auto_attach, |
7fadd565 | 329 | .detach = comedi_pcmcia_disable, |
2f018267 HS |
330 | }; |
331 | ||
c2107ff4 | 332 | static int das16cs_pcmcia_attach(struct pcmcia_device *link) |
55a19b39 | 333 | { |
2bdaef1a | 334 | return comedi_pcmcia_auto_config(link, &driver_das16cs); |
1b19f53e | 335 | } |
f0922ec5 | 336 | |
2202a5a7 | 337 | static const struct pcmcia_device_id das16cs_id_table[] = { |
f0922ec5 DS |
338 | PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0039), |
339 | PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4009), | |
340 | PCMCIA_DEVICE_NULL | |
341 | }; | |
f0922ec5 DS |
342 | MODULE_DEVICE_TABLE(pcmcia, das16cs_id_table); |
343 | ||
e8bed9c6 HS |
344 | static struct pcmcia_driver das16cs_driver = { |
345 | .name = "cb_das16_cs", | |
346 | .owner = THIS_MODULE, | |
e8bed9c6 | 347 | .id_table = das16cs_id_table, |
2bdaef1a HS |
348 | .probe = das16cs_pcmcia_attach, |
349 | .remove = comedi_pcmcia_auto_unconfig, | |
f0922ec5 | 350 | }; |
66bf1ed6 | 351 | module_comedi_pcmcia_driver(driver_das16cs, das16cs_driver); |
16920671 HS |
352 | |
353 | MODULE_AUTHOR("David A. Schleef <ds@schleef.org>"); | |
354 | MODULE_DESCRIPTION("Comedi driver for Computer Boards PC-CARD DAS16/16"); | |
355 | MODULE_LICENSE("GPL"); |