Linux-libre 3.6.3-gnu1
[librecmc/linux-libre.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 void C6X_pwmInit(unsigned long baseAddr)
101 {
102         int timeout = 0;
103
104 /* printk("Inside C6X_pwmInit\n"); */
105
106         WriteByteToHwPort(baseAddr, 0x70);
107         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
108                && (timeout < C6XDIGIO_TIME_OUT)) {
109                 timeout++;
110         }
111
112         WriteByteToHwPort(baseAddr, 0x74);
113         timeout = 0;
114         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
115                && (timeout < C6XDIGIO_TIME_OUT)) {
116                 timeout++;
117         }
118
119         WriteByteToHwPort(baseAddr, 0x70);
120         timeout = 0;
121         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
122                && (timeout < C6XDIGIO_TIME_OUT)) {
123                 timeout++;
124         }
125
126         WriteByteToHwPort(baseAddr, 0x0);
127         timeout = 0;
128         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
129                && (timeout < C6XDIGIO_TIME_OUT)) {
130                 timeout++;
131         }
132
133 }
134
135 static void C6X_pwmOutput(unsigned long baseAddr, unsigned channel, int value)
136 {
137         unsigned ppcmd;
138         union pwmcmdtype pwm;
139         int timeout = 0;
140         unsigned tmp;
141
142         /* printk("Inside C6X_pwmOutput\n"); */
143
144         pwm.cmd = value;
145         if (pwm.cmd > 498)
146                 pwm.cmd = 498;
147         if (pwm.cmd < 2)
148                 pwm.cmd = 2;
149
150         if (channel == 0) {
151                 ppcmd = 0x28;
152         } else {                /*  if channel == 1 */
153                 ppcmd = 0x30;
154         }                       /* endif */
155
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);
160                 timeout++;
161         }
162
163         WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb1 + 0x4);
164         timeout = 0;
165         tmp = ReadByteFromHwPort(baseAddr + 1);
166         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
167                 tmp = ReadByteFromHwPort(baseAddr + 1);
168                 timeout++;
169         }
170
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);
175                 timeout++;
176         }
177
178         WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb3 + 0x4);
179         timeout = 0;
180         tmp = ReadByteFromHwPort(baseAddr + 1);
181         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
182                 tmp = ReadByteFromHwPort(baseAddr + 1);
183                 timeout++;
184         }
185
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);
190                 timeout++;
191         }
192
193         WriteByteToHwPort(baseAddr, 0x0);
194         timeout = 0;
195         tmp = ReadByteFromHwPort(baseAddr + 1);
196         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
197                 tmp = ReadByteFromHwPort(baseAddr + 1);
198                 timeout++;
199         }
200
201 }
202
203 static int C6X_encInput(unsigned long baseAddr, unsigned channel)
204 {
205         unsigned ppcmd;
206         union encvaluetype enc;
207         int timeout = 0;
208         int tmp;
209
210         /* printk("Inside C6X_encInput\n"); */
211
212         enc.value = 0;
213         if (channel == 0)
214                 ppcmd = 0x48;
215         else
216                 ppcmd = 0x50;
217
218         WriteByteToHwPort(baseAddr, ppcmd);
219         tmp = ReadByteFromHwPort(baseAddr + 1);
220         while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
221                 tmp = ReadByteFromHwPort(baseAddr + 1);
222                 timeout++;
223         }
224
225         enc.bits.sb0 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
226         WriteByteToHwPort(baseAddr, ppcmd + 0x4);
227         timeout = 0;
228         tmp = ReadByteFromHwPort(baseAddr + 1);
229         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
230                 tmp = ReadByteFromHwPort(baseAddr + 1);
231                 timeout++;
232         }
233         enc.bits.sb1 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
234         WriteByteToHwPort(baseAddr, ppcmd);
235         timeout = 0;
236         tmp = ReadByteFromHwPort(baseAddr + 1);
237         while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
238                 tmp = ReadByteFromHwPort(baseAddr + 1);
239                 timeout++;
240         }
241         enc.bits.sb2 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
242         WriteByteToHwPort(baseAddr, ppcmd + 0x4);
243         timeout = 0;
244         tmp = ReadByteFromHwPort(baseAddr + 1);
245         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
246                 tmp = ReadByteFromHwPort(baseAddr + 1);
247                 timeout++;
248         }
249         enc.bits.sb3 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
250         WriteByteToHwPort(baseAddr, ppcmd);
251         timeout = 0;
252         tmp = ReadByteFromHwPort(baseAddr + 1);
253         while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
254                 tmp = ReadByteFromHwPort(baseAddr + 1);
255                 timeout++;
256         }
257         enc.bits.sb4 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
258         WriteByteToHwPort(baseAddr, ppcmd + 0x4);
259         timeout = 0;
260         tmp = ReadByteFromHwPort(baseAddr + 1);
261         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
262                 tmp = ReadByteFromHwPort(baseAddr + 1);
263                 timeout++;
264         }
265         enc.bits.sb5 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
266         WriteByteToHwPort(baseAddr, ppcmd);
267         timeout = 0;
268         tmp = ReadByteFromHwPort(baseAddr + 1);
269         while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
270                 tmp = ReadByteFromHwPort(baseAddr + 1);
271                 timeout++;
272         }
273         enc.bits.sb6 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
274         WriteByteToHwPort(baseAddr, ppcmd + 0x4);
275         timeout = 0;
276         tmp = ReadByteFromHwPort(baseAddr + 1);
277         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
278                 tmp = ReadByteFromHwPort(baseAddr + 1);
279                 timeout++;
280         }
281         enc.bits.sb7 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
282         WriteByteToHwPort(baseAddr, ppcmd);
283         timeout = 0;
284         tmp = ReadByteFromHwPort(baseAddr + 1);
285         while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
286                 tmp = ReadByteFromHwPort(baseAddr + 1);
287                 timeout++;
288         }
289
290         WriteByteToHwPort(baseAddr, 0x0);
291         timeout = 0;
292         tmp = ReadByteFromHwPort(baseAddr + 1);
293         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
294                 tmp = ReadByteFromHwPort(baseAddr + 1);
295                 timeout++;
296         }
297
298         return enc.value ^ 0x800000;
299 }
300
301 static void C6X_encResetAll(unsigned long baseAddr)
302 {
303         unsigned timeout = 0;
304
305 /* printk("Inside C6X_encResetAll\n"); */
306
307         WriteByteToHwPort(baseAddr, 0x68);
308         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
309                && (timeout < C6XDIGIO_TIME_OUT)) {
310                 timeout++;
311         }
312         WriteByteToHwPort(baseAddr, 0x6C);
313         timeout = 0;
314         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
315                && (timeout < C6XDIGIO_TIME_OUT)) {
316                 timeout++;
317         }
318         WriteByteToHwPort(baseAddr, 0x68);
319         timeout = 0;
320         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
321                && (timeout < C6XDIGIO_TIME_OUT)) {
322                 timeout++;
323         }
324         WriteByteToHwPort(baseAddr, 0x0);
325         timeout = 0;
326         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
327                && (timeout < C6XDIGIO_TIME_OUT)) {
328                 timeout++;
329         }
330 }
331
332 static int c6xdigio_pwmo_insn_read(struct comedi_device *dev,
333                                    struct comedi_subdevice *s,
334                                    struct comedi_insn *insn, unsigned int *data)
335 {
336         printk(KERN_DEBUG "c6xdigio_pwmo_insn_read %x\n", insn->n);
337         return insn->n;
338 }
339
340 static int c6xdigio_pwmo_insn_write(struct comedi_device *dev,
341                                     struct comedi_subdevice *s,
342                                     struct comedi_insn *insn,
343                                     unsigned int *data)
344 {
345         int i;
346         int chan = CR_CHAN(insn->chanspec);
347
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]; */
352         }
353         return i;
354 }
355
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) */
360 /* { */
361 /* printk("c6xdigio_ei_init_insn_read %x\n", insn->n); */
362 /* return insn->n; */
363 /* } */
364
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) */
369 /* { */
370 /* int i; */
371 /* int chan = CR_CHAN(insn->chanspec); */
372       /*  *//* C6X_encResetAll( dev->iobase ); */
373       /*  *//* return insn->n; */
374 /* } */
375
376 static int c6xdigio_ei_insn_read(struct comedi_device *dev,
377                                  struct comedi_subdevice *s,
378                                  struct comedi_insn *insn, unsigned int *data)
379 {
380         /*   printk("c6xdigio_ei__insn_read %x\n", insn->n); */
381         int n;
382         int chan = CR_CHAN(insn->chanspec);
383
384         for (n = 0; n < insn->n; n++)
385                 data[n] = (C6X_encInput(dev->iobase, chan) & 0xffffff);
386
387         return n;
388 }
389
390 static void board_init(struct comedi_device *dev)
391 {
392
393         /* printk("Inside board_init\n"); */
394
395         C6X_pwmInit(dev->iobase);
396         C6X_encResetAll(dev->iobase);
397
398 }
399
400 /*
401    options[0] - I/O port
402    options[1] - irq
403    options[2] - number of encoder chips installed
404  */
405
406 static const struct pnp_device_id c6xdigio_pnp_tbl[] = {
407         /* Standard LPT Printer Port */
408         {.id = "PNP0400", .driver_data = 0},
409         /* ECP Printer Port */
410         {.id = "PNP0401", .driver_data = 0},
411         {}
412 };
413
414 static struct pnp_driver c6xdigio_pnp_driver = {
415         .name = "c6xdigio",
416         .id_table = c6xdigio_pnp_tbl,
417 };
418
419 static int c6xdigio_attach(struct comedi_device *dev,
420                            struct comedi_devconfig *it)
421 {
422         int result = 0;
423         unsigned long iobase;
424         unsigned int irq;
425         struct comedi_subdevice *s;
426
427         iobase = it->options[0];
428         printk(KERN_DEBUG "comedi%d: c6xdigio: 0x%04lx\n", dev->minor, iobase);
429         if (!request_region(iobase, C6XDIGIO_SIZE, "c6xdigio")) {
430                 printk(KERN_ERR "comedi%d: I/O port conflict\n", dev->minor);
431                 return -EIO;
432         }
433         dev->iobase = iobase;
434         dev->board_name = "c6xdigio";
435
436         result = comedi_alloc_subdevices(dev, 2);
437         if (result)
438                 return result;
439
440         /*  Make sure that PnP ports get activated */
441         pnp_register_driver(&c6xdigio_pnp_driver);
442
443         irq = it->options[1];
444         if (irq > 0)
445                 printk(KERN_DEBUG "comedi%d: irq = %u ignored\n",
446                                 dev->minor, irq);
447         else if (irq == 0)
448                 printk(KERN_DEBUG "comedi%d: no irq\n", dev->minor);
449
450         s = dev->subdevices + 0;
451         /* pwm output subdevice */
452         s->type = COMEDI_SUBD_AO;       /*  Not sure what to put here */
453         s->subdev_flags = SDF_WRITEABLE;
454         s->n_chan = 2;
455         /*      s->trig[0] = c6xdigio_pwmo; */
456         s->insn_read = c6xdigio_pwmo_insn_read;
457         s->insn_write = c6xdigio_pwmo_insn_write;
458         s->maxdata = 500;
459         s->range_table = &range_bipolar10;      /*  A suitable lie */
460
461         s = dev->subdevices + 1;
462         /* encoder (counter) subdevice */
463         s->type = COMEDI_SUBD_COUNTER;
464         s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
465         s->n_chan = 2;
466         /* s->trig[0] = c6xdigio_ei; */
467         s->insn_read = c6xdigio_ei_insn_read;
468         s->maxdata = 0xffffff;
469         s->range_table = &range_unknown;
470
471         /*      s = dev->subdevices + 2; */
472         /* pwm output subdevice */
473         /*      s->type = COMEDI_SUBD_COUNTER;  // Not sure what to put here */
474         /*      s->subdev_flags = SDF_WRITEABLE; */
475         /*      s->n_chan = 1; */
476         /*      s->trig[0] = c6xdigio_ei_init; */
477         /*      s->insn_read = c6xdigio_ei_init_insn_read; */
478         /*      s->insn_write = c6xdigio_ei_init_insn_write; */
479         /*      s->maxdata = 0xFFFF;  // Really just a don't care */
480         /*      s->range_table = &range_unknown; // Not sure what to put here */
481
482         /*  I will call this init anyway but more than likely the DSP board */
483         /*  will not be connected when device driver is loaded. */
484         board_init(dev);
485
486         return 0;
487 }
488
489 static void c6xdigio_detach(struct comedi_device *dev)
490 {
491         if (dev->iobase)
492                 release_region(dev->iobase, C6XDIGIO_SIZE);
493         if (dev->irq)
494                 free_irq(dev->irq, dev);
495         pnp_unregister_driver(&c6xdigio_pnp_driver);
496 }
497
498 static struct comedi_driver c6xdigio_driver = {
499         .driver_name    = "c6xdigio",
500         .module         = THIS_MODULE,
501         .attach         = c6xdigio_attach,
502         .detach         = c6xdigio_detach,
503 };
504 module_comedi_driver(c6xdigio_driver);
505
506 MODULE_AUTHOR("Comedi http://www.comedi.org");
507 MODULE_DESCRIPTION("Comedi low-level driver");
508 MODULE_LICENSE("GPL");