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.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 Description: Mechatronic Systems Inc. C6x_DIGIO DSP daughter card
30 Devices: [Mechatronic Systems Inc.] C6x_DIGIO DSP daughter card (c6xdigio)
31 Updated: Sun Nov 20 20:18:34 EST 2005
33 This driver will not work with a 2.4 kernel.
34 http://robot0.ge.uiuc.edu/~spong/mecha/
38 #include <linux/kernel.h>
39 #include <linux/module.h>
40 #include <linux/sched.h>
42 #include <linux/errno.h>
43 #include <linux/ioport.h>
44 #include <linux/delay.h>
45 #include <linux/interrupt.h>
46 #include <linux/timex.h>
47 #include <linux/timer.h>
49 #include <linux/pnp.h>
51 #include "../comedidev.h"
53 static u8 ReadByteFromHwPort(unsigned long addr)
55 u8 result = inb(addr);
59 static void WriteByteToHwPort(unsigned long addr, u8 val)
64 #define C6XDIGIO_SIZE 3
69 #define C6XDIGIO_PARALLEL_DATA 0
70 #define C6XDIGIO_PARALLEL_STATUS 1
71 #define C6XDIGIO_PARALLEL_CONTROL 2
80 unsigned cmd; // assuming here that int is 32bit
81 struct pwmbitstype bits;
95 struct encbitstype bits;
98 #define C6XDIGIO_TIME_OUT 20
100 static int c6xdigio_attach(comedi_device * dev, comedi_devconfig * it);
101 static int c6xdigio_detach(comedi_device * dev);
102 comedi_driver driver_c6xdigio = {
103 driver_name:"c6xdigio",
105 attach:c6xdigio_attach,
106 detach:c6xdigio_detach,
109 static void C6X_pwmInit(unsigned long baseAddr)
113 //printk("Inside C6X_pwmInit\n");
115 WriteByteToHwPort(baseAddr, 0x70);
116 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
117 && (timeout < C6XDIGIO_TIME_OUT)) {
121 WriteByteToHwPort(baseAddr, 0x74);
123 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
124 && (timeout < C6XDIGIO_TIME_OUT)) {
128 WriteByteToHwPort(baseAddr, 0x70);
130 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
131 && (timeout < C6XDIGIO_TIME_OUT)) {
135 WriteByteToHwPort(baseAddr, 0x0);
137 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
138 && (timeout < C6XDIGIO_TIME_OUT)) {
144 static void C6X_pwmOutput(unsigned long baseAddr, unsigned channel, int value)
147 union pwmcmdtype pwm;
151 //printk("Inside C6X_pwmOutput\n");
161 } else { // if channel == 1
165 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb0);
166 tmp = ReadByteFromHwPort(baseAddr + 1);
167 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
168 tmp = ReadByteFromHwPort(baseAddr + 1);
172 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb1 + 0x4);
174 tmp = ReadByteFromHwPort(baseAddr + 1);
175 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
176 tmp = ReadByteFromHwPort(baseAddr + 1);
180 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb2);
181 tmp = ReadByteFromHwPort(baseAddr + 1);
182 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
183 tmp = ReadByteFromHwPort(baseAddr + 1);
187 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb3 + 0x4);
189 tmp = ReadByteFromHwPort(baseAddr + 1);
190 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
191 tmp = ReadByteFromHwPort(baseAddr + 1);
195 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb4);
196 tmp = ReadByteFromHwPort(baseAddr + 1);
197 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
198 tmp = ReadByteFromHwPort(baseAddr + 1);
202 WriteByteToHwPort(baseAddr, 0x0);
204 tmp = ReadByteFromHwPort(baseAddr + 1);
205 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
206 tmp = ReadByteFromHwPort(baseAddr + 1);
212 static int C6X_encInput(unsigned long baseAddr, unsigned channel)
215 union encvaluetype enc;
219 //printk("Inside C6X_encInput\n");
227 WriteByteToHwPort(baseAddr, ppcmd);
228 tmp = ReadByteFromHwPort(baseAddr + 1);
229 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
230 tmp = ReadByteFromHwPort(baseAddr + 1);
234 enc.bits.sb0 = ((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.sb1 = ((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.sb2 = ((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.sb3 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
259 WriteByteToHwPort(baseAddr, ppcmd);
261 tmp = ReadByteFromHwPort(baseAddr + 1);
262 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
263 tmp = ReadByteFromHwPort(baseAddr + 1);
266 enc.bits.sb4 = ((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.sb5 = ((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);
282 enc.bits.sb6 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
283 WriteByteToHwPort(baseAddr, ppcmd + 0x4);
285 tmp = ReadByteFromHwPort(baseAddr + 1);
286 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
287 tmp = ReadByteFromHwPort(baseAddr + 1);
290 enc.bits.sb7 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
291 WriteByteToHwPort(baseAddr, ppcmd);
293 tmp = ReadByteFromHwPort(baseAddr + 1);
294 while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
295 tmp = ReadByteFromHwPort(baseAddr + 1);
299 WriteByteToHwPort(baseAddr, 0x0);
301 tmp = ReadByteFromHwPort(baseAddr + 1);
302 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
303 tmp = ReadByteFromHwPort(baseAddr + 1);
307 return (enc.value ^ 0x800000);
310 static void C6X_encResetAll(unsigned long baseAddr)
312 unsigned timeout = 0;
314 //printk("Inside C6X_encResetAll\n");
316 WriteByteToHwPort(baseAddr, 0x68);
317 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
318 && (timeout < C6XDIGIO_TIME_OUT)) {
321 WriteByteToHwPort(baseAddr, 0x6C);
323 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
324 && (timeout < C6XDIGIO_TIME_OUT)) {
327 WriteByteToHwPort(baseAddr, 0x68);
329 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
330 && (timeout < C6XDIGIO_TIME_OUT)) {
333 WriteByteToHwPort(baseAddr, 0x0);
335 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
336 && (timeout < C6XDIGIO_TIME_OUT)) {
341 static int c6xdigio_pwmo_insn_read(comedi_device * dev,
342 comedi_subdevice * s, comedi_insn * insn, unsigned int * data)
344 printk("c6xdigio_pwmo_insn_read %x\n", insn->n);
348 static int c6xdigio_pwmo_insn_write(comedi_device * dev,
349 comedi_subdevice * s, comedi_insn * insn, unsigned int * data)
352 int chan = CR_CHAN(insn->chanspec);
354 // printk("c6xdigio_pwmo_insn_write %x\n", insn->n);
355 for (i = 0; i < insn->n; i++) {
356 C6X_pwmOutput(dev->iobase, chan, data[i]);
357 /* devpriv->ao_readback[chan] = data[i]; */
362 //static int c6xdigio_ei_init_insn_read(comedi_device *dev,
363 // comedi_subdevice *s,
364 // comedi_insn *insn,
365 // unsigned int *data)
367 // printk("c6xdigio_ei_init_insn_read %x\n", insn->n);
371 //static int c6xdigio_ei_init_insn_write(comedi_device *dev,
372 // comedi_subdevice *s,
373 // comedi_insn *insn,
374 // unsigned int *data)
377 // int chan = CR_CHAN(insn->chanspec);
379 // C6X_encResetAll( dev->iobase );
384 static int c6xdigio_ei_insn_read(comedi_device * dev,
385 comedi_subdevice * s, comedi_insn * insn, unsigned int * data)
387 // printk("c6xdigio_ei__insn_read %x\n", insn->n);
389 int chan = CR_CHAN(insn->chanspec);
391 for (n = 0; n < insn->n; n++) {
392 data[n] = (C6X_encInput(dev->iobase, chan) & 0xffffff);
398 static void board_init(comedi_device * dev)
401 //printk("Inside board_init\n");
403 C6X_pwmInit(dev->iobase);
404 C6X_encResetAll(dev->iobase);
408 //static void board_halt(comedi_device *dev) {
409 // C6X_pwmInit(dev->iobase);
413 options[0] - I/O port
415 options[2] - number of encoder chips installed
418 static const struct pnp_device_id c6xdigio_pnp_tbl[] = {
419 /* Standard LPT Printer Port */
420 {.id = "PNP0400",.driver_data = 0},
421 /* ECP Printer Port */
422 {.id = "PNP0401",.driver_data = 0},
426 static struct pnp_driver c6xdigio_pnp_driver = {
428 .id_table = c6xdigio_pnp_tbl,
431 static int c6xdigio_attach(comedi_device * dev, comedi_devconfig * it)
434 unsigned long iobase;
438 iobase = it->options[0];
439 printk("comedi%d: c6xdigio: 0x%04lx\n", dev->minor, iobase);
440 if (!request_region(iobase, C6XDIGIO_SIZE, "c6xdigio")) {
441 printk("comedi%d: I/O port conflict\n", dev->minor);
444 dev->iobase = iobase;
445 dev->board_name = "c6xdigio";
447 result = alloc_subdevices(dev, 2); // 3 with encoder_init write
451 // Make sure that PnP ports gets activated
452 pnp_register_driver(&c6xdigio_pnp_driver);
454 irq = it->options[1];
456 printk("comedi%d: irq = %u ignored\n", dev->minor, irq);
457 } else if (irq == 0) {
458 printk("comedi%d: no irq\n", dev->minor);
461 s = dev->subdevices + 0;
462 /* pwm output subdevice */
463 s->type = COMEDI_SUBD_AO; // Not sure what to put here
464 s->subdev_flags = SDF_WRITEABLE;
466 /* s->trig[0] = c6xdigio_pwmo; */
467 s->insn_read = c6xdigio_pwmo_insn_read;
468 s->insn_write = c6xdigio_pwmo_insn_write;
470 s->range_table = &range_bipolar10; // A suitable lie
472 s = dev->subdevices + 1;
473 /* encoder (counter) subdevice */
474 s->type = COMEDI_SUBD_COUNTER;
475 s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
477 /* s->trig[0] = c6xdigio_ei; */
478 s->insn_read = c6xdigio_ei_insn_read;
479 s->maxdata = 0xffffff;
480 s->range_table = &range_unknown;
482 // s = dev->subdevices + 2;
483 // /* pwm output subdevice */
484 // s->type = COMEDI_SUBD_COUNTER; // Not sure what to put here
485 // s->subdev_flags = SDF_WRITEABLE;
487 // /* s->trig[0] = c6xdigio_ei_init; */
488 // s->insn_read = c6xdigio_ei_init_insn_read;
489 // s->insn_write = c6xdigio_ei_init_insn_write;
490 // s->maxdata = 0xFFFF; // Really just a don't care
491 // s->range_table = &range_unknown; // Not sure what to put here
493 // I will call this init anyway but more than likely the DSP board will not be connect
494 // when device driver is loaded.
500 static int c6xdigio_detach(comedi_device * dev)
502 // board_halt(dev); // may not need this
504 printk("comedi%d: c6xdigio: remove\n", dev->minor);
507 release_region(dev->iobase, C6XDIGIO_SIZE);
510 free_irq(dev->irq, dev);
511 } // Not using IRQ so I am not sure if I need this
512 pnp_unregister_driver(&c6xdigio_pnp_driver);
517 COMEDI_INITCLEANUP(driver_c6xdigio);