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>
49 #define S526_START_AI_CONV 0
50 #define S526_AI_READ 0
53 #define S526_IOSIZE 0x40
54 #define S526_NUM_PORTS 27
85 static const int s526_ports[] = {
115 struct counter_mode_register_t {
116 unsigned short coutSource:1;
117 unsigned short coutPolarity:1;
118 unsigned short autoLoadResetRcap:3;
119 unsigned short hwCtEnableSource:2;
120 unsigned short ctEnableCtrl:2;
121 unsigned short clockSource:2;
122 unsigned short countDir:1;
123 unsigned short countDirCtrl:1;
124 unsigned short outputRegLatchCtrl:1;
125 unsigned short preloadRegSel:1;
126 unsigned short reserved:1;
130 struct counter_mode_register_t reg;
131 unsigned short value;
134 #define MAX_GPCT_CONFIG_DATA 6
136 /* Different Application Classes for GPCT Subdevices */
137 /* The list is not exhaustive and needs discussion! */
138 enum S526_GPCT_APP_CLASS {
139 CountingAndTimeMeasurement,
140 SinglePulseGeneration,
141 PulseTrainGeneration,
146 /* Config struct for different GPCT subdevice Application Classes and
149 struct s526GPCTConfig {
150 enum S526_GPCT_APP_CLASS app;
151 int data[MAX_GPCT_CONFIG_DATA];
155 * Board descriptions for two imaginary boards. Describing the
156 * boards in this way is optional, and completely driver-dependent.
157 * Some drivers use arrays such as this, other do not.
170 static const struct s526_board s526_boards[] = {
183 #define ADDR_REG(reg) (dev->iobase + (reg))
184 #define ADDR_CHAN_REG(reg, chan) (dev->iobase + (reg) + (chan) * 8)
187 * Useful for shorthand access to the particular board structure
189 #define thisboard ((const struct s526_board *)dev->board_ptr)
191 /* this structure is for data unique to this hardware driver. If
192 several hardware drivers keep similar information in this structure,
193 feel free to suggest moving the variable to the struct comedi_device struct. */
194 struct s526_private {
198 /* would be useful for a PCI device */
199 struct pci_dev *pci_dev;
201 /* Used for AO readback */
202 unsigned int ao_readback[2];
204 struct s526GPCTConfig s526_gpct_config[4];
205 unsigned short s526_ai_config;
209 * most drivers define the following macro to make it easy to
210 * access the private structure.
212 #define devpriv ((struct s526_private *)dev->private)
215 * The struct comedi_driver structure tells the Comedi core module
216 * which functions to call to configure/deconfigure (attach/detach)
217 * the board, and also about the kernel module that contains
220 static int s526_attach(struct comedi_device * dev, struct comedi_devconfig * it);
221 static int s526_detach(struct comedi_device * dev);
222 static struct comedi_driver driver_s526 = {
227 /* It is not necessary to implement the following members if you are
228 * writing a driver for a ISA PnP or PCI card */
229 /* Most drivers will support multiple types of boards by
230 * having an array of board structures. These were defined
231 * in s526_boards[] above. Note that the element 'name'
232 * was first in the structure -- Comedi uses this fact to
233 * extract the name of the board without knowing any details
234 * about the structure except for its length.
235 * When a device is attached (by comedi_config), the name
236 * of the device is given to Comedi, and Comedi tries to
237 * match it by going through the list of board names. If
238 * there is a match, the address of the pointer is put
239 * into dev->board_ptr and driver->attach() is called.
241 * Note that these are not necessary if you can determine
242 * the type of board in software. ISA PnP, PCI, and PCMCIA
243 * devices are such boards.
245 board_name:&s526_boards[0].name,
246 offset:sizeof(struct s526_board),
247 num_names:sizeof(s526_boards) / sizeof(struct s526_board),
250 static int s526_gpct_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
251 struct comedi_insn * insn, unsigned int * data);
252 static int s526_gpct_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
253 struct comedi_insn * insn, unsigned int * data);
254 static int s526_gpct_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
255 struct comedi_insn * insn, unsigned int * data);
256 static int s526_ai_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
257 struct comedi_insn * insn, unsigned int * data);
258 static int s526_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
259 struct comedi_insn * insn, unsigned int * data);
260 static int s526_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
261 struct comedi_insn * insn, unsigned int * data);
262 static int s526_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
263 struct comedi_insn * insn, unsigned int * data);
264 static int s526_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
265 struct comedi_insn * insn, unsigned int * data);
266 static int s526_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
267 struct comedi_insn * insn, unsigned int * data);
270 * Attach is called by the Comedi core to configure the driver
271 * for a particular board. If you specified a board_name array
272 * in the driver structure, dev->board_ptr contains that
275 static int s526_attach(struct comedi_device * dev, struct comedi_devconfig * it)
277 struct comedi_subdevice *s;
281 /* int subdev_channel = 0; */
283 printk("comedi%d: s526: ", dev->minor);
285 iobase = it->options[0];
286 if (!iobase || !request_region(iobase, S526_IOSIZE, thisboard->name)) {
287 comedi_error(dev, "I/O port conflict");
290 dev->iobase = iobase;
292 printk("iobase=0x%lx\n", dev->iobase);
294 /*** make it a little quieter, exw, 8/29/06
295 for (i = 0; i < S526_NUM_PORTS; i++) {
296 printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i])));
301 * Initialize dev->board_name. Note that we can use the "thisboard"
302 * macro now, since we just initialized it in the last line.
304 dev->board_ptr = &s526_boards[0];
306 dev->board_name = thisboard->name;
309 * Allocate the private structure area. alloc_private() is a
310 * convenient macro defined in comedidev.h.
312 if (alloc_private(dev, sizeof(struct s526_private)) < 0)
316 * Allocate the subdevice structures. alloc_subdevice() is a
317 * convenient macro defined in comedidev.h.
319 dev->n_subdevices = 4;
320 if (alloc_subdevices(dev, dev->n_subdevices) < 0)
323 s = dev->subdevices + 0;
324 /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
325 s->type = COMEDI_SUBD_COUNTER;
326 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
327 /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */
328 s->n_chan = thisboard->gpct_chans;
329 s->maxdata = 0x00ffffff; /* 24 bit counter */
330 s->insn_read = s526_gpct_rinsn;
331 s->insn_config = s526_gpct_insn_config;
332 s->insn_write = s526_gpct_winsn;
334 /* Command are not implemented yet, however they are necessary to
335 allocate the necessary memory for the comedi_async struct (used
336 to trigger the GPCT in case of pulsegenerator function */
337 /* s->do_cmd = s526_gpct_cmd; */
338 /* s->do_cmdtest = s526_gpct_cmdtest; */
339 /* s->cancel = s526_gpct_cancel; */
341 s = dev->subdevices + 1;
342 /* dev->read_subdev=s; */
343 /* analog input subdevice */
344 s->type = COMEDI_SUBD_AI;
345 /* we support differential */
346 s->subdev_flags = SDF_READABLE | SDF_DIFF;
347 /* channels 0 to 7 are the regular differential inputs */
348 /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
351 s->range_table = &range_bipolar10;
352 s->len_chanlist = 16; /* This is the maximum chanlist length that
353 the board can handle */
354 s->insn_read = s526_ai_rinsn;
355 s->insn_config = s526_ai_insn_config;
357 s = dev->subdevices + 2;
358 /* analog output subdevice */
359 s->type = COMEDI_SUBD_AO;
360 s->subdev_flags = SDF_WRITABLE;
363 s->range_table = &range_bipolar10;
364 s->insn_write = s526_ao_winsn;
365 s->insn_read = s526_ao_rinsn;
367 s = dev->subdevices + 3;
368 /* digital i/o subdevice */
369 if (thisboard->have_dio) {
370 s->type = COMEDI_SUBD_DIO;
371 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
374 s->range_table = &range_digital;
375 s->insn_bits = s526_dio_insn_bits;
376 s->insn_config = s526_dio_insn_config;
378 s->type = COMEDI_SUBD_UNUSED;
381 printk("attached\n");
386 /* Example of Counter Application */
387 /* One-shot (software trigger) */
388 cmReg.reg.coutSource = 0; /* out RCAP */
389 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
390 cmReg.reg.autoLoadResetRcap = 1; /* Auto load 0:disabled, 1:enabled */
391 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
392 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
393 cmReg.reg.clockSource = 2; /* Internal */
394 cmReg.reg.countDir = 1; /* Down */
395 cmReg.reg.countDirCtrl = 1; /* Software */
396 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
397 cmReg.reg.preloadRegSel = 0; /* PR0 */
398 cmReg.reg.reserved = 0;
400 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
402 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
403 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
405 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */
406 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */
408 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset RCAP (fires one-shot) */
412 /* Set Counter Mode Register */
413 cmReg.reg.coutSource = 0; /* out RCAP */
414 cmReg.reg.coutPolarity = 0; /* Polarity inverted */
415 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */
416 cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */
417 cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */
418 cmReg.reg.clockSource = 3; /* x4 */
419 cmReg.reg.countDir = 0; /* up */
420 cmReg.reg.countDirCtrl = 0; /* quadrature */
421 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
422 cmReg.reg.preloadRegSel = 0; /* PR0 */
423 cmReg.reg.reserved = 0;
426 printk("Mode reg=0x%04x, 0x%04lx\n", cmReg.value, ADDR_CHAN_REG(REG_C0M,
428 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
430 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n)));
432 /* Load the pre-laod register high word */
433 /* value = (short) (0x55); */
434 /* outw(value, ADDR_CHAN_REG(REG_C0H, n)); */
436 /* Load the pre-laod register low word */
437 /* value = (short)(0xaa55); */
438 /* outw(value, ADDR_CHAN_REG(REG_C0L, n)); */
440 /* Write the Counter Control Register */
441 /* outw(value, ADDR_CHAN_REG(REG_C0C, 0)); */
443 /* Reset the counter if it is software preload */
444 if (cmReg.reg.autoLoadResetRcap == 0) {
445 outw(0x8000, ADDR_CHAN_REG(REG_C0C, n)); /* Reset the counter */
446 outw(0x4000, ADDR_CHAN_REG(REG_C0C, n)); /* Load the counter from PR0 */
449 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
451 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n)));
454 printk("Current registres:\n");
456 for (i = 0; i < S526_NUM_PORTS; i++) {
457 printk("0x%02lx: 0x%04x\n", ADDR_REG(s526_ports[i]),
458 inw(ADDR_REG(s526_ports[i])));
464 * _detach is called to deconfigure a device. It should deallocate
466 * This function is also called when _attach() fails, so it should be
467 * careful not to release resources that were not necessarily
468 * allocated by _attach(). dev->private and dev->subdevices are
469 * deallocated automatically by the core.
471 static int s526_detach(struct comedi_device * dev)
473 printk("comedi%d: s526: remove\n", dev->minor);
476 release_region(dev->iobase, S526_IOSIZE);
481 static int s526_gpct_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
482 struct comedi_insn * insn, unsigned int * data)
484 int i; /* counts the Data */
485 int counter_channel = CR_CHAN(insn->chanspec);
486 unsigned short datalow;
487 unsigned short datahigh;
489 /* Check if (n > 0) */
491 printk("s526: INSN_READ: n should be > 0\n");
494 /* Read the low word first */
495 for (i = 0; i < insn->n; i++) {
496 datalow = inw(ADDR_CHAN_REG(REG_C0L, counter_channel));
497 datahigh = inw(ADDR_CHAN_REG(REG_C0H, counter_channel));
498 data[i] = (int)(datahigh & 0x00FF);
499 data[i] = (data[i] << 16) | (datalow & 0xFFFF);
500 /* printk("s526 GPCT[%d]: %x(0x%04x, 0x%04x)\n", counter_channel, data[i], datahigh, datalow); */
505 static int s526_gpct_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
506 struct comedi_insn * insn, unsigned int * data)
508 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
512 /* printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n", subdev_channel); */
514 for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++) {
515 devpriv->s526_gpct_config[subdev_channel].data[i] =
517 /* printk("data[%d]=%x\n", i, insn->data[i]); */
520 /* Check what type of Counter the user requested, data[0] contains */
521 /* the Application type */
522 switch (insn->data[0]) {
523 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
525 data[0]: Application Type
526 data[1]: Counter Mode Register Value
527 data[2]: Pre-load Register Value
528 data[3]: Conter Control Register
530 printk("s526: GPCT_INSN_CONFIG: Configuring Encoder\n");
531 devpriv->s526_gpct_config[subdev_channel].app =
535 /* Example of Counter Application */
536 /* One-shot (software trigger) */
537 cmReg.reg.coutSource = 0; /* out RCAP */
538 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
539 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */
540 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
541 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
542 cmReg.reg.clockSource = 2; /* Internal */
543 cmReg.reg.countDir = 1; /* Down */
544 cmReg.reg.countDirCtrl = 1; /* Software */
545 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
546 cmReg.reg.preloadRegSel = 0; /* PR0 */
547 cmReg.reg.reserved = 0;
549 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
551 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
552 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
554 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */
555 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */
557 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset RCAP (fires one-shot) */
562 /* Set Counter Mode Register */
563 cmReg.reg.coutSource = 0; /* out RCAP */
564 cmReg.reg.coutPolarity = 0; /* Polarity inverted */
565 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */
566 cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */
567 cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */
568 cmReg.reg.clockSource = 3; /* x4 */
569 cmReg.reg.countDir = 0; /* up */
570 cmReg.reg.countDirCtrl = 0; /* quadrature */
571 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
572 cmReg.reg.preloadRegSel = 0; /* PR0 */
573 cmReg.reg.reserved = 0;
575 /* Set Counter Mode Register */
576 /* printk("s526: Counter Mode register=%x\n", cmReg.value); */
577 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
579 /* Reset the counter if it is software preload */
580 if (cmReg.reg.autoLoadResetRcap == 0) {
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 */
585 cmReg.reg.countDirCtrl = 0; /* 0 quadrature, 1 software control */
587 /* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
588 if (insn->data[1] == GPCT_X2) {
589 cmReg.reg.clockSource = 1;
590 } else if (insn->data[1] == GPCT_X4) {
591 cmReg.reg.clockSource = 2;
593 cmReg.reg.clockSource = 0;
596 /* When to take into account the indexpulse: */
597 if (insn->data[2] == GPCT_IndexPhaseLowLow) {
598 } else if (insn->data[2] == GPCT_IndexPhaseLowHigh) {
599 } else if (insn->data[2] == GPCT_IndexPhaseHighLow) {
600 } else if (insn->data[2] == GPCT_IndexPhaseHighHigh) {
602 /* Take into account the index pulse? */
603 if (insn->data[3] == GPCT_RESET_COUNTER_ON_INDEX)
604 cmReg.reg.autoLoadResetRcap = 4; /* Auto load with INDEX^ */
606 /* Set Counter Mode Register */
607 cmReg.value = (short) (insn->data[1] & 0xFFFF);
608 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
610 /* Load the pre-laod register high word */
611 value = (short) ((insn->data[2] >> 16) & 0xFFFF);
612 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
614 /* Load the pre-laod register low word */
615 value = (short) (insn->data[2] & 0xFFFF);
616 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
618 /* Write the Counter Control Register */
619 if (insn->data[3] != 0) {
620 value = (short) (insn->data[3] & 0xFFFF);
621 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
623 /* Reset the counter if it is software preload */
624 if (cmReg.reg.autoLoadResetRcap == 0) {
625 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */
626 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */
631 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
633 data[0]: Application Type
634 data[1]: Counter Mode Register Value
635 data[2]: Pre-load Register 0 Value
636 data[3]: Pre-load Register 1 Value
637 data[4]: Conter Control Register
639 printk("s526: GPCT_INSN_CONFIG: Configuring SPG\n");
640 devpriv->s526_gpct_config[subdev_channel].app =
641 SinglePulseGeneration;
643 /* Set Counter Mode Register */
644 cmReg.value = (short) (insn->data[1] & 0xFFFF);
645 cmReg.reg.preloadRegSel = 0; /* PR0 */
646 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
648 /* Load the pre-laod register 0 high word */
649 value = (short) ((insn->data[2] >> 16) & 0xFFFF);
650 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
652 /* Load the pre-laod register 0 low word */
653 value = (short) (insn->data[2] & 0xFFFF);
654 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
656 /* Set Counter Mode Register */
657 cmReg.value = (short) (insn->data[1] & 0xFFFF);
658 cmReg.reg.preloadRegSel = 1; /* PR1 */
659 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
661 /* Load the pre-laod register 1 high word */
662 value = (short) ((insn->data[3] >> 16) & 0xFFFF);
663 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
665 /* Load the pre-laod register 1 low word */
666 value = (short) (insn->data[3] & 0xFFFF);
667 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
669 /* Write the Counter Control Register */
670 if (insn->data[3] != 0) {
671 value = (short) (insn->data[3] & 0xFFFF);
672 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
676 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
678 data[0]: Application Type
679 data[1]: Counter Mode Register Value
680 data[2]: Pre-load Register 0 Value
681 data[3]: Pre-load Register 1 Value
682 data[4]: Conter Control Register
684 printk("s526: GPCT_INSN_CONFIG: Configuring PTG\n");
685 devpriv->s526_gpct_config[subdev_channel].app =
686 PulseTrainGeneration;
688 /* Set Counter Mode Register */
689 cmReg.value = (short) (insn->data[1] & 0xFFFF);
690 cmReg.reg.preloadRegSel = 0; /* PR0 */
691 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
693 /* Load the pre-laod register 0 high word */
694 value = (short) ((insn->data[2] >> 16) & 0xFFFF);
695 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
697 /* Load the pre-laod register 0 low word */
698 value = (short) (insn->data[2] & 0xFFFF);
699 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
701 /* Set Counter Mode Register */
702 cmReg.value = (short) (insn->data[1] & 0xFFFF);
703 cmReg.reg.preloadRegSel = 1; /* PR1 */
704 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
706 /* Load the pre-laod register 1 high word */
707 value = (short) ((insn->data[3] >> 16) & 0xFFFF);
708 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
710 /* Load the pre-laod register 1 low word */
711 value = (short) (insn->data[3] & 0xFFFF);
712 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
714 /* Write the Counter Control Register */
715 if (insn->data[3] != 0) {
716 value = (short) (insn->data[3] & 0xFFFF);
717 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
722 printk("s526: unsupported GPCT_insn_config\n");
730 static int s526_gpct_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
731 struct comedi_insn * insn, unsigned int * data)
733 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
736 printk("s526: GPCT_INSN_WRITE on channel %d\n", subdev_channel);
737 cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel));
738 printk("s526: Counter Mode Register: %x\n", cmReg.value);
739 /* Check what Application of Counter this channel is configured for */
740 switch (devpriv->s526_gpct_config[subdev_channel].app) {
741 case PositionMeasurement:
742 printk("S526: INSN_WRITE: PM\n");
743 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
745 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
748 case SinglePulseGeneration:
749 printk("S526: INSN_WRITE: SPG\n");
750 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
752 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
755 case PulseTrainGeneration:
756 /* data[0] contains the PULSE_WIDTH
757 data[1] contains the PULSE_PERIOD
758 @pre PULSE_PERIOD > PULSE_WIDTH > 0
759 The above periods must be expressed as a multiple of the
760 pulse frequency on the selected source
762 printk("S526: INSN_WRITE: PTG\n");
763 if ((insn->data[1] > insn->data[0]) && (insn->data[0] > 0)) {
764 (devpriv->s526_gpct_config[subdev_channel]).data[0] =
766 (devpriv->s526_gpct_config[subdev_channel]).data[1] =
769 printk("%d \t %d\n", insn->data[1], insn->data[2]);
770 printk("s526: INSN_WRITE: PTG: Problem with Pulse params\n");
774 value = (short) ((*data >> 16) & 0xFFFF);
775 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
776 value = (short) (*data & 0xFFFF);
777 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
779 default: /* Impossible */
780 printk("s526: INSN_WRITE: Functionality %d not implemented yet\n", devpriv->s526_gpct_config[subdev_channel].app);
784 /* return the number of samples written */
788 #define ISR_ADC_DONE 0x4
789 static int s526_ai_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
790 struct comedi_insn * insn, unsigned int * data)
792 int result = -EINVAL;
799 /* data[0] : channels was set in relevant bits.
802 /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
803 * enable channels here. The channel should be enabled in the
804 * INSN_READ handler. */
806 /* Enable ADC interrupt */
807 outw(ISR_ADC_DONE, ADDR_REG(REG_IER));
808 /* printk("s526: ADC current value: 0x%04x\n", inw(ADDR_REG(REG_ADC))); */
809 devpriv->s526_ai_config = (data[0] & 0x3FF) << 5;
811 devpriv->s526_ai_config |= 0x8000; /* set the delay */
813 devpriv->s526_ai_config |= 0x0001; /* ADC start bit. */
819 * "instructions" read/write data in "one-shot" or "software-triggered"
822 static int s526_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
823 struct comedi_insn * insn, unsigned int * data)
826 int chan = CR_CHAN(insn->chanspec);
827 unsigned short value;
831 /* Set configured delay, enable channel for this channel only,
832 * select "ADC read" channel, set "ADC start" bit. */
833 value = (devpriv->s526_ai_config & 0x8000) |
834 ((1 << 5) << chan) | (chan << 1) | 0x0001;
836 /* convert n samples */
837 for (n = 0; n < insn->n; n++) {
838 /* trigger conversion */
839 outw(value, ADDR_REG(REG_ADC));
840 /* printk("s526: Wrote 0x%04x to ADC\n", value); */
841 /* printk("s526: ADC reg=0x%04x\n", inw(ADDR_REG(REG_ADC))); */
844 /* wait for conversion to end */
845 for (i = 0; i < TIMEOUT; i++) {
846 status = inw(ADDR_REG(REG_ISR));
847 if (status & ISR_ADC_DONE) {
848 outw(ISR_ADC_DONE, ADDR_REG(REG_ISR));
853 /* rt_printk() should be used instead of printk()
854 * whenever the code can be called from real-time. */
855 rt_printk("s526: ADC(0x%04x) timeout\n",
856 inw(ADDR_REG(REG_ISR)));
861 d = inw(ADDR_REG(REG_ADD));
862 /* printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF)); */
865 data[n] = d ^ 0x8000;
868 /* return the number of samples read/written */
872 static int s526_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
873 struct comedi_insn * insn, unsigned int * data)
876 int chan = CR_CHAN(insn->chanspec);
879 /* printk("s526_ao_winsn\n"); */
881 /* outw(val, dev->iobase + REG_DAC); */
882 outw(val, ADDR_REG(REG_DAC));
884 /* Writing a list of values to an AO channel is probably not
885 * very useful, but that's how the interface is defined. */
886 for (i = 0; i < insn->n; i++) {
887 /* a typical programming sequence */
888 /* outw(data[i], dev->iobase + REG_ADD); write the data to preload register */
889 outw(data[i], ADDR_REG(REG_ADD)); /* write the data to preload register */
890 devpriv->ao_readback[chan] = data[i];
891 /* outw(val + 1, dev->iobase + REG_DAC); starts the D/A conversion. */
892 outw(val + 1, ADDR_REG(REG_DAC)); /* starts the D/A conversion. */
895 /* return the number of samples read/written */
899 /* AO subdevices should have a read insn as well as a write insn.
900 * Usually this means copying a value stored in devpriv. */
901 static int s526_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
902 struct comedi_insn * insn, unsigned int * data)
905 int chan = CR_CHAN(insn->chanspec);
907 for (i = 0; i < insn->n; i++)
908 data[i] = devpriv->ao_readback[chan];
913 /* DIO devices are slightly special. Although it is possible to
914 * implement the insn_read/insn_write interface, it is much more
915 * useful to applications if you implement the insn_bits interface.
916 * This allows packed reading/writing of the DIO channels. The
917 * comedi core can convert between insn_bits and insn_read/write */
918 static int s526_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
919 struct comedi_insn * insn, unsigned int * data)
924 /* The insn data is a mask in data[0] and the new data
925 * in data[1], each channel cooresponding to a bit. */
927 s->state &= ~data[0];
928 s->state |= data[0] & data[1];
929 /* Write out the new digital output lines */
930 outw(s->state, ADDR_REG(REG_DIO));
933 /* on return, data[1] contains the value of the digital
934 * input and output lines. */
935 data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF; /* low 8 bits are the data */
936 /* or we could just return the software copy of the output values if
937 * it was a purely digital output subdevice */
938 /* data[1]=s->state; */
943 static int s526_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
944 struct comedi_insn * insn, unsigned int * data)
946 int chan = CR_CHAN(insn->chanspec);
949 printk("S526 DIO insn_config\n");
954 value = inw(ADDR_REG(REG_DIO));
956 /* The input or output configuration of each digital line is
957 * configured by a special insn_config instruction. chanspec
958 * contains the channel to be changed, and data[0] contains the
959 * value COMEDI_INPUT or COMEDI_OUTPUT. */
961 if (data[0] == COMEDI_OUTPUT) {
962 value |= 1 << (chan + 10); /* bit 10/11 set the group 1/2's mode */
963 s->io_bits |= (0xF << chan);
965 value &= ~(1 << (chan + 10)); /* 1 is output, 0 is input. */
966 s->io_bits &= ~(0xF << chan);
968 outw(value, ADDR_REG(REG_DIO));
974 * A convenient macro that defines init_module() and cleanup_module(),
977 COMEDI_INITCLEANUP(driver_s526);