Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
s526.c
Go to the documentation of this file.
1 /*
2  comedi/drivers/s526.c
3  Sensoray s526 Comedi driver
4 
5  COMEDI - Linux Control and Measurement Device Interface
6  Copyright (C) 2000 David A. Schleef <[email protected]>
7 
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 
22 */
23 /*
24 Driver: s526
25 Description: Sensoray 526 driver
26 Devices: [Sensoray] 526 (s526)
27 Author: Richie
28  Everett Wang <[email protected]>
29 Updated: Thu, 14 Sep. 2006
30 Status: experimental
31 
32 Encoder works
33 Analog input works
34 Analog output works
35 PWM output works
36 Commands are not supported yet.
37 
38 Configuration Options:
39 
40 comedi_config /dev/comedi0 s526 0x2C0,0x3
41 
42 */
43 
44 #include "../comedidev.h"
45 #include <linux/ioport.h>
46 #include <asm/byteorder.h>
47 
48 #define S526_SIZE 64
49 
50 #define S526_START_AI_CONV 0
51 #define S526_AI_READ 0
52 
53 /* Ports */
54 #define S526_IOSIZE 0x40
55 #define S526_NUM_PORTS 27
56 
57 /* registers */
58 #define REG_TCR 0x00
59 #define REG_WDC 0x02
60 #define REG_DAC 0x04
61 #define REG_ADC 0x06
62 #define REG_ADD 0x08
63 #define REG_DIO 0x0A
64 #define REG_IER 0x0C
65 #define REG_ISR 0x0E
66 #define REG_MSC 0x10
67 #define REG_C0L 0x12
68 #define REG_C0H 0x14
69 #define REG_C0M 0x16
70 #define REG_C0C 0x18
71 #define REG_C1L 0x1A
72 #define REG_C1H 0x1C
73 #define REG_C1M 0x1E
74 #define REG_C1C 0x20
75 #define REG_C2L 0x22
76 #define REG_C2H 0x24
77 #define REG_C2M 0x26
78 #define REG_C2C 0x28
79 #define REG_C3L 0x2A
80 #define REG_C3H 0x2C
81 #define REG_C3M 0x2E
82 #define REG_C3C 0x30
83 #define REG_EED 0x32
84 #define REG_EEC 0x34
85 
87 #if defined(__LITTLE_ENDIAN_BITFIELD)
88  unsigned short coutSource:1;
89  unsigned short coutPolarity:1;
90  unsigned short autoLoadResetRcap:3;
91  unsigned short hwCtEnableSource:2;
92  unsigned short ctEnableCtrl:2;
93  unsigned short clockSource:2;
94  unsigned short countDir:1;
95  unsigned short countDirCtrl:1;
96  unsigned short outputRegLatchCtrl:1;
97  unsigned short preloadRegSel:1;
98  unsigned short reserved:1;
99  #elif defined(__BIG_ENDIAN_BITFIELD)
100  unsigned short reserved:1;
101  unsigned short preloadRegSel:1;
102  unsigned short outputRegLatchCtrl:1;
103  unsigned short countDirCtrl:1;
104  unsigned short countDir:1;
105  unsigned short clockSource:2;
106  unsigned short ctEnableCtrl:2;
107  unsigned short hwCtEnableSource:2;
108  unsigned short autoLoadResetRcap:3;
109  unsigned short coutPolarity:1;
110  unsigned short coutSource:1;
111 #else
112 #error Unknown bit field order
113 #endif
114 };
115 
116 union cmReg {
118  unsigned short value;
119 };
120 
121 struct s526_private {
122  unsigned int ao_readback[2];
123  unsigned int gpct_config[4];
124  unsigned short ai_config;
125 };
126 
127 static int s526_gpct_rinsn(struct comedi_device *dev,
128  struct comedi_subdevice *s,
129  struct comedi_insn *insn,
130  unsigned int *data)
131 {
132  unsigned int chan = CR_CHAN(insn->chanspec);
133  unsigned long chan_iobase = dev->iobase + chan * 8;
134  unsigned int lo;
135  unsigned int hi;
136  int i;
137 
138  for (i = 0; i < insn->n; i++) {
139  /* Read the low word first */
140  lo = inw(chan_iobase + REG_C0L) & 0xffff;
141  hi = inw(chan_iobase + REG_C0H) & 0xff;
142 
143  data[i] = (hi << 16) | lo;
144  }
145 
146  return insn->n;
147 }
148 
149 static int s526_gpct_insn_config(struct comedi_device *dev,
150  struct comedi_subdevice *s,
151  struct comedi_insn *insn,
152  unsigned int *data)
153 {
154  struct s526_private *devpriv = dev->private;
155  unsigned int chan = CR_CHAN(insn->chanspec);
156  unsigned long chan_iobase = dev->iobase + chan * 8;
157  unsigned int val;
158  union cmReg cmReg;
159 
160  /* Check what type of Counter the user requested, data[0] contains */
161  /* the Application type */
162  switch (data[0]) {
164  /*
165  data[0]: Application Type
166  data[1]: Counter Mode Register Value
167  data[2]: Pre-load Register Value
168  data[3]: Conter Control Register
169  */
170  devpriv->gpct_config[chan] = data[0];
171 
172 #if 0
173  /* Example of Counter Application */
174  /* One-shot (software trigger) */
175  cmReg.reg.coutSource = 0; /* out RCAP */
176  cmReg.reg.coutPolarity = 1; /* Polarity inverted */
177  cmReg.reg.autoLoadResetRcap = 0;/* Auto load disabled */
178  cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
179  cmReg.reg.ctEnableCtrl = 2; /* Hardware */
180  cmReg.reg.clockSource = 2; /* Internal */
181  cmReg.reg.countDir = 1; /* Down */
182  cmReg.reg.countDirCtrl = 1; /* Software */
183  cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
184  cmReg.reg.preloadRegSel = 0; /* PR0 */
185  cmReg.reg.reserved = 0;
186 
187  outw(cmReg.value, chan_iobase + REG_C0M);
188 
189  outw(0x0001, chan_iobase + REG_C0H);
190  outw(0x3C68, chan_iobase + REG_C0L);
191 
192  /* Reset the counter */
193  outw(0x8000, chan_iobase + REG_C0C);
194  /* Load the counter from PR0 */
195  outw(0x4000, chan_iobase + REG_C0C);
196 
197  /* Reset RCAP (fires one-shot) */
198  outw(0x0008, chan_iobase + REG_C0C);
199 
200 #endif
201 
202 #if 1
203  /* Set Counter Mode Register */
204  cmReg.value = data[1] & 0xffff;
205  outw(cmReg.value, chan_iobase + REG_C0M);
206 
207  /* Reset the counter if it is software preload */
208  if (cmReg.reg.autoLoadResetRcap == 0) {
209  /* Reset the counter */
210  outw(0x8000, chan_iobase + REG_C0C);
211  /* Load the counter from PR0
212  * outw(0x4000, chan_iobase + REG_C0C);
213  */
214  }
215 #else
216  /* 0 quadrature, 1 software control */
217  cmReg.reg.countDirCtrl = 0;
218 
219  /* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
220  if (data[1] == GPCT_X2)
221  cmReg.reg.clockSource = 1;
222  else if (data[1] == GPCT_X4)
223  cmReg.reg.clockSource = 2;
224  else
225  cmReg.reg.clockSource = 0;
226 
227  /* When to take into account the indexpulse: */
228  /*if (data[2] == GPCT_IndexPhaseLowLow) {
229  } else if (data[2] == GPCT_IndexPhaseLowHigh) {
230  } else if (data[2] == GPCT_IndexPhaseHighLow) {
231  } else if (data[2] == GPCT_IndexPhaseHighHigh) {
232  }*/
233  /* Take into account the index pulse? */
234  if (data[3] == GPCT_RESET_COUNTER_ON_INDEX)
235  /* Auto load with INDEX^ */
236  cmReg.reg.autoLoadResetRcap = 4;
237 
238  /* Set Counter Mode Register */
239  cmReg.value = data[1] & 0xffff;
240  outw(cmReg.value, chan_iobase + REG_C0M);
241 
242  /* Load the pre-load register high word */
243  val = (data[2] >> 16) & 0xffff;
244  outw(val, chan_iobase + REG_C0H);
245 
246  /* Load the pre-load register low word */
247  val = data[2] & 0xffff;
248  outw(val, chan_iobase + REG_C0L);
249 
250  /* Write the Counter Control Register */
251  if (data[3]) {
252  val = data[3] & 0xffff;
253  outw(val, chan_iobase + REG_C0C);
254  }
255  /* Reset the counter if it is software preload */
256  if (cmReg.reg.autoLoadResetRcap == 0) {
257  /* Reset the counter */
258  outw(0x8000, chan_iobase + REG_C0C);
259  /* Load the counter from PR0 */
260  outw(0x4000, chan_iobase + REG_C0C);
261  }
262 #endif
263  break;
264 
266  /*
267  data[0]: Application Type
268  data[1]: Counter Mode Register Value
269  data[2]: Pre-load Register 0 Value
270  data[3]: Pre-load Register 1 Value
271  data[4]: Conter Control Register
272  */
273  devpriv->gpct_config[chan] = data[0];
274 
275  /* Set Counter Mode Register */
276  cmReg.value = data[1] & 0xffff;
277  cmReg.reg.preloadRegSel = 0; /* PR0 */
278  outw(cmReg.value, chan_iobase + REG_C0M);
279 
280  /* Load the pre-load register 0 high word */
281  val = (data[2] >> 16) & 0xffff;
282  outw(val, chan_iobase + REG_C0H);
283 
284  /* Load the pre-load register 0 low word */
285  val = data[2] & 0xffff;
286  outw(val, chan_iobase + REG_C0L);
287 
288  /* Set Counter Mode Register */
289  cmReg.value = data[1] & 0xffff;
290  cmReg.reg.preloadRegSel = 1; /* PR1 */
291  outw(cmReg.value, chan_iobase + REG_C0M);
292 
293  /* Load the pre-load register 1 high word */
294  val = (data[3] >> 16) & 0xffff;
295  outw(val, chan_iobase + REG_C0H);
296 
297  /* Load the pre-load register 1 low word */
298  val = data[3] & 0xffff;
299  outw(val, chan_iobase + REG_C0L);
300 
301  /* Write the Counter Control Register */
302  if (data[4]) {
303  val = data[4] & 0xffff;
304  outw(val, chan_iobase + REG_C0C);
305  }
306  break;
307 
309  /*
310  data[0]: Application Type
311  data[1]: Counter Mode Register Value
312  data[2]: Pre-load Register 0 Value
313  data[3]: Pre-load Register 1 Value
314  data[4]: Conter Control Register
315  */
316  devpriv->gpct_config[chan] = data[0];
317 
318  /* Set Counter Mode Register */
319  cmReg.value = data[1] & 0xffff;
320  cmReg.reg.preloadRegSel = 0; /* PR0 */
321  outw(cmReg.value, chan_iobase + REG_C0M);
322 
323  /* Load the pre-load register 0 high word */
324  val = (data[2] >> 16) & 0xffff;
325  outw(val, chan_iobase + REG_C0H);
326 
327  /* Load the pre-load register 0 low word */
328  val = data[2] & 0xffff;
329  outw(val, chan_iobase + REG_C0L);
330 
331  /* Set Counter Mode Register */
332  cmReg.value = data[1] & 0xffff;
333  cmReg.reg.preloadRegSel = 1; /* PR1 */
334  outw(cmReg.value, chan_iobase + REG_C0M);
335 
336  /* Load the pre-load register 1 high word */
337  val = (data[3] >> 16) & 0xffff;
338  outw(val, chan_iobase + REG_C0H);
339 
340  /* Load the pre-load register 1 low word */
341  val = data[3] & 0xffff;
342  outw(val, chan_iobase + REG_C0L);
343 
344  /* Write the Counter Control Register */
345  if (data[4]) {
346  val = data[4] & 0xffff;
347  outw(val, chan_iobase + REG_C0C);
348  }
349  break;
350 
351  default:
352  return -EINVAL;
353  break;
354  }
355 
356  return insn->n;
357 }
358 
359 static int s526_gpct_winsn(struct comedi_device *dev,
360  struct comedi_subdevice *s,
361  struct comedi_insn *insn,
362  unsigned int *data)
363 {
364  struct s526_private *devpriv = dev->private;
365  unsigned int chan = CR_CHAN(insn->chanspec);
366  unsigned long chan_iobase = dev->iobase + chan * 8;
367 
368  inw(chan_iobase + REG_C0M); /* Is this read required? */
369 
370  /* Check what Application of Counter this channel is configured for */
371  switch (devpriv->gpct_config[chan]) {
373  /* data[0] contains the PULSE_WIDTH
374  data[1] contains the PULSE_PERIOD
375  @pre PULSE_PERIOD > PULSE_WIDTH > 0
376  The above periods must be expressed as a multiple of the
377  pulse frequency on the selected source
378  */
379  if ((data[1] <= data[0]) || !data[0])
380  return -EINVAL;
381 
382  /* Fall thru to write the PULSE_WIDTH */
383 
386  outw((data[0] >> 16) & 0xffff, chan_iobase + REG_C0H);
387  outw(data[0] & 0xffff, chan_iobase + REG_C0L);
388  break;
389 
390  default:
391  return -EINVAL;
392  }
393 
394  return insn->n;
395 }
396 
397 #define ISR_ADC_DONE 0x4
398 static int s526_ai_insn_config(struct comedi_device *dev,
399  struct comedi_subdevice *s,
400  struct comedi_insn *insn, unsigned int *data)
401 {
402  struct s526_private *devpriv = dev->private;
403  int result = -EINVAL;
404 
405  if (insn->n < 1)
406  return result;
407 
408  result = insn->n;
409 
410  /* data[0] : channels was set in relevant bits.
411  data[1] : delay
412  */
413  /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
414  * enable channels here. The channel should be enabled in the
415  * INSN_READ handler. */
416 
417  /* Enable ADC interrupt */
418  outw(ISR_ADC_DONE, dev->iobase + REG_IER);
419  devpriv->ai_config = (data[0] & 0x3ff) << 5;
420  if (data[1] > 0)
421  devpriv->ai_config |= 0x8000; /* set the delay */
422 
423  devpriv->ai_config |= 0x0001; /* ADC start bit */
424 
425  return result;
426 }
427 
428 static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
429  struct comedi_insn *insn, unsigned int *data)
430 {
431  struct s526_private *devpriv = dev->private;
432  unsigned int chan = CR_CHAN(insn->chanspec);
433  int n, i;
434  unsigned short value;
435  unsigned int d;
436  unsigned int status;
437 
438  /* Set configured delay, enable channel for this channel only,
439  * select "ADC read" channel, set "ADC start" bit. */
440  value = (devpriv->ai_config & 0x8000) |
441  ((1 << 5) << chan) | (chan << 1) | 0x0001;
442 
443  /* convert n samples */
444  for (n = 0; n < insn->n; n++) {
445  /* trigger conversion */
446  outw(value, dev->iobase + REG_ADC);
447 
448 #define TIMEOUT 100
449  /* wait for conversion to end */
450  for (i = 0; i < TIMEOUT; i++) {
451  status = inw(dev->iobase + REG_ISR);
452  if (status & ISR_ADC_DONE) {
453  outw(ISR_ADC_DONE, dev->iobase + REG_ISR);
454  break;
455  }
456  }
457  if (i == TIMEOUT)
458  return -ETIMEDOUT;
459 
460  /* read data */
461  d = inw(dev->iobase + REG_ADD);
462 
463  /* munge data */
464  data[n] = d ^ 0x8000;
465  }
466 
467  /* return the number of samples read/written */
468  return n;
469 }
470 
471 static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
472  struct comedi_insn *insn, unsigned int *data)
473 {
474  struct s526_private *devpriv = dev->private;
475  unsigned int chan = CR_CHAN(insn->chanspec);
476  unsigned short val;
477  int i;
478 
479  val = chan << 1;
480  outw(val, dev->iobase + REG_DAC);
481 
482  for (i = 0; i < insn->n; i++) {
483  outw(data[i], dev->iobase + REG_ADD);
484  devpriv->ao_readback[chan] = data[i];
485  /* starts the D/A conversion */
486  outw(val + 1, dev->iobase + REG_DAC);
487  }
488 
489  return i;
490 }
491 
492 static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
493  struct comedi_insn *insn, unsigned int *data)
494 {
495  struct s526_private *devpriv = dev->private;
496  unsigned int chan = CR_CHAN(insn->chanspec);
497  int i;
498 
499  for (i = 0; i < insn->n; i++)
500  data[i] = devpriv->ao_readback[chan];
501 
502  return i;
503 }
504 
505 static int s526_dio_insn_bits(struct comedi_device *dev,
506  struct comedi_subdevice *s,
507  struct comedi_insn *insn, unsigned int *data)
508 {
509  if (data[0]) {
510  s->state &= ~data[0];
511  s->state |= data[0] & data[1];
512 
513  outw(s->state, dev->iobase + REG_DIO);
514  }
515 
516  data[1] = inw(dev->iobase + REG_DIO) & 0xff;
517 
518  return insn->n;
519 }
520 
521 static int s526_dio_insn_config(struct comedi_device *dev,
522  struct comedi_subdevice *s,
523  struct comedi_insn *insn, unsigned int *data)
524 {
525  unsigned int chan = CR_CHAN(insn->chanspec);
526  int group, mask;
527 
528  group = chan >> 2;
529  mask = 0xF << (group << 2);
530  switch (data[0]) {
532  /* bit 10/11 set the group 1/2's mode */
533  s->state |= 1 << (group + 10);
534  s->io_bits |= mask;
535  break;
537  s->state &= ~(1 << (group + 10)); /* 1 is output, 0 is input. */
538  s->io_bits &= ~mask;
539  break;
541  data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
542  return insn->n;
543  default:
544  return -EINVAL;
545  }
546  outw(s->state, dev->iobase + REG_DIO);
547 
548  return 1;
549 }
550 
551 static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
552 {
553  struct s526_private *devpriv;
554  struct comedi_subdevice *s;
555  int iobase;
556  int ret;
557 
558  dev->board_name = dev->driver->driver_name;
559 
560  iobase = it->options[0];
561  if (!iobase || !request_region(iobase, S526_IOSIZE, dev->board_name)) {
562  comedi_error(dev, "I/O port conflict");
563  return -EIO;
564  }
565  dev->iobase = iobase;
566 
567  ret = alloc_private(dev, sizeof(*devpriv));
568  if (ret)
569  return ret;
570  devpriv = dev->private;
571 
572  ret = comedi_alloc_subdevices(dev, 4);
573  if (ret)
574  return ret;
575 
576  s = &dev->subdevices[0];
577  /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
580  s->n_chan = 4;
581  s->maxdata = 0x00ffffff; /* 24 bit counter */
582  s->insn_read = s526_gpct_rinsn;
583  s->insn_config = s526_gpct_insn_config;
584  s->insn_write = s526_gpct_winsn;
585 
586  s = &dev->subdevices[1];
587  /* analog input subdevice */
588  s->type = COMEDI_SUBD_AI;
590  /* channels 0 to 7 are the regular differential inputs */
591  /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
592  s->n_chan = 10;
593  s->maxdata = 0xffff;
595  s->len_chanlist = 16;
596  s->insn_read = s526_ai_rinsn;
597  s->insn_config = s526_ai_insn_config;
598 
599  s = &dev->subdevices[2];
600  /* analog output subdevice */
601  s->type = COMEDI_SUBD_AO;
603  s->n_chan = 4;
604  s->maxdata = 0xffff;
606  s->insn_write = s526_ao_winsn;
607  s->insn_read = s526_ao_rinsn;
608 
609  s = &dev->subdevices[3];
610  /* digital i/o subdevice */
611  s->type = COMEDI_SUBD_DIO;
613  s->n_chan = 8;
614  s->maxdata = 1;
616  s->insn_bits = s526_dio_insn_bits;
617  s->insn_config = s526_dio_insn_config;
618 
619  dev_info(dev->class_dev, "%s attached\n", dev->board_name);
620 
621  return 1;
622 }
623 
624 static void s526_detach(struct comedi_device *dev)
625 {
626  if (dev->iobase > 0)
628 }
629 
630 static struct comedi_driver s526_driver = {
631  .driver_name = "s526",
632  .module = THIS_MODULE,
633  .attach = s526_attach,
634  .detach = s526_detach,
635 };
636 module_comedi_driver(s526_driver);
637 
638 MODULE_AUTHOR("Comedi http://www.comedi.org");
639 MODULE_DESCRIPTION("Comedi low-level driver");
640 MODULE_LICENSE("GPL");