Merge branch 'fix/asoc' into for-linus
[pandora-kernel.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 <asm/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, struct comedi_devconfig * it);
101 static int c6xdigio_detach(struct comedi_device * dev);
102 struct comedi_driver driver_c6xdigio = {
103       driver_name:"c6xdigio",
104       module:THIS_MODULE,
105       attach:c6xdigio_attach,
106       detach:c6xdigio_detach,
107 };
108
109 static void C6X_pwmInit(unsigned long baseAddr)
110 {
111         int timeout = 0;
112
113 //printk("Inside C6X_pwmInit\n");
114
115         WriteByteToHwPort(baseAddr, 0x70);
116         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
117                 && (timeout < C6XDIGIO_TIME_OUT)) {
118                 timeout++;
119         }
120
121         WriteByteToHwPort(baseAddr, 0x74);
122         timeout = 0;
123         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
124                 && (timeout < C6XDIGIO_TIME_OUT)) {
125                 timeout++;
126         }
127
128         WriteByteToHwPort(baseAddr, 0x70);
129         timeout = 0;
130         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
131                 && (timeout < C6XDIGIO_TIME_OUT)) {
132                 timeout++;
133         }
134
135         WriteByteToHwPort(baseAddr, 0x0);
136         timeout = 0;
137         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
138                 && (timeout < C6XDIGIO_TIME_OUT)) {
139                 timeout++;
140         }
141
142 }
143
144 static void C6X_pwmOutput(unsigned long baseAddr, unsigned channel, int value)
145 {
146         unsigned ppcmd;
147         union pwmcmdtype pwm;
148         int timeout = 0;
149         unsigned tmp;
150
151         //printk("Inside C6X_pwmOutput\n");
152
153         pwm.cmd = value;
154         if (pwm.cmd > 498)
155                 pwm.cmd = 498;
156         if (pwm.cmd < 2)
157                 pwm.cmd = 2;
158
159         if (channel == 0) {
160                 ppcmd = 0x28;
161         } else {                // if channel == 1
162                 ppcmd = 0x30;
163         }                       /* endif */
164
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);
169                 timeout++;
170         }
171
172         WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb1 + 0x4);
173         timeout = 0;
174         tmp = ReadByteFromHwPort(baseAddr + 1);
175         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
176                 tmp = ReadByteFromHwPort(baseAddr + 1);
177                 timeout++;
178         }
179
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);
184                 timeout++;
185         }
186
187         WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb3 + 0x4);
188         timeout = 0;
189         tmp = ReadByteFromHwPort(baseAddr + 1);
190         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
191                 tmp = ReadByteFromHwPort(baseAddr + 1);
192                 timeout++;
193         }
194
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);
199                 timeout++;
200         }
201
202         WriteByteToHwPort(baseAddr, 0x0);
203         timeout = 0;
204         tmp = ReadByteFromHwPort(baseAddr + 1);
205         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
206                 tmp = ReadByteFromHwPort(baseAddr + 1);
207                 timeout++;
208         }
209
210 }
211
212 static int C6X_encInput(unsigned long baseAddr, unsigned channel)
213 {
214         unsigned ppcmd;
215         union encvaluetype enc;
216         int timeout = 0;
217         int tmp;
218
219         //printk("Inside C6X_encInput\n");
220
221         enc.value = 0;
222         if (channel == 0) {
223                 ppcmd = 0x48;
224         } else {
225                 ppcmd = 0x50;
226         }
227         WriteByteToHwPort(baseAddr, ppcmd);
228         tmp = ReadByteFromHwPort(baseAddr + 1);
229         while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
230                 tmp = ReadByteFromHwPort(baseAddr + 1);
231                 timeout++;
232         }
233
234         enc.bits.sb0 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
235         WriteByteToHwPort(baseAddr, ppcmd + 0x4);
236         timeout = 0;
237         tmp = ReadByteFromHwPort(baseAddr + 1);
238         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
239                 tmp = ReadByteFromHwPort(baseAddr + 1);
240                 timeout++;
241         }
242         enc.bits.sb1 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
243         WriteByteToHwPort(baseAddr, ppcmd);
244         timeout = 0;
245         tmp = ReadByteFromHwPort(baseAddr + 1);
246         while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
247                 tmp = ReadByteFromHwPort(baseAddr + 1);
248                 timeout++;
249         }
250         enc.bits.sb2 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
251         WriteByteToHwPort(baseAddr, ppcmd + 0x4);
252         timeout = 0;
253         tmp = ReadByteFromHwPort(baseAddr + 1);
254         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
255                 tmp = ReadByteFromHwPort(baseAddr + 1);
256                 timeout++;
257         }
258         enc.bits.sb3 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
259         WriteByteToHwPort(baseAddr, ppcmd);
260         timeout = 0;
261         tmp = ReadByteFromHwPort(baseAddr + 1);
262         while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
263                 tmp = ReadByteFromHwPort(baseAddr + 1);
264                 timeout++;
265         }
266         enc.bits.sb4 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
267         WriteByteToHwPort(baseAddr, ppcmd + 0x4);
268         timeout = 0;
269         tmp = ReadByteFromHwPort(baseAddr + 1);
270         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
271                 tmp = ReadByteFromHwPort(baseAddr + 1);
272                 timeout++;
273         }
274         enc.bits.sb5 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
275         WriteByteToHwPort(baseAddr, ppcmd);
276         timeout = 0;
277         tmp = ReadByteFromHwPort(baseAddr + 1);
278         while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
279                 tmp = ReadByteFromHwPort(baseAddr + 1);
280                 timeout++;
281         }
282         enc.bits.sb6 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
283         WriteByteToHwPort(baseAddr, ppcmd + 0x4);
284         timeout = 0;
285         tmp = ReadByteFromHwPort(baseAddr + 1);
286         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
287                 tmp = ReadByteFromHwPort(baseAddr + 1);
288                 timeout++;
289         }
290         enc.bits.sb7 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
291         WriteByteToHwPort(baseAddr, ppcmd);
292         timeout = 0;
293         tmp = ReadByteFromHwPort(baseAddr + 1);
294         while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
295                 tmp = ReadByteFromHwPort(baseAddr + 1);
296                 timeout++;
297         }
298
299         WriteByteToHwPort(baseAddr, 0x0);
300         timeout = 0;
301         tmp = ReadByteFromHwPort(baseAddr + 1);
302         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
303                 tmp = ReadByteFromHwPort(baseAddr + 1);
304                 timeout++;
305         }
306
307         return (enc.value ^ 0x800000);
308 }
309
310 static void C6X_encResetAll(unsigned long baseAddr)
311 {
312         unsigned timeout = 0;
313
314 //printk("Inside C6X_encResetAll\n");
315
316         WriteByteToHwPort(baseAddr, 0x68);
317         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
318                 && (timeout < C6XDIGIO_TIME_OUT)) {
319                 timeout++;
320         }
321         WriteByteToHwPort(baseAddr, 0x6C);
322         timeout = 0;
323         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
324                 && (timeout < C6XDIGIO_TIME_OUT)) {
325                 timeout++;
326         }
327         WriteByteToHwPort(baseAddr, 0x68);
328         timeout = 0;
329         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
330                 && (timeout < C6XDIGIO_TIME_OUT)) {
331                 timeout++;
332         }
333         WriteByteToHwPort(baseAddr, 0x0);
334         timeout = 0;
335         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
336                 && (timeout < C6XDIGIO_TIME_OUT)) {
337                 timeout++;
338         }
339 }
340
341 static int c6xdigio_pwmo_insn_read(struct comedi_device * dev,
342         struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
343 {
344         printk("c6xdigio_pwmo_insn_read %x\n", insn->n);
345         return insn->n;
346 }
347
348 static int c6xdigio_pwmo_insn_write(struct comedi_device * dev,
349         struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
350 {
351         int i;
352         int chan = CR_CHAN(insn->chanspec);
353
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]; */
358         }
359         return i;
360 }
361
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)
366 //{
367 //  printk("c6xdigio_ei_init_insn_read %x\n", insn->n);
368 //  return insn->n;
369 //}
370
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)
375 //{
376 //  int i;
377 //  int chan = CR_CHAN(insn->chanspec);
378 //
379 //  C6X_encResetAll( dev->iobase );
380 //
381 //  return insn->n;
382 //}
383
384 static int c6xdigio_ei_insn_read(struct comedi_device * dev,
385         struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
386 {
387         //  printk("c6xdigio_ei__insn_read %x\n", insn->n);
388         int n;
389         int chan = CR_CHAN(insn->chanspec);
390
391         for (n = 0; n < insn->n; n++) {
392                 data[n] = (C6X_encInput(dev->iobase, chan) & 0xffffff);
393         }
394
395         return n;
396 }
397
398 static void board_init(struct comedi_device * dev)
399 {
400
401         //printk("Inside board_init\n");
402
403         C6X_pwmInit(dev->iobase);
404         C6X_encResetAll(dev->iobase);
405
406 }
407
408 //static void board_halt(struct comedi_device *dev) {
409 //  C6X_pwmInit(dev->iobase);
410 //}
411
412 /*
413    options[0] - I/O port
414    options[1] - irq
415    options[2] - number of encoder chips installed
416  */
417
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},
423         {}
424 };
425
426 static struct pnp_driver c6xdigio_pnp_driver = {
427         .name = "c6xdigio",
428         .id_table = c6xdigio_pnp_tbl,
429 };
430
431 static int c6xdigio_attach(struct comedi_device * dev, struct comedi_devconfig * it)
432 {
433         int result = 0;
434         unsigned long iobase;
435         unsigned int irq;
436         struct comedi_subdevice *s;
437
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);
442                 return -EIO;
443         }
444         dev->iobase = iobase;
445         dev->board_name = "c6xdigio";
446
447         result = alloc_subdevices(dev, 2);      // 3 with encoder_init write
448         if (result < 0)
449                 return result;
450
451         // Make sure that PnP ports gets activated
452         pnp_register_driver(&c6xdigio_pnp_driver);
453
454         irq = it->options[1];
455         if (irq > 0) {
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);
459         }
460
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;
465         s->n_chan = 2;
466         /*      s->trig[0] = c6xdigio_pwmo; */
467         s->insn_read = c6xdigio_pwmo_insn_read;
468         s->insn_write = c6xdigio_pwmo_insn_write;
469         s->maxdata = 500;
470         s->range_table = &range_bipolar10;      // A suitable lie
471
472         s = dev->subdevices + 1;
473         /* encoder (counter) subdevice */
474         s->type = COMEDI_SUBD_COUNTER;
475         s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
476         s->n_chan = 2;
477         /* s->trig[0] = c6xdigio_ei; */
478         s->insn_read = c6xdigio_ei_insn_read;
479         s->maxdata = 0xffffff;
480         s->range_table = &range_unknown;
481
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;
486         //      s->n_chan = 1;
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
492
493         // I will call this init anyway but more than likely the DSP board will not be connect
494         // when device driver is loaded.
495         board_init(dev);
496
497         return 0;
498 }
499
500 static int c6xdigio_detach(struct comedi_device * dev)
501 {
502 //  board_halt(dev);  // may not need this
503
504         printk("comedi%d: c6xdigio: remove\n", dev->minor);
505
506         if (dev->iobase) {
507                 release_region(dev->iobase, C6XDIGIO_SIZE);
508         }
509         if (dev->irq) {
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);
513
514         return 0;
515 }
516
517 COMEDI_INITCLEANUP(driver_c6xdigio);