c6xdigio.c revision f7cbd7aad063b2a4b7aff6a743b2b00015ce3c3e
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>
48#include <asm/io.h>
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 {
80	unsigned cmd;		/*  assuming here that int is 32bit */
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
100static int c6xdigio_attach(struct comedi_device *dev, struct comedi_devconfig *it);
101static int c6xdigio_detach(struct comedi_device *dev);
102struct comedi_driver driver_c6xdigio = {
103      driver_name:"c6xdigio",
104      module:THIS_MODULE,
105      attach:c6xdigio_attach,
106      detach:c6xdigio_detach,
107};
108
109static 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
144static 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
212static 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
310static 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
341static 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
348static 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/*  *//* C6X_encResetAll( dev->iobase ); */
379/*  *//* return insn->n; */
380/* } */
381
382static int c6xdigio_ei_insn_read(struct comedi_device *dev,
383	struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data)
384{
385	/*   printk("c6xdigio_ei__insn_read %x\n", insn->n); */
386	int n;
387	int chan = CR_CHAN(insn->chanspec);
388
389	for (n = 0; n < insn->n; n++) {
390		data[n] = (C6X_encInput(dev->iobase, chan) & 0xffffff);
391	}
392
393	return n;
394}
395
396static void board_init(struct comedi_device *dev)
397{
398
399	/* printk("Inside board_init\n"); */
400
401	C6X_pwmInit(dev->iobase);
402	C6X_encResetAll(dev->iobase);
403
404}
405
406/* static void board_halt(struct comedi_device *dev) { */
407/* C6X_pwmInit(dev->iobase); */
408/* } */
409
410/*
411   options[0] - I/O port
412   options[1] - irq
413   options[2] - number of encoder chips installed
414 */
415
416static const struct pnp_device_id c6xdigio_pnp_tbl[] = {
417	/* Standard LPT Printer Port */
418	{.id = "PNP0400", .driver_data = 0},
419	/* ECP Printer Port */
420	{.id = "PNP0401", .driver_data = 0},
421	{}
422};
423
424static struct pnp_driver c6xdigio_pnp_driver = {
425	.name = "c6xdigio",
426	.id_table = c6xdigio_pnp_tbl,
427};
428
429static int c6xdigio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
430{
431	int result = 0;
432	unsigned long iobase;
433	unsigned int irq;
434	struct comedi_subdevice *s;
435
436	iobase = it->options[0];
437	printk("comedi%d: c6xdigio: 0x%04lx\n", dev->minor, iobase);
438	if (!request_region(iobase, C6XDIGIO_SIZE, "c6xdigio")) {
439		printk("comedi%d: I/O port conflict\n", dev->minor);
440		return -EIO;
441	}
442	dev->iobase = iobase;
443	dev->board_name = "c6xdigio";
444
445	result = alloc_subdevices(dev, 2);	/*  3 with encoder_init write */
446	if (result < 0)
447		return result;
448
449	/*  Make sure that PnP ports gets activated */
450	pnp_register_driver(&c6xdigio_pnp_driver);
451
452	irq = it->options[1];
453	if (irq > 0) {
454		printk("comedi%d: irq = %u ignored\n", dev->minor, irq);
455	} else if (irq == 0) {
456		printk("comedi%d: no irq\n", dev->minor);
457	}
458
459	s = dev->subdevices + 0;
460	/* pwm output subdevice */
461	s->type = COMEDI_SUBD_AO;	/*  Not sure what to put here */
462	s->subdev_flags = SDF_WRITEABLE;
463	s->n_chan = 2;
464	/*      s->trig[0] = c6xdigio_pwmo; */
465	s->insn_read = c6xdigio_pwmo_insn_read;
466	s->insn_write = c6xdigio_pwmo_insn_write;
467	s->maxdata = 500;
468	s->range_table = &range_bipolar10;	/*  A suitable lie */
469
470	s = dev->subdevices + 1;
471	/* encoder (counter) subdevice */
472	s->type = COMEDI_SUBD_COUNTER;
473	s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
474	s->n_chan = 2;
475	/* s->trig[0] = c6xdigio_ei; */
476	s->insn_read = c6xdigio_ei_insn_read;
477	s->maxdata = 0xffffff;
478	s->range_table = &range_unknown;
479
480	/*           s = dev->subdevices + 2; */
481	      /* pwm output subdevice */
482	/*       s->type = COMEDI_SUBD_COUNTER;  // Not sure what to put here */
483	/*       s->subdev_flags = SDF_WRITEABLE; */
484	/*       s->n_chan = 1; */
485	/*       s->trig[0] = c6xdigio_ei_init; */
486	/*       s->insn_read = c6xdigio_ei_init_insn_read; */
487	/*       s->insn_write = c6xdigio_ei_init_insn_write; */
488	/*       s->maxdata = 0xFFFF;  // Really just a don't care */
489	/*       s->range_table = &range_unknown; // Not sure what to put here */
490
491	/*  I will call this init anyway but more than likely the DSP board will not be connect */
492	/*  when device driver is loaded. */
493	board_init(dev);
494
495	return 0;
496}
497
498static int c6xdigio_detach(struct comedi_device *dev)
499{
500/* board_halt(dev);  may not need this */
501
502	printk("comedi%d: c6xdigio: remove\n", dev->minor);
503
504	if (dev->iobase) {
505		release_region(dev->iobase, C6XDIGIO_SIZE);
506	}
507	if (dev->irq) {
508		free_irq(dev->irq, dev);
509	}			/*  Not using IRQ so I am not sure if I need this */
510	pnp_unregister_driver(&c6xdigio_pnp_driver);
511
512	return 0;
513}
514
515COMEDI_INITCLEANUP(driver_c6xdigio);
516