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)
234 * The struct comedi_driver structure tells the Comedi core module
235 * which functions to call to configure/deconfigure (attach/detach)
236 * the board, and also about the kernel module that contains
239 static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it);
240 static int s526_detach(struct comedi_device *dev);
241 static struct comedi_driver driver_s526 = {
242 .driver_name = "s526",
243 .module = THIS_MODULE,
244 .attach = s526_attach,
245 .detach = s526_detach,
246 /* It is not necessary to implement the following members if you are
247 * writing a driver for a ISA PnP or PCI card */
248 /* Most drivers will support multiple types of boards by
249 * having an array of board structures. These were defined
250 * in s526_boards[] above. Note that the element 'name'
251 * was first in the structure -- Comedi uses this fact to
252 * extract the name of the board without knowing any details
253 * about the structure except for its length.
254 * When a device is attached (by comedi_config), the name
255 * of the device is given to Comedi, and Comedi tries to
256 * match it by going through the list of board names. If
257 * there is a match, the address of the pointer is put
258 * into dev->board_ptr and driver->attach() is called.
260 * Note that these are not necessary if you can determine
261 * the type of board in software. ISA PnP, PCI, and PCMCIA
262 * devices are such boards.
264 .board_name = &s526_boards[0].name,
265 .offset = sizeof(struct s526_board),
266 .num_names = ARRAY_SIZE(s526_boards),
269 static int s526_gpct_rinsn(struct comedi_device *dev,
270 struct comedi_subdevice *s, struct comedi_insn *insn,
272 static int s526_gpct_insn_config(struct comedi_device *dev,
273 struct comedi_subdevice *s,
274 struct comedi_insn *insn, unsigned int *data);
275 static int s526_gpct_winsn(struct comedi_device *dev,
276 struct comedi_subdevice *s, struct comedi_insn *insn,
278 static int s526_ai_insn_config(struct comedi_device *dev,
279 struct comedi_subdevice *s,
280 struct comedi_insn *insn, unsigned int *data);
281 static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
282 struct comedi_insn *insn, unsigned int *data);
283 static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
284 struct comedi_insn *insn, unsigned int *data);
285 static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
286 struct comedi_insn *insn, unsigned int *data);
287 static int s526_dio_insn_bits(struct comedi_device *dev,
288 struct comedi_subdevice *s,
289 struct comedi_insn *insn, unsigned int *data);
290 static int s526_dio_insn_config(struct comedi_device *dev,
291 struct comedi_subdevice *s,
292 struct comedi_insn *insn, unsigned int *data);
295 * Attach is called by the Comedi core to configure the driver
296 * for a particular board. If you specified a board_name array
297 * in the driver structure, dev->board_ptr contains that
300 static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
302 struct comedi_subdevice *s;
306 /* int subdev_channel = 0; */
309 printk(KERN_INFO "comedi%d: s526: ", dev->minor);
311 iobase = it->options[0];
312 if (!iobase || !request_region(iobase, S526_IOSIZE, thisboard->name)) {
313 comedi_error(dev, "I/O port conflict");
316 dev->iobase = iobase;
318 printk("iobase=0x%lx\n", dev->iobase);
320 /*** make it a little quieter, exw, 8/29/06
321 for (i = 0; i < S526_NUM_PORTS; i++) {
322 printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]),
323 inw(ADDR_REG(s526_ports[i])));
328 * Initialize dev->board_name. Note that we can use the "thisboard"
329 * macro now, since we just initialized it in the last line.
331 dev->board_ptr = &s526_boards[0];
333 dev->board_name = thisboard->name;
336 * Allocate the private structure area. alloc_private() is a
337 * convenient macro defined in comedidev.h.
339 if (alloc_private(dev, sizeof(struct s526_private)) < 0)
343 * Allocate the subdevice structures. alloc_subdevice() is a
344 * convenient macro defined in comedidev.h.
346 dev->n_subdevices = 4;
347 if (alloc_subdevices(dev, dev->n_subdevices) < 0)
350 s = dev->subdevices + 0;
351 /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
352 s->type = COMEDI_SUBD_COUNTER;
353 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
354 /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */
355 s->n_chan = thisboard->gpct_chans;
356 s->maxdata = 0x00ffffff; /* 24 bit counter */
357 s->insn_read = s526_gpct_rinsn;
358 s->insn_config = s526_gpct_insn_config;
359 s->insn_write = s526_gpct_winsn;
361 /* Command are not implemented yet, however they are necessary to
362 allocate the necessary memory for the comedi_async struct (used
363 to trigger the GPCT in case of pulsegenerator function */
364 /* s->do_cmd = s526_gpct_cmd; */
365 /* s->do_cmdtest = s526_gpct_cmdtest; */
366 /* s->cancel = s526_gpct_cancel; */
368 s = dev->subdevices + 1;
369 /* dev->read_subdev=s; */
370 /* analog input subdevice */
371 s->type = COMEDI_SUBD_AI;
372 /* we support differential */
373 s->subdev_flags = SDF_READABLE | SDF_DIFF;
374 /* channels 0 to 7 are the regular differential inputs */
375 /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
378 s->range_table = &range_bipolar10;
379 s->len_chanlist = 16; /* This is the maximum chanlist length that
380 the board can handle */
381 s->insn_read = s526_ai_rinsn;
382 s->insn_config = s526_ai_insn_config;
384 s = dev->subdevices + 2;
385 /* analog output subdevice */
386 s->type = COMEDI_SUBD_AO;
387 s->subdev_flags = SDF_WRITABLE;
390 s->range_table = &range_bipolar10;
391 s->insn_write = s526_ao_winsn;
392 s->insn_read = s526_ao_rinsn;
394 s = dev->subdevices + 3;
395 /* digital i/o subdevice */
396 if (thisboard->have_dio) {
397 s->type = COMEDI_SUBD_DIO;
398 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
401 s->range_table = &range_digital;
402 s->insn_bits = s526_dio_insn_bits;
403 s->insn_config = s526_dio_insn_config;
405 s->type = COMEDI_SUBD_UNUSED;
408 printk(KERN_INFO "attached\n");
413 /* Example of Counter Application */
414 /* One-shot (software trigger) */
415 cmReg.reg.coutSource = 0; /* out RCAP */
416 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
417 cmReg.reg.autoLoadResetRcap = 1;/* Auto load 0:disabled, 1:enabled */
418 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
419 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
420 cmReg.reg.clockSource = 2; /* Internal */
421 cmReg.reg.countDir = 1; /* Down */
422 cmReg.reg.countDirCtrl = 1; /* Software */
423 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
424 cmReg.reg.preloadRegSel = 0; /* PR0 */
425 cmReg.reg.reserved = 0;
427 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
429 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
430 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
432 /* Reset the counter */
433 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
434 /* Load the counter from PR0 */
435 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
436 /* Reset RCAP (fires one-shot) */
437 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel));
441 /* Set Counter Mode Register */
442 cmReg.reg.coutSource = 0; /* out RCAP */
443 cmReg.reg.coutPolarity = 0; /* Polarity inverted */
444 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */
445 cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */
446 cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */
447 cmReg.reg.clockSource = 3; /* x4 */
448 cmReg.reg.countDir = 0; /* up */
449 cmReg.reg.countDirCtrl = 0; /* quadrature */
450 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
451 cmReg.reg.preloadRegSel = 0; /* PR0 */
452 cmReg.reg.reserved = 0;
455 printk(KERN_INFO "Mode reg=0x%04x, 0x%04lx\n",
456 cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
457 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
459 printk(KERN_INFO "Read back mode reg=0x%04x\n",
460 inw(ADDR_CHAN_REG(REG_C0M, n)));
462 /* Load the pre-load register high word */
463 /* value = (short) (0x55); */
464 /* outw(value, ADDR_CHAN_REG(REG_C0H, n)); */
466 /* Load the pre-load register low word */
467 /* value = (short)(0xaa55); */
468 /* outw(value, ADDR_CHAN_REG(REG_C0L, n)); */
470 /* Write the Counter Control Register */
471 /* outw(value, ADDR_CHAN_REG(REG_C0C, 0)); */
473 /* Reset the counter if it is software preload */
474 if (cmReg.reg.autoLoadResetRcap == 0) {
475 /* Reset the counter */
476 outw(0x8000, ADDR_CHAN_REG(REG_C0C, n));
477 /* Load the counter from PR0 */
478 outw(0x4000, ADDR_CHAN_REG(REG_C0C, n));
481 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
483 printk(KERN_INFO "Read back mode reg=0x%04x\n",
484 inw(ADDR_CHAN_REG(REG_C0M, n)));
487 printk(KERN_INFO "Current registres:\n");
489 for (i = 0; i < S526_NUM_PORTS; i++) {
490 printk(KERN_INFO "0x%02lx: 0x%04x\n",
491 ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i])));
497 * _detach is called to deconfigure a device. It should deallocate
499 * This function is also called when _attach() fails, so it should be
500 * careful not to release resources that were not necessarily
501 * allocated by _attach(). dev->private and dev->subdevices are
502 * deallocated automatically by the core.
504 static int s526_detach(struct comedi_device *dev)
506 printk(KERN_INFO "comedi%d: s526: remove\n", dev->minor);
509 release_region(dev->iobase, S526_IOSIZE);
514 static int s526_gpct_rinsn(struct comedi_device *dev,
515 struct comedi_subdevice *s, struct comedi_insn *insn,
518 int i; /* counts the Data */
519 int counter_channel = CR_CHAN(insn->chanspec);
520 unsigned short datalow;
521 unsigned short datahigh;
523 /* Check if (n > 0) */
525 printk(KERN_ERR "s526: INSN_READ: n should be > 0\n");
528 /* Read the low word first */
529 for (i = 0; i < insn->n; i++) {
530 datalow = inw(ADDR_CHAN_REG(REG_C0L, counter_channel));
531 datahigh = inw(ADDR_CHAN_REG(REG_C0H, counter_channel));
532 data[i] = (int)(datahigh & 0x00FF);
533 data[i] = (data[i] << 16) | (datalow & 0xFFFF);
534 /* printk("s526 GPCT[%d]: %x(0x%04x, 0x%04x)\n",
535 counter_channel, data[i], datahigh, datalow); */
540 static int s526_gpct_insn_config(struct comedi_device *dev,
541 struct comedi_subdevice *s,
542 struct comedi_insn *insn, unsigned int *data)
544 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
549 /* printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n",
552 for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++) {
553 devpriv->s526_gpct_config[subdev_channel].data[i] =
555 /* printk("data[%d]=%x\n", i, insn->data[i]); */
558 /* Check what type of Counter the user requested, data[0] contains */
559 /* the Application type */
560 switch (insn->data[0]) {
561 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
563 data[0]: Application Type
564 data[1]: Counter Mode Register Value
565 data[2]: Pre-load Register Value
566 data[3]: Conter Control Register
568 printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring Encoder\n");
569 devpriv->s526_gpct_config[subdev_channel].app =
573 /* Example of Counter Application */
574 /* One-shot (software trigger) */
575 cmReg.reg.coutSource = 0; /* out RCAP */
576 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
577 cmReg.reg.autoLoadResetRcap = 0;/* Auto load disabled */
578 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
579 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
580 cmReg.reg.clockSource = 2; /* Internal */
581 cmReg.reg.countDir = 1; /* Down */
582 cmReg.reg.countDirCtrl = 1; /* Software */
583 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
584 cmReg.reg.preloadRegSel = 0; /* PR0 */
585 cmReg.reg.reserved = 0;
587 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
589 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
590 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
592 /* Reset the counter */
593 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
594 /* Load the counter from PR0 */
595 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
597 /* Reset RCAP (fires one-shot) */
598 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel));
603 /* Set Counter Mode Register */
604 cmReg.value = insn->data[1] & 0xFFFF;
606 /* printk("s526: Counter Mode register=%x\n", cmReg.value); */
607 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
609 /* Reset the counter if it is software preload */
610 if (cmReg.reg.autoLoadResetRcap == 0) {
611 /* Reset the counter */
612 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
613 /* Load the counter from PR0
614 * outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
618 /* 0 quadrature, 1 software control */
619 cmReg.reg.countDirCtrl = 0;
621 /* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
622 if (insn->data[1] == GPCT_X2)
623 cmReg.reg.clockSource = 1;
624 else if (insn->data[1] == GPCT_X4)
625 cmReg.reg.clockSource = 2;
627 cmReg.reg.clockSource = 0;
629 /* When to take into account the indexpulse: */
630 /*if (insn->data[2] == GPCT_IndexPhaseLowLow) {
631 } else if (insn->data[2] == GPCT_IndexPhaseLowHigh) {
632 } else if (insn->data[2] == GPCT_IndexPhaseHighLow) {
633 } else if (insn->data[2] == GPCT_IndexPhaseHighHigh) {
635 /* Take into account the index pulse? */
636 if (insn->data[3] == GPCT_RESET_COUNTER_ON_INDEX)
637 /* Auto load with INDEX^ */
638 cmReg.reg.autoLoadResetRcap = 4;
640 /* Set Counter Mode Register */
641 cmReg.value = (short)(insn->data[1] & 0xFFFF);
642 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
644 /* Load the pre-load register high word */
645 value = (short)((insn->data[2] >> 16) & 0xFFFF);
646 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
648 /* Load the pre-load register low word */
649 value = (short)(insn->data[2] & 0xFFFF);
650 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
652 /* Write the Counter Control Register */
653 if (insn->data[3] != 0) {
654 value = (short)(insn->data[3] & 0xFFFF);
655 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
657 /* Reset the counter if it is software preload */
658 if (cmReg.reg.autoLoadResetRcap == 0) {
659 /* Reset the counter */
660 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
661 /* Load the counter from PR0 */
662 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
667 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
669 data[0]: Application Type
670 data[1]: Counter Mode Register Value
671 data[2]: Pre-load Register 0 Value
672 data[3]: Pre-load Register 1 Value
673 data[4]: Conter Control Register
675 printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring SPG\n");
676 devpriv->s526_gpct_config[subdev_channel].app =
677 SinglePulseGeneration;
679 /* Set Counter Mode Register */
680 cmReg.value = (short)(insn->data[1] & 0xFFFF);
681 cmReg.reg.preloadRegSel = 0; /* PR0 */
682 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
684 /* Load the pre-load register 0 high word */
685 value = (short)((insn->data[2] >> 16) & 0xFFFF);
686 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
688 /* Load the pre-load register 0 low word */
689 value = (short)(insn->data[2] & 0xFFFF);
690 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
692 /* Set Counter Mode Register */
693 cmReg.value = (short)(insn->data[1] & 0xFFFF);
694 cmReg.reg.preloadRegSel = 1; /* PR1 */
695 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
697 /* Load the pre-load register 1 high word */
698 value = (short)((insn->data[3] >> 16) & 0xFFFF);
699 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
701 /* Load the pre-load register 1 low word */
702 value = (short)(insn->data[3] & 0xFFFF);
703 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
705 /* Write the Counter Control Register */
706 if (insn->data[4] != 0) {
707 value = (short)(insn->data[4] & 0xFFFF);
708 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
712 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
714 data[0]: Application Type
715 data[1]: Counter Mode Register Value
716 data[2]: Pre-load Register 0 Value
717 data[3]: Pre-load Register 1 Value
718 data[4]: Conter Control Register
720 printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring PTG\n");
721 devpriv->s526_gpct_config[subdev_channel].app =
722 PulseTrainGeneration;
724 /* Set Counter Mode Register */
725 cmReg.value = (short)(insn->data[1] & 0xFFFF);
726 cmReg.reg.preloadRegSel = 0; /* PR0 */
727 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
729 /* Load the pre-load register 0 high word */
730 value = (short)((insn->data[2] >> 16) & 0xFFFF);
731 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
733 /* Load the pre-load register 0 low word */
734 value = (short)(insn->data[2] & 0xFFFF);
735 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
737 /* Set Counter Mode Register */
738 cmReg.value = (short)(insn->data[1] & 0xFFFF);
739 cmReg.reg.preloadRegSel = 1; /* PR1 */
740 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
742 /* Load the pre-load register 1 high word */
743 value = (short)((insn->data[3] >> 16) & 0xFFFF);
744 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
746 /* Load the pre-load register 1 low word */
747 value = (short)(insn->data[3] & 0xFFFF);
748 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
750 /* Write the Counter Control Register */
751 if (insn->data[4] != 0) {
752 value = (short)(insn->data[4] & 0xFFFF);
753 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
758 printk(KERN_ERR "s526: unsupported GPCT_insn_config\n");
766 static int s526_gpct_winsn(struct comedi_device *dev,
767 struct comedi_subdevice *s, struct comedi_insn *insn,
770 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
774 printk(KERN_INFO "s526: GPCT_INSN_WRITE on channel %d\n",
776 cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel));
777 printk(KERN_INFO "s526: Counter Mode Register: %x\n", cmReg.value);
778 /* Check what Application of Counter this channel is configured for */
779 switch (devpriv->s526_gpct_config[subdev_channel].app) {
780 case PositionMeasurement:
781 printk(KERN_INFO "S526: INSN_WRITE: PM\n");
782 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
784 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
787 case SinglePulseGeneration:
788 printk(KERN_INFO "S526: INSN_WRITE: SPG\n");
789 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
791 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
794 case PulseTrainGeneration:
795 /* data[0] contains the PULSE_WIDTH
796 data[1] contains the PULSE_PERIOD
797 @pre PULSE_PERIOD > PULSE_WIDTH > 0
798 The above periods must be expressed as a multiple of the
799 pulse frequency on the selected source
801 printk(KERN_INFO "S526: INSN_WRITE: PTG\n");
802 if ((insn->data[1] > insn->data[0]) && (insn->data[0] > 0)) {
803 (devpriv->s526_gpct_config[subdev_channel]).data[0] =
805 (devpriv->s526_gpct_config[subdev_channel]).data[1] =
808 printk(KERN_ERR "s526: INSN_WRITE: PTG: Problem with Pulse params -> %d %d\n",
809 insn->data[0], insn->data[1]);
813 value = (short)((*data >> 16) & 0xFFFF);
814 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
815 value = (short)(*data & 0xFFFF);
816 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
818 default: /* Impossible */
820 ("s526: INSN_WRITE: Functionality %d not implemented yet\n",
821 devpriv->s526_gpct_config[subdev_channel].app);
825 /* return the number of samples written */
829 #define ISR_ADC_DONE 0x4
830 static int s526_ai_insn_config(struct comedi_device *dev,
831 struct comedi_subdevice *s,
832 struct comedi_insn *insn, unsigned int *data)
834 int result = -EINVAL;
841 /* data[0] : channels was set in relevant bits.
844 /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
845 * enable channels here. The channel should be enabled in the
846 * INSN_READ handler. */
848 /* Enable ADC interrupt */
849 outw(ISR_ADC_DONE, ADDR_REG(REG_IER));
850 /* printk("s526: ADC current value: 0x%04x\n", inw(ADDR_REG(REG_ADC))); */
851 devpriv->s526_ai_config = (data[0] & 0x3FF) << 5;
853 devpriv->s526_ai_config |= 0x8000; /* set the delay */
855 devpriv->s526_ai_config |= 0x0001; /* ADC start bit. */
861 * "instructions" read/write data in "one-shot" or "software-triggered"
864 static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
865 struct comedi_insn *insn, unsigned int *data)
868 int chan = CR_CHAN(insn->chanspec);
869 unsigned short value;
873 /* Set configured delay, enable channel for this channel only,
874 * select "ADC read" channel, set "ADC start" bit. */
875 value = (devpriv->s526_ai_config & 0x8000) |
876 ((1 << 5) << chan) | (chan << 1) | 0x0001;
878 /* convert n samples */
879 for (n = 0; n < insn->n; n++) {
880 /* trigger conversion */
881 outw(value, ADDR_REG(REG_ADC));
882 /* printk("s526: Wrote 0x%04x to ADC\n", value); */
883 /* printk("s526: ADC reg=0x%04x\n", inw(ADDR_REG(REG_ADC))); */
886 /* wait for conversion to end */
887 for (i = 0; i < TIMEOUT; i++) {
888 status = inw(ADDR_REG(REG_ISR));
889 if (status & ISR_ADC_DONE) {
890 outw(ISR_ADC_DONE, ADDR_REG(REG_ISR));
895 /* printk() should be used instead of printk()
896 * whenever the code can be called from real-time. */
897 printk(KERN_ERR "s526: ADC(0x%04x) timeout\n",
898 inw(ADDR_REG(REG_ISR)));
903 d = inw(ADDR_REG(REG_ADD));
904 /* printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF)); */
907 data[n] = d ^ 0x8000;
910 /* return the number of samples read/written */
914 static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
915 struct comedi_insn *insn, unsigned int *data)
918 int chan = CR_CHAN(insn->chanspec);
921 /* printk("s526_ao_winsn\n"); */
923 /* outw(val, dev->iobase + REG_DAC); */
924 outw(val, ADDR_REG(REG_DAC));
926 /* Writing a list of values to an AO channel is probably not
927 * very useful, but that's how the interface is defined. */
928 for (i = 0; i < insn->n; i++) {
929 /* a typical programming sequence */
930 /* write the data to preload register
931 * outw(data[i], dev->iobase + REG_ADD);
933 /* write the data to preload register */
934 outw(data[i], ADDR_REG(REG_ADD));
935 devpriv->ao_readback[chan] = data[i];
936 /* outw(val + 1, dev->iobase + REG_DAC); starts the D/A conversion. */
937 outw(val + 1, ADDR_REG(REG_DAC)); /*starts the D/A conversion.*/
940 /* return the number of samples read/written */
944 /* AO subdevices should have a read insn as well as a write insn.
945 * Usually this means copying a value stored in devpriv. */
946 static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
947 struct comedi_insn *insn, unsigned int *data)
950 int chan = CR_CHAN(insn->chanspec);
952 for (i = 0; i < insn->n; i++)
953 data[i] = devpriv->ao_readback[chan];
958 /* DIO devices are slightly special. Although it is possible to
959 * implement the insn_read/insn_write interface, it is much more
960 * useful to applications if you implement the insn_bits interface.
961 * This allows packed reading/writing of the DIO channels. The
962 * comedi core can convert between insn_bits and insn_read/write */
963 static int s526_dio_insn_bits(struct comedi_device *dev,
964 struct comedi_subdevice *s,
965 struct comedi_insn *insn, unsigned int *data)
970 /* The insn data is a mask in data[0] and the new data
971 * in data[1], each channel cooresponding to a bit. */
973 s->state &= ~data[0];
974 s->state |= data[0] & data[1];
975 /* Write out the new digital output lines */
976 outw(s->state, ADDR_REG(REG_DIO));
979 /* on return, data[1] contains the value of the digital
980 * input and output lines. */
981 data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF; /* low 8 bits are the data */
982 /* or we could just return the software copy of the output values if
983 * it was a purely digital output subdevice */
984 /* data[1]=s->state & 0xFF; */
989 static int s526_dio_insn_config(struct comedi_device *dev,
990 struct comedi_subdevice *s,
991 struct comedi_insn *insn, unsigned int *data)
993 int chan = CR_CHAN(insn->chanspec);
996 printk(KERN_INFO "S526 DIO insn_config\n");
998 /* The input or output configuration of each digital line is
999 * configured by a special insn_config instruction. chanspec
1000 * contains the channel to be changed, and data[0] contains the
1001 * value COMEDI_INPUT or COMEDI_OUTPUT. */
1004 mask = 0xF << (group << 2);
1006 case INSN_CONFIG_DIO_OUTPUT:
1007 /* bit 10/11 set the group 1/2's mode */
1008 s->state |= 1 << (group + 10);
1011 case INSN_CONFIG_DIO_INPUT:
1012 s->state &= ~(1 << (group + 10)); /* 1 is output, 0 is input. */
1013 s->io_bits &= ~mask;
1015 case INSN_CONFIG_DIO_QUERY:
1016 data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
1021 outw(s->state, ADDR_REG(REG_DIO));
1027 * A convenient macro that defines init_module() and cleanup_module(),
1030 static int __init driver_s526_init_module(void)
1032 return comedi_driver_register(&driver_s526);
1035 static void __exit driver_s526_cleanup_module(void)
1037 comedi_driver_unregister(&driver_s526);
1040 module_init(driver_s526_init_module);
1041 module_exit(driver_s526_cleanup_module);
1043 MODULE_AUTHOR("Comedi http://www.comedi.org");
1044 MODULE_DESCRIPTION("Comedi low-level driver");
1045 MODULE_LICENSE("GPL");