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
, struct comedi_devconfig
*it
);
101 static int c6xdigio_detach(struct comedi_device
*dev
);
102 struct 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 struct comedi_subdevice
*s
, struct 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 struct comedi_subdevice
*s
, struct 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 /* struct comedi_subdevice *s, */
364 /* struct comedi_insn *insn, */
365 /* unsigned int *data) */
367 /* printk("c6xdigio_ei_init_insn_read %x\n", insn->n); */
368 /* return insn->n; */
371 /* static int c6xdigio_ei_init_insn_write(struct comedi_device *dev, */
372 /* struct comedi_subdevice *s, */
373 /* struct comedi_insn *insn, */
374 /* unsigned int *data) */
377 /* int chan = CR_CHAN(insn->chanspec); */
378 /* *//* C6X_encResetAll( dev->iobase ); */
379 /* *//* return insn->n; */
382 static int c6xdigio_ei_insn_read(struct comedi_device
*dev
,
383 struct comedi_subdevice
*s
, struct comedi_insn
*insn
, unsigned int *data
)
385 /* printk("c6xdigio_ei__insn_read %x\n", insn->n); */
387 int chan
= CR_CHAN(insn
->chanspec
);
389 for (n
= 0; n
< insn
->n
; n
++) {
390 data
[n
] = (C6X_encInput(dev
->iobase
, chan
) & 0xffffff);
396 static void board_init(struct comedi_device
*dev
)
399 /* printk("Inside board_init\n"); */
401 C6X_pwmInit(dev
->iobase
);
402 C6X_encResetAll(dev
->iobase
);
406 /* static void board_halt(struct comedi_device *dev) { */
407 /* C6X_pwmInit(dev->iobase); */
411 options[0] - I/O port
413 options[2] - number of encoder chips installed
416 static const struct pnp_device_id c6xdigio_pnp_tbl
[] = {
417 /* Standard LPT Printer Port */
418 {.id
= "PNP0400", .driver_data
= 0},
419 /* ECP Printer Port */
420 {.id
= "PNP0401", .driver_data
= 0},
424 static struct pnp_driver c6xdigio_pnp_driver
= {
426 .id_table
= c6xdigio_pnp_tbl
,
429 static int c6xdigio_attach(struct comedi_device
*dev
, struct comedi_devconfig
*it
)
432 unsigned long iobase
;
434 struct comedi_subdevice
*s
;
436 iobase
= it
->options
[0];
437 printk("comedi%d: c6xdigio: 0x%04lx\n", dev
->minor
, iobase
);
438 if (!request_region(iobase
, C6XDIGIO_SIZE
, "c6xdigio")) {
439 printk("comedi%d: I/O port conflict\n", dev
->minor
);
442 dev
->iobase
= iobase
;
443 dev
->board_name
= "c6xdigio";
445 result
= alloc_subdevices(dev
, 2); /* 3 with encoder_init write */
449 /* Make sure that PnP ports gets activated */
450 pnp_register_driver(&c6xdigio_pnp_driver
);
452 irq
= it
->options
[1];
454 printk("comedi%d: irq = %u ignored\n", dev
->minor
, irq
);
455 } else if (irq
== 0) {
456 printk("comedi%d: no irq\n", dev
->minor
);
459 s
= dev
->subdevices
+ 0;
460 /* pwm output subdevice */
461 s
->type
= COMEDI_SUBD_AO
; /* Not sure what to put here */
462 s
->subdev_flags
= SDF_WRITEABLE
;
464 /* s->trig[0] = c6xdigio_pwmo; */
465 s
->insn_read
= c6xdigio_pwmo_insn_read
;
466 s
->insn_write
= c6xdigio_pwmo_insn_write
;
468 s
->range_table
= &range_bipolar10
; /* A suitable lie */
470 s
= dev
->subdevices
+ 1;
471 /* encoder (counter) subdevice */
472 s
->type
= COMEDI_SUBD_COUNTER
;
473 s
->subdev_flags
= SDF_READABLE
| SDF_LSAMPL
;
475 /* s->trig[0] = c6xdigio_ei; */
476 s
->insn_read
= c6xdigio_ei_insn_read
;
477 s
->maxdata
= 0xffffff;
478 s
->range_table
= &range_unknown
;
480 /* s = dev->subdevices + 2; */
481 /* pwm output subdevice */
482 /* s->type = COMEDI_SUBD_COUNTER; // Not sure what to put here */
483 /* s->subdev_flags = SDF_WRITEABLE; */
485 /* s->trig[0] = c6xdigio_ei_init; */
486 /* s->insn_read = c6xdigio_ei_init_insn_read; */
487 /* s->insn_write = c6xdigio_ei_init_insn_write; */
488 /* s->maxdata = 0xFFFF; // Really just a don't care */
489 /* s->range_table = &range_unknown; // Not sure what to put here */
491 /* I will call this init anyway but more than likely the DSP board will not be connect */
492 /* when device driver is loaded. */
498 static int c6xdigio_detach(struct comedi_device
*dev
)
500 /* board_halt(dev); may not need this */
502 printk("comedi%d: c6xdigio: remove\n", dev
->minor
);
505 release_region(dev
->iobase
, C6XDIGIO_SIZE
);
508 free_irq(dev
->irq
, dev
);
509 } /* Not using IRQ so I am not sure if I need this */
510 pnp_unregister_driver(&c6xdigio_pnp_driver
);
515 COMEDI_INITCLEANUP(driver_c6xdigio
);