Commit | Line | Data |
---|---|---|
2c89e159 | 1 | /* |
cc284a09 HS |
2 | * c6xdigio.c |
3 | * Hardware driver for Mechatronic Systems Inc. C6x_DIGIO DSP daughter card. | |
4 | * http://web.archive.org/web/%2A/http://robot0.ge.uiuc.edu/~spong/mecha/ | |
5 | * | |
6 | * COMEDI - Linux Control and Measurement Device Interface | |
7 | * Copyright (C) 1999 Dan Block | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
2c89e159 | 18 | */ |
2c89e159 | 19 | |
cc284a09 HS |
20 | /* |
21 | * Driver: c6xdigio | |
22 | * Description: Mechatronic Systems Inc. C6x_DIGIO DSP daughter card | |
23 | * Author: Dan Block | |
24 | * Status: unknown | |
cd07d33a | 25 | * Devices: [Mechatronic Systems Inc.] C6x_DIGIO DSP daughter card (c6xdigio) |
cc284a09 HS |
26 | * Updated: Sun Nov 20 20:18:34 EST 2005 |
27 | * | |
28 | * Configuration Options: | |
29 | * [0] - base address | |
30 | */ | |
2c89e159 GKH |
31 | |
32 | #include <linux/kernel.h> | |
33 | #include <linux/module.h> | |
34 | #include <linux/sched.h> | |
35 | #include <linux/mm.h> | |
36 | #include <linux/errno.h> | |
2c89e159 GKH |
37 | #include <linux/interrupt.h> |
38 | #include <linux/timex.h> | |
39 | #include <linux/timer.h> | |
23d53b17 | 40 | #include <linux/io.h> |
2c89e159 GKH |
41 | #include <linux/pnp.h> |
42 | ||
43 | #include "../comedidev.h" | |
44 | ||
2c89e159 | 45 | /* |
721869e8 | 46 | * Register I/O map |
2c89e159 | 47 | */ |
721869e8 | 48 | #define C6XDIGIO_DATA_REG 0x00 |
28c2c50c HS |
49 | #define C6XDIGIO_DATA_CHAN(x) (((x) + 1) << 4) |
50 | #define C6XDIGIO_DATA_PWM (1 << 5) | |
51 | #define C6XDIGIO_DATA_ENCODER (1 << 6) | |
721869e8 HS |
52 | #define C6XDIGIO_STATUS_REG 0x01 |
53 | #define C6XDIGIO_CTRL_REG 0x02 | |
54 | ||
2c89e159 GKH |
55 | #define C6XDIGIO_TIME_OUT 20 |
56 | ||
bf336ebf | 57 | static int c6xdigio_chk_status(struct comedi_device *dev, unsigned long context) |
2c89e159 | 58 | { |
8a0b0077 | 59 | unsigned int status; |
2c89e159 GKH |
60 | int timeout = 0; |
61 | ||
8a0b0077 | 62 | do { |
721869e8 | 63 | status = inb(dev->iobase + C6XDIGIO_STATUS_REG); |
8a0b0077 HS |
64 | if ((status & 0x80) != context) |
65 | return 0; | |
2c89e159 | 66 | timeout++; |
8a0b0077 HS |
67 | } while (timeout < C6XDIGIO_TIME_OUT); |
68 | ||
69 | return -EBUSY; | |
70 | } | |
71 | ||
6d758307 HS |
72 | static int c6xdigio_write_data(struct comedi_device *dev, |
73 | unsigned int val, unsigned int status) | |
8a0b0077 | 74 | { |
721869e8 | 75 | outb_p(val, dev->iobase + C6XDIGIO_DATA_REG); |
6d758307 HS |
76 | return c6xdigio_chk_status(dev, status); |
77 | } | |
2c89e159 | 78 | |
bea80520 HS |
79 | static int c6xdigio_get_encoder_bits(struct comedi_device *dev, |
80 | unsigned int *bits, | |
81 | unsigned int cmd, | |
82 | unsigned int status) | |
83 | { | |
84 | unsigned int val; | |
85 | ||
86 | val = inb(dev->iobase + C6XDIGIO_STATUS_REG); | |
87 | val >>= 3; | |
88 | val &= 0x07; | |
89 | ||
90 | *bits = val; | |
91 | ||
92 | return c6xdigio_write_data(dev, cmd, status); | |
93 | } | |
94 | ||
c774049a HS |
95 | static void c6xdigio_pwm_write(struct comedi_device *dev, |
96 | unsigned int chan, unsigned int val) | |
2c89e159 | 97 | { |
28c2c50c | 98 | unsigned int cmd = C6XDIGIO_DATA_PWM | C6XDIGIO_DATA_CHAN(chan); |
19d0a872 | 99 | unsigned int bits; |
2c89e159 | 100 | |
19d0a872 HS |
101 | if (val > 498) |
102 | val = 498; | |
103 | if (val < 2) | |
104 | val = 2; | |
105 | ||
106 | bits = (val >> 0) & 0x03; | |
28c2c50c | 107 | c6xdigio_write_data(dev, cmd | bits | (0 << 2), 0x00); |
19d0a872 | 108 | bits = (val >> 2) & 0x03; |
28c2c50c | 109 | c6xdigio_write_data(dev, cmd | bits | (1 << 2), 0x80); |
19d0a872 | 110 | bits = (val >> 4) & 0x03; |
28c2c50c | 111 | c6xdigio_write_data(dev, cmd | bits | (0 << 2), 0x00); |
19d0a872 | 112 | bits = (val >> 6) & 0x03; |
28c2c50c | 113 | c6xdigio_write_data(dev, cmd | bits | (1 << 2), 0x80); |
19d0a872 | 114 | bits = (val >> 8) & 0x03; |
28c2c50c | 115 | c6xdigio_write_data(dev, cmd | bits | (0 << 2), 0x00); |
19d0a872 | 116 | |
6d758307 | 117 | c6xdigio_write_data(dev, 0x00, 0x80); |
2c89e159 GKH |
118 | } |
119 | ||
375a0281 HS |
120 | static int c6xdigio_encoder_read(struct comedi_device *dev, |
121 | unsigned int chan) | |
2c89e159 | 122 | { |
28c2c50c | 123 | unsigned int cmd = C6XDIGIO_DATA_ENCODER | C6XDIGIO_DATA_CHAN(chan); |
bea80520 HS |
124 | unsigned int val = 0; |
125 | unsigned int bits; | |
23d53b17 | 126 | |
28c2c50c | 127 | c6xdigio_write_data(dev, cmd, 0x00); |
2c89e159 | 128 | |
28c2c50c | 129 | c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80); |
bea80520 | 130 | val |= (bits << 0); |
8a0b0077 | 131 | |
28c2c50c | 132 | c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00); |
bea80520 | 133 | val |= (bits << 3); |
8a0b0077 | 134 | |
28c2c50c | 135 | c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80); |
bea80520 | 136 | val |= (bits << 6); |
8a0b0077 | 137 | |
28c2c50c | 138 | c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00); |
bea80520 | 139 | val |= (bits << 9); |
8a0b0077 | 140 | |
28c2c50c | 141 | c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80); |
bea80520 | 142 | val |= (bits << 12); |
8a0b0077 | 143 | |
28c2c50c | 144 | c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00); |
bea80520 | 145 | val |= (bits << 15); |
8a0b0077 | 146 | |
28c2c50c | 147 | c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80); |
bea80520 | 148 | val |= (bits << 18); |
8a0b0077 | 149 | |
28c2c50c | 150 | c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00); |
bea80520 | 151 | val |= (bits << 21); |
2c89e159 | 152 | |
6d758307 | 153 | c6xdigio_write_data(dev, 0x00, 0x80); |
2c89e159 | 154 | |
a7b31d39 | 155 | return val; |
2c89e159 GKH |
156 | } |
157 | ||
6c8df38a HS |
158 | static int c6xdigio_pwm_insn_write(struct comedi_device *dev, |
159 | struct comedi_subdevice *s, | |
160 | struct comedi_insn *insn, | |
161 | unsigned int *data) | |
2c89e159 | 162 | { |
c774049a | 163 | unsigned int chan = CR_CHAN(insn->chanspec); |
8d9c93b4 | 164 | unsigned int val = (s->state >> (16 * chan)) & 0xffff; |
2c89e159 | 165 | int i; |
2c89e159 | 166 | |
2c89e159 | 167 | for (i = 0; i < insn->n; i++) { |
8d9c93b4 HS |
168 | val = data[i]; |
169 | c6xdigio_pwm_write(dev, chan, val); | |
2c89e159 | 170 | } |
8d9c93b4 HS |
171 | |
172 | /* | |
173 | * There are only 2 PWM channels and they have a maxdata of 500. | |
174 | * Instead of allocating private data to save the values in for | |
175 | * readback this driver just packs the values for the two channels | |
176 | * in the s->state. | |
177 | */ | |
178 | s->state &= (0xffff << (16 * chan)); | |
179 | s->state |= (val << (16 * chan)); | |
180 | ||
181 | return insn->n; | |
182 | } | |
183 | ||
184 | static int c6xdigio_pwm_insn_read(struct comedi_device *dev, | |
185 | struct comedi_subdevice *s, | |
186 | struct comedi_insn *insn, | |
187 | unsigned int *data) | |
188 | { | |
189 | unsigned int chan = CR_CHAN(insn->chanspec); | |
190 | unsigned int val; | |
191 | int i; | |
192 | ||
193 | val = (s->state >> (16 * chan)) & 0xffff; | |
194 | ||
195 | for (i = 0; i < insn->n; i++) | |
196 | data[i] = val; | |
197 | ||
198 | return insn->n; | |
2c89e159 GKH |
199 | } |
200 | ||
3ccdcf32 HS |
201 | static int c6xdigio_encoder_insn_read(struct comedi_device *dev, |
202 | struct comedi_subdevice *s, | |
203 | struct comedi_insn *insn, | |
204 | unsigned int *data) | |
2c89e159 | 205 | { |
375a0281 | 206 | unsigned int chan = CR_CHAN(insn->chanspec); |
a7b31d39 HS |
207 | unsigned int val; |
208 | int i; | |
2c89e159 | 209 | |
a7b31d39 HS |
210 | for (i = 0; i < insn->n; i++) { |
211 | val = c6xdigio_encoder_read(dev, chan); | |
212 | ||
213 | /* munge two's complement value to offset binary */ | |
214 | data[i] = comedi_offset_munge(s, val); | |
215 | } | |
2c89e159 | 216 | |
a7b31d39 | 217 | return insn->n; |
2c89e159 GKH |
218 | } |
219 | ||
c034f1f6 | 220 | static void c6xdigio_init(struct comedi_device *dev) |
2c89e159 | 221 | { |
c034f1f6 HS |
222 | /* Initialize the PWM */ |
223 | c6xdigio_write_data(dev, 0x70, 0x00); | |
224 | c6xdigio_write_data(dev, 0x74, 0x80); | |
225 | c6xdigio_write_data(dev, 0x70, 0x00); | |
226 | c6xdigio_write_data(dev, 0x00, 0x80); | |
227 | ||
228 | /* Reset the encoders */ | |
229 | c6xdigio_write_data(dev, 0x68, 0x00); | |
230 | c6xdigio_write_data(dev, 0x6c, 0x80); | |
231 | c6xdigio_write_data(dev, 0x68, 0x00); | |
232 | c6xdigio_write_data(dev, 0x00, 0x80); | |
2c89e159 GKH |
233 | } |
234 | ||
2c89e159 GKH |
235 | static const struct pnp_device_id c6xdigio_pnp_tbl[] = { |
236 | /* Standard LPT Printer Port */ | |
23d53b17 | 237 | {.id = "PNP0400", .driver_data = 0}, |
2c89e159 | 238 | /* ECP Printer Port */ |
23d53b17 | 239 | {.id = "PNP0401", .driver_data = 0}, |
2c89e159 GKH |
240 | {} |
241 | }; | |
242 | ||
243 | static struct pnp_driver c6xdigio_pnp_driver = { | |
244 | .name = "c6xdigio", | |
245 | .id_table = c6xdigio_pnp_tbl, | |
246 | }; | |
247 | ||
0a85b6f0 MT |
248 | static int c6xdigio_attach(struct comedi_device *dev, |
249 | struct comedi_devconfig *it) | |
2c89e159 | 250 | { |
34c43922 | 251 | struct comedi_subdevice *s; |
02edcb2a | 252 | int ret; |
2c89e159 | 253 | |
2ec577ce | 254 | ret = comedi_request_region(dev, it->options[0], 0x03); |
02edcb2a HS |
255 | if (ret) |
256 | return ret; | |
2c89e159 | 257 | |
02edcb2a HS |
258 | ret = comedi_alloc_subdevices(dev, 2); |
259 | if (ret) | |
260 | return ret; | |
2c89e159 | 261 | |
23d53b17 | 262 | /* Make sure that PnP ports get activated */ |
2c89e159 GKH |
263 | pnp_register_driver(&c6xdigio_pnp_driver); |
264 | ||
0c2d3aac | 265 | s = &dev->subdevices[0]; |
2c89e159 | 266 | /* pwm output subdevice */ |
6c8df38a | 267 | s->type = COMEDI_SUBD_PWM; |
ef49d832 | 268 | s->subdev_flags = SDF_WRITABLE; |
6c8df38a HS |
269 | s->n_chan = 2; |
270 | s->maxdata = 500; | |
271 | s->range_table = &range_unknown; | |
272 | s->insn_write = c6xdigio_pwm_insn_write; | |
8d9c93b4 | 273 | s->insn_read = c6xdigio_pwm_insn_read; |
2c89e159 | 274 | |
0c2d3aac | 275 | s = &dev->subdevices[1]; |
2c89e159 | 276 | /* encoder (counter) subdevice */ |
3ccdcf32 HS |
277 | s->type = COMEDI_SUBD_COUNTER; |
278 | s->subdev_flags = SDF_READABLE | SDF_LSAMPL; | |
279 | s->n_chan = 2; | |
280 | s->maxdata = 0xffffff; | |
281 | s->range_table = &range_unknown; | |
282 | s->insn_read = c6xdigio_encoder_insn_read; | |
2c89e159 | 283 | |
23d53b17 BA |
284 | /* I will call this init anyway but more than likely the DSP board */ |
285 | /* will not be connected when device driver is loaded. */ | |
c034f1f6 | 286 | c6xdigio_init(dev); |
2c89e159 GKH |
287 | |
288 | return 0; | |
289 | } | |
290 | ||
484ecc95 | 291 | static void c6xdigio_detach(struct comedi_device *dev) |
2c89e159 | 292 | { |
a32c6d00 | 293 | comedi_legacy_detach(dev); |
2c89e159 | 294 | pnp_unregister_driver(&c6xdigio_pnp_driver); |
2c89e159 GKH |
295 | } |
296 | ||
294f930d | 297 | static struct comedi_driver c6xdigio_driver = { |
77114526 HS |
298 | .driver_name = "c6xdigio", |
299 | .module = THIS_MODULE, | |
300 | .attach = c6xdigio_attach, | |
301 | .detach = c6xdigio_detach, | |
302 | }; | |
294f930d | 303 | module_comedi_driver(c6xdigio_driver); |
90f703d3 AT |
304 | |
305 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
82604bfb | 306 | MODULE_DESCRIPTION("Comedi driver for the C6x_DIGIO DSP daughter card"); |
90f703d3 | 307 | MODULE_LICENSE("GPL"); |