Merge tag 'metag-for-v3.13' of git://git.kernel.org/pub/scm/linux/kernel/git/jhogan...
[cascardo/linux.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 /*
21 Driver: c6xdigio
22 Description: Mechatronic Systems Inc. C6x_DIGIO DSP daughter card
23 Author: Dan Block
24 Status: unknown
25 Devices: [Mechatronic Systems Inc.] C6x_DIGIO DSP daughter card (c6xdigio)
26 Updated: Sun Nov 20 20:18:34 EST 2005
27
28 This driver will not work with a 2.4 kernel.
29 http://robot0.ge.uiuc.edu/~spong/mecha/
30
31 */
32
33 #include <linux/kernel.h>
34 #include <linux/module.h>
35 #include <linux/sched.h>
36 #include <linux/mm.h>
37 #include <linux/errno.h>
38 #include <linux/interrupt.h>
39 #include <linux/timex.h>
40 #include <linux/timer.h>
41 #include <linux/io.h>
42 #include <linux/pnp.h>
43
44 #include "../comedidev.h"
45
46 static u8 ReadByteFromHwPort(unsigned long addr)
47 {
48         u8 result = inb(addr);
49         return result;
50 }
51
52 static void WriteByteToHwPort(unsigned long addr, u8 val)
53 {
54         outb_p(val, addr);
55 }
56
57 #define C6XDIGIO_SIZE 3
58
59 /*
60  * port offsets
61  */
62 #define C6XDIGIO_PARALLEL_DATA 0
63 #define C6XDIGIO_PARALLEL_STATUS 1
64 #define C6XDIGIO_PARALLEL_CONTROL 2
65 struct pwmbitstype {
66         unsigned sb0:2;
67         unsigned sb1:2;
68         unsigned sb2:2;
69         unsigned sb3:2;
70         unsigned sb4:2;
71 };
72 union pwmcmdtype {
73         unsigned cmd;           /*  assuming here that int is 32bit */
74         struct pwmbitstype bits;
75 };
76 struct encbitstype {
77         unsigned sb0:3;
78         unsigned sb1:3;
79         unsigned sb2:3;
80         unsigned sb3:3;
81         unsigned sb4:3;
82         unsigned sb5:3;
83         unsigned sb6:3;
84         unsigned sb7:3;
85 };
86 union encvaluetype {
87         unsigned value;
88         struct encbitstype bits;
89 };
90
91 #define C6XDIGIO_TIME_OUT 20
92
93 static void C6X_pwmInit(unsigned long baseAddr)
94 {
95         int timeout = 0;
96
97 /* printk("Inside C6X_pwmInit\n"); */
98
99         WriteByteToHwPort(baseAddr, 0x70);
100         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
101                && (timeout < C6XDIGIO_TIME_OUT)) {
102                 timeout++;
103         }
104
105         WriteByteToHwPort(baseAddr, 0x74);
106         timeout = 0;
107         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
108                && (timeout < C6XDIGIO_TIME_OUT)) {
109                 timeout++;
110         }
111
112         WriteByteToHwPort(baseAddr, 0x70);
113         timeout = 0;
114         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
115                && (timeout < C6XDIGIO_TIME_OUT)) {
116                 timeout++;
117         }
118
119         WriteByteToHwPort(baseAddr, 0x0);
120         timeout = 0;
121         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
122                && (timeout < C6XDIGIO_TIME_OUT)) {
123                 timeout++;
124         }
125
126 }
127
128 static void C6X_pwmOutput(unsigned long baseAddr, unsigned channel, int value)
129 {
130         unsigned ppcmd;
131         union pwmcmdtype pwm;
132         int timeout = 0;
133         unsigned tmp;
134
135         /* printk("Inside C6X_pwmOutput\n"); */
136
137         pwm.cmd = value;
138         if (pwm.cmd > 498)
139                 pwm.cmd = 498;
140         if (pwm.cmd < 2)
141                 pwm.cmd = 2;
142
143         if (channel == 0) {
144                 ppcmd = 0x28;
145         } else {                /*  if channel == 1 */
146                 ppcmd = 0x30;
147         }                       /* endif */
148
149         WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb0);
150         tmp = ReadByteFromHwPort(baseAddr + 1);
151         while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
152                 tmp = ReadByteFromHwPort(baseAddr + 1);
153                 timeout++;
154         }
155
156         WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb1 + 0x4);
157         timeout = 0;
158         tmp = ReadByteFromHwPort(baseAddr + 1);
159         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
160                 tmp = ReadByteFromHwPort(baseAddr + 1);
161                 timeout++;
162         }
163
164         WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb2);
165         tmp = ReadByteFromHwPort(baseAddr + 1);
166         while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
167                 tmp = ReadByteFromHwPort(baseAddr + 1);
168                 timeout++;
169         }
170
171         WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb3 + 0x4);
172         timeout = 0;
173         tmp = ReadByteFromHwPort(baseAddr + 1);
174         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
175                 tmp = ReadByteFromHwPort(baseAddr + 1);
176                 timeout++;
177         }
178
179         WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb4);
180         tmp = ReadByteFromHwPort(baseAddr + 1);
181         while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
182                 tmp = ReadByteFromHwPort(baseAddr + 1);
183                 timeout++;
184         }
185
186         WriteByteToHwPort(baseAddr, 0x0);
187         timeout = 0;
188         tmp = ReadByteFromHwPort(baseAddr + 1);
189         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
190                 tmp = ReadByteFromHwPort(baseAddr + 1);
191                 timeout++;
192         }
193
194 }
195
196 static int C6X_encInput(unsigned long baseAddr, unsigned channel)
197 {
198         unsigned ppcmd;
199         union encvaluetype enc;
200         int timeout = 0;
201         int tmp;
202
203         /* printk("Inside C6X_encInput\n"); */
204
205         enc.value = 0;
206         if (channel == 0)
207                 ppcmd = 0x48;
208         else
209                 ppcmd = 0x50;
210
211         WriteByteToHwPort(baseAddr, ppcmd);
212         tmp = ReadByteFromHwPort(baseAddr + 1);
213         while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
214                 tmp = ReadByteFromHwPort(baseAddr + 1);
215                 timeout++;
216         }
217
218         enc.bits.sb0 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
219         WriteByteToHwPort(baseAddr, ppcmd + 0x4);
220         timeout = 0;
221         tmp = ReadByteFromHwPort(baseAddr + 1);
222         while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
223                 tmp = ReadByteFromHwPort(baseAddr + 1);
224                 timeout++;
225         }
226         enc.bits.sb1 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
227         WriteByteToHwPort(baseAddr, ppcmd);
228         timeout = 0;
229         tmp = ReadByteFromHwPort(baseAddr + 1);
230         while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
231                 tmp = ReadByteFromHwPort(baseAddr + 1);
232                 timeout++;
233         }
234         enc.bits.sb2 = ((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.sb3 = ((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.sb4 = ((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.sb5 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
259         WriteByteToHwPort(baseAddr, ppcmd);
260         timeout = 0;
261         tmp = ReadByteFromHwPort(baseAddr + 1);
262         while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
263                 tmp = ReadByteFromHwPort(baseAddr + 1);
264                 timeout++;
265         }
266         enc.bits.sb6 = ((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.sb7 = ((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
283         WriteByteToHwPort(baseAddr, 0x0);
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
291         return enc.value ^ 0x800000;
292 }
293
294 static void C6X_encResetAll(unsigned long baseAddr)
295 {
296         unsigned timeout = 0;
297
298 /* printk("Inside C6X_encResetAll\n"); */
299
300         WriteByteToHwPort(baseAddr, 0x68);
301         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
302                && (timeout < C6XDIGIO_TIME_OUT)) {
303                 timeout++;
304         }
305         WriteByteToHwPort(baseAddr, 0x6C);
306         timeout = 0;
307         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
308                && (timeout < C6XDIGIO_TIME_OUT)) {
309                 timeout++;
310         }
311         WriteByteToHwPort(baseAddr, 0x68);
312         timeout = 0;
313         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
314                && (timeout < C6XDIGIO_TIME_OUT)) {
315                 timeout++;
316         }
317         WriteByteToHwPort(baseAddr, 0x0);
318         timeout = 0;
319         while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
320                && (timeout < C6XDIGIO_TIME_OUT)) {
321                 timeout++;
322         }
323 }
324
325 static int c6xdigio_pwmo_insn_read(struct comedi_device *dev,
326                                    struct comedi_subdevice *s,
327                                    struct comedi_insn *insn, unsigned int *data)
328 {
329         printk(KERN_DEBUG "c6xdigio_pwmo_insn_read %x\n", insn->n);
330         return insn->n;
331 }
332
333 static int c6xdigio_pwmo_insn_write(struct comedi_device *dev,
334                                     struct comedi_subdevice *s,
335                                     struct comedi_insn *insn,
336                                     unsigned int *data)
337 {
338         int i;
339         int chan = CR_CHAN(insn->chanspec);
340
341         /*   printk("c6xdigio_pwmo_insn_write %x\n", insn->n); */
342         for (i = 0; i < insn->n; i++) {
343                 C6X_pwmOutput(dev->iobase, chan, data[i]);
344                 /*    devpriv->ao_readback[chan] = data[i]; */
345         }
346         return i;
347 }
348
349 /* static int c6xdigio_ei_init_insn_read(struct comedi_device *dev, */
350 /* struct comedi_subdevice *s, */
351 /* struct comedi_insn *insn, */
352 /* unsigned int *data) */
353 /* { */
354 /* printk("c6xdigio_ei_init_insn_read %x\n", insn->n); */
355 /* return insn->n; */
356 /* } */
357
358 /* static int c6xdigio_ei_init_insn_write(struct comedi_device *dev, */
359 /* struct comedi_subdevice *s, */
360 /* struct comedi_insn *insn, */
361 /* unsigned int *data) */
362 /* { */
363 /* int i; */
364 /* int chan = CR_CHAN(insn->chanspec); */
365       /*  *//* C6X_encResetAll( dev->iobase ); */
366       /*  *//* return insn->n; */
367 /* } */
368
369 static int c6xdigio_ei_insn_read(struct comedi_device *dev,
370                                  struct comedi_subdevice *s,
371                                  struct comedi_insn *insn, unsigned int *data)
372 {
373         /*   printk("c6xdigio_ei__insn_read %x\n", insn->n); */
374         int n;
375         int chan = CR_CHAN(insn->chanspec);
376
377         for (n = 0; n < insn->n; n++)
378                 data[n] = (C6X_encInput(dev->iobase, chan) & 0xffffff);
379
380         return n;
381 }
382
383 static void board_init(struct comedi_device *dev)
384 {
385
386         /* printk("Inside board_init\n"); */
387
388         C6X_pwmInit(dev->iobase);
389         C6X_encResetAll(dev->iobase);
390
391 }
392
393 static const struct pnp_device_id c6xdigio_pnp_tbl[] = {
394         /* Standard LPT Printer Port */
395         {.id = "PNP0400", .driver_data = 0},
396         /* ECP Printer Port */
397         {.id = "PNP0401", .driver_data = 0},
398         {}
399 };
400
401 static struct pnp_driver c6xdigio_pnp_driver = {
402         .name = "c6xdigio",
403         .id_table = c6xdigio_pnp_tbl,
404 };
405
406 static int c6xdigio_attach(struct comedi_device *dev,
407                            struct comedi_devconfig *it)
408 {
409         struct comedi_subdevice *s;
410         int ret;
411
412         ret = comedi_request_region(dev, it->options[0], C6XDIGIO_SIZE);
413         if (ret)
414                 return ret;
415
416         ret = comedi_alloc_subdevices(dev, 2);
417         if (ret)
418                 return ret;
419
420         /*  Make sure that PnP ports get activated */
421         pnp_register_driver(&c6xdigio_pnp_driver);
422
423         s = &dev->subdevices[0];
424         /* pwm output subdevice */
425         s->type = COMEDI_SUBD_AO;       /*  Not sure what to put here */
426         s->subdev_flags = SDF_WRITEABLE;
427         s->n_chan = 2;
428         /*      s->trig[0] = c6xdigio_pwmo; */
429         s->insn_read = c6xdigio_pwmo_insn_read;
430         s->insn_write = c6xdigio_pwmo_insn_write;
431         s->maxdata = 500;
432         s->range_table = &range_bipolar10;      /*  A suitable lie */
433
434         s = &dev->subdevices[1];
435         /* encoder (counter) subdevice */
436         s->type = COMEDI_SUBD_COUNTER;
437         s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
438         s->n_chan = 2;
439         /* s->trig[0] = c6xdigio_ei; */
440         s->insn_read = c6xdigio_ei_insn_read;
441         s->maxdata = 0xffffff;
442         s->range_table = &range_unknown;
443
444         /*      s = &dev->subdevices[2]; */
445         /* pwm output subdevice */
446         /*      s->type = COMEDI_SUBD_COUNTER;  // Not sure what to put here */
447         /*      s->subdev_flags = SDF_WRITEABLE; */
448         /*      s->n_chan = 1; */
449         /*      s->trig[0] = c6xdigio_ei_init; */
450         /*      s->insn_read = c6xdigio_ei_init_insn_read; */
451         /*      s->insn_write = c6xdigio_ei_init_insn_write; */
452         /*      s->maxdata = 0xFFFF;  // Really just a don't care */
453         /*      s->range_table = &range_unknown; // Not sure what to put here */
454
455         /*  I will call this init anyway but more than likely the DSP board */
456         /*  will not be connected when device driver is loaded. */
457         board_init(dev);
458
459         return 0;
460 }
461
462 static void c6xdigio_detach(struct comedi_device *dev)
463 {
464         comedi_legacy_detach(dev);
465         pnp_unregister_driver(&c6xdigio_pnp_driver);
466 }
467
468 static struct comedi_driver c6xdigio_driver = {
469         .driver_name    = "c6xdigio",
470         .module         = THIS_MODULE,
471         .attach         = c6xdigio_attach,
472         .detach         = c6xdigio_detach,
473 };
474 module_comedi_driver(c6xdigio_driver);
475
476 MODULE_AUTHOR("Comedi http://www.comedi.org");
477 MODULE_DESCRIPTION("Comedi low-level driver");
478 MODULE_LICENSE("GPL");