upload tizen1.0 source
[kernel/linux-2.6.36.git] / drivers / staging / comedi / drivers / c6xdigio.c
1 /*
2    comedi/drivers/c6xdigio.c
3
4    Hardware driver for Mechatronic Systems Inc. C6x_DIGIO DSP daughter card.
5    (http://robot0.ge.uiuc.edu/~spong/mecha/)
6
7    COMEDI - Linux Control and Measurement Device Interface
8    Copyright (C) 1999 Dan Block
9
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.
14
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.
19
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.
23
24  */
25 /*
26 Driver: c6xdigio
27 Description: Mechatronic Systems Inc. C6x_DIGIO DSP daughter card
28 Author: Dan Block
29 Status: unknown
30 Devices: [Mechatronic Systems Inc.] C6x_DIGIO DSP daughter card (c6xdigio)
31 Updated: Sun Nov 20 20:18:34 EST 2005
32
33 This driver will not work with a 2.4 kernel.
34 http://robot0.ge.uiuc.edu/~spong/mecha/
35
36 */
37
38 #include <linux/kernel.h>
39 #include <linux/module.h>
40 #include <linux/sched.h>
41 #include <linux/mm.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>
48 #include <linux/io.h>
49 #include <linux/pnp.h>
50
51 #include "../comedidev.h"
52
53 static u8 ReadByteFromHwPort(unsigned long addr)
54 {
55         u8 result = inb(addr);
56         return result;
57 }
58
59 static void WriteByteToHwPort(unsigned long addr, u8 val)
60 {
61         outb_p(val, addr);
62 }
63
64 #define C6XDIGIO_SIZE 3
65
66 /*
67  * port offsets
68  */
69 #define C6XDIGIO_PARALLEL_DATA 0
70 #define C6XDIGIO_PARALLEL_STATUS 1
71 #define C6XDIGIO_PARALLEL_CONTROL 2
72 struct pwmbitstype {
73         unsigned sb0:2;
74         unsigned sb1:2;
75         unsigned sb2:2;
76         unsigned sb3:2;
77         unsigned sb4:2;
78 };
79 union pwmcmdtype {
80         unsigned cmd;           /*  assuming here that int is 32bit */
81         struct pwmbitstype bits;
82 };
83 struct encbitstype {
84         unsigned sb0:3;
85         unsigned sb1:3;
86         unsigned sb2:3;
87         unsigned sb3:3;
88         unsigned sb4:3;
89         unsigned sb5:3;
90         unsigned sb6:3;
91         unsigned sb7:3;
92 };
93 union encvaluetype {
94         unsigned value;
95         struct encbitstype bits;
96 };
97
98 #define C6XDIGIO_TIME_OUT 20
99
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,
108 };
109
110 static void C6X_pwmInit(unsigned long baseAddr)
111 {
112         int timeout = 0;
113
114 /* printk("Inside C6X_pwmInit\n"); */
115
116         WriteByteToHwPort(baseAddr, 0x70);
117         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
118                && (timeout < C6XDIGIO_TIME_OUT)) {
119                 timeout++;
120         }
121
122         WriteByteToHwPort(baseAddr, 0x74);
123         timeout = 0;
124         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
125                && (timeout < C6XDIGIO_TIME_OUT)) {
126                 timeout++;
127         }
128
129         WriteByteToHwPort(baseAddr, 0x70);
130         timeout = 0;
131         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
132                && (timeout < C6XDIGIO_TIME_OUT)) {
133                 timeout++;
134         }
135
136         WriteByteToHwPort(baseAddr, 0x0);
137         timeout = 0;
138         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
139                && (timeout < C6XDIGIO_TIME_OUT)) {
140                 timeout++;
141         }
142
143 }
144
145 static void C6X_pwmOutput(unsigned long baseAddr, unsigned channel, int value)
146 {
147         unsigned ppcmd;
148         union pwmcmdtype pwm;
149         int timeout = 0;
150         unsigned tmp;
151
152         /* printk("Inside C6X_pwmOutput\n"); */
153
154         pwm.cmd = value;
155         if (pwm.cmd > 498)
156                 pwm.cmd = 498;
157         if (pwm.cmd < 2)
158                 pwm.cmd = 2;
159
160         if (channel == 0) {
161                 ppcmd = 0x28;
162         } else {                /*  if channel == 1 */
163                 ppcmd = 0x30;
164         }                       /* endif */
165
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);
170                 timeout++;
171         }
172
173         WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb1 + 0x4);
174         timeout = 0;
175         tmp = ReadByteFromHwPort(baseAddr + 1);
176         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
177                 tmp = ReadByteFromHwPort(baseAddr + 1);
178                 timeout++;
179         }
180
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);
185                 timeout++;
186         }
187
188         WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb3 + 0x4);
189         timeout = 0;
190         tmp = ReadByteFromHwPort(baseAddr + 1);
191         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
192                 tmp = ReadByteFromHwPort(baseAddr + 1);
193                 timeout++;
194         }
195
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);
200                 timeout++;
201         }
202
203         WriteByteToHwPort(baseAddr, 0x0);
204         timeout = 0;
205         tmp = ReadByteFromHwPort(baseAddr + 1);
206         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
207                 tmp = ReadByteFromHwPort(baseAddr + 1);
208                 timeout++;
209         }
210
211 }
212
213 static int C6X_encInput(unsigned long baseAddr, unsigned channel)
214 {
215         unsigned ppcmd;
216         union encvaluetype enc;
217         int timeout = 0;
218         int tmp;
219
220         /* printk("Inside C6X_encInput\n"); */
221
222         enc.value = 0;
223         if (channel == 0)
224                 ppcmd = 0x48;
225         else
226                 ppcmd = 0x50;
227
228         WriteByteToHwPort(baseAddr, ppcmd);
229         tmp = ReadByteFromHwPort(baseAddr + 1);
230         while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
231                 tmp = ReadByteFromHwPort(baseAddr + 1);
232                 timeout++;
233         }
234
235         enc.bits.sb0 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
236         WriteByteToHwPort(baseAddr, ppcmd + 0x4);
237         timeout = 0;
238         tmp = ReadByteFromHwPort(baseAddr + 1);
239         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
240                 tmp = ReadByteFromHwPort(baseAddr + 1);
241                 timeout++;
242         }
243         enc.bits.sb1 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
244         WriteByteToHwPort(baseAddr, ppcmd);
245         timeout = 0;
246         tmp = ReadByteFromHwPort(baseAddr + 1);
247         while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
248                 tmp = ReadByteFromHwPort(baseAddr + 1);
249                 timeout++;
250         }
251         enc.bits.sb2 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
252         WriteByteToHwPort(baseAddr, ppcmd + 0x4);
253         timeout = 0;
254         tmp = ReadByteFromHwPort(baseAddr + 1);
255         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
256                 tmp = ReadByteFromHwPort(baseAddr + 1);
257                 timeout++;
258         }
259         enc.bits.sb3 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
260         WriteByteToHwPort(baseAddr, ppcmd);
261         timeout = 0;
262         tmp = ReadByteFromHwPort(baseAddr + 1);
263         while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
264                 tmp = ReadByteFromHwPort(baseAddr + 1);
265                 timeout++;
266         }
267         enc.bits.sb4 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
268         WriteByteToHwPort(baseAddr, ppcmd + 0x4);
269         timeout = 0;
270         tmp = ReadByteFromHwPort(baseAddr + 1);
271         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
272                 tmp = ReadByteFromHwPort(baseAddr + 1);
273                 timeout++;
274         }
275         enc.bits.sb5 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
276         WriteByteToHwPort(baseAddr, ppcmd);
277         timeout = 0;
278         tmp = ReadByteFromHwPort(baseAddr + 1);
279         while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
280                 tmp = ReadByteFromHwPort(baseAddr + 1);
281                 timeout++;
282         }
283         enc.bits.sb6 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
284         WriteByteToHwPort(baseAddr, ppcmd + 0x4);
285         timeout = 0;
286         tmp = ReadByteFromHwPort(baseAddr + 1);
287         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
288                 tmp = ReadByteFromHwPort(baseAddr + 1);
289                 timeout++;
290         }
291         enc.bits.sb7 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
292         WriteByteToHwPort(baseAddr, ppcmd);
293         timeout = 0;
294         tmp = ReadByteFromHwPort(baseAddr + 1);
295         while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
296                 tmp = ReadByteFromHwPort(baseAddr + 1);
297                 timeout++;
298         }
299
300         WriteByteToHwPort(baseAddr, 0x0);
301         timeout = 0;
302         tmp = ReadByteFromHwPort(baseAddr + 1);
303         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
304                 tmp = ReadByteFromHwPort(baseAddr + 1);
305                 timeout++;
306         }
307
308         return enc.value ^ 0x800000;
309 }
310
311 static void C6X_encResetAll(unsigned long baseAddr)
312 {
313         unsigned timeout = 0;
314
315 /* printk("Inside C6X_encResetAll\n"); */
316
317         WriteByteToHwPort(baseAddr, 0x68);
318         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
319                && (timeout < C6XDIGIO_TIME_OUT)) {
320                 timeout++;
321         }
322         WriteByteToHwPort(baseAddr, 0x6C);
323         timeout = 0;
324         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
325                && (timeout < C6XDIGIO_TIME_OUT)) {
326                 timeout++;
327         }
328         WriteByteToHwPort(baseAddr, 0x68);
329         timeout = 0;
330         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
331                && (timeout < C6XDIGIO_TIME_OUT)) {
332                 timeout++;
333         }
334         WriteByteToHwPort(baseAddr, 0x0);
335         timeout = 0;
336         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
337                && (timeout < C6XDIGIO_TIME_OUT)) {
338                 timeout++;
339         }
340 }
341
342 static int c6xdigio_pwmo_insn_read(struct comedi_device *dev,
343                                    struct comedi_subdevice *s,
344                                    struct comedi_insn *insn, unsigned int *data)
345 {
346         printk("c6xdigio_pwmo_insn_read %x\n", insn->n);
347         return insn->n;
348 }
349
350 static int c6xdigio_pwmo_insn_write(struct comedi_device *dev,
351                                     struct comedi_subdevice *s,
352                                     struct comedi_insn *insn,
353                                     unsigned int *data)
354 {
355         int i;
356         int chan = CR_CHAN(insn->chanspec);
357
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]; */
362         }
363         return i;
364 }
365
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) */
370 /* { */
371 /* printk("c6xdigio_ei_init_insn_read %x\n", insn->n); */
372 /* return insn->n; */
373 /* } */
374
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) */
379 /* { */
380 /* int i; */
381 /* int chan = CR_CHAN(insn->chanspec); */
382       /*  *//* C6X_encResetAll( dev->iobase ); */
383       /*  *//* return insn->n; */
384 /* } */
385
386 static int c6xdigio_ei_insn_read(struct comedi_device *dev,
387                                  struct comedi_subdevice *s,
388                                  struct comedi_insn *insn, unsigned int *data)
389 {
390         /*   printk("c6xdigio_ei__insn_read %x\n", insn->n); */
391         int n;
392         int chan = CR_CHAN(insn->chanspec);
393
394         for (n = 0; n < insn->n; n++)
395                 data[n] = (C6X_encInput(dev->iobase, chan) & 0xffffff);
396
397         return n;
398 }
399
400 static void board_init(struct comedi_device *dev)
401 {
402
403         /* printk("Inside board_init\n"); */
404
405         C6X_pwmInit(dev->iobase);
406         C6X_encResetAll(dev->iobase);
407
408 }
409
410 /* static void board_halt(struct comedi_device *dev) { */
411 /* C6X_pwmInit(dev->iobase); */
412 /* } */
413
414 /*
415    options[0] - I/O port
416    options[1] - irq
417    options[2] - number of encoder chips installed
418  */
419
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},
425         {}
426 };
427
428 static struct pnp_driver c6xdigio_pnp_driver = {
429         .name = "c6xdigio",
430         .id_table = c6xdigio_pnp_tbl,
431 };
432
433 static int c6xdigio_attach(struct comedi_device *dev,
434                            struct comedi_devconfig *it)
435 {
436         int result = 0;
437         unsigned long iobase;
438         unsigned int irq;
439         struct comedi_subdevice *s;
440
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);
445                 return -EIO;
446         }
447         dev->iobase = iobase;
448         dev->board_name = "c6xdigio";
449
450         result = alloc_subdevices(dev, 2);      /*  3 with encoder_init write */
451         if (result < 0)
452                 return result;
453
454         /*  Make sure that PnP ports get activated */
455         pnp_register_driver(&c6xdigio_pnp_driver);
456
457         irq = it->options[1];
458         if (irq > 0)
459                 printk("comedi%d: irq = %u ignored\n", dev->minor, irq);
460         else if (irq == 0)
461                 printk("comedi%d: no irq\n", dev->minor);
462
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;
467         s->n_chan = 2;
468         /*      s->trig[0] = c6xdigio_pwmo; */
469         s->insn_read = c6xdigio_pwmo_insn_read;
470         s->insn_write = c6xdigio_pwmo_insn_write;
471         s->maxdata = 500;
472         s->range_table = &range_bipolar10;      /*  A suitable lie */
473
474         s = dev->subdevices + 1;
475         /* encoder (counter) subdevice */
476         s->type = COMEDI_SUBD_COUNTER;
477         s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
478         s->n_chan = 2;
479         /* s->trig[0] = c6xdigio_ei; */
480         s->insn_read = c6xdigio_ei_insn_read;
481         s->maxdata = 0xffffff;
482         s->range_table = &range_unknown;
483
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; */
488         /*      s->n_chan = 1; */
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 */
494
495         /*  I will call this init anyway but more than likely the DSP board */
496         /*  will not be connected when device driver is loaded. */
497         board_init(dev);
498
499         return 0;
500 }
501
502 static int c6xdigio_detach(struct comedi_device *dev)
503 {
504         /* board_halt(dev);  may not need this */
505
506         printk("comedi%d: c6xdigio: remove\n", dev->minor);
507
508         if (dev->iobase)
509                 release_region(dev->iobase, C6XDIGIO_SIZE);
510
511         /*  Not using IRQ so I am not sure if I need this */
512         if (dev->irq)
513                 free_irq(dev->irq, dev);
514
515         pnp_unregister_driver(&c6xdigio_pnp_driver);
516
517         return 0;
518 }
519
520 static int __init driver_c6xdigio_init_module(void)
521 {
522         return comedi_driver_register(&driver_c6xdigio);
523 }
524
525 static void __exit driver_c6xdigio_cleanup_module(void)
526 {
527         comedi_driver_unregister(&driver_c6xdigio);
528 }
529
530 module_init(driver_c6xdigio_init_module);
531 module_exit(driver_c6xdigio_cleanup_module);
532
533 MODULE_AUTHOR("Comedi http://www.comedi.org");
534 MODULE_DESCRIPTION("Comedi low-level driver");
535 MODULE_LICENSE("GPL");