c6xdigio.c revision 90f703d30dd3e0c16ff80f35e34e511385a05ad5
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 <linux/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,
101			   struct comedi_devconfig *it);
102static int c6xdigio_detach(struct comedi_device *dev);
103struct comedi_driver driver_c6xdigio = {
104	.driver_name = "c6xdigio",
105	.module = THIS_MODULE,
106	.attach = c6xdigio_attach,
107	.detach = c6xdigio_detach,
108};
109
110static void C6X_pwmInit(unsigned long baseAddr)
111{
112	int timeout = 0;
113
114/* printk("Inside C6X_pwmInit\n"); */
115
116	WriteByteToHwPort(baseAddr, 0x70);
117	while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
118	       && (timeout < C6XDIGIO_TIME_OUT)) {
119		timeout++;
120	}
121
122	WriteByteToHwPort(baseAddr, 0x74);
123	timeout = 0;
124	while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
125	       && (timeout < C6XDIGIO_TIME_OUT)) {
126		timeout++;
127	}
128
129	WriteByteToHwPort(baseAddr, 0x70);
130	timeout = 0;
131	while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
132	       && (timeout < C6XDIGIO_TIME_OUT)) {
133		timeout++;
134	}
135
136	WriteByteToHwPort(baseAddr, 0x0);
137	timeout = 0;
138	while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
139	       && (timeout < C6XDIGIO_TIME_OUT)) {
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
152	/* printk("Inside C6X_pwmOutput\n"); */
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;
162	} else {		/*  if channel == 1 */
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
220	/* printk("Inside C6X_encInput\n"); */
221
222	enc.value = 0;
223	if (channel == 0)
224		ppcmd = 0x48;
225	else
226		ppcmd = 0x50;
227
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
308	return enc.value ^ 0x800000;
309}
310
311static void C6X_encResetAll(unsigned long baseAddr)
312{
313	unsigned timeout = 0;
314
315/* printk("Inside C6X_encResetAll\n"); */
316
317	WriteByteToHwPort(baseAddr, 0x68);
318	while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
319	       && (timeout < C6XDIGIO_TIME_OUT)) {
320		timeout++;
321	}
322	WriteByteToHwPort(baseAddr, 0x6C);
323	timeout = 0;
324	while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
325	       && (timeout < C6XDIGIO_TIME_OUT)) {
326		timeout++;
327	}
328	WriteByteToHwPort(baseAddr, 0x68);
329	timeout = 0;
330	while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
331	       && (timeout < C6XDIGIO_TIME_OUT)) {
332		timeout++;
333	}
334	WriteByteToHwPort(baseAddr, 0x0);
335	timeout = 0;
336	while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
337	       && (timeout < C6XDIGIO_TIME_OUT)) {
338		timeout++;
339	}
340}
341
342static int c6xdigio_pwmo_insn_read(struct comedi_device *dev,
343				   struct comedi_subdevice *s,
344				   struct comedi_insn *insn, unsigned int *data)
345{
346	printk("c6xdigio_pwmo_insn_read %x\n", insn->n);
347	return insn->n;
348}
349
350static int c6xdigio_pwmo_insn_write(struct comedi_device *dev,
351				    struct comedi_subdevice *s,
352				    struct comedi_insn *insn,
353				    unsigned int *data)
354{
355	int i;
356	int chan = CR_CHAN(insn->chanspec);
357
358	/*   printk("c6xdigio_pwmo_insn_write %x\n", insn->n); */
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
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); */
382      /*  *//* C6X_encResetAll( dev->iobase ); */
383      /*  *//* return insn->n; */
384/* } */
385
386static int c6xdigio_ei_insn_read(struct comedi_device *dev,
387				 struct comedi_subdevice *s,
388				 struct comedi_insn *insn, unsigned int *data)
389{
390	/*   printk("c6xdigio_ei__insn_read %x\n", insn->n); */
391	int n;
392	int chan = CR_CHAN(insn->chanspec);
393
394	for (n = 0; n < insn->n; n++)
395		data[n] = (C6X_encInput(dev->iobase, chan) & 0xffffff);
396
397	return n;
398}
399
400static void board_init(struct comedi_device *dev)
401{
402
403	/* printk("Inside board_init\n"); */
404
405	C6X_pwmInit(dev->iobase);
406	C6X_encResetAll(dev->iobase);
407
408}
409
410/* static void board_halt(struct comedi_device *dev) { */
411/* C6X_pwmInit(dev->iobase); */
412/* } */
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 */
422	{.id = "PNP0400", .driver_data = 0},
423	/* ECP Printer Port */
424	{.id = "PNP0401", .driver_data = 0},
425	{}
426};
427
428static struct pnp_driver c6xdigio_pnp_driver = {
429	.name = "c6xdigio",
430	.id_table = c6xdigio_pnp_tbl,
431};
432
433static int c6xdigio_attach(struct comedi_device *dev,
434			   struct comedi_devconfig *it)
435{
436	int result = 0;
437	unsigned long iobase;
438	unsigned int irq;
439	struct comedi_subdevice *s;
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
450	result = alloc_subdevices(dev, 2);	/*  3 with encoder_init write */
451	if (result < 0)
452		return result;
453
454	/*  Make sure that PnP ports get activated */
455	pnp_register_driver(&c6xdigio_pnp_driver);
456
457	irq = it->options[1];
458	if (irq > 0)
459		printk("comedi%d: irq = %u ignored\n", dev->minor, irq);
460	else if (irq == 0)
461		printk("comedi%d: no irq\n", dev->minor);
462
463	s = dev->subdevices + 0;
464	/* pwm output subdevice */
465	s->type = COMEDI_SUBD_AO;	/*  Not sure what to put here */
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;
472	s->range_table = &range_bipolar10;	/*  A suitable lie */
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
484	/*	s = dev->subdevices + 2; */
485	/* pwm output subdevice */
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. */
497	board_init(dev);
498
499	return 0;
500}
501
502static int c6xdigio_detach(struct comedi_device *dev)
503{
504	/* board_halt(dev);  may not need this */
505
506	printk("comedi%d: c6xdigio: remove\n", dev->minor);
507
508	if (dev->iobase)
509		release_region(dev->iobase, C6XDIGIO_SIZE);
510
511	/*  Not using IRQ so I am not sure if I need this */
512	if (dev->irq)
513		free_irq(dev->irq, dev);
514
515	pnp_unregister_driver(&c6xdigio_pnp_driver);
516
517	return 0;
518}
519
520COMEDI_INITCLEANUP(driver_c6xdigio);
521
522MODULE_AUTHOR("Comedi http://www.comedi.org");
523MODULE_DESCRIPTION("Comedi low-level driver");
524MODULE_LICENSE("GPL");
525