2 comedi/drivers/c6xdigio.c
4 Hardware driver for Mechatronic Systems Inc. C6x_DIGIO DSP daughter card.
5 (http://robot0.ge.uiuc.edu/~spong/mecha/)
7 COMEDI - Linux Control and Measurement Device Interface
8 Copyright (C) 1999 Dan Block
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
22 Description: Mechatronic Systems Inc. C6x_DIGIO DSP daughter card
25 Devices: [Mechatronic Systems Inc.] C6x_DIGIO DSP daughter card (c6xdigio)
26 Updated: Sun Nov 20 20:18:34 EST 2005
28 This driver will not work with a 2.4 kernel.
29 http://robot0.ge.uiuc.edu/~spong/mecha/
33 #include <linux/kernel.h>
34 #include <linux/module.h>
35 #include <linux/sched.h>
37 #include <linux/errno.h>
38 #include <linux/interrupt.h>
39 #include <linux/timex.h>
40 #include <linux/timer.h>
42 #include <linux/pnp.h>
44 #include "../comedidev.h"
46 static u8 ReadByteFromHwPort(unsigned long addr)
48 u8 result = inb(addr);
52 static void WriteByteToHwPort(unsigned long addr, u8 val)
57 #define C6XDIGIO_SIZE 3
62 #define C6XDIGIO_PARALLEL_DATA 0
63 #define C6XDIGIO_PARALLEL_STATUS 1
64 #define C6XDIGIO_PARALLEL_CONTROL 2
73 unsigned cmd; /* assuming here that int is 32bit */
74 struct pwmbitstype bits;
88 struct encbitstype bits;
91 #define C6XDIGIO_TIME_OUT 20
93 static void C6X_pwmInit(unsigned long baseAddr)
97 /* printk("Inside C6X_pwmInit\n"); */
99 WriteByteToHwPort(baseAddr, 0x70);
100 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
101 && (timeout < C6XDIGIO_TIME_OUT)) {
105 WriteByteToHwPort(baseAddr, 0x74);
107 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
108 && (timeout < C6XDIGIO_TIME_OUT)) {
112 WriteByteToHwPort(baseAddr, 0x70);
114 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
115 && (timeout < C6XDIGIO_TIME_OUT)) {
119 WriteByteToHwPort(baseAddr, 0x0);
121 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
122 && (timeout < C6XDIGIO_TIME_OUT)) {
128 static void C6X_pwmOutput(unsigned long baseAddr, unsigned channel, int value)
131 union pwmcmdtype pwm;
135 /* printk("Inside C6X_pwmOutput\n"); */
145 } else { /* if channel == 1 */
149 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb0);
150 tmp = ReadByteFromHwPort(baseAddr + 1);
151 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
152 tmp = ReadByteFromHwPort(baseAddr + 1);
156 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb1 + 0x4);
158 tmp = ReadByteFromHwPort(baseAddr + 1);
159 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
160 tmp = ReadByteFromHwPort(baseAddr + 1);
164 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb2);
165 tmp = ReadByteFromHwPort(baseAddr + 1);
166 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
167 tmp = ReadByteFromHwPort(baseAddr + 1);
171 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb3 + 0x4);
173 tmp = ReadByteFromHwPort(baseAddr + 1);
174 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
175 tmp = ReadByteFromHwPort(baseAddr + 1);
179 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb4);
180 tmp = ReadByteFromHwPort(baseAddr + 1);
181 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
182 tmp = ReadByteFromHwPort(baseAddr + 1);
186 WriteByteToHwPort(baseAddr, 0x0);
188 tmp = ReadByteFromHwPort(baseAddr + 1);
189 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
190 tmp = ReadByteFromHwPort(baseAddr + 1);
196 static int C6X_encInput(unsigned long baseAddr, unsigned channel)
199 union encvaluetype enc;
203 /* printk("Inside C6X_encInput\n"); */
211 WriteByteToHwPort(baseAddr, ppcmd);
212 tmp = ReadByteFromHwPort(baseAddr + 1);
213 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
214 tmp = ReadByteFromHwPort(baseAddr + 1);
218 enc.bits.sb0 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
219 WriteByteToHwPort(baseAddr, ppcmd + 0x4);
221 tmp = ReadByteFromHwPort(baseAddr + 1);
222 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
223 tmp = ReadByteFromHwPort(baseAddr + 1);
226 enc.bits.sb1 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
227 WriteByteToHwPort(baseAddr, ppcmd);
229 tmp = ReadByteFromHwPort(baseAddr + 1);
230 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
231 tmp = ReadByteFromHwPort(baseAddr + 1);
234 enc.bits.sb2 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
235 WriteByteToHwPort(baseAddr, ppcmd + 0x4);
237 tmp = ReadByteFromHwPort(baseAddr + 1);
238 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
239 tmp = ReadByteFromHwPort(baseAddr + 1);
242 enc.bits.sb3 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
243 WriteByteToHwPort(baseAddr, ppcmd);
245 tmp = ReadByteFromHwPort(baseAddr + 1);
246 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
247 tmp = ReadByteFromHwPort(baseAddr + 1);
250 enc.bits.sb4 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
251 WriteByteToHwPort(baseAddr, ppcmd + 0x4);
253 tmp = ReadByteFromHwPort(baseAddr + 1);
254 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
255 tmp = ReadByteFromHwPort(baseAddr + 1);
258 enc.bits.sb5 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
259 WriteByteToHwPort(baseAddr, ppcmd);
261 tmp = ReadByteFromHwPort(baseAddr + 1);
262 while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
263 tmp = ReadByteFromHwPort(baseAddr + 1);
266 enc.bits.sb6 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
267 WriteByteToHwPort(baseAddr, ppcmd + 0x4);
269 tmp = ReadByteFromHwPort(baseAddr + 1);
270 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
271 tmp = ReadByteFromHwPort(baseAddr + 1);
274 enc.bits.sb7 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
275 WriteByteToHwPort(baseAddr, ppcmd);
277 tmp = ReadByteFromHwPort(baseAddr + 1);
278 while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
279 tmp = ReadByteFromHwPort(baseAddr + 1);
283 WriteByteToHwPort(baseAddr, 0x0);
285 tmp = ReadByteFromHwPort(baseAddr + 1);
286 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
287 tmp = ReadByteFromHwPort(baseAddr + 1);
291 return enc.value ^ 0x800000;
294 static void C6X_encResetAll(unsigned long baseAddr)
296 unsigned timeout = 0;
298 /* printk("Inside C6X_encResetAll\n"); */
300 WriteByteToHwPort(baseAddr, 0x68);
301 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
302 && (timeout < C6XDIGIO_TIME_OUT)) {
305 WriteByteToHwPort(baseAddr, 0x6C);
307 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
308 && (timeout < C6XDIGIO_TIME_OUT)) {
311 WriteByteToHwPort(baseAddr, 0x68);
313 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
314 && (timeout < C6XDIGIO_TIME_OUT)) {
317 WriteByteToHwPort(baseAddr, 0x0);
319 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
320 && (timeout < C6XDIGIO_TIME_OUT)) {
325 static int c6xdigio_pwmo_insn_read(struct comedi_device *dev,
326 struct comedi_subdevice *s,
327 struct comedi_insn *insn, unsigned int *data)
329 printk(KERN_DEBUG "c6xdigio_pwmo_insn_read %x\n", insn->n);
333 static int c6xdigio_pwmo_insn_write(struct comedi_device *dev,
334 struct comedi_subdevice *s,
335 struct comedi_insn *insn,
339 int chan = CR_CHAN(insn->chanspec);
341 /* printk("c6xdigio_pwmo_insn_write %x\n", insn->n); */
342 for (i = 0; i < insn->n; i++) {
343 C6X_pwmOutput(dev->iobase, chan, data[i]);
344 /* devpriv->ao_readback[chan] = data[i]; */
349 /* static int c6xdigio_ei_init_insn_read(struct comedi_device *dev, */
350 /* struct comedi_subdevice *s, */
351 /* struct comedi_insn *insn, */
352 /* unsigned int *data) */
354 /* printk("c6xdigio_ei_init_insn_read %x\n", insn->n); */
355 /* return insn->n; */
358 /* static int c6xdigio_ei_init_insn_write(struct comedi_device *dev, */
359 /* struct comedi_subdevice *s, */
360 /* struct comedi_insn *insn, */
361 /* unsigned int *data) */
364 /* int chan = CR_CHAN(insn->chanspec); */
365 /* *//* C6X_encResetAll( dev->iobase ); */
366 /* *//* return insn->n; */
369 static int c6xdigio_ei_insn_read(struct comedi_device *dev,
370 struct comedi_subdevice *s,
371 struct comedi_insn *insn, unsigned int *data)
373 /* printk("c6xdigio_ei__insn_read %x\n", insn->n); */
375 int chan = CR_CHAN(insn->chanspec);
377 for (n = 0; n < insn->n; n++)
378 data[n] = (C6X_encInput(dev->iobase, chan) & 0xffffff);
383 static void board_init(struct comedi_device *dev)
386 /* printk("Inside board_init\n"); */
388 C6X_pwmInit(dev->iobase);
389 C6X_encResetAll(dev->iobase);
393 static const struct pnp_device_id c6xdigio_pnp_tbl[] = {
394 /* Standard LPT Printer Port */
395 {.id = "PNP0400", .driver_data = 0},
396 /* ECP Printer Port */
397 {.id = "PNP0401", .driver_data = 0},
401 static struct pnp_driver c6xdigio_pnp_driver = {
403 .id_table = c6xdigio_pnp_tbl,
406 static int c6xdigio_attach(struct comedi_device *dev,
407 struct comedi_devconfig *it)
409 struct comedi_subdevice *s;
412 ret = comedi_request_region(dev, it->options[0], C6XDIGIO_SIZE);
416 ret = comedi_alloc_subdevices(dev, 2);
420 /* Make sure that PnP ports get activated */
421 pnp_register_driver(&c6xdigio_pnp_driver);
423 s = &dev->subdevices[0];
424 /* pwm output subdevice */
425 s->type = COMEDI_SUBD_AO; /* Not sure what to put here */
426 s->subdev_flags = SDF_WRITEABLE;
428 /* s->trig[0] = c6xdigio_pwmo; */
429 s->insn_read = c6xdigio_pwmo_insn_read;
430 s->insn_write = c6xdigio_pwmo_insn_write;
432 s->range_table = &range_bipolar10; /* A suitable lie */
434 s = &dev->subdevices[1];
435 /* encoder (counter) subdevice */
436 s->type = COMEDI_SUBD_COUNTER;
437 s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
439 /* s->trig[0] = c6xdigio_ei; */
440 s->insn_read = c6xdigio_ei_insn_read;
441 s->maxdata = 0xffffff;
442 s->range_table = &range_unknown;
444 /* s = &dev->subdevices[2]; */
445 /* pwm output subdevice */
446 /* s->type = COMEDI_SUBD_COUNTER; // Not sure what to put here */
447 /* s->subdev_flags = SDF_WRITEABLE; */
449 /* s->trig[0] = c6xdigio_ei_init; */
450 /* s->insn_read = c6xdigio_ei_init_insn_read; */
451 /* s->insn_write = c6xdigio_ei_init_insn_write; */
452 /* s->maxdata = 0xFFFF; // Really just a don't care */
453 /* s->range_table = &range_unknown; // Not sure what to put here */
455 /* I will call this init anyway but more than likely the DSP board */
456 /* will not be connected when device driver is loaded. */
462 static void c6xdigio_detach(struct comedi_device *dev)
464 comedi_legacy_detach(dev);
465 pnp_unregister_driver(&c6xdigio_pnp_driver);
468 static struct comedi_driver c6xdigio_driver = {
469 .driver_name = "c6xdigio",
470 .module = THIS_MODULE,
471 .attach = c6xdigio_attach,
472 .detach = c6xdigio_detach,
474 module_comedi_driver(c6xdigio_driver);
476 MODULE_AUTHOR("Comedi http://www.comedi.org");
477 MODULE_DESCRIPTION("Comedi low-level driver");
478 MODULE_LICENSE("GPL");