1 // SPDX-License-Identifier: GPL-2.0+
4 * Hardware driver for Mechatronic Systems Inc. C6x_DIGIO DSP daughter card.
5 * http://web.archive.org/web/%2A/http://robot0.ge.uiuc.edu/~spong/mecha/
7 * COMEDI - Linux Control and Measurement Device Interface
8 * Copyright (C) 1999 Dan Block
13 * Description: Mechatronic Systems Inc. C6x_DIGIO DSP daughter card
16 * Devices: [Mechatronic Systems Inc.] C6x_DIGIO DSP daughter card (c6xdigio)
17 * Updated: Sun Nov 20 20:18:34 EST 2005
19 * Configuration Options:
23 #include <linux/kernel.h>
24 #include <linux/module.h>
25 #include <linux/sched.h>
27 #include <linux/errno.h>
28 #include <linux/interrupt.h>
29 #include <linux/timex.h>
30 #include <linux/timer.h>
32 #include <linux/pnp.h>
34 #include "../comedidev.h"
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
46 #define C6XDIGIO_TIME_OUT 20
48 static int c6xdigio_chk_status(struct comedi_device *dev, unsigned long context)
54 status = inb(dev->iobase + C6XDIGIO_STATUS_REG);
55 if ((status & 0x80) != context)
58 } while (timeout < C6XDIGIO_TIME_OUT);
63 static int c6xdigio_write_data(struct comedi_device *dev,
64 unsigned int val, unsigned int status)
66 outb_p(val, dev->iobase + C6XDIGIO_DATA_REG);
67 return c6xdigio_chk_status(dev, status);
70 static int c6xdigio_get_encoder_bits(struct comedi_device *dev,
77 val = inb(dev->iobase + C6XDIGIO_STATUS_REG);
83 return c6xdigio_write_data(dev, cmd, status);
86 static void c6xdigio_pwm_write(struct comedi_device *dev,
87 unsigned int chan, unsigned int val)
89 unsigned int cmd = C6XDIGIO_DATA_PWM | C6XDIGIO_DATA_CHAN(chan);
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);
108 c6xdigio_write_data(dev, 0x00, 0x80);
111 static int c6xdigio_encoder_read(struct comedi_device *dev,
114 unsigned int cmd = C6XDIGIO_DATA_ENCODER | C6XDIGIO_DATA_CHAN(chan);
115 unsigned int val = 0;
118 c6xdigio_write_data(dev, cmd, 0x00);
120 c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80);
123 c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00);
126 c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80);
129 c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00);
132 c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80);
135 c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00);
138 c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80);
141 c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00);
144 c6xdigio_write_data(dev, 0x00, 0x80);
149 static int c6xdigio_pwm_insn_write(struct comedi_device *dev,
150 struct comedi_subdevice *s,
151 struct comedi_insn *insn,
154 unsigned int chan = CR_CHAN(insn->chanspec);
155 unsigned int val = (s->state >> (16 * chan)) & 0xffff;
158 for (i = 0; i < insn->n; i++) {
160 c6xdigio_pwm_write(dev, chan, val);
164 * There are only 2 PWM channels and they have a maxdata of 500.
165 * Instead of allocating private data to save the values in for
166 * readback this driver just packs the values for the two channels
169 s->state &= (0xffff << (16 * chan));
170 s->state |= (val << (16 * chan));
175 static int c6xdigio_pwm_insn_read(struct comedi_device *dev,
176 struct comedi_subdevice *s,
177 struct comedi_insn *insn,
180 unsigned int chan = CR_CHAN(insn->chanspec);
184 val = (s->state >> (16 * chan)) & 0xffff;
186 for (i = 0; i < insn->n; i++)
192 static int c6xdigio_encoder_insn_read(struct comedi_device *dev,
193 struct comedi_subdevice *s,
194 struct comedi_insn *insn,
197 unsigned int chan = CR_CHAN(insn->chanspec);
201 for (i = 0; i < insn->n; i++) {
202 val = c6xdigio_encoder_read(dev, chan);
204 /* munge two's complement value to offset binary */
205 data[i] = comedi_offset_munge(s, val);
211 static void c6xdigio_init(struct comedi_device *dev)
213 /* Initialize the PWM */
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);
219 /* Reset the encoders */
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);
226 static const struct pnp_device_id c6xdigio_pnp_tbl[] = {
227 /* Standard LPT Printer Port */
228 {.id = "PNP0400", .driver_data = 0},
229 /* ECP Printer Port */
230 {.id = "PNP0401", .driver_data = 0},
234 static struct pnp_driver c6xdigio_pnp_driver = {
236 .id_table = c6xdigio_pnp_tbl,
239 static int c6xdigio_attach(struct comedi_device *dev,
240 struct comedi_devconfig *it)
242 struct comedi_subdevice *s;
245 ret = comedi_request_region(dev, it->options[0], 0x03);
249 ret = comedi_alloc_subdevices(dev, 2);
253 /* Make sure that PnP ports get activated */
254 pnp_register_driver(&c6xdigio_pnp_driver);
256 s = &dev->subdevices[0];
257 /* pwm output subdevice */
258 s->type = COMEDI_SUBD_PWM;
259 s->subdev_flags = SDF_WRITABLE;
262 s->range_table = &range_unknown;
263 s->insn_write = c6xdigio_pwm_insn_write;
264 s->insn_read = c6xdigio_pwm_insn_read;
266 s = &dev->subdevices[1];
267 /* encoder (counter) subdevice */
268 s->type = COMEDI_SUBD_COUNTER;
269 s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
271 s->maxdata = 0xffffff;
272 s->range_table = &range_unknown;
273 s->insn_read = c6xdigio_encoder_insn_read;
275 /* I will call this init anyway but more than likely the DSP board */
276 /* will not be connected when device driver is loaded. */
282 static void c6xdigio_detach(struct comedi_device *dev)
284 comedi_legacy_detach(dev);
285 pnp_unregister_driver(&c6xdigio_pnp_driver);
288 static struct comedi_driver c6xdigio_driver = {
289 .driver_name = "c6xdigio",
290 .module = THIS_MODULE,
291 .attach = c6xdigio_attach,
292 .detach = c6xdigio_detach,
294 module_comedi_driver(c6xdigio_driver);
296 MODULE_AUTHOR("Comedi https://www.comedi.org");
297 MODULE_DESCRIPTION("Comedi driver for the C6x_DIGIO DSP daughter card");
298 MODULE_LICENSE("GPL");