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)
204 * Useful for shorthand access to the particular board structure
206 #define thisboard ((const struct s526_board *)dev->board_ptr)
208 /* this structure is for data unique to this hardware driver. If
209 several hardware drivers keep similar information in this structure,
210 feel free to suggest moving the variable to the struct comedi_device
213 struct s526_private {
217 /* would be useful for a PCI device */
218 struct pci_dev *pci_dev;
220 /* Used for AO readback */
221 unsigned int ao_readback[2];
223 struct s526GPCTConfig s526_gpct_config[4];
224 unsigned short s526_ai_config;
228 * most drivers define the following macro to make it easy to
229 * access the private structure.
231 #define devpriv ((struct s526_private *)dev->private)
233 static int s526_gpct_rinsn(struct comedi_device *dev,
234 struct comedi_subdevice *s, struct comedi_insn *insn,
237 int i; /* counts the Data */
238 int counter_channel = CR_CHAN(insn->chanspec);
239 unsigned short datalow;
240 unsigned short datahigh;
242 /* Check if (n > 0) */
244 printk(KERN_ERR "s526: INSN_READ: n should be > 0\n");
247 /* Read the low word first */
248 for (i = 0; i < insn->n; i++) {
249 datalow = inw(ADDR_CHAN_REG(REG_C0L, counter_channel));
250 datahigh = inw(ADDR_CHAN_REG(REG_C0H, counter_channel));
251 data[i] = (int)(datahigh & 0x00FF);
252 data[i] = (data[i] << 16) | (datalow & 0xFFFF);
253 /* printk("s526 GPCT[%d]: %x(0x%04x, 0x%04x)\n",
254 counter_channel, data[i], datahigh, datalow); */
259 static int s526_gpct_insn_config(struct comedi_device *dev,
260 struct comedi_subdevice *s,
261 struct comedi_insn *insn, unsigned int *data)
263 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
268 /* printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n",
271 for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++) {
272 devpriv->s526_gpct_config[subdev_channel].data[i] =
274 /* printk("data[%d]=%x\n", i, insn->data[i]); */
277 /* Check what type of Counter the user requested, data[0] contains */
278 /* the Application type */
279 switch (insn->data[0]) {
280 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
282 data[0]: Application Type
283 data[1]: Counter Mode Register Value
284 data[2]: Pre-load Register Value
285 data[3]: Conter Control Register
287 printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring Encoder\n");
288 devpriv->s526_gpct_config[subdev_channel].app =
292 /* Example of Counter Application */
293 /* One-shot (software trigger) */
294 cmReg.reg.coutSource = 0; /* out RCAP */
295 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
296 cmReg.reg.autoLoadResetRcap = 0;/* Auto load disabled */
297 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
298 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
299 cmReg.reg.clockSource = 2; /* Internal */
300 cmReg.reg.countDir = 1; /* Down */
301 cmReg.reg.countDirCtrl = 1; /* Software */
302 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
303 cmReg.reg.preloadRegSel = 0; /* PR0 */
304 cmReg.reg.reserved = 0;
306 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
308 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
309 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
311 /* Reset the counter */
312 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
313 /* Load the counter from PR0 */
314 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
316 /* Reset RCAP (fires one-shot) */
317 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel));
322 /* Set Counter Mode Register */
323 cmReg.value = insn->data[1] & 0xFFFF;
325 /* printk("s526: Counter Mode register=%x\n", cmReg.value); */
326 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
328 /* Reset the counter if it is software preload */
329 if (cmReg.reg.autoLoadResetRcap == 0) {
330 /* Reset the counter */
331 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
332 /* Load the counter from PR0
333 * outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
337 /* 0 quadrature, 1 software control */
338 cmReg.reg.countDirCtrl = 0;
340 /* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
341 if (insn->data[1] == GPCT_X2)
342 cmReg.reg.clockSource = 1;
343 else if (insn->data[1] == GPCT_X4)
344 cmReg.reg.clockSource = 2;
346 cmReg.reg.clockSource = 0;
348 /* When to take into account the indexpulse: */
349 /*if (insn->data[2] == GPCT_IndexPhaseLowLow) {
350 } else if (insn->data[2] == GPCT_IndexPhaseLowHigh) {
351 } else if (insn->data[2] == GPCT_IndexPhaseHighLow) {
352 } else if (insn->data[2] == GPCT_IndexPhaseHighHigh) {
354 /* Take into account the index pulse? */
355 if (insn->data[3] == GPCT_RESET_COUNTER_ON_INDEX)
356 /* Auto load with INDEX^ */
357 cmReg.reg.autoLoadResetRcap = 4;
359 /* Set Counter Mode Register */
360 cmReg.value = (short)(insn->data[1] & 0xFFFF);
361 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
363 /* Load the pre-load register high word */
364 value = (short)((insn->data[2] >> 16) & 0xFFFF);
365 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
367 /* Load the pre-load register low word */
368 value = (short)(insn->data[2] & 0xFFFF);
369 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
371 /* Write the Counter Control Register */
372 if (insn->data[3] != 0) {
373 value = (short)(insn->data[3] & 0xFFFF);
374 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
376 /* Reset the counter if it is software preload */
377 if (cmReg.reg.autoLoadResetRcap == 0) {
378 /* Reset the counter */
379 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
380 /* Load the counter from PR0 */
381 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
386 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
388 data[0]: Application Type
389 data[1]: Counter Mode Register Value
390 data[2]: Pre-load Register 0 Value
391 data[3]: Pre-load Register 1 Value
392 data[4]: Conter Control Register
394 printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring SPG\n");
395 devpriv->s526_gpct_config[subdev_channel].app =
396 SinglePulseGeneration;
398 /* Set Counter Mode Register */
399 cmReg.value = (short)(insn->data[1] & 0xFFFF);
400 cmReg.reg.preloadRegSel = 0; /* PR0 */
401 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
403 /* Load the pre-load register 0 high word */
404 value = (short)((insn->data[2] >> 16) & 0xFFFF);
405 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
407 /* Load the pre-load register 0 low word */
408 value = (short)(insn->data[2] & 0xFFFF);
409 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
411 /* Set Counter Mode Register */
412 cmReg.value = (short)(insn->data[1] & 0xFFFF);
413 cmReg.reg.preloadRegSel = 1; /* PR1 */
414 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
416 /* Load the pre-load register 1 high word */
417 value = (short)((insn->data[3] >> 16) & 0xFFFF);
418 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
420 /* Load the pre-load register 1 low word */
421 value = (short)(insn->data[3] & 0xFFFF);
422 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
424 /* Write the Counter Control Register */
425 if (insn->data[4] != 0) {
426 value = (short)(insn->data[4] & 0xFFFF);
427 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
431 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
433 data[0]: Application Type
434 data[1]: Counter Mode Register Value
435 data[2]: Pre-load Register 0 Value
436 data[3]: Pre-load Register 1 Value
437 data[4]: Conter Control Register
439 printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring PTG\n");
440 devpriv->s526_gpct_config[subdev_channel].app =
441 PulseTrainGeneration;
443 /* Set Counter Mode Register */
444 cmReg.value = (short)(insn->data[1] & 0xFFFF);
445 cmReg.reg.preloadRegSel = 0; /* PR0 */
446 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
448 /* Load the pre-load register 0 high word */
449 value = (short)((insn->data[2] >> 16) & 0xFFFF);
450 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
452 /* Load the pre-load register 0 low word */
453 value = (short)(insn->data[2] & 0xFFFF);
454 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
456 /* Set Counter Mode Register */
457 cmReg.value = (short)(insn->data[1] & 0xFFFF);
458 cmReg.reg.preloadRegSel = 1; /* PR1 */
459 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
461 /* Load the pre-load register 1 high word */
462 value = (short)((insn->data[3] >> 16) & 0xFFFF);
463 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
465 /* Load the pre-load register 1 low word */
466 value = (short)(insn->data[3] & 0xFFFF);
467 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
469 /* Write the Counter Control Register */
470 if (insn->data[4] != 0) {
471 value = (short)(insn->data[4] & 0xFFFF);
472 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
477 printk(KERN_ERR "s526: unsupported GPCT_insn_config\n");
485 static int s526_gpct_winsn(struct comedi_device *dev,
486 struct comedi_subdevice *s, struct comedi_insn *insn,
489 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
493 printk(KERN_INFO "s526: GPCT_INSN_WRITE on channel %d\n",
495 cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel));
496 printk(KERN_INFO "s526: Counter Mode Register: %x\n", cmReg.value);
497 /* Check what Application of Counter this channel is configured for */
498 switch (devpriv->s526_gpct_config[subdev_channel].app) {
499 case PositionMeasurement:
500 printk(KERN_INFO "S526: INSN_WRITE: PM\n");
501 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
503 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
506 case SinglePulseGeneration:
507 printk(KERN_INFO "S526: INSN_WRITE: SPG\n");
508 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
510 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
513 case PulseTrainGeneration:
514 /* data[0] contains the PULSE_WIDTH
515 data[1] contains the PULSE_PERIOD
516 @pre PULSE_PERIOD > PULSE_WIDTH > 0
517 The above periods must be expressed as a multiple of the
518 pulse frequency on the selected source
520 printk(KERN_INFO "S526: INSN_WRITE: PTG\n");
521 if ((insn->data[1] > insn->data[0]) && (insn->data[0] > 0)) {
522 (devpriv->s526_gpct_config[subdev_channel]).data[0] =
524 (devpriv->s526_gpct_config[subdev_channel]).data[1] =
527 printk(KERN_ERR "s526: INSN_WRITE: PTG: Problem with Pulse params -> %d %d\n",
528 insn->data[0], insn->data[1]);
532 value = (short)((*data >> 16) & 0xFFFF);
533 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
534 value = (short)(*data & 0xFFFF);
535 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
537 default: /* Impossible */
539 ("s526: INSN_WRITE: Functionality %d not implemented yet\n",
540 devpriv->s526_gpct_config[subdev_channel].app);
544 /* return the number of samples written */
548 #define ISR_ADC_DONE 0x4
549 static int s526_ai_insn_config(struct comedi_device *dev,
550 struct comedi_subdevice *s,
551 struct comedi_insn *insn, unsigned int *data)
553 int result = -EINVAL;
560 /* data[0] : channels was set in relevant bits.
563 /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
564 * enable channels here. The channel should be enabled in the
565 * INSN_READ handler. */
567 /* Enable ADC interrupt */
568 outw(ISR_ADC_DONE, ADDR_REG(REG_IER));
569 /* printk("s526: ADC current value: 0x%04x\n", inw(ADDR_REG(REG_ADC))); */
570 devpriv->s526_ai_config = (data[0] & 0x3FF) << 5;
572 devpriv->s526_ai_config |= 0x8000; /* set the delay */
574 devpriv->s526_ai_config |= 0x0001; /* ADC start bit. */
580 * "instructions" read/write data in "one-shot" or "software-triggered"
583 static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
584 struct comedi_insn *insn, unsigned int *data)
587 int chan = CR_CHAN(insn->chanspec);
588 unsigned short value;
592 /* Set configured delay, enable channel for this channel only,
593 * select "ADC read" channel, set "ADC start" bit. */
594 value = (devpriv->s526_ai_config & 0x8000) |
595 ((1 << 5) << chan) | (chan << 1) | 0x0001;
597 /* convert n samples */
598 for (n = 0; n < insn->n; n++) {
599 /* trigger conversion */
600 outw(value, ADDR_REG(REG_ADC));
601 /* printk("s526: Wrote 0x%04x to ADC\n", value); */
602 /* printk("s526: ADC reg=0x%04x\n", inw(ADDR_REG(REG_ADC))); */
605 /* wait for conversion to end */
606 for (i = 0; i < TIMEOUT; i++) {
607 status = inw(ADDR_REG(REG_ISR));
608 if (status & ISR_ADC_DONE) {
609 outw(ISR_ADC_DONE, ADDR_REG(REG_ISR));
614 /* printk() should be used instead of printk()
615 * whenever the code can be called from real-time. */
616 printk(KERN_ERR "s526: ADC(0x%04x) timeout\n",
617 inw(ADDR_REG(REG_ISR)));
622 d = inw(ADDR_REG(REG_ADD));
623 /* printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF)); */
626 data[n] = d ^ 0x8000;
629 /* return the number of samples read/written */
633 static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
634 struct comedi_insn *insn, unsigned int *data)
637 int chan = CR_CHAN(insn->chanspec);
640 /* printk("s526_ao_winsn\n"); */
642 /* outw(val, dev->iobase + REG_DAC); */
643 outw(val, ADDR_REG(REG_DAC));
645 /* Writing a list of values to an AO channel is probably not
646 * very useful, but that's how the interface is defined. */
647 for (i = 0; i < insn->n; i++) {
648 /* a typical programming sequence */
649 /* write the data to preload register
650 * outw(data[i], dev->iobase + REG_ADD);
652 /* write the data to preload register */
653 outw(data[i], ADDR_REG(REG_ADD));
654 devpriv->ao_readback[chan] = data[i];
655 /* outw(val + 1, dev->iobase + REG_DAC); starts the D/A conversion. */
656 outw(val + 1, ADDR_REG(REG_DAC)); /*starts the D/A conversion.*/
659 /* return the number of samples read/written */
663 /* AO subdevices should have a read insn as well as a write insn.
664 * Usually this means copying a value stored in devpriv. */
665 static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
666 struct comedi_insn *insn, unsigned int *data)
669 int chan = CR_CHAN(insn->chanspec);
671 for (i = 0; i < insn->n; i++)
672 data[i] = devpriv->ao_readback[chan];
677 /* DIO devices are slightly special. Although it is possible to
678 * implement the insn_read/insn_write interface, it is much more
679 * useful to applications if you implement the insn_bits interface.
680 * This allows packed reading/writing of the DIO channels. The
681 * comedi core can convert between insn_bits and insn_read/write */
682 static int s526_dio_insn_bits(struct comedi_device *dev,
683 struct comedi_subdevice *s,
684 struct comedi_insn *insn, unsigned int *data)
689 /* The insn data is a mask in data[0] and the new data
690 * in data[1], each channel cooresponding to a bit. */
692 s->state &= ~data[0];
693 s->state |= data[0] & data[1];
694 /* Write out the new digital output lines */
695 outw(s->state, ADDR_REG(REG_DIO));
698 /* on return, data[1] contains the value of the digital
699 * input and output lines. */
700 data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF; /* low 8 bits are the data */
701 /* or we could just return the software copy of the output values if
702 * it was a purely digital output subdevice */
703 /* data[1]=s->state & 0xFF; */
708 static int s526_dio_insn_config(struct comedi_device *dev,
709 struct comedi_subdevice *s,
710 struct comedi_insn *insn, unsigned int *data)
712 int chan = CR_CHAN(insn->chanspec);
715 printk(KERN_INFO "S526 DIO insn_config\n");
717 /* The input or output configuration of each digital line is
718 * configured by a special insn_config instruction. chanspec
719 * contains the channel to be changed, and data[0] contains the
720 * value COMEDI_INPUT or COMEDI_OUTPUT. */
723 mask = 0xF << (group << 2);
725 case INSN_CONFIG_DIO_OUTPUT:
726 /* bit 10/11 set the group 1/2's mode */
727 s->state |= 1 << (group + 10);
730 case INSN_CONFIG_DIO_INPUT:
731 s->state &= ~(1 << (group + 10)); /* 1 is output, 0 is input. */
734 case INSN_CONFIG_DIO_QUERY:
735 data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
740 outw(s->state, ADDR_REG(REG_DIO));
745 static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
747 struct comedi_subdevice *s;
751 /* int subdev_channel = 0; */
754 printk(KERN_INFO "comedi%d: s526: ", dev->minor);
756 iobase = it->options[0];
757 if (!iobase || !request_region(iobase, S526_IOSIZE, thisboard->name)) {
758 comedi_error(dev, "I/O port conflict");
761 dev->iobase = iobase;
763 printk("iobase=0x%lx\n", dev->iobase);
765 /*** make it a little quieter, exw, 8/29/06
766 for (i = 0; i < S526_NUM_PORTS; i++) {
767 printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]),
768 inw(ADDR_REG(s526_ports[i])));
773 * Initialize dev->board_name. Note that we can use the "thisboard"
774 * macro now, since we just initialized it in the last line.
776 dev->board_ptr = &s526_boards[0];
778 dev->board_name = thisboard->name;
781 * Allocate the private structure area. alloc_private() is a
782 * convenient macro defined in comedidev.h.
784 if (alloc_private(dev, sizeof(struct s526_private)) < 0)
788 * Allocate the subdevice structures. alloc_subdevice() is a
789 * convenient macro defined in comedidev.h.
791 dev->n_subdevices = 4;
792 if (alloc_subdevices(dev, dev->n_subdevices) < 0)
795 s = dev->subdevices + 0;
796 /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
797 s->type = COMEDI_SUBD_COUNTER;
798 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
799 /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */
800 s->n_chan = thisboard->gpct_chans;
801 s->maxdata = 0x00ffffff; /* 24 bit counter */
802 s->insn_read = s526_gpct_rinsn;
803 s->insn_config = s526_gpct_insn_config;
804 s->insn_write = s526_gpct_winsn;
806 /* Command are not implemented yet, however they are necessary to
807 allocate the necessary memory for the comedi_async struct (used
808 to trigger the GPCT in case of pulsegenerator function */
809 /* s->do_cmd = s526_gpct_cmd; */
810 /* s->do_cmdtest = s526_gpct_cmdtest; */
811 /* s->cancel = s526_gpct_cancel; */
813 s = dev->subdevices + 1;
814 /* dev->read_subdev=s; */
815 /* analog input subdevice */
816 s->type = COMEDI_SUBD_AI;
817 /* we support differential */
818 s->subdev_flags = SDF_READABLE | SDF_DIFF;
819 /* channels 0 to 7 are the regular differential inputs */
820 /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
823 s->range_table = &range_bipolar10;
824 s->len_chanlist = 16; /* This is the maximum chanlist length that
825 the board can handle */
826 s->insn_read = s526_ai_rinsn;
827 s->insn_config = s526_ai_insn_config;
829 s = dev->subdevices + 2;
830 /* analog output subdevice */
831 s->type = COMEDI_SUBD_AO;
832 s->subdev_flags = SDF_WRITABLE;
835 s->range_table = &range_bipolar10;
836 s->insn_write = s526_ao_winsn;
837 s->insn_read = s526_ao_rinsn;
839 s = dev->subdevices + 3;
840 /* digital i/o subdevice */
841 if (thisboard->have_dio) {
842 s->type = COMEDI_SUBD_DIO;
843 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
846 s->range_table = &range_digital;
847 s->insn_bits = s526_dio_insn_bits;
848 s->insn_config = s526_dio_insn_config;
850 s->type = COMEDI_SUBD_UNUSED;
853 printk(KERN_INFO "attached\n");
858 /* Example of Counter Application */
859 /* One-shot (software trigger) */
860 cmReg.reg.coutSource = 0; /* out RCAP */
861 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
862 cmReg.reg.autoLoadResetRcap = 1;/* Auto load 0:disabled, 1:enabled */
863 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
864 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
865 cmReg.reg.clockSource = 2; /* Internal */
866 cmReg.reg.countDir = 1; /* Down */
867 cmReg.reg.countDirCtrl = 1; /* Software */
868 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
869 cmReg.reg.preloadRegSel = 0; /* PR0 */
870 cmReg.reg.reserved = 0;
872 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
874 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
875 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
877 /* Reset the counter */
878 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
879 /* Load the counter from PR0 */
880 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
881 /* Reset RCAP (fires one-shot) */
882 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel));
886 /* Set Counter Mode Register */
887 cmReg.reg.coutSource = 0; /* out RCAP */
888 cmReg.reg.coutPolarity = 0; /* Polarity inverted */
889 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */
890 cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */
891 cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */
892 cmReg.reg.clockSource = 3; /* x4 */
893 cmReg.reg.countDir = 0; /* up */
894 cmReg.reg.countDirCtrl = 0; /* quadrature */
895 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
896 cmReg.reg.preloadRegSel = 0; /* PR0 */
897 cmReg.reg.reserved = 0;
900 printk(KERN_INFO "Mode reg=0x%04x, 0x%04lx\n",
901 cmReg.value, ADDR_CHAN_REG(REG_C0M, 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)));
907 /* Load the pre-load register high word */
908 /* value = (short) (0x55); */
909 /* outw(value, ADDR_CHAN_REG(REG_C0H, n)); */
911 /* Load the pre-load register low word */
912 /* value = (short)(0xaa55); */
913 /* outw(value, ADDR_CHAN_REG(REG_C0L, n)); */
915 /* Write the Counter Control Register */
916 /* outw(value, ADDR_CHAN_REG(REG_C0C, 0)); */
918 /* Reset the counter if it is software preload */
919 if (cmReg.reg.autoLoadResetRcap == 0) {
920 /* Reset the counter */
921 outw(0x8000, ADDR_CHAN_REG(REG_C0C, n));
922 /* Load the counter from PR0 */
923 outw(0x4000, ADDR_CHAN_REG(REG_C0C, n));
926 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
928 printk(KERN_INFO "Read back mode reg=0x%04x\n",
929 inw(ADDR_CHAN_REG(REG_C0M, n)));
932 printk(KERN_INFO "Current registres:\n");
934 for (i = 0; i < S526_NUM_PORTS; i++) {
935 printk(KERN_INFO "0x%02lx: 0x%04x\n",
936 ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i])));
941 static void s526_detach(struct comedi_device *dev)
944 release_region(dev->iobase, S526_IOSIZE);
947 static struct comedi_driver s526_driver = {
948 .driver_name = "s526",
949 .module = THIS_MODULE,
950 .attach = s526_attach,
951 .detach = s526_detach,
952 .board_name = &s526_boards[0].name,
953 .offset = sizeof(struct s526_board),
954 .num_names = ARRAY_SIZE(s526_boards),
956 module_comedi_driver(s526_driver);
958 MODULE_AUTHOR("Comedi http://www.comedi.org");
959 MODULE_DESCRIPTION("Comedi low-level driver");
960 MODULE_LICENSE("GPL");