Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
c6xdigio.c
Go to the documentation of this file.
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 <linux/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 void C6X_pwmInit(unsigned long baseAddr)
101 {
102  int timeout = 0;
103 
104 /* printk("Inside C6X_pwmInit\n"); */
105 
106  WriteByteToHwPort(baseAddr, 0x70);
107  while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
108  && (timeout < C6XDIGIO_TIME_OUT)) {
109  timeout++;
110  }
111 
112  WriteByteToHwPort(baseAddr, 0x74);
113  timeout = 0;
114  while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
115  && (timeout < C6XDIGIO_TIME_OUT)) {
116  timeout++;
117  }
118 
119  WriteByteToHwPort(baseAddr, 0x70);
120  timeout = 0;
121  while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
122  && (timeout < C6XDIGIO_TIME_OUT)) {
123  timeout++;
124  }
125 
126  WriteByteToHwPort(baseAddr, 0x0);
127  timeout = 0;
128  while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
129  && (timeout < C6XDIGIO_TIME_OUT)) {
130  timeout++;
131  }
132 
133 }
134 
135 static void C6X_pwmOutput(unsigned long baseAddr, unsigned channel, int value)
136 {
137  unsigned ppcmd;
138  union pwmcmdtype pwm;
139  int timeout = 0;
140  unsigned tmp;
141 
142  /* printk("Inside C6X_pwmOutput\n"); */
143 
144  pwm.cmd = value;
145  if (pwm.cmd > 498)
146  pwm.cmd = 498;
147  if (pwm.cmd < 2)
148  pwm.cmd = 2;
149 
150  if (channel == 0) {
151  ppcmd = 0x28;
152  } else { /* if channel == 1 */
153  ppcmd = 0x30;
154  } /* endif */
155 
156  WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb0);
157  tmp = ReadByteFromHwPort(baseAddr + 1);
158  while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
159  tmp = ReadByteFromHwPort(baseAddr + 1);
160  timeout++;
161  }
162 
163  WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb1 + 0x4);
164  timeout = 0;
165  tmp = ReadByteFromHwPort(baseAddr + 1);
166  while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
167  tmp = ReadByteFromHwPort(baseAddr + 1);
168  timeout++;
169  }
170 
171  WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb2);
172  tmp = ReadByteFromHwPort(baseAddr + 1);
173  while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
174  tmp = ReadByteFromHwPort(baseAddr + 1);
175  timeout++;
176  }
177 
178  WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb3 + 0x4);
179  timeout = 0;
180  tmp = ReadByteFromHwPort(baseAddr + 1);
181  while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
182  tmp = ReadByteFromHwPort(baseAddr + 1);
183  timeout++;
184  }
185 
186  WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb4);
187  tmp = ReadByteFromHwPort(baseAddr + 1);
188  while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
189  tmp = ReadByteFromHwPort(baseAddr + 1);
190  timeout++;
191  }
192 
193  WriteByteToHwPort(baseAddr, 0x0);
194  timeout = 0;
195  tmp = ReadByteFromHwPort(baseAddr + 1);
196  while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
197  tmp = ReadByteFromHwPort(baseAddr + 1);
198  timeout++;
199  }
200 
201 }
202 
203 static int C6X_encInput(unsigned long baseAddr, unsigned channel)
204 {
205  unsigned ppcmd;
206  union encvaluetype enc;
207  int timeout = 0;
208  int tmp;
209 
210  /* printk("Inside C6X_encInput\n"); */
211 
212  enc.value = 0;
213  if (channel == 0)
214  ppcmd = 0x48;
215  else
216  ppcmd = 0x50;
217 
218  WriteByteToHwPort(baseAddr, ppcmd);
219  tmp = ReadByteFromHwPort(baseAddr + 1);
220  while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
221  tmp = ReadByteFromHwPort(baseAddr + 1);
222  timeout++;
223  }
224 
225  enc.bits.sb0 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
226  WriteByteToHwPort(baseAddr, ppcmd + 0x4);
227  timeout = 0;
228  tmp = ReadByteFromHwPort(baseAddr + 1);
229  while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
230  tmp = ReadByteFromHwPort(baseAddr + 1);
231  timeout++;
232  }
233  enc.bits.sb1 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
234  WriteByteToHwPort(baseAddr, ppcmd);
235  timeout = 0;
236  tmp = ReadByteFromHwPort(baseAddr + 1);
237  while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
238  tmp = ReadByteFromHwPort(baseAddr + 1);
239  timeout++;
240  }
241  enc.bits.sb2 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
242  WriteByteToHwPort(baseAddr, ppcmd + 0x4);
243  timeout = 0;
244  tmp = ReadByteFromHwPort(baseAddr + 1);
245  while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
246  tmp = ReadByteFromHwPort(baseAddr + 1);
247  timeout++;
248  }
249  enc.bits.sb3 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
250  WriteByteToHwPort(baseAddr, ppcmd);
251  timeout = 0;
252  tmp = ReadByteFromHwPort(baseAddr + 1);
253  while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
254  tmp = ReadByteFromHwPort(baseAddr + 1);
255  timeout++;
256  }
257  enc.bits.sb4 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
258  WriteByteToHwPort(baseAddr, ppcmd + 0x4);
259  timeout = 0;
260  tmp = ReadByteFromHwPort(baseAddr + 1);
261  while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
262  tmp = ReadByteFromHwPort(baseAddr + 1);
263  timeout++;
264  }
265  enc.bits.sb5 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
266  WriteByteToHwPort(baseAddr, ppcmd);
267  timeout = 0;
268  tmp = ReadByteFromHwPort(baseAddr + 1);
269  while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
270  tmp = ReadByteFromHwPort(baseAddr + 1);
271  timeout++;
272  }
273  enc.bits.sb6 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
274  WriteByteToHwPort(baseAddr, ppcmd + 0x4);
275  timeout = 0;
276  tmp = ReadByteFromHwPort(baseAddr + 1);
277  while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
278  tmp = ReadByteFromHwPort(baseAddr + 1);
279  timeout++;
280  }
281  enc.bits.sb7 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
282  WriteByteToHwPort(baseAddr, ppcmd);
283  timeout = 0;
284  tmp = ReadByteFromHwPort(baseAddr + 1);
285  while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
286  tmp = ReadByteFromHwPort(baseAddr + 1);
287  timeout++;
288  }
289 
290  WriteByteToHwPort(baseAddr, 0x0);
291  timeout = 0;
292  tmp = ReadByteFromHwPort(baseAddr + 1);
293  while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
294  tmp = ReadByteFromHwPort(baseAddr + 1);
295  timeout++;
296  }
297 
298  return enc.value ^ 0x800000;
299 }
300 
301 static void C6X_encResetAll(unsigned long baseAddr)
302 {
303  unsigned timeout = 0;
304 
305 /* printk("Inside C6X_encResetAll\n"); */
306 
307  WriteByteToHwPort(baseAddr, 0x68);
308  while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
309  && (timeout < C6XDIGIO_TIME_OUT)) {
310  timeout++;
311  }
312  WriteByteToHwPort(baseAddr, 0x6C);
313  timeout = 0;
314  while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
315  && (timeout < C6XDIGIO_TIME_OUT)) {
316  timeout++;
317  }
318  WriteByteToHwPort(baseAddr, 0x68);
319  timeout = 0;
320  while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
321  && (timeout < C6XDIGIO_TIME_OUT)) {
322  timeout++;
323  }
324  WriteByteToHwPort(baseAddr, 0x0);
325  timeout = 0;
326  while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
327  && (timeout < C6XDIGIO_TIME_OUT)) {
328  timeout++;
329  }
330 }
331 
332 static int c6xdigio_pwmo_insn_read(struct comedi_device *dev,
333  struct comedi_subdevice *s,
334  struct comedi_insn *insn, unsigned int *data)
335 {
336  printk(KERN_DEBUG "c6xdigio_pwmo_insn_read %x\n", insn->n);
337  return insn->n;
338 }
339 
340 static int c6xdigio_pwmo_insn_write(struct comedi_device *dev,
341  struct comedi_subdevice *s,
342  struct comedi_insn *insn,
343  unsigned int *data)
344 {
345  int i;
346  int chan = CR_CHAN(insn->chanspec);
347 
348  /* printk("c6xdigio_pwmo_insn_write %x\n", insn->n); */
349  for (i = 0; i < insn->n; i++) {
350  C6X_pwmOutput(dev->iobase, chan, data[i]);
351  /* devpriv->ao_readback[chan] = data[i]; */
352  }
353  return i;
354 }
355 
356 /* static int c6xdigio_ei_init_insn_read(struct comedi_device *dev, */
357 /* struct comedi_subdevice *s, */
358 /* struct comedi_insn *insn, */
359 /* unsigned int *data) */
360 /* { */
361 /* printk("c6xdigio_ei_init_insn_read %x\n", insn->n); */
362 /* return insn->n; */
363 /* } */
364 
365 /* static int c6xdigio_ei_init_insn_write(struct comedi_device *dev, */
366 /* struct comedi_subdevice *s, */
367 /* struct comedi_insn *insn, */
368 /* unsigned int *data) */
369 /* { */
370 /* int i; */
371 /* int chan = CR_CHAN(insn->chanspec); */
372  /* *//* C6X_encResetAll( dev->iobase ); */
373  /* *//* return insn->n; */
374 /* } */
375 
376 static int c6xdigio_ei_insn_read(struct comedi_device *dev,
377  struct comedi_subdevice *s,
378  struct comedi_insn *insn, unsigned int *data)
379 {
380  /* printk("c6xdigio_ei__insn_read %x\n", insn->n); */
381  int n;
382  int chan = CR_CHAN(insn->chanspec);
383 
384  for (n = 0; n < insn->n; n++)
385  data[n] = (C6X_encInput(dev->iobase, chan) & 0xffffff);
386 
387  return n;
388 }
389 
390 static void board_init(struct comedi_device *dev)
391 {
392 
393  /* printk("Inside board_init\n"); */
394 
395  C6X_pwmInit(dev->iobase);
396  C6X_encResetAll(dev->iobase);
397 
398 }
399 
400 /*
401  options[0] - I/O port
402  options[1] - irq
403  options[2] - number of encoder chips installed
404  */
405 
406 static const struct pnp_device_id c6xdigio_pnp_tbl[] = {
407  /* Standard LPT Printer Port */
408  {.id = "PNP0400", .driver_data = 0},
409  /* ECP Printer Port */
410  {.id = "PNP0401", .driver_data = 0},
411  {}
412 };
413 
414 static struct pnp_driver c6xdigio_pnp_driver = {
415  .name = "c6xdigio",
416  .id_table = c6xdigio_pnp_tbl,
417 };
418 
419 static int c6xdigio_attach(struct comedi_device *dev,
420  struct comedi_devconfig *it)
421 {
422  int result = 0;
423  unsigned long iobase;
424  unsigned int irq;
425  struct comedi_subdevice *s;
426 
427  iobase = it->options[0];
428  printk(KERN_DEBUG "comedi%d: c6xdigio: 0x%04lx\n", dev->minor, iobase);
429  if (!request_region(iobase, C6XDIGIO_SIZE, "c6xdigio")) {
430  printk(KERN_ERR "comedi%d: I/O port conflict\n", dev->minor);
431  return -EIO;
432  }
433  dev->iobase = iobase;
434  dev->board_name = "c6xdigio";
435 
436  result = comedi_alloc_subdevices(dev, 2);
437  if (result)
438  return result;
439 
440  /* Make sure that PnP ports get activated */
441  pnp_register_driver(&c6xdigio_pnp_driver);
442 
443  irq = it->options[1];
444  if (irq > 0)
445  printk(KERN_DEBUG "comedi%d: irq = %u ignored\n",
446  dev->minor, irq);
447  else if (irq == 0)
448  printk(KERN_DEBUG "comedi%d: no irq\n", dev->minor);
449 
450  s = &dev->subdevices[0];
451  /* pwm output subdevice */
452  s->type = COMEDI_SUBD_AO; /* Not sure what to put here */
454  s->n_chan = 2;
455  /* s->trig[0] = c6xdigio_pwmo; */
456  s->insn_read = c6xdigio_pwmo_insn_read;
457  s->insn_write = c6xdigio_pwmo_insn_write;
458  s->maxdata = 500;
459  s->range_table = &range_bipolar10; /* A suitable lie */
460 
461  s = &dev->subdevices[1];
462  /* encoder (counter) subdevice */
465  s->n_chan = 2;
466  /* s->trig[0] = c6xdigio_ei; */
467  s->insn_read = c6xdigio_ei_insn_read;
468  s->maxdata = 0xffffff;
470 
471  /* s = &dev->subdevices[2]; */
472  /* pwm output subdevice */
473  /* s->type = COMEDI_SUBD_COUNTER; // Not sure what to put here */
474  /* s->subdev_flags = SDF_WRITEABLE; */
475  /* s->n_chan = 1; */
476  /* s->trig[0] = c6xdigio_ei_init; */
477  /* s->insn_read = c6xdigio_ei_init_insn_read; */
478  /* s->insn_write = c6xdigio_ei_init_insn_write; */
479  /* s->maxdata = 0xFFFF; // Really just a don't care */
480  /* s->range_table = &range_unknown; // Not sure what to put here */
481 
482  /* I will call this init anyway but more than likely the DSP board */
483  /* will not be connected when device driver is loaded. */
484  board_init(dev);
485 
486  return 0;
487 }
488 
489 static void c6xdigio_detach(struct comedi_device *dev)
490 {
491  if (dev->iobase)
493  if (dev->irq)
494  free_irq(dev->irq, dev);
495  pnp_unregister_driver(&c6xdigio_pnp_driver);
496 }
497 
498 static struct comedi_driver c6xdigio_driver = {
499  .driver_name = "c6xdigio",
500  .module = THIS_MODULE,
501  .attach = c6xdigio_attach,
502  .detach = c6xdigio_detach,
503 };
504 module_comedi_driver(c6xdigio_driver);
505 
506 MODULE_AUTHOR("Comedi http://www.comedi.org");
507 MODULE_DESCRIPTION("Comedi low-level driver");
508 MODULE_LICENSE("GPL");