3 Sensoray s526 Comedi driver
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
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.
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.
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.
25 Description: Sensoray 526 driver
26 Devices: [Sensoray] 526 (s526)
28 Everett Wang <everett.wang@everteq.com>
29 Updated: Thu, 14 Sep. 2006
36 Commands are not supported yet.
38 Configuration Options:
40 comedi_config /dev/comedi0 s526 0x2C0,0x3
44 #include "../comedidev.h"
45 #include <linux/ioport.h>
46 #include <asm/byteorder.h>
50 #define S526_START_AI_CONV 0
51 #define S526_AI_READ 0
54 #define S526_IOSIZE 0x40
55 #define S526_NUM_PORTS 27
86 static const int s526_ports[] = {
116 struct counter_mode_register_t {
117 #if defined(__LITTLE_ENDIAN_BITFIELD)
118 unsigned short coutSource:1;
119 unsigned short coutPolarity:1;
120 unsigned short autoLoadResetRcap:3;
121 unsigned short hwCtEnableSource:2;
122 unsigned short ctEnableCtrl:2;
123 unsigned short clockSource:2;
124 unsigned short countDir:1;
125 unsigned short countDirCtrl:1;
126 unsigned short outputRegLatchCtrl:1;
127 unsigned short preloadRegSel:1;
128 unsigned short reserved:1;
129 #elif defined(__BIG_ENDIAN_BITFIELD)
130 unsigned short reserved:1;
131 unsigned short preloadRegSel:1;
132 unsigned short outputRegLatchCtrl:1;
133 unsigned short countDirCtrl:1;
134 unsigned short countDir:1;
135 unsigned short clockSource:2;
136 unsigned short ctEnableCtrl:2;
137 unsigned short hwCtEnableSource:2;
138 unsigned short autoLoadResetRcap:3;
139 unsigned short coutPolarity:1;
140 unsigned short coutSource:1;
142 #error Unknown bit field order
147 struct counter_mode_register_t reg;
148 unsigned short value;
151 #define MAX_GPCT_CONFIG_DATA 6
153 /* Different Application Classes for GPCT Subdevices */
154 /* The list is not exhaustive and needs discussion! */
155 enum S526_GPCT_APP_CLASS {
156 CountingAndTimeMeasurement,
157 SinglePulseGeneration,
158 PulseTrainGeneration,
163 /* Config struct for different GPCT subdevice Application Classes and
166 struct s526GPCTConfig {
167 enum S526_GPCT_APP_CLASS app;
168 int data[MAX_GPCT_CONFIG_DATA];
172 * Board descriptions for two imaginary boards. Describing the
173 * boards in this way is optional, and completely driver-dependent.
174 * Some drivers use arrays such as this, other do not.
187 static const struct s526_board s526_boards[] = {
200 #define ADDR_REG(reg) (dev->iobase + (reg))
201 #define ADDR_CHAN_REG(reg, chan) (dev->iobase + (reg) + (chan) * 8)
203 /* this structure is for data unique to this hardware driver. If
204 several hardware drivers keep similar information in this structure,
205 feel free to suggest moving the variable to the struct comedi_device
208 struct s526_private {
209 unsigned int ao_readback[2];
210 struct s526GPCTConfig s526_gpct_config[4];
211 unsigned short s526_ai_config;
215 * most drivers define the following macro to make it easy to
216 * access the private structure.
218 #define devpriv ((struct s526_private *)dev->private)
220 static int s526_gpct_rinsn(struct comedi_device *dev,
221 struct comedi_subdevice *s, struct comedi_insn *insn,
224 int i; /* counts the Data */
225 int counter_channel = CR_CHAN(insn->chanspec);
226 unsigned short datalow;
227 unsigned short datahigh;
229 /* Check if (n > 0) */
231 printk(KERN_ERR "s526: INSN_READ: n should be > 0\n");
234 /* Read the low word first */
235 for (i = 0; i < insn->n; i++) {
236 datalow = inw(ADDR_CHAN_REG(REG_C0L, counter_channel));
237 datahigh = inw(ADDR_CHAN_REG(REG_C0H, counter_channel));
238 data[i] = (int)(datahigh & 0x00FF);
239 data[i] = (data[i] << 16) | (datalow & 0xFFFF);
240 /* printk("s526 GPCT[%d]: %x(0x%04x, 0x%04x)\n",
241 counter_channel, data[i], datahigh, datalow); */
246 static int s526_gpct_insn_config(struct comedi_device *dev,
247 struct comedi_subdevice *s,
248 struct comedi_insn *insn, unsigned int *data)
250 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
255 /* printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n",
258 for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++) {
259 devpriv->s526_gpct_config[subdev_channel].data[i] =
261 /* printk("data[%d]=%x\n", i, insn->data[i]); */
264 /* Check what type of Counter the user requested, data[0] contains */
265 /* the Application type */
266 switch (insn->data[0]) {
267 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
269 data[0]: Application Type
270 data[1]: Counter Mode Register Value
271 data[2]: Pre-load Register Value
272 data[3]: Conter Control Register
274 printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring Encoder\n");
275 devpriv->s526_gpct_config[subdev_channel].app =
279 /* Example of Counter Application */
280 /* One-shot (software trigger) */
281 cmReg.reg.coutSource = 0; /* out RCAP */
282 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
283 cmReg.reg.autoLoadResetRcap = 0;/* Auto load disabled */
284 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
285 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
286 cmReg.reg.clockSource = 2; /* Internal */
287 cmReg.reg.countDir = 1; /* Down */
288 cmReg.reg.countDirCtrl = 1; /* Software */
289 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
290 cmReg.reg.preloadRegSel = 0; /* PR0 */
291 cmReg.reg.reserved = 0;
293 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
295 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
296 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
298 /* Reset the counter */
299 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
300 /* Load the counter from PR0 */
301 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
303 /* Reset RCAP (fires one-shot) */
304 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel));
309 /* Set Counter Mode Register */
310 cmReg.value = insn->data[1] & 0xFFFF;
312 /* printk("s526: Counter Mode register=%x\n", cmReg.value); */
313 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
315 /* Reset the counter if it is software preload */
316 if (cmReg.reg.autoLoadResetRcap == 0) {
317 /* Reset the counter */
318 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
319 /* Load the counter from PR0
320 * outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
324 /* 0 quadrature, 1 software control */
325 cmReg.reg.countDirCtrl = 0;
327 /* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
328 if (insn->data[1] == GPCT_X2)
329 cmReg.reg.clockSource = 1;
330 else if (insn->data[1] == GPCT_X4)
331 cmReg.reg.clockSource = 2;
333 cmReg.reg.clockSource = 0;
335 /* When to take into account the indexpulse: */
336 /*if (insn->data[2] == GPCT_IndexPhaseLowLow) {
337 } else if (insn->data[2] == GPCT_IndexPhaseLowHigh) {
338 } else if (insn->data[2] == GPCT_IndexPhaseHighLow) {
339 } else if (insn->data[2] == GPCT_IndexPhaseHighHigh) {
341 /* Take into account the index pulse? */
342 if (insn->data[3] == GPCT_RESET_COUNTER_ON_INDEX)
343 /* Auto load with INDEX^ */
344 cmReg.reg.autoLoadResetRcap = 4;
346 /* Set Counter Mode Register */
347 cmReg.value = (short)(insn->data[1] & 0xFFFF);
348 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
350 /* Load the pre-load register high word */
351 value = (short)((insn->data[2] >> 16) & 0xFFFF);
352 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
354 /* Load the pre-load register low word */
355 value = (short)(insn->data[2] & 0xFFFF);
356 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
358 /* Write the Counter Control Register */
359 if (insn->data[3] != 0) {
360 value = (short)(insn->data[3] & 0xFFFF);
361 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
363 /* Reset the counter if it is software preload */
364 if (cmReg.reg.autoLoadResetRcap == 0) {
365 /* Reset the counter */
366 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
367 /* Load the counter from PR0 */
368 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
373 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
375 data[0]: Application Type
376 data[1]: Counter Mode Register Value
377 data[2]: Pre-load Register 0 Value
378 data[3]: Pre-load Register 1 Value
379 data[4]: Conter Control Register
381 printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring SPG\n");
382 devpriv->s526_gpct_config[subdev_channel].app =
383 SinglePulseGeneration;
385 /* Set Counter Mode Register */
386 cmReg.value = (short)(insn->data[1] & 0xFFFF);
387 cmReg.reg.preloadRegSel = 0; /* PR0 */
388 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
390 /* Load the pre-load register 0 high word */
391 value = (short)((insn->data[2] >> 16) & 0xFFFF);
392 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
394 /* Load the pre-load register 0 low word */
395 value = (short)(insn->data[2] & 0xFFFF);
396 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
398 /* Set Counter Mode Register */
399 cmReg.value = (short)(insn->data[1] & 0xFFFF);
400 cmReg.reg.preloadRegSel = 1; /* PR1 */
401 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
403 /* Load the pre-load register 1 high word */
404 value = (short)((insn->data[3] >> 16) & 0xFFFF);
405 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
407 /* Load the pre-load register 1 low word */
408 value = (short)(insn->data[3] & 0xFFFF);
409 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
411 /* Write the Counter Control Register */
412 if (insn->data[4] != 0) {
413 value = (short)(insn->data[4] & 0xFFFF);
414 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
418 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
420 data[0]: Application Type
421 data[1]: Counter Mode Register Value
422 data[2]: Pre-load Register 0 Value
423 data[3]: Pre-load Register 1 Value
424 data[4]: Conter Control Register
426 printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring PTG\n");
427 devpriv->s526_gpct_config[subdev_channel].app =
428 PulseTrainGeneration;
430 /* Set Counter Mode Register */
431 cmReg.value = (short)(insn->data[1] & 0xFFFF);
432 cmReg.reg.preloadRegSel = 0; /* PR0 */
433 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
435 /* Load the pre-load register 0 high word */
436 value = (short)((insn->data[2] >> 16) & 0xFFFF);
437 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
439 /* Load the pre-load register 0 low word */
440 value = (short)(insn->data[2] & 0xFFFF);
441 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
443 /* Set Counter Mode Register */
444 cmReg.value = (short)(insn->data[1] & 0xFFFF);
445 cmReg.reg.preloadRegSel = 1; /* PR1 */
446 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
448 /* Load the pre-load register 1 high word */
449 value = (short)((insn->data[3] >> 16) & 0xFFFF);
450 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
452 /* Load the pre-load register 1 low word */
453 value = (short)(insn->data[3] & 0xFFFF);
454 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
456 /* Write the Counter Control Register */
457 if (insn->data[4] != 0) {
458 value = (short)(insn->data[4] & 0xFFFF);
459 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
464 printk(KERN_ERR "s526: unsupported GPCT_insn_config\n");
472 static int s526_gpct_winsn(struct comedi_device *dev,
473 struct comedi_subdevice *s, struct comedi_insn *insn,
476 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
480 printk(KERN_INFO "s526: GPCT_INSN_WRITE on channel %d\n",
482 cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel));
483 printk(KERN_INFO "s526: Counter Mode Register: %x\n", cmReg.value);
484 /* Check what Application of Counter this channel is configured for */
485 switch (devpriv->s526_gpct_config[subdev_channel].app) {
486 case PositionMeasurement:
487 printk(KERN_INFO "S526: INSN_WRITE: PM\n");
488 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
490 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
493 case SinglePulseGeneration:
494 printk(KERN_INFO "S526: INSN_WRITE: SPG\n");
495 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
497 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
500 case PulseTrainGeneration:
501 /* data[0] contains the PULSE_WIDTH
502 data[1] contains the PULSE_PERIOD
503 @pre PULSE_PERIOD > PULSE_WIDTH > 0
504 The above periods must be expressed as a multiple of the
505 pulse frequency on the selected source
507 printk(KERN_INFO "S526: INSN_WRITE: PTG\n");
508 if ((insn->data[1] > insn->data[0]) && (insn->data[0] > 0)) {
509 (devpriv->s526_gpct_config[subdev_channel]).data[0] =
511 (devpriv->s526_gpct_config[subdev_channel]).data[1] =
514 printk(KERN_ERR "s526: INSN_WRITE: PTG: Problem with Pulse params -> %d %d\n",
515 insn->data[0], insn->data[1]);
519 value = (short)((*data >> 16) & 0xFFFF);
520 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
521 value = (short)(*data & 0xFFFF);
522 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
524 default: /* Impossible */
526 ("s526: INSN_WRITE: Functionality %d not implemented yet\n",
527 devpriv->s526_gpct_config[subdev_channel].app);
531 /* return the number of samples written */
535 #define ISR_ADC_DONE 0x4
536 static int s526_ai_insn_config(struct comedi_device *dev,
537 struct comedi_subdevice *s,
538 struct comedi_insn *insn, unsigned int *data)
540 int result = -EINVAL;
547 /* data[0] : channels was set in relevant bits.
550 /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
551 * enable channels here. The channel should be enabled in the
552 * INSN_READ handler. */
554 /* Enable ADC interrupt */
555 outw(ISR_ADC_DONE, ADDR_REG(REG_IER));
556 /* printk("s526: ADC current value: 0x%04x\n", inw(ADDR_REG(REG_ADC))); */
557 devpriv->s526_ai_config = (data[0] & 0x3FF) << 5;
559 devpriv->s526_ai_config |= 0x8000; /* set the delay */
561 devpriv->s526_ai_config |= 0x0001; /* ADC start bit. */
567 * "instructions" read/write data in "one-shot" or "software-triggered"
570 static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
571 struct comedi_insn *insn, unsigned int *data)
574 int chan = CR_CHAN(insn->chanspec);
575 unsigned short value;
579 /* Set configured delay, enable channel for this channel only,
580 * select "ADC read" channel, set "ADC start" bit. */
581 value = (devpriv->s526_ai_config & 0x8000) |
582 ((1 << 5) << chan) | (chan << 1) | 0x0001;
584 /* convert n samples */
585 for (n = 0; n < insn->n; n++) {
586 /* trigger conversion */
587 outw(value, ADDR_REG(REG_ADC));
588 /* printk("s526: Wrote 0x%04x to ADC\n", value); */
589 /* printk("s526: ADC reg=0x%04x\n", inw(ADDR_REG(REG_ADC))); */
592 /* wait for conversion to end */
593 for (i = 0; i < TIMEOUT; i++) {
594 status = inw(ADDR_REG(REG_ISR));
595 if (status & ISR_ADC_DONE) {
596 outw(ISR_ADC_DONE, ADDR_REG(REG_ISR));
601 /* printk() should be used instead of printk()
602 * whenever the code can be called from real-time. */
603 printk(KERN_ERR "s526: ADC(0x%04x) timeout\n",
604 inw(ADDR_REG(REG_ISR)));
609 d = inw(ADDR_REG(REG_ADD));
610 /* printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF)); */
613 data[n] = d ^ 0x8000;
616 /* return the number of samples read/written */
620 static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
621 struct comedi_insn *insn, unsigned int *data)
624 int chan = CR_CHAN(insn->chanspec);
627 /* printk("s526_ao_winsn\n"); */
629 /* outw(val, dev->iobase + REG_DAC); */
630 outw(val, ADDR_REG(REG_DAC));
632 /* Writing a list of values to an AO channel is probably not
633 * very useful, but that's how the interface is defined. */
634 for (i = 0; i < insn->n; i++) {
635 /* a typical programming sequence */
636 /* write the data to preload register
637 * outw(data[i], dev->iobase + REG_ADD);
639 /* write the data to preload register */
640 outw(data[i], ADDR_REG(REG_ADD));
641 devpriv->ao_readback[chan] = data[i];
642 /* outw(val + 1, dev->iobase + REG_DAC); starts the D/A conversion. */
643 outw(val + 1, ADDR_REG(REG_DAC)); /*starts the D/A conversion.*/
646 /* return the number of samples read/written */
650 /* AO subdevices should have a read insn as well as a write insn.
651 * Usually this means copying a value stored in devpriv. */
652 static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
653 struct comedi_insn *insn, unsigned int *data)
656 int chan = CR_CHAN(insn->chanspec);
658 for (i = 0; i < insn->n; i++)
659 data[i] = devpriv->ao_readback[chan];
664 /* DIO devices are slightly special. Although it is possible to
665 * implement the insn_read/insn_write interface, it is much more
666 * useful to applications if you implement the insn_bits interface.
667 * This allows packed reading/writing of the DIO channels. The
668 * comedi core can convert between insn_bits and insn_read/write */
669 static int s526_dio_insn_bits(struct comedi_device *dev,
670 struct comedi_subdevice *s,
671 struct comedi_insn *insn, unsigned int *data)
673 /* The insn data is a mask in data[0] and the new data
674 * in data[1], each channel cooresponding to a bit. */
676 s->state &= ~data[0];
677 s->state |= data[0] & data[1];
678 /* Write out the new digital output lines */
679 outw(s->state, ADDR_REG(REG_DIO));
682 /* on return, data[1] contains the value of the digital
683 * input and output lines. */
684 data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF; /* low 8 bits are the data */
685 /* or we could just return the software copy of the output values if
686 * it was a purely digital output subdevice */
687 /* data[1]=s->state & 0xFF; */
692 static int s526_dio_insn_config(struct comedi_device *dev,
693 struct comedi_subdevice *s,
694 struct comedi_insn *insn, unsigned int *data)
696 int chan = CR_CHAN(insn->chanspec);
699 printk(KERN_INFO "S526 DIO insn_config\n");
701 /* The input or output configuration of each digital line is
702 * configured by a special insn_config instruction. chanspec
703 * contains the channel to be changed, and data[0] contains the
704 * value COMEDI_INPUT or COMEDI_OUTPUT. */
707 mask = 0xF << (group << 2);
709 case INSN_CONFIG_DIO_OUTPUT:
710 /* bit 10/11 set the group 1/2's mode */
711 s->state |= 1 << (group + 10);
714 case INSN_CONFIG_DIO_INPUT:
715 s->state &= ~(1 << (group + 10)); /* 1 is output, 0 is input. */
718 case INSN_CONFIG_DIO_QUERY:
719 data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
724 outw(s->state, ADDR_REG(REG_DIO));
729 static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
731 const struct s526_board *board = comedi_board(dev);
732 struct comedi_subdevice *s;
737 /* int subdev_channel = 0; */
740 printk(KERN_INFO "comedi%d: s526: ", dev->minor);
742 iobase = it->options[0];
743 if (!iobase || !request_region(iobase, S526_IOSIZE, board->name)) {
744 comedi_error(dev, "I/O port conflict");
747 dev->iobase = iobase;
749 printk("iobase=0x%lx\n", dev->iobase);
751 /*** make it a little quieter, exw, 8/29/06
752 for (i = 0; i < S526_NUM_PORTS; i++) {
753 printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]),
754 inw(ADDR_REG(s526_ports[i])));
758 dev->board_name = board->name;
761 * Allocate the private structure area. alloc_private() is a
762 * convenient macro defined in comedidev.h.
764 if (alloc_private(dev, sizeof(struct s526_private)) < 0)
767 ret = comedi_alloc_subdevices(dev, 4);
771 s = dev->subdevices + 0;
772 /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
773 s->type = COMEDI_SUBD_COUNTER;
774 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
775 /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */
776 s->n_chan = board->gpct_chans;
777 s->maxdata = 0x00ffffff; /* 24 bit counter */
778 s->insn_read = s526_gpct_rinsn;
779 s->insn_config = s526_gpct_insn_config;
780 s->insn_write = s526_gpct_winsn;
782 /* Command are not implemented yet, however they are necessary to
783 allocate the necessary memory for the comedi_async struct (used
784 to trigger the GPCT in case of pulsegenerator function */
785 /* s->do_cmd = s526_gpct_cmd; */
786 /* s->do_cmdtest = s526_gpct_cmdtest; */
787 /* s->cancel = s526_gpct_cancel; */
789 s = dev->subdevices + 1;
790 /* dev->read_subdev=s; */
791 /* analog input subdevice */
792 s->type = COMEDI_SUBD_AI;
793 /* we support differential */
794 s->subdev_flags = SDF_READABLE | SDF_DIFF;
795 /* channels 0 to 7 are the regular differential inputs */
796 /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
799 s->range_table = &range_bipolar10;
800 s->len_chanlist = 16; /* This is the maximum chanlist length that
801 the board can handle */
802 s->insn_read = s526_ai_rinsn;
803 s->insn_config = s526_ai_insn_config;
805 s = dev->subdevices + 2;
806 /* analog output subdevice */
807 s->type = COMEDI_SUBD_AO;
808 s->subdev_flags = SDF_WRITABLE;
811 s->range_table = &range_bipolar10;
812 s->insn_write = s526_ao_winsn;
813 s->insn_read = s526_ao_rinsn;
815 s = dev->subdevices + 3;
816 /* digital i/o subdevice */
817 if (board->have_dio) {
818 s->type = COMEDI_SUBD_DIO;
819 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
822 s->range_table = &range_digital;
823 s->insn_bits = s526_dio_insn_bits;
824 s->insn_config = s526_dio_insn_config;
826 s->type = COMEDI_SUBD_UNUSED;
829 printk(KERN_INFO "attached\n");
834 /* Example of Counter Application */
835 /* One-shot (software trigger) */
836 cmReg.reg.coutSource = 0; /* out RCAP */
837 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
838 cmReg.reg.autoLoadResetRcap = 1;/* Auto load 0:disabled, 1:enabled */
839 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
840 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
841 cmReg.reg.clockSource = 2; /* Internal */
842 cmReg.reg.countDir = 1; /* Down */
843 cmReg.reg.countDirCtrl = 1; /* Software */
844 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
845 cmReg.reg.preloadRegSel = 0; /* PR0 */
846 cmReg.reg.reserved = 0;
848 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
850 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
851 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
853 /* Reset the counter */
854 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
855 /* Load the counter from PR0 */
856 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
857 /* Reset RCAP (fires one-shot) */
858 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel));
862 /* Set Counter Mode Register */
863 cmReg.reg.coutSource = 0; /* out RCAP */
864 cmReg.reg.coutPolarity = 0; /* Polarity inverted */
865 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */
866 cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */
867 cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */
868 cmReg.reg.clockSource = 3; /* x4 */
869 cmReg.reg.countDir = 0; /* up */
870 cmReg.reg.countDirCtrl = 0; /* quadrature */
871 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
872 cmReg.reg.preloadRegSel = 0; /* PR0 */
873 cmReg.reg.reserved = 0;
876 printk(KERN_INFO "Mode reg=0x%04x, 0x%04lx\n",
877 cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
878 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
880 printk(KERN_INFO "Read back mode reg=0x%04x\n",
881 inw(ADDR_CHAN_REG(REG_C0M, n)));
883 /* Load the pre-load register high word */
884 /* value = (short) (0x55); */
885 /* outw(value, ADDR_CHAN_REG(REG_C0H, n)); */
887 /* Load the pre-load register low word */
888 /* value = (short)(0xaa55); */
889 /* outw(value, ADDR_CHAN_REG(REG_C0L, n)); */
891 /* Write the Counter Control Register */
892 /* outw(value, ADDR_CHAN_REG(REG_C0C, 0)); */
894 /* Reset the counter if it is software preload */
895 if (cmReg.reg.autoLoadResetRcap == 0) {
896 /* Reset the counter */
897 outw(0x8000, ADDR_CHAN_REG(REG_C0C, n));
898 /* Load the counter from PR0 */
899 outw(0x4000, ADDR_CHAN_REG(REG_C0C, n));
902 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
904 printk(KERN_INFO "Read back mode reg=0x%04x\n",
905 inw(ADDR_CHAN_REG(REG_C0M, n)));
908 printk(KERN_INFO "Current registres:\n");
910 for (i = 0; i < S526_NUM_PORTS; i++) {
911 printk(KERN_INFO "0x%02lx: 0x%04x\n",
912 ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i])));
917 static void s526_detach(struct comedi_device *dev)
920 release_region(dev->iobase, S526_IOSIZE);
923 static struct comedi_driver s526_driver = {
924 .driver_name = "s526",
925 .module = THIS_MODULE,
926 .attach = s526_attach,
927 .detach = s526_detach,
928 .board_name = &s526_boards[0].name,
929 .offset = sizeof(struct s526_board),
930 .num_names = ARRAY_SIZE(s526_boards),
932 module_comedi_driver(s526_driver);
934 MODULE_AUTHOR("Comedi http://www.comedi.org");
935 MODULE_DESCRIPTION("Comedi low-level driver");
936 MODULE_LICENSE("GPL");