1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23#include <linux/kernel.h>
24#include <linux/module.h>
25#include <linux/sched.h>
26#include <linux/mm.h>
27#include <linux/errno.h>
28#include <linux/interrupt.h>
29#include <linux/timex.h>
30#include <linux/timer.h>
31#include <linux/io.h>
32#include <linux/pnp.h>
33
34#include "../comedidev.h"
35
36
37
38
39#define C6XDIGIO_DATA_REG 0x00
40#define C6XDIGIO_DATA_CHAN(x) (((x) + 1) << 4)
41#define C6XDIGIO_DATA_PWM BIT(5)
42#define C6XDIGIO_DATA_ENCODER BIT(6)
43#define C6XDIGIO_STATUS_REG 0x01
44#define C6XDIGIO_CTRL_REG 0x02
45
46#define C6XDIGIO_TIME_OUT 20
47
48static int c6xdigio_chk_status(struct comedi_device *dev, unsigned long context)
49{
50 unsigned int status;
51 int timeout = 0;
52
53 do {
54 status = inb(dev->iobase + C6XDIGIO_STATUS_REG);
55 if ((status & 0x80) != context)
56 return 0;
57 timeout++;
58 } while (timeout < C6XDIGIO_TIME_OUT);
59
60 return -EBUSY;
61}
62
63static int c6xdigio_write_data(struct comedi_device *dev,
64 unsigned int val, unsigned int status)
65{
66 outb_p(val, dev->iobase + C6XDIGIO_DATA_REG);
67 return c6xdigio_chk_status(dev, status);
68}
69
70static int c6xdigio_get_encoder_bits(struct comedi_device *dev,
71 unsigned int *bits,
72 unsigned int cmd,
73 unsigned int status)
74{
75 unsigned int val;
76
77 val = inb(dev->iobase + C6XDIGIO_STATUS_REG);
78 val >>= 3;
79 val &= 0x07;
80
81 *bits = val;
82
83 return c6xdigio_write_data(dev, cmd, status);
84}
85
86static void c6xdigio_pwm_write(struct comedi_device *dev,
87 unsigned int chan, unsigned int val)
88{
89 unsigned int cmd = C6XDIGIO_DATA_PWM | C6XDIGIO_DATA_CHAN(chan);
90 unsigned int bits;
91
92 if (val > 498)
93 val = 498;
94 if (val < 2)
95 val = 2;
96
97 bits = (val >> 0) & 0x03;
98 c6xdigio_write_data(dev, cmd | bits | (0 << 2), 0x00);
99 bits = (val >> 2) & 0x03;
100 c6xdigio_write_data(dev, cmd | bits | (1 << 2), 0x80);
101 bits = (val >> 4) & 0x03;
102 c6xdigio_write_data(dev, cmd | bits | (0 << 2), 0x00);
103 bits = (val >> 6) & 0x03;
104 c6xdigio_write_data(dev, cmd | bits | (1 << 2), 0x80);
105 bits = (val >> 8) & 0x03;
106 c6xdigio_write_data(dev, cmd | bits | (0 << 2), 0x00);
107
108 c6xdigio_write_data(dev, 0x00, 0x80);
109}
110
111static int c6xdigio_encoder_read(struct comedi_device *dev,
112 unsigned int chan)
113{
114 unsigned int cmd = C6XDIGIO_DATA_ENCODER | C6XDIGIO_DATA_CHAN(chan);
115 unsigned int val = 0;
116 unsigned int bits;
117
118 c6xdigio_write_data(dev, cmd, 0x00);
119
120 c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80);
121 val |= (bits << 0);
122
123 c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00);
124 val |= (bits << 3);
125
126 c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80);
127 val |= (bits << 6);
128
129 c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00);
130 val |= (bits << 9);
131
132 c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80);
133 val |= (bits << 12);
134
135 c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00);
136 val |= (bits << 15);
137
138 c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80);
139 val |= (bits << 18);
140
141 c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00);
142 val |= (bits << 21);
143
144 c6xdigio_write_data(dev, 0x00, 0x80);
145
146 return val;
147}
148
149static int c6xdigio_pwm_insn_write(struct comedi_device *dev,
150 struct comedi_subdevice *s,
151 struct comedi_insn *insn,
152 unsigned int *data)
153{
154 unsigned int chan = CR_CHAN(insn->chanspec);
155 unsigned int val = (s->state >> (16 * chan)) & 0xffff;
156 int i;
157
158 for (i = 0; i < insn->n; i++) {
159 val = data[i];
160 c6xdigio_pwm_write(dev, chan, val);
161 }
162
163
164
165
166
167
168
169 s->state &= (0xffff << (16 * chan));
170 s->state |= (val << (16 * chan));
171
172 return insn->n;
173}
174
175static int c6xdigio_pwm_insn_read(struct comedi_device *dev,
176 struct comedi_subdevice *s,
177 struct comedi_insn *insn,
178 unsigned int *data)
179{
180 unsigned int chan = CR_CHAN(insn->chanspec);
181 unsigned int val;
182 int i;
183
184 val = (s->state >> (16 * chan)) & 0xffff;
185
186 for (i = 0; i < insn->n; i++)
187 data[i] = val;
188
189 return insn->n;
190}
191
192static int c6xdigio_encoder_insn_read(struct comedi_device *dev,
193 struct comedi_subdevice *s,
194 struct comedi_insn *insn,
195 unsigned int *data)
196{
197 unsigned int chan = CR_CHAN(insn->chanspec);
198 unsigned int val;
199 int i;
200
201 for (i = 0; i < insn->n; i++) {
202 val = c6xdigio_encoder_read(dev, chan);
203
204
205 data[i] = comedi_offset_munge(s, val);
206 }
207
208 return insn->n;
209}
210
211static void c6xdigio_init(struct comedi_device *dev)
212{
213
214 c6xdigio_write_data(dev, 0x70, 0x00);
215 c6xdigio_write_data(dev, 0x74, 0x80);
216 c6xdigio_write_data(dev, 0x70, 0x00);
217 c6xdigio_write_data(dev, 0x00, 0x80);
218
219
220 c6xdigio_write_data(dev, 0x68, 0x00);
221 c6xdigio_write_data(dev, 0x6c, 0x80);
222 c6xdigio_write_data(dev, 0x68, 0x00);
223 c6xdigio_write_data(dev, 0x00, 0x80);
224}
225
226static const struct pnp_device_id c6xdigio_pnp_tbl[] = {
227
228 {.id = "PNP0400", .driver_data = 0},
229
230 {.id = "PNP0401", .driver_data = 0},
231 {}
232};
233
234static struct pnp_driver c6xdigio_pnp_driver = {
235 .name = "c6xdigio",
236 .id_table = c6xdigio_pnp_tbl,
237};
238
239static int c6xdigio_attach(struct comedi_device *dev,
240 struct comedi_devconfig *it)
241{
242 struct comedi_subdevice *s;
243 int ret;
244
245 ret = comedi_request_region(dev, it->options[0], 0x03);
246 if (ret)
247 return ret;
248
249 ret = comedi_alloc_subdevices(dev, 2);
250 if (ret)
251 return ret;
252
253
254 pnp_register_driver(&c6xdigio_pnp_driver);
255
256 s = &dev->subdevices[0];
257
258 s->type = COMEDI_SUBD_PWM;
259 s->subdev_flags = SDF_WRITABLE;
260 s->n_chan = 2;
261 s->maxdata = 500;
262 s->range_table = &range_unknown;
263 s->insn_write = c6xdigio_pwm_insn_write;
264 s->insn_read = c6xdigio_pwm_insn_read;
265
266 s = &dev->subdevices[1];
267
268 s->type = COMEDI_SUBD_COUNTER;
269 s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
270 s->n_chan = 2;
271 s->maxdata = 0xffffff;
272 s->range_table = &range_unknown;
273 s->insn_read = c6xdigio_encoder_insn_read;
274
275
276
277 c6xdigio_init(dev);
278
279 return 0;
280}
281
282static void c6xdigio_detach(struct comedi_device *dev)
283{
284 comedi_legacy_detach(dev);
285 pnp_unregister_driver(&c6xdigio_pnp_driver);
286}
287
288static struct comedi_driver c6xdigio_driver = {
289 .driver_name = "c6xdigio",
290 .module = THIS_MODULE,
291 .attach = c6xdigio_attach,
292 .detach = c6xdigio_detach,
293};
294module_comedi_driver(c6xdigio_driver);
295
296MODULE_AUTHOR("Comedi https://www.comedi.org");
297MODULE_DESCRIPTION("Comedi driver for the C6x_DIGIO DSP daughter card");
298MODULE_LICENSE("GPL");
299