Merge branch 'for-linus' of git://github.com/schandinat/linux-2.6
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / staging / comedi / drivers / c6xdigio.c
CommitLineData
2c89e159
GKH
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/*
26Driver: c6xdigio
27Description: Mechatronic Systems Inc. C6x_DIGIO DSP daughter card
28Author: Dan Block
29Status: unknown
30Devices: [Mechatronic Systems Inc.] C6x_DIGIO DSP daughter card (c6xdigio)
31Updated: Sun Nov 20 20:18:34 EST 2005
32
33This driver will not work with a 2.4 kernel.
34http://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>
23d53b17 48#include <linux/io.h>
2c89e159
GKH
49#include <linux/pnp.h>
50
51#include "../comedidev.h"
52
53static u8 ReadByteFromHwPort(unsigned long addr)
54{
55 u8 result = inb(addr);
56 return result;
57}
58
59static 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
72struct pwmbitstype {
73 unsigned sb0:2;
74 unsigned sb1:2;
75 unsigned sb2:2;
76 unsigned sb3:2;
77 unsigned sb4:2;
78};
79union pwmcmdtype {
6a438139 80 unsigned cmd; /* assuming here that int is 32bit */
2c89e159
GKH
81 struct pwmbitstype bits;
82};
83struct 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};
93union encvaluetype {
94 unsigned value;
95 struct encbitstype bits;
96};
97
98#define C6XDIGIO_TIME_OUT 20
99
0a85b6f0
MT
100static int c6xdigio_attach(struct comedi_device *dev,
101 struct comedi_devconfig *it);
da91b269 102static int c6xdigio_detach(struct comedi_device *dev);
139dfbdf 103struct comedi_driver driver_c6xdigio = {
68c3dbff
BP
104 .driver_name = "c6xdigio",
105 .module = THIS_MODULE,
106 .attach = c6xdigio_attach,
107 .detach = c6xdigio_detach,
2c89e159
GKH
108};
109
110static void C6X_pwmInit(unsigned long baseAddr)
111{
112 int timeout = 0;
113
6a438139 114/* printk("Inside C6X_pwmInit\n"); */
2c89e159
GKH
115
116 WriteByteToHwPort(baseAddr, 0x70);
117 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
0a85b6f0 118 && (timeout < C6XDIGIO_TIME_OUT)) {
2c89e159
GKH
119 timeout++;
120 }
121
122 WriteByteToHwPort(baseAddr, 0x74);
123 timeout = 0;
124 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
0a85b6f0 125 && (timeout < C6XDIGIO_TIME_OUT)) {
2c89e159
GKH
126 timeout++;
127 }
128
129 WriteByteToHwPort(baseAddr, 0x70);
130 timeout = 0;
131 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
0a85b6f0 132 && (timeout < C6XDIGIO_TIME_OUT)) {
2c89e159
GKH
133 timeout++;
134 }
135
136 WriteByteToHwPort(baseAddr, 0x0);
137 timeout = 0;
138 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
0a85b6f0 139 && (timeout < C6XDIGIO_TIME_OUT)) {
2c89e159
GKH
140 timeout++;
141 }
142
143}
144
145static 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
6a438139 152 /* printk("Inside C6X_pwmOutput\n"); */
2c89e159
GKH
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;
6a438139 162 } else { /* if channel == 1 */
2c89e159
GKH
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
213static int C6X_encInput(unsigned long baseAddr, unsigned channel)
214{
215 unsigned ppcmd;
216 union encvaluetype enc;
217 int timeout = 0;
218 int tmp;
219
6a438139 220 /* printk("Inside C6X_encInput\n"); */
2c89e159
GKH
221
222 enc.value = 0;
23d53b17 223 if (channel == 0)
2c89e159 224 ppcmd = 0x48;
23d53b17 225 else
2c89e159 226 ppcmd = 0x50;
23d53b17 227
2c89e159
GKH
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
dae0dc30 308 return enc.value ^ 0x800000;
2c89e159
GKH
309}
310
311static void C6X_encResetAll(unsigned long baseAddr)
312{
313 unsigned timeout = 0;
314
6a438139 315/* printk("Inside C6X_encResetAll\n"); */
2c89e159
GKH
316
317 WriteByteToHwPort(baseAddr, 0x68);
318 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
0a85b6f0 319 && (timeout < C6XDIGIO_TIME_OUT)) {
2c89e159
GKH
320 timeout++;
321 }
322 WriteByteToHwPort(baseAddr, 0x6C);
323 timeout = 0;
324 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
0a85b6f0 325 && (timeout < C6XDIGIO_TIME_OUT)) {
2c89e159
GKH
326 timeout++;
327 }
328 WriteByteToHwPort(baseAddr, 0x68);
329 timeout = 0;
330 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
0a85b6f0 331 && (timeout < C6XDIGIO_TIME_OUT)) {
2c89e159
GKH
332 timeout++;
333 }
334 WriteByteToHwPort(baseAddr, 0x0);
335 timeout = 0;
336 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
0a85b6f0 337 && (timeout < C6XDIGIO_TIME_OUT)) {
2c89e159
GKH
338 timeout++;
339 }
340}
341
da91b269 342static int c6xdigio_pwmo_insn_read(struct comedi_device *dev,
0a85b6f0
MT
343 struct comedi_subdevice *s,
344 struct comedi_insn *insn, unsigned int *data)
2c89e159
GKH
345{
346 printk("c6xdigio_pwmo_insn_read %x\n", insn->n);
347 return insn->n;
348}
349
da91b269 350static int c6xdigio_pwmo_insn_write(struct comedi_device *dev,
0a85b6f0
MT
351 struct comedi_subdevice *s,
352 struct comedi_insn *insn,
353 unsigned int *data)
2c89e159
GKH
354{
355 int i;
356 int chan = CR_CHAN(insn->chanspec);
357
6a438139 358 /* printk("c6xdigio_pwmo_insn_write %x\n", insn->n); */
2c89e159
GKH
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
6a438139
BP
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); */
0a85b6f0
MT
382 /* *//* C6X_encResetAll( dev->iobase ); */
383 /* *//* return insn->n; */
6a438139 384/* } */
2c89e159 385
da91b269 386static int c6xdigio_ei_insn_read(struct comedi_device *dev,
0a85b6f0
MT
387 struct comedi_subdevice *s,
388 struct comedi_insn *insn, unsigned int *data)
2c89e159 389{
6a438139 390 /* printk("c6xdigio_ei__insn_read %x\n", insn->n); */
2c89e159
GKH
391 int n;
392 int chan = CR_CHAN(insn->chanspec);
393
23d53b17 394 for (n = 0; n < insn->n; n++)
2c89e159 395 data[n] = (C6X_encInput(dev->iobase, chan) & 0xffffff);
2c89e159
GKH
396
397 return n;
398}
399
da91b269 400static void board_init(struct comedi_device *dev)
2c89e159
GKH
401{
402
6a438139 403 /* printk("Inside board_init\n"); */
2c89e159
GKH
404
405 C6X_pwmInit(dev->iobase);
406 C6X_encResetAll(dev->iobase);
407
408}
409
6a438139
BP
410/* static void board_halt(struct comedi_device *dev) { */
411/* C6X_pwmInit(dev->iobase); */
412/* } */
2c89e159
GKH
413
414/*
415 options[0] - I/O port
416 options[1] - irq
417 options[2] - number of encoder chips installed
418 */
419
420static const struct pnp_device_id c6xdigio_pnp_tbl[] = {
421 /* Standard LPT Printer Port */
23d53b17 422 {.id = "PNP0400", .driver_data = 0},
2c89e159 423 /* ECP Printer Port */
23d53b17 424 {.id = "PNP0401", .driver_data = 0},
2c89e159
GKH
425 {}
426};
427
428static struct pnp_driver c6xdigio_pnp_driver = {
429 .name = "c6xdigio",
430 .id_table = c6xdigio_pnp_tbl,
431};
432
0a85b6f0
MT
433static int c6xdigio_attach(struct comedi_device *dev,
434 struct comedi_devconfig *it)
2c89e159
GKH
435{
436 int result = 0;
437 unsigned long iobase;
438 unsigned int irq;
34c43922 439 struct comedi_subdevice *s;
2c89e159
GKH
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
6a438139 450 result = alloc_subdevices(dev, 2); /* 3 with encoder_init write */
2c89e159
GKH
451 if (result < 0)
452 return result;
453
23d53b17 454 /* Make sure that PnP ports get activated */
2c89e159
GKH
455 pnp_register_driver(&c6xdigio_pnp_driver);
456
457 irq = it->options[1];
23d53b17 458 if (irq > 0)
2c89e159 459 printk("comedi%d: irq = %u ignored\n", dev->minor, irq);
23d53b17 460 else if (irq == 0)
2c89e159 461 printk("comedi%d: no irq\n", dev->minor);
2c89e159
GKH
462
463 s = dev->subdevices + 0;
464 /* pwm output subdevice */
6a438139 465 s->type = COMEDI_SUBD_AO; /* Not sure what to put here */
2c89e159
GKH
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;
6a438139 472 s->range_table = &range_bipolar10; /* A suitable lie */
2c89e159
GKH
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
23d53b17 484 /* s = dev->subdevices + 2; */
0a85b6f0 485 /* pwm output subdevice */
23d53b17
BA
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. */
2c89e159
GKH
497 board_init(dev);
498
499 return 0;
500}
501
da91b269 502static int c6xdigio_detach(struct comedi_device *dev)
2c89e159 503{
23d53b17 504 /* board_halt(dev); may not need this */
2c89e159
GKH
505
506 printk("comedi%d: c6xdigio: remove\n", dev->minor);
507
23d53b17 508 if (dev->iobase)
2c89e159 509 release_region(dev->iobase, C6XDIGIO_SIZE);
23d53b17
BA
510
511 /* Not using IRQ so I am not sure if I need this */
512 if (dev->irq)
2c89e159 513 free_irq(dev->irq, dev);
23d53b17 514
2c89e159
GKH
515 pnp_unregister_driver(&c6xdigio_pnp_driver);
516
517 return 0;
518}
519
7114a280
AT
520static int __init driver_c6xdigio_init_module(void)
521{
522 return comedi_driver_register(&driver_c6xdigio);
523}
524
525static void __exit driver_c6xdigio_cleanup_module(void)
526{
527 comedi_driver_unregister(&driver_c6xdigio);
528}
529
530module_init(driver_c6xdigio_init_module);
531module_exit(driver_c6xdigio_cleanup_module);
90f703d3
AT
532
533MODULE_AUTHOR("Comedi http://www.comedi.org");
534MODULE_DESCRIPTION("Comedi low-level driver");
535MODULE_LICENSE("GPL");