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 struct. */
211 struct s526_private {
215 /* would be useful for a PCI device */
216 struct pci_dev *pci_dev;
218 /* Used for AO readback */
219 unsigned int ao_readback[2];
221 struct s526GPCTConfig s526_gpct_config[4];
222 unsigned short s526_ai_config;
226 * most drivers define the following macro to make it easy to
227 * access the private structure.
229 #define devpriv ((struct s526_private *)dev->private)
232 * The struct comedi_driver structure tells the Comedi core module
233 * which functions to call to configure/deconfigure (attach/detach)
234 * the board, and also about the kernel module that contains
237 static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it);
238 static int s526_detach(struct comedi_device *dev);
239 static struct comedi_driver driver_s526 = {
240 .driver_name = "s526",
241 .module = THIS_MODULE,
242 .attach = s526_attach,
243 .detach = s526_detach,
244 /* It is not necessary to implement the following members if you are
245 * writing a driver for a ISA PnP or PCI card */
246 /* Most drivers will support multiple types of boards by
247 * having an array of board structures. These were defined
248 * in s526_boards[] above. Note that the element 'name'
249 * was first in the structure -- Comedi uses this fact to
250 * extract the name of the board without knowing any details
251 * about the structure except for its length.
252 * When a device is attached (by comedi_config), the name
253 * of the device is given to Comedi, and Comedi tries to
254 * match it by going through the list of board names. If
255 * there is a match, the address of the pointer is put
256 * into dev->board_ptr and driver->attach() is called.
258 * Note that these are not necessary if you can determine
259 * the type of board in software. ISA PnP, PCI, and PCMCIA
260 * devices are such boards.
262 .board_name = &s526_boards[0].name,
263 .offset = sizeof(struct s526_board),
264 .num_names = ARRAY_SIZE(s526_boards),
267 static int s526_gpct_rinsn(struct comedi_device *dev,
268 struct comedi_subdevice *s, struct comedi_insn *insn,
270 static int s526_gpct_insn_config(struct comedi_device *dev,
271 struct comedi_subdevice *s,
272 struct comedi_insn *insn, unsigned int *data);
273 static int s526_gpct_winsn(struct comedi_device *dev,
274 struct comedi_subdevice *s, struct comedi_insn *insn,
276 static int s526_ai_insn_config(struct comedi_device *dev,
277 struct comedi_subdevice *s,
278 struct comedi_insn *insn, unsigned int *data);
279 static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
280 struct comedi_insn *insn, unsigned int *data);
281 static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
282 struct comedi_insn *insn, unsigned int *data);
283 static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
284 struct comedi_insn *insn, unsigned int *data);
285 static int s526_dio_insn_bits(struct comedi_device *dev,
286 struct comedi_subdevice *s,
287 struct comedi_insn *insn, unsigned int *data);
288 static int s526_dio_insn_config(struct comedi_device *dev,
289 struct comedi_subdevice *s,
290 struct comedi_insn *insn, unsigned int *data);
293 * Attach is called by the Comedi core to configure the driver
294 * for a particular board. If you specified a board_name array
295 * in the driver structure, dev->board_ptr contains that
298 static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
300 struct comedi_subdevice *s;
304 /* int subdev_channel = 0; */
307 printk("comedi%d: s526: ", dev->minor);
309 iobase = it->options[0];
310 if (!iobase || !request_region(iobase, S526_IOSIZE, thisboard->name)) {
311 comedi_error(dev, "I/O port conflict");
314 dev->iobase = iobase;
316 printk("iobase=0x%lx\n", dev->iobase);
318 /*** make it a little quieter, exw, 8/29/06
319 for (i = 0; i < S526_NUM_PORTS; i++) {
320 printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i])));
325 * Initialize dev->board_name. Note that we can use the "thisboard"
326 * macro now, since we just initialized it in the last line.
328 dev->board_ptr = &s526_boards[0];
330 dev->board_name = thisboard->name;
333 * Allocate the private structure area. alloc_private() is a
334 * convenient macro defined in comedidev.h.
336 if (alloc_private(dev, sizeof(struct s526_private)) < 0)
340 * Allocate the subdevice structures. alloc_subdevice() is a
341 * convenient macro defined in comedidev.h.
343 dev->n_subdevices = 4;
344 if (alloc_subdevices(dev, dev->n_subdevices) < 0)
347 s = dev->subdevices + 0;
348 /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
349 s->type = COMEDI_SUBD_COUNTER;
350 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
351 /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */
352 s->n_chan = thisboard->gpct_chans;
353 s->maxdata = 0x00ffffff; /* 24 bit counter */
354 s->insn_read = s526_gpct_rinsn;
355 s->insn_config = s526_gpct_insn_config;
356 s->insn_write = s526_gpct_winsn;
358 /* Command are not implemented yet, however they are necessary to
359 allocate the necessary memory for the comedi_async struct (used
360 to trigger the GPCT in case of pulsegenerator function */
361 /* s->do_cmd = s526_gpct_cmd; */
362 /* s->do_cmdtest = s526_gpct_cmdtest; */
363 /* s->cancel = s526_gpct_cancel; */
365 s = dev->subdevices + 1;
366 /* dev->read_subdev=s; */
367 /* analog input subdevice */
368 s->type = COMEDI_SUBD_AI;
369 /* we support differential */
370 s->subdev_flags = SDF_READABLE | SDF_DIFF;
371 /* channels 0 to 7 are the regular differential inputs */
372 /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
375 s->range_table = &range_bipolar10;
376 s->len_chanlist = 16; /* This is the maximum chanlist length that
377 the board can handle */
378 s->insn_read = s526_ai_rinsn;
379 s->insn_config = s526_ai_insn_config;
381 s = dev->subdevices + 2;
382 /* analog output subdevice */
383 s->type = COMEDI_SUBD_AO;
384 s->subdev_flags = SDF_WRITABLE;
387 s->range_table = &range_bipolar10;
388 s->insn_write = s526_ao_winsn;
389 s->insn_read = s526_ao_rinsn;
391 s = dev->subdevices + 3;
392 /* digital i/o subdevice */
393 if (thisboard->have_dio) {
394 s->type = COMEDI_SUBD_DIO;
395 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
398 s->range_table = &range_digital;
399 s->insn_bits = s526_dio_insn_bits;
400 s->insn_config = s526_dio_insn_config;
402 s->type = COMEDI_SUBD_UNUSED;
405 printk("attached\n");
410 /* Example of Counter Application */
411 /* One-shot (software trigger) */
412 cmReg.reg.coutSource = 0; /* out RCAP */
413 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
414 cmReg.reg.autoLoadResetRcap = 1; /* Auto load 0:disabled, 1:enabled */
415 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
416 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
417 cmReg.reg.clockSource = 2; /* Internal */
418 cmReg.reg.countDir = 1; /* Down */
419 cmReg.reg.countDirCtrl = 1; /* Software */
420 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
421 cmReg.reg.preloadRegSel = 0; /* PR0 */
422 cmReg.reg.reserved = 0;
424 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
426 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
427 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
429 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */
430 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */
432 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset RCAP (fires one-shot) */
436 /* Set Counter Mode Register */
437 cmReg.reg.coutSource = 0; /* out RCAP */
438 cmReg.reg.coutPolarity = 0; /* Polarity inverted */
439 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */
440 cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */
441 cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */
442 cmReg.reg.clockSource = 3; /* x4 */
443 cmReg.reg.countDir = 0; /* up */
444 cmReg.reg.countDirCtrl = 0; /* quadrature */
445 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
446 cmReg.reg.preloadRegSel = 0; /* PR0 */
447 cmReg.reg.reserved = 0;
450 printk("Mode reg=0x%04x, 0x%04lx\n", cmReg.value, ADDR_CHAN_REG(REG_C0M,
452 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
454 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n)));
456 /* Load the pre-load register high word */
457 /* value = (short) (0x55); */
458 /* outw(value, ADDR_CHAN_REG(REG_C0H, n)); */
460 /* Load the pre-load register low word */
461 /* value = (short)(0xaa55); */
462 /* outw(value, ADDR_CHAN_REG(REG_C0L, n)); */
464 /* Write the Counter Control Register */
465 /* outw(value, ADDR_CHAN_REG(REG_C0C, 0)); */
467 /* Reset the counter if it is software preload */
468 if (cmReg.reg.autoLoadResetRcap == 0) {
469 outw(0x8000, ADDR_CHAN_REG(REG_C0C, n)); /* Reset the counter */
470 outw(0x4000, ADDR_CHAN_REG(REG_C0C, n)); /* Load the counter from PR0 */
473 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
475 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n)));
478 printk("Current registres:\n");
480 for (i = 0; i < S526_NUM_PORTS; i++) {
481 printk("0x%02lx: 0x%04x\n", ADDR_REG(s526_ports[i]),
482 inw(ADDR_REG(s526_ports[i])));
488 * _detach is called to deconfigure a device. It should deallocate
490 * This function is also called when _attach() fails, so it should be
491 * careful not to release resources that were not necessarily
492 * allocated by _attach(). dev->private and dev->subdevices are
493 * deallocated automatically by the core.
495 static int s526_detach(struct comedi_device *dev)
497 printk("comedi%d: s526: remove\n", dev->minor);
500 release_region(dev->iobase, S526_IOSIZE);
505 static int s526_gpct_rinsn(struct comedi_device *dev,
506 struct comedi_subdevice *s, struct comedi_insn *insn,
509 int i; /* counts the Data */
510 int counter_channel = CR_CHAN(insn->chanspec);
511 unsigned short datalow;
512 unsigned short datahigh;
514 /* Check if (n > 0) */
516 printk("s526: INSN_READ: n should be > 0\n");
519 /* Read the low word first */
520 for (i = 0; i < insn->n; i++) {
521 datalow = inw(ADDR_CHAN_REG(REG_C0L, counter_channel));
522 datahigh = inw(ADDR_CHAN_REG(REG_C0H, counter_channel));
523 data[i] = (int)(datahigh & 0x00FF);
524 data[i] = (data[i] << 16) | (datalow & 0xFFFF);
525 /* printk("s526 GPCT[%d]: %x(0x%04x, 0x%04x)\n", counter_channel, data[i], datahigh, datalow); */
530 static int s526_gpct_insn_config(struct comedi_device *dev,
531 struct comedi_subdevice *s,
532 struct comedi_insn *insn, unsigned int *data)
534 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
539 /* printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n", subdev_channel); */
541 for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++) {
542 devpriv->s526_gpct_config[subdev_channel].data[i] =
544 /* printk("data[%d]=%x\n", i, insn->data[i]); */
547 /* Check what type of Counter the user requested, data[0] contains */
548 /* the Application type */
549 switch (insn->data[0]) {
550 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
552 data[0]: Application Type
553 data[1]: Counter Mode Register Value
554 data[2]: Pre-load Register Value
555 data[3]: Conter Control Register
557 printk("s526: GPCT_INSN_CONFIG: Configuring Encoder\n");
558 devpriv->s526_gpct_config[subdev_channel].app =
562 /* Example of Counter Application */
563 /* One-shot (software trigger) */
564 cmReg.reg.coutSource = 0; /* out RCAP */
565 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
566 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */
567 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
568 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
569 cmReg.reg.clockSource = 2; /* Internal */
570 cmReg.reg.countDir = 1; /* Down */
571 cmReg.reg.countDirCtrl = 1; /* Software */
572 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
573 cmReg.reg.preloadRegSel = 0; /* PR0 */
574 cmReg.reg.reserved = 0;
576 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
578 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
579 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
581 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */
582 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */
584 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset RCAP (fires one-shot) */
589 /* Set Counter Mode Register */
590 cmReg.value = insn->data[1] & 0xFFFF;
592 /* printk("s526: Counter Mode register=%x\n", cmReg.value); */
593 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
595 /* Reset the counter if it is software preload */
596 if (cmReg.reg.autoLoadResetRcap == 0) {
597 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */
598 /* outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); Load the counter from PR0 */
601 cmReg.reg.countDirCtrl = 0; /* 0 quadrature, 1 software control */
603 /* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
604 if (insn->data[1] == GPCT_X2) {
605 cmReg.reg.clockSource = 1;
606 } else if (insn->data[1] == GPCT_X4) {
607 cmReg.reg.clockSource = 2;
609 cmReg.reg.clockSource = 0;
612 /* When to take into account the indexpulse: */
613 if (insn->data[2] == GPCT_IndexPhaseLowLow) {
614 } else if (insn->data[2] == GPCT_IndexPhaseLowHigh) {
615 } else if (insn->data[2] == GPCT_IndexPhaseHighLow) {
616 } else if (insn->data[2] == GPCT_IndexPhaseHighHigh) {
618 /* Take into account the index pulse? */
619 if (insn->data[3] == GPCT_RESET_COUNTER_ON_INDEX)
620 cmReg.reg.autoLoadResetRcap = 4; /* Auto load with INDEX^ */
622 /* Set Counter Mode Register */
623 cmReg.value = (short)(insn->data[1] & 0xFFFF);
624 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
626 /* Load the pre-load register high word */
627 value = (short)((insn->data[2] >> 16) & 0xFFFF);
628 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
630 /* Load the pre-load register low word */
631 value = (short)(insn->data[2] & 0xFFFF);
632 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
634 /* Write the Counter Control Register */
635 if (insn->data[3] != 0) {
636 value = (short)(insn->data[3] & 0xFFFF);
637 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
639 /* Reset the counter if it is software preload */
640 if (cmReg.reg.autoLoadResetRcap == 0) {
641 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */
642 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */
647 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
649 data[0]: Application Type
650 data[1]: Counter Mode Register Value
651 data[2]: Pre-load Register 0 Value
652 data[3]: Pre-load Register 1 Value
653 data[4]: Conter Control Register
655 printk("s526: GPCT_INSN_CONFIG: Configuring SPG\n");
656 devpriv->s526_gpct_config[subdev_channel].app =
657 SinglePulseGeneration;
659 /* Set Counter Mode Register */
660 cmReg.value = (short)(insn->data[1] & 0xFFFF);
661 cmReg.reg.preloadRegSel = 0; /* PR0 */
662 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
664 /* Load the pre-load register 0 high word */
665 value = (short)((insn->data[2] >> 16) & 0xFFFF);
666 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
668 /* Load the pre-load register 0 low word */
669 value = (short)(insn->data[2] & 0xFFFF);
670 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
672 /* Set Counter Mode Register */
673 cmReg.value = (short)(insn->data[1] & 0xFFFF);
674 cmReg.reg.preloadRegSel = 1; /* PR1 */
675 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
677 /* Load the pre-load register 1 high word */
678 value = (short)((insn->data[3] >> 16) & 0xFFFF);
679 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
681 /* Load the pre-load register 1 low word */
682 value = (short)(insn->data[3] & 0xFFFF);
683 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
685 /* Write the Counter Control Register */
686 if (insn->data[4] != 0) {
687 value = (short)(insn->data[4] & 0xFFFF);
688 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
692 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
694 data[0]: Application Type
695 data[1]: Counter Mode Register Value
696 data[2]: Pre-load Register 0 Value
697 data[3]: Pre-load Register 1 Value
698 data[4]: Conter Control Register
700 printk("s526: GPCT_INSN_CONFIG: Configuring PTG\n");
701 devpriv->s526_gpct_config[subdev_channel].app =
702 PulseTrainGeneration;
704 /* Set Counter Mode Register */
705 cmReg.value = (short)(insn->data[1] & 0xFFFF);
706 cmReg.reg.preloadRegSel = 0; /* PR0 */
707 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
709 /* Load the pre-load register 0 high word */
710 value = (short)((insn->data[2] >> 16) & 0xFFFF);
711 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
713 /* Load the pre-load register 0 low word */
714 value = (short)(insn->data[2] & 0xFFFF);
715 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
717 /* Set Counter Mode Register */
718 cmReg.value = (short)(insn->data[1] & 0xFFFF);
719 cmReg.reg.preloadRegSel = 1; /* PR1 */
720 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
722 /* Load the pre-load register 1 high word */
723 value = (short)((insn->data[3] >> 16) & 0xFFFF);
724 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
726 /* Load the pre-load register 1 low word */
727 value = (short)(insn->data[3] & 0xFFFF);
728 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
730 /* Write the Counter Control Register */
731 if (insn->data[4] != 0) {
732 value = (short)(insn->data[4] & 0xFFFF);
733 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
738 printk("s526: unsupported GPCT_insn_config\n");
746 static int s526_gpct_winsn(struct comedi_device *dev,
747 struct comedi_subdevice *s, struct comedi_insn *insn,
750 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
754 printk("s526: GPCT_INSN_WRITE on channel %d\n", subdev_channel);
755 cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel));
756 printk("s526: Counter Mode Register: %x\n", cmReg.value);
757 /* Check what Application of Counter this channel is configured for */
758 switch (devpriv->s526_gpct_config[subdev_channel].app) {
759 case PositionMeasurement:
760 printk("S526: INSN_WRITE: PM\n");
761 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
763 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
766 case SinglePulseGeneration:
767 printk("S526: INSN_WRITE: SPG\n");
768 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
770 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
773 case PulseTrainGeneration:
774 /* data[0] contains the PULSE_WIDTH
775 data[1] contains the PULSE_PERIOD
776 @pre PULSE_PERIOD > PULSE_WIDTH > 0
777 The above periods must be expressed as a multiple of the
778 pulse frequency on the selected source
780 printk("S526: INSN_WRITE: PTG\n");
781 if ((insn->data[1] > insn->data[0]) && (insn->data[0] > 0)) {
782 (devpriv->s526_gpct_config[subdev_channel]).data[0] =
784 (devpriv->s526_gpct_config[subdev_channel]).data[1] =
787 printk("s526: INSN_WRITE: PTG: Problem with Pulse params -> %d %d\n",
788 insn->data[0], insn->data[1]);
792 value = (short)((*data >> 16) & 0xFFFF);
793 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
794 value = (short)(*data & 0xFFFF);
795 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
797 default: /* Impossible */
799 ("s526: INSN_WRITE: Functionality %d not implemented yet\n",
800 devpriv->s526_gpct_config[subdev_channel].app);
804 /* return the number of samples written */
808 #define ISR_ADC_DONE 0x4
809 static int s526_ai_insn_config(struct comedi_device *dev,
810 struct comedi_subdevice *s,
811 struct comedi_insn *insn, unsigned int *data)
813 int result = -EINVAL;
820 /* data[0] : channels was set in relevant bits.
823 /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
824 * enable channels here. The channel should be enabled in the
825 * INSN_READ handler. */
827 /* Enable ADC interrupt */
828 outw(ISR_ADC_DONE, ADDR_REG(REG_IER));
829 /* printk("s526: ADC current value: 0x%04x\n", inw(ADDR_REG(REG_ADC))); */
830 devpriv->s526_ai_config = (data[0] & 0x3FF) << 5;
832 devpriv->s526_ai_config |= 0x8000; /* set the delay */
834 devpriv->s526_ai_config |= 0x0001; /* ADC start bit. */
840 * "instructions" read/write data in "one-shot" or "software-triggered"
843 static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
844 struct comedi_insn *insn, unsigned int *data)
847 int chan = CR_CHAN(insn->chanspec);
848 unsigned short value;
852 /* Set configured delay, enable channel for this channel only,
853 * select "ADC read" channel, set "ADC start" bit. */
854 value = (devpriv->s526_ai_config & 0x8000) |
855 ((1 << 5) << chan) | (chan << 1) | 0x0001;
857 /* convert n samples */
858 for (n = 0; n < insn->n; n++) {
859 /* trigger conversion */
860 outw(value, ADDR_REG(REG_ADC));
861 /* printk("s526: Wrote 0x%04x to ADC\n", value); */
862 /* printk("s526: ADC reg=0x%04x\n", inw(ADDR_REG(REG_ADC))); */
865 /* wait for conversion to end */
866 for (i = 0; i < TIMEOUT; i++) {
867 status = inw(ADDR_REG(REG_ISR));
868 if (status & ISR_ADC_DONE) {
869 outw(ISR_ADC_DONE, ADDR_REG(REG_ISR));
874 /* printk() should be used instead of printk()
875 * whenever the code can be called from real-time. */
876 printk("s526: ADC(0x%04x) timeout\n",
877 inw(ADDR_REG(REG_ISR)));
882 d = inw(ADDR_REG(REG_ADD));
883 /* printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF)); */
886 data[n] = d ^ 0x8000;
889 /* return the number of samples read/written */
893 static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
894 struct comedi_insn *insn, unsigned int *data)
897 int chan = CR_CHAN(insn->chanspec);
900 /* printk("s526_ao_winsn\n"); */
902 /* outw(val, dev->iobase + REG_DAC); */
903 outw(val, ADDR_REG(REG_DAC));
905 /* Writing a list of values to an AO channel is probably not
906 * very useful, but that's how the interface is defined. */
907 for (i = 0; i < insn->n; i++) {
908 /* a typical programming sequence */
909 /* outw(data[i], dev->iobase + REG_ADD); write the data to preload register */
910 outw(data[i], ADDR_REG(REG_ADD)); /* write the data to preload register */
911 devpriv->ao_readback[chan] = data[i];
912 /* outw(val + 1, dev->iobase + REG_DAC); starts the D/A conversion. */
913 outw(val + 1, ADDR_REG(REG_DAC)); /* starts the D/A conversion. */
916 /* return the number of samples read/written */
920 /* AO subdevices should have a read insn as well as a write insn.
921 * Usually this means copying a value stored in devpriv. */
922 static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
923 struct comedi_insn *insn, unsigned int *data)
926 int chan = CR_CHAN(insn->chanspec);
928 for (i = 0; i < insn->n; i++)
929 data[i] = devpriv->ao_readback[chan];
934 /* DIO devices are slightly special. Although it is possible to
935 * implement the insn_read/insn_write interface, it is much more
936 * useful to applications if you implement the insn_bits interface.
937 * This allows packed reading/writing of the DIO channels. The
938 * comedi core can convert between insn_bits and insn_read/write */
939 static int s526_dio_insn_bits(struct comedi_device *dev,
940 struct comedi_subdevice *s,
941 struct comedi_insn *insn, unsigned int *data)
946 /* The insn data is a mask in data[0] and the new data
947 * in data[1], each channel cooresponding to a bit. */
949 s->state &= ~data[0];
950 s->state |= data[0] & data[1];
951 /* Write out the new digital output lines */
952 outw(s->state, ADDR_REG(REG_DIO));
955 /* on return, data[1] contains the value of the digital
956 * input and output lines. */
957 data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF; /* low 8 bits are the data */
958 /* or we could just return the software copy of the output values if
959 * it was a purely digital output subdevice */
960 /* data[1]=s->state & 0xFF; */
965 static int s526_dio_insn_config(struct comedi_device *dev,
966 struct comedi_subdevice *s,
967 struct comedi_insn *insn, unsigned int *data)
969 int chan = CR_CHAN(insn->chanspec);
972 printk("S526 DIO insn_config\n");
974 /* The input or output configuration of each digital line is
975 * configured by a special insn_config instruction. chanspec
976 * contains the channel to be changed, and data[0] contains the
977 * value COMEDI_INPUT or COMEDI_OUTPUT. */
980 mask = 0xF << (group << 2);
982 case INSN_CONFIG_DIO_OUTPUT:
983 s->state |= 1 << (group + 10); // bit 10/11 set the group 1/2's mode
986 case INSN_CONFIG_DIO_INPUT:
987 s->state &= ~(1 << (group + 10));// 1 is output, 0 is input.
990 case INSN_CONFIG_DIO_QUERY:
991 data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
996 outw(s->state, ADDR_REG(REG_DIO));
1002 * A convenient macro that defines init_module() and cleanup_module(),
1005 COMEDI_INITCLEANUP(driver_s526);