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
, comedi_devconfig
* it
);
101 static int c6xdigio_detach(struct 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(struct 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(struct 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(struct 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(struct 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(struct 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(struct comedi_device
* dev
)
401 //printk("Inside board_init\n");
403 C6X_pwmInit(dev
->iobase
);
404 C6X_encResetAll(dev
->iobase
);
408 //static void board_halt(struct 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(struct 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(struct 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
);