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(struct comedi_device
*dev
,
101 struct comedi_devconfig
*it
);
102 static int c6xdigio_detach(struct comedi_device
*dev
);
103 struct comedi_driver driver_c6xdigio
= {
104 .driver_name
= "c6xdigio",
105 .module
= THIS_MODULE
,
106 .attach
= c6xdigio_attach
,
107 .detach
= c6xdigio_detach
,
110 static void C6X_pwmInit(unsigned long baseAddr
)
114 /* printk("Inside C6X_pwmInit\n"); */
116 WriteByteToHwPort(baseAddr
, 0x70);
117 while (((ReadByteFromHwPort(baseAddr
+ 1) & 0x80) == 0)
118 && (timeout
< C6XDIGIO_TIME_OUT
)) {
122 WriteByteToHwPort(baseAddr
, 0x74);
124 while (((ReadByteFromHwPort(baseAddr
+ 1) & 0x80) == 0x80)
125 && (timeout
< C6XDIGIO_TIME_OUT
)) {
129 WriteByteToHwPort(baseAddr
, 0x70);
131 while (((ReadByteFromHwPort(baseAddr
+ 1) & 0x80) == 0x0)
132 && (timeout
< C6XDIGIO_TIME_OUT
)) {
136 WriteByteToHwPort(baseAddr
, 0x0);
138 while (((ReadByteFromHwPort(baseAddr
+ 1) & 0x80) == 0x80)
139 && (timeout
< C6XDIGIO_TIME_OUT
)) {
145 static void C6X_pwmOutput(unsigned long baseAddr
, unsigned channel
, int value
)
148 union pwmcmdtype pwm
;
152 /* printk("Inside C6X_pwmOutput\n"); */
162 } else { /* if channel == 1 */
166 WriteByteToHwPort(baseAddr
, ppcmd
+ pwm
.bits
.sb0
);
167 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
168 while (((tmp
& 0x80) == 0) && (timeout
< C6XDIGIO_TIME_OUT
)) {
169 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
173 WriteByteToHwPort(baseAddr
, ppcmd
+ pwm
.bits
.sb1
+ 0x4);
175 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
176 while (((tmp
& 0x80) == 0x80) && (timeout
< C6XDIGIO_TIME_OUT
)) {
177 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
181 WriteByteToHwPort(baseAddr
, ppcmd
+ pwm
.bits
.sb2
);
182 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
183 while (((tmp
& 0x80) == 0) && (timeout
< C6XDIGIO_TIME_OUT
)) {
184 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
188 WriteByteToHwPort(baseAddr
, ppcmd
+ pwm
.bits
.sb3
+ 0x4);
190 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
191 while (((tmp
& 0x80) == 0x80) && (timeout
< C6XDIGIO_TIME_OUT
)) {
192 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
196 WriteByteToHwPort(baseAddr
, ppcmd
+ pwm
.bits
.sb4
);
197 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
198 while (((tmp
& 0x80) == 0) && (timeout
< C6XDIGIO_TIME_OUT
)) {
199 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
203 WriteByteToHwPort(baseAddr
, 0x0);
205 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
206 while (((tmp
& 0x80) == 0x80) && (timeout
< C6XDIGIO_TIME_OUT
)) {
207 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
213 static int C6X_encInput(unsigned long baseAddr
, unsigned channel
)
216 union encvaluetype enc
;
220 /* printk("Inside C6X_encInput\n"); */
228 WriteByteToHwPort(baseAddr
, ppcmd
);
229 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
230 while (((tmp
& 0x80) == 0) && (timeout
< C6XDIGIO_TIME_OUT
)) {
231 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
235 enc
.bits
.sb0
= ((ReadByteFromHwPort(baseAddr
+ 1) >> 3) & 0x7);
236 WriteByteToHwPort(baseAddr
, ppcmd
+ 0x4);
238 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
239 while (((tmp
& 0x80) == 0x80) && (timeout
< C6XDIGIO_TIME_OUT
)) {
240 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
243 enc
.bits
.sb1
= ((ReadByteFromHwPort(baseAddr
+ 1) >> 3) & 0x7);
244 WriteByteToHwPort(baseAddr
, ppcmd
);
246 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
247 while (((tmp
& 0x80) == 0) && (timeout
< C6XDIGIO_TIME_OUT
)) {
248 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
251 enc
.bits
.sb2
= ((ReadByteFromHwPort(baseAddr
+ 1) >> 3) & 0x7);
252 WriteByteToHwPort(baseAddr
, ppcmd
+ 0x4);
254 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
255 while (((tmp
& 0x80) == 0x80) && (timeout
< C6XDIGIO_TIME_OUT
)) {
256 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
259 enc
.bits
.sb3
= ((ReadByteFromHwPort(baseAddr
+ 1) >> 3) & 0x7);
260 WriteByteToHwPort(baseAddr
, ppcmd
);
262 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
263 while (((tmp
& 0x80) == 0) && (timeout
< C6XDIGIO_TIME_OUT
)) {
264 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
267 enc
.bits
.sb4
= ((ReadByteFromHwPort(baseAddr
+ 1) >> 3) & 0x7);
268 WriteByteToHwPort(baseAddr
, ppcmd
+ 0x4);
270 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
271 while (((tmp
& 0x80) == 0x80) && (timeout
< C6XDIGIO_TIME_OUT
)) {
272 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
275 enc
.bits
.sb5
= ((ReadByteFromHwPort(baseAddr
+ 1) >> 3) & 0x7);
276 WriteByteToHwPort(baseAddr
, ppcmd
);
278 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
279 while (((tmp
& 0x80) == 0x0) && (timeout
< C6XDIGIO_TIME_OUT
)) {
280 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
283 enc
.bits
.sb6
= ((ReadByteFromHwPort(baseAddr
+ 1) >> 3) & 0x7);
284 WriteByteToHwPort(baseAddr
, ppcmd
+ 0x4);
286 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
287 while (((tmp
& 0x80) == 0x80) && (timeout
< C6XDIGIO_TIME_OUT
)) {
288 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
291 enc
.bits
.sb7
= ((ReadByteFromHwPort(baseAddr
+ 1) >> 3) & 0x7);
292 WriteByteToHwPort(baseAddr
, ppcmd
);
294 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
295 while (((tmp
& 0x80) == 0x0) && (timeout
< C6XDIGIO_TIME_OUT
)) {
296 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
300 WriteByteToHwPort(baseAddr
, 0x0);
302 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
303 while (((tmp
& 0x80) == 0x80) && (timeout
< C6XDIGIO_TIME_OUT
)) {
304 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
308 return enc
.value
^ 0x800000;
311 static void C6X_encResetAll(unsigned long baseAddr
)
313 unsigned timeout
= 0;
315 /* printk("Inside C6X_encResetAll\n"); */
317 WriteByteToHwPort(baseAddr
, 0x68);
318 while (((ReadByteFromHwPort(baseAddr
+ 1) & 0x80) == 0)
319 && (timeout
< C6XDIGIO_TIME_OUT
)) {
322 WriteByteToHwPort(baseAddr
, 0x6C);
324 while (((ReadByteFromHwPort(baseAddr
+ 1) & 0x80) == 0x80)
325 && (timeout
< C6XDIGIO_TIME_OUT
)) {
328 WriteByteToHwPort(baseAddr
, 0x68);
330 while (((ReadByteFromHwPort(baseAddr
+ 1) & 0x80) == 0x0)
331 && (timeout
< C6XDIGIO_TIME_OUT
)) {
334 WriteByteToHwPort(baseAddr
, 0x0);
336 while (((ReadByteFromHwPort(baseAddr
+ 1) & 0x80) == 0x80)
337 && (timeout
< C6XDIGIO_TIME_OUT
)) {
342 static int c6xdigio_pwmo_insn_read(struct comedi_device
*dev
,
343 struct comedi_subdevice
*s
,
344 struct comedi_insn
*insn
, unsigned int *data
)
346 printk("c6xdigio_pwmo_insn_read %x\n", insn
->n
);
350 static int c6xdigio_pwmo_insn_write(struct comedi_device
*dev
,
351 struct comedi_subdevice
*s
,
352 struct comedi_insn
*insn
,
356 int chan
= CR_CHAN(insn
->chanspec
);
358 /* printk("c6xdigio_pwmo_insn_write %x\n", insn->n); */
359 for (i
= 0; i
< insn
->n
; i
++) {
360 C6X_pwmOutput(dev
->iobase
, chan
, data
[i
]);
361 /* devpriv->ao_readback[chan] = data[i]; */
366 /* static int c6xdigio_ei_init_insn_read(struct comedi_device *dev, */
367 /* struct comedi_subdevice *s, */
368 /* struct comedi_insn *insn, */
369 /* unsigned int *data) */
371 /* printk("c6xdigio_ei_init_insn_read %x\n", insn->n); */
372 /* return insn->n; */
375 /* static int c6xdigio_ei_init_insn_write(struct comedi_device *dev, */
376 /* struct comedi_subdevice *s, */
377 /* struct comedi_insn *insn, */
378 /* unsigned int *data) */
381 /* int chan = CR_CHAN(insn->chanspec); */
382 /* *//* C6X_encResetAll( dev->iobase ); */
383 /* *//* return insn->n; */
386 static int c6xdigio_ei_insn_read(struct comedi_device
*dev
,
387 struct comedi_subdevice
*s
,
388 struct comedi_insn
*insn
, unsigned int *data
)
390 /* printk("c6xdigio_ei__insn_read %x\n", insn->n); */
392 int chan
= CR_CHAN(insn
->chanspec
);
394 for (n
= 0; n
< insn
->n
; n
++)
395 data
[n
] = (C6X_encInput(dev
->iobase
, chan
) & 0xffffff);
400 static void board_init(struct comedi_device
*dev
)
403 /* printk("Inside board_init\n"); */
405 C6X_pwmInit(dev
->iobase
);
406 C6X_encResetAll(dev
->iobase
);
410 /* static void board_halt(struct comedi_device *dev) { */
411 /* C6X_pwmInit(dev->iobase); */
415 options[0] - I/O port
417 options[2] - number of encoder chips installed
420 static const struct pnp_device_id c6xdigio_pnp_tbl
[] = {
421 /* Standard LPT Printer Port */
422 {.id
= "PNP0400", .driver_data
= 0},
423 /* ECP Printer Port */
424 {.id
= "PNP0401", .driver_data
= 0},
428 static struct pnp_driver c6xdigio_pnp_driver
= {
430 .id_table
= c6xdigio_pnp_tbl
,
433 static int c6xdigio_attach(struct comedi_device
*dev
,
434 struct comedi_devconfig
*it
)
437 unsigned long iobase
;
439 struct comedi_subdevice
*s
;
441 iobase
= it
->options
[0];
442 printk("comedi%d: c6xdigio: 0x%04lx\n", dev
->minor
, iobase
);
443 if (!request_region(iobase
, C6XDIGIO_SIZE
, "c6xdigio")) {
444 printk("comedi%d: I/O port conflict\n", dev
->minor
);
447 dev
->iobase
= iobase
;
448 dev
->board_name
= "c6xdigio";
450 result
= alloc_subdevices(dev
, 2); /* 3 with encoder_init write */
454 /* Make sure that PnP ports get activated */
455 pnp_register_driver(&c6xdigio_pnp_driver
);
457 irq
= it
->options
[1];
459 printk("comedi%d: irq = %u ignored\n", dev
->minor
, irq
);
461 printk("comedi%d: no irq\n", dev
->minor
);
463 s
= dev
->subdevices
+ 0;
464 /* pwm output subdevice */
465 s
->type
= COMEDI_SUBD_AO
; /* Not sure what to put here */
466 s
->subdev_flags
= SDF_WRITEABLE
;
468 /* s->trig[0] = c6xdigio_pwmo; */
469 s
->insn_read
= c6xdigio_pwmo_insn_read
;
470 s
->insn_write
= c6xdigio_pwmo_insn_write
;
472 s
->range_table
= &range_bipolar10
; /* A suitable lie */
474 s
= dev
->subdevices
+ 1;
475 /* encoder (counter) subdevice */
476 s
->type
= COMEDI_SUBD_COUNTER
;
477 s
->subdev_flags
= SDF_READABLE
| SDF_LSAMPL
;
479 /* s->trig[0] = c6xdigio_ei; */
480 s
->insn_read
= c6xdigio_ei_insn_read
;
481 s
->maxdata
= 0xffffff;
482 s
->range_table
= &range_unknown
;
484 /* s = dev->subdevices + 2; */
485 /* pwm output subdevice */
486 /* s->type = COMEDI_SUBD_COUNTER; // Not sure what to put here */
487 /* s->subdev_flags = SDF_WRITEABLE; */
489 /* s->trig[0] = c6xdigio_ei_init; */
490 /* s->insn_read = c6xdigio_ei_init_insn_read; */
491 /* s->insn_write = c6xdigio_ei_init_insn_write; */
492 /* s->maxdata = 0xFFFF; // Really just a don't care */
493 /* s->range_table = &range_unknown; // Not sure what to put here */
495 /* I will call this init anyway but more than likely the DSP board */
496 /* will not be connected when device driver is loaded. */
502 static int c6xdigio_detach(struct comedi_device
*dev
)
504 /* board_halt(dev); may not need this */
506 printk("comedi%d: c6xdigio: remove\n", dev
->minor
);
509 release_region(dev
->iobase
, C6XDIGIO_SIZE
);
511 /* Not using IRQ so I am not sure if I need this */
513 free_irq(dev
->irq
, dev
);
515 pnp_unregister_driver(&c6xdigio_pnp_driver
);
520 static int __init
driver_c6xdigio_init_module(void)
522 return comedi_driver_register(&driver_c6xdigio
);
525 static void __exit
driver_c6xdigio_cleanup_module(void)
527 comedi_driver_unregister(&driver_c6xdigio
);
530 module_init(driver_c6xdigio_init_module
);
531 module_exit(driver_c6xdigio_cleanup_module
);
533 MODULE_AUTHOR("Comedi http://www.comedi.org");
534 MODULE_DESCRIPTION("Comedi low-level driver");
535 MODULE_LICENSE("GPL");