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 void C6X_pwmInit(unsigned long baseAddr
)
104 /* printk("Inside C6X_pwmInit\n"); */
106 WriteByteToHwPort(baseAddr
, 0x70);
107 while (((ReadByteFromHwPort(baseAddr
+ 1) & 0x80) == 0)
108 && (timeout
< C6XDIGIO_TIME_OUT
)) {
112 WriteByteToHwPort(baseAddr
, 0x74);
114 while (((ReadByteFromHwPort(baseAddr
+ 1) & 0x80) == 0x80)
115 && (timeout
< C6XDIGIO_TIME_OUT
)) {
119 WriteByteToHwPort(baseAddr
, 0x70);
121 while (((ReadByteFromHwPort(baseAddr
+ 1) & 0x80) == 0x0)
122 && (timeout
< C6XDIGIO_TIME_OUT
)) {
126 WriteByteToHwPort(baseAddr
, 0x0);
128 while (((ReadByteFromHwPort(baseAddr
+ 1) & 0x80) == 0x80)
129 && (timeout
< C6XDIGIO_TIME_OUT
)) {
135 static void C6X_pwmOutput(unsigned long baseAddr
, unsigned channel
, int value
)
138 union pwmcmdtype pwm
;
142 /* printk("Inside C6X_pwmOutput\n"); */
152 } else { /* if channel == 1 */
156 WriteByteToHwPort(baseAddr
, ppcmd
+ pwm
.bits
.sb0
);
157 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
158 while (((tmp
& 0x80) == 0) && (timeout
< C6XDIGIO_TIME_OUT
)) {
159 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
163 WriteByteToHwPort(baseAddr
, ppcmd
+ pwm
.bits
.sb1
+ 0x4);
165 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
166 while (((tmp
& 0x80) == 0x80) && (timeout
< C6XDIGIO_TIME_OUT
)) {
167 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
171 WriteByteToHwPort(baseAddr
, ppcmd
+ pwm
.bits
.sb2
);
172 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
173 while (((tmp
& 0x80) == 0) && (timeout
< C6XDIGIO_TIME_OUT
)) {
174 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
178 WriteByteToHwPort(baseAddr
, ppcmd
+ pwm
.bits
.sb3
+ 0x4);
180 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
181 while (((tmp
& 0x80) == 0x80) && (timeout
< C6XDIGIO_TIME_OUT
)) {
182 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
186 WriteByteToHwPort(baseAddr
, ppcmd
+ pwm
.bits
.sb4
);
187 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
188 while (((tmp
& 0x80) == 0) && (timeout
< C6XDIGIO_TIME_OUT
)) {
189 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
193 WriteByteToHwPort(baseAddr
, 0x0);
195 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
196 while (((tmp
& 0x80) == 0x80) && (timeout
< C6XDIGIO_TIME_OUT
)) {
197 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
203 static int C6X_encInput(unsigned long baseAddr
, unsigned channel
)
206 union encvaluetype enc
;
210 /* printk("Inside C6X_encInput\n"); */
218 WriteByteToHwPort(baseAddr
, ppcmd
);
219 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
220 while (((tmp
& 0x80) == 0) && (timeout
< C6XDIGIO_TIME_OUT
)) {
221 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
225 enc
.bits
.sb0
= ((ReadByteFromHwPort(baseAddr
+ 1) >> 3) & 0x7);
226 WriteByteToHwPort(baseAddr
, ppcmd
+ 0x4);
228 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
229 while (((tmp
& 0x80) == 0x80) && (timeout
< C6XDIGIO_TIME_OUT
)) {
230 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
233 enc
.bits
.sb1
= ((ReadByteFromHwPort(baseAddr
+ 1) >> 3) & 0x7);
234 WriteByteToHwPort(baseAddr
, ppcmd
);
236 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
237 while (((tmp
& 0x80) == 0) && (timeout
< C6XDIGIO_TIME_OUT
)) {
238 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
241 enc
.bits
.sb2
= ((ReadByteFromHwPort(baseAddr
+ 1) >> 3) & 0x7);
242 WriteByteToHwPort(baseAddr
, ppcmd
+ 0x4);
244 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
245 while (((tmp
& 0x80) == 0x80) && (timeout
< C6XDIGIO_TIME_OUT
)) {
246 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
249 enc
.bits
.sb3
= ((ReadByteFromHwPort(baseAddr
+ 1) >> 3) & 0x7);
250 WriteByteToHwPort(baseAddr
, ppcmd
);
252 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
253 while (((tmp
& 0x80) == 0) && (timeout
< C6XDIGIO_TIME_OUT
)) {
254 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
257 enc
.bits
.sb4
= ((ReadByteFromHwPort(baseAddr
+ 1) >> 3) & 0x7);
258 WriteByteToHwPort(baseAddr
, ppcmd
+ 0x4);
260 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
261 while (((tmp
& 0x80) == 0x80) && (timeout
< C6XDIGIO_TIME_OUT
)) {
262 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
265 enc
.bits
.sb5
= ((ReadByteFromHwPort(baseAddr
+ 1) >> 3) & 0x7);
266 WriteByteToHwPort(baseAddr
, ppcmd
);
268 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
269 while (((tmp
& 0x80) == 0x0) && (timeout
< C6XDIGIO_TIME_OUT
)) {
270 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
273 enc
.bits
.sb6
= ((ReadByteFromHwPort(baseAddr
+ 1) >> 3) & 0x7);
274 WriteByteToHwPort(baseAddr
, ppcmd
+ 0x4);
276 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
277 while (((tmp
& 0x80) == 0x80) && (timeout
< C6XDIGIO_TIME_OUT
)) {
278 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
281 enc
.bits
.sb7
= ((ReadByteFromHwPort(baseAddr
+ 1) >> 3) & 0x7);
282 WriteByteToHwPort(baseAddr
, ppcmd
);
284 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
285 while (((tmp
& 0x80) == 0x0) && (timeout
< C6XDIGIO_TIME_OUT
)) {
286 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
290 WriteByteToHwPort(baseAddr
, 0x0);
292 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
293 while (((tmp
& 0x80) == 0x80) && (timeout
< C6XDIGIO_TIME_OUT
)) {
294 tmp
= ReadByteFromHwPort(baseAddr
+ 1);
298 return enc
.value
^ 0x800000;
301 static void C6X_encResetAll(unsigned long baseAddr
)
303 unsigned timeout
= 0;
305 /* printk("Inside C6X_encResetAll\n"); */
307 WriteByteToHwPort(baseAddr
, 0x68);
308 while (((ReadByteFromHwPort(baseAddr
+ 1) & 0x80) == 0)
309 && (timeout
< C6XDIGIO_TIME_OUT
)) {
312 WriteByteToHwPort(baseAddr
, 0x6C);
314 while (((ReadByteFromHwPort(baseAddr
+ 1) & 0x80) == 0x80)
315 && (timeout
< C6XDIGIO_TIME_OUT
)) {
318 WriteByteToHwPort(baseAddr
, 0x68);
320 while (((ReadByteFromHwPort(baseAddr
+ 1) & 0x80) == 0x0)
321 && (timeout
< C6XDIGIO_TIME_OUT
)) {
324 WriteByteToHwPort(baseAddr
, 0x0);
326 while (((ReadByteFromHwPort(baseAddr
+ 1) & 0x80) == 0x80)
327 && (timeout
< C6XDIGIO_TIME_OUT
)) {
332 static int c6xdigio_pwmo_insn_read(struct comedi_device
*dev
,
333 struct comedi_subdevice
*s
,
334 struct comedi_insn
*insn
, unsigned int *data
)
336 printk(KERN_DEBUG
"c6xdigio_pwmo_insn_read %x\n", insn
->n
);
340 static int c6xdigio_pwmo_insn_write(struct comedi_device
*dev
,
341 struct comedi_subdevice
*s
,
342 struct comedi_insn
*insn
,
346 int chan
= CR_CHAN(insn
->chanspec
);
348 /* printk("c6xdigio_pwmo_insn_write %x\n", insn->n); */
349 for (i
= 0; i
< insn
->n
; i
++) {
350 C6X_pwmOutput(dev
->iobase
, chan
, data
[i
]);
351 /* devpriv->ao_readback[chan] = data[i]; */
356 /* static int c6xdigio_ei_init_insn_read(struct comedi_device *dev, */
357 /* struct comedi_subdevice *s, */
358 /* struct comedi_insn *insn, */
359 /* unsigned int *data) */
361 /* printk("c6xdigio_ei_init_insn_read %x\n", insn->n); */
362 /* return insn->n; */
365 /* static int c6xdigio_ei_init_insn_write(struct comedi_device *dev, */
366 /* struct comedi_subdevice *s, */
367 /* struct comedi_insn *insn, */
368 /* unsigned int *data) */
371 /* int chan = CR_CHAN(insn->chanspec); */
372 /* *//* C6X_encResetAll( dev->iobase ); */
373 /* *//* return insn->n; */
376 static int c6xdigio_ei_insn_read(struct comedi_device
*dev
,
377 struct comedi_subdevice
*s
,
378 struct comedi_insn
*insn
, unsigned int *data
)
380 /* printk("c6xdigio_ei__insn_read %x\n", insn->n); */
382 int chan
= CR_CHAN(insn
->chanspec
);
384 for (n
= 0; n
< insn
->n
; n
++)
385 data
[n
] = (C6X_encInput(dev
->iobase
, chan
) & 0xffffff);
390 static void board_init(struct comedi_device
*dev
)
393 /* printk("Inside board_init\n"); */
395 C6X_pwmInit(dev
->iobase
);
396 C6X_encResetAll(dev
->iobase
);
400 static const struct pnp_device_id c6xdigio_pnp_tbl
[] = {
401 /* Standard LPT Printer Port */
402 {.id
= "PNP0400", .driver_data
= 0},
403 /* ECP Printer Port */
404 {.id
= "PNP0401", .driver_data
= 0},
408 static struct pnp_driver c6xdigio_pnp_driver
= {
410 .id_table
= c6xdigio_pnp_tbl
,
413 static int c6xdigio_attach(struct comedi_device
*dev
,
414 struct comedi_devconfig
*it
)
416 struct comedi_subdevice
*s
;
419 ret
= comedi_request_region(dev
, it
->options
[0], C6XDIGIO_SIZE
);
423 ret
= comedi_alloc_subdevices(dev
, 2);
427 /* Make sure that PnP ports get activated */
428 pnp_register_driver(&c6xdigio_pnp_driver
);
430 s
= &dev
->subdevices
[0];
431 /* pwm output subdevice */
432 s
->type
= COMEDI_SUBD_AO
; /* Not sure what to put here */
433 s
->subdev_flags
= SDF_WRITEABLE
;
435 /* s->trig[0] = c6xdigio_pwmo; */
436 s
->insn_read
= c6xdigio_pwmo_insn_read
;
437 s
->insn_write
= c6xdigio_pwmo_insn_write
;
439 s
->range_table
= &range_bipolar10
; /* A suitable lie */
441 s
= &dev
->subdevices
[1];
442 /* encoder (counter) subdevice */
443 s
->type
= COMEDI_SUBD_COUNTER
;
444 s
->subdev_flags
= SDF_READABLE
| SDF_LSAMPL
;
446 /* s->trig[0] = c6xdigio_ei; */
447 s
->insn_read
= c6xdigio_ei_insn_read
;
448 s
->maxdata
= 0xffffff;
449 s
->range_table
= &range_unknown
;
451 /* s = &dev->subdevices[2]; */
452 /* pwm output subdevice */
453 /* s->type = COMEDI_SUBD_COUNTER; // Not sure what to put here */
454 /* s->subdev_flags = SDF_WRITEABLE; */
456 /* s->trig[0] = c6xdigio_ei_init; */
457 /* s->insn_read = c6xdigio_ei_init_insn_read; */
458 /* s->insn_write = c6xdigio_ei_init_insn_write; */
459 /* s->maxdata = 0xFFFF; // Really just a don't care */
460 /* s->range_table = &range_unknown; // Not sure what to put here */
462 /* I will call this init anyway but more than likely the DSP board */
463 /* will not be connected when device driver is loaded. */
469 static void c6xdigio_detach(struct comedi_device
*dev
)
471 comedi_legacy_detach(dev
);
472 pnp_unregister_driver(&c6xdigio_pnp_driver
);
475 static struct comedi_driver c6xdigio_driver
= {
476 .driver_name
= "c6xdigio",
477 .module
= THIS_MODULE
,
478 .attach
= c6xdigio_attach
,
479 .detach
= c6xdigio_detach
,
481 module_comedi_driver(c6xdigio_driver
);
483 MODULE_AUTHOR("Comedi http://www.comedi.org");
484 MODULE_DESCRIPTION("Comedi low-level driver");
485 MODULE_LICENSE("GPL");