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 = {
223 .driver_name = "s526",
224 .module = THIS_MODULE,
225 .attach = s526_attach,
226 .detach = s526_detach,
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 = ARRAY_SIZE(s526_boards),
250 static int s526_gpct_rinsn(struct comedi_device *dev,
251 struct comedi_subdevice *s, struct comedi_insn *insn,
253 static int s526_gpct_insn_config(struct comedi_device *dev,
254 struct comedi_subdevice *s,
255 struct comedi_insn *insn, unsigned int *data);
256 static int s526_gpct_winsn(struct comedi_device *dev,
257 struct comedi_subdevice *s, struct comedi_insn *insn,
259 static int s526_ai_insn_config(struct comedi_device *dev,
260 struct comedi_subdevice *s,
261 struct comedi_insn *insn, unsigned int *data);
262 static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
263 struct comedi_insn *insn, unsigned int *data);
264 static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
265 struct comedi_insn *insn, unsigned int *data);
266 static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
267 struct comedi_insn *insn, unsigned int *data);
268 static int s526_dio_insn_bits(struct comedi_device *dev,
269 struct comedi_subdevice *s,
270 struct comedi_insn *insn, unsigned int *data);
271 static int s526_dio_insn_config(struct comedi_device *dev,
272 struct comedi_subdevice *s,
273 struct comedi_insn *insn, unsigned int *data);
276 * Attach is called by the Comedi core to configure the driver
277 * for a particular board. If you specified a board_name array
278 * in the driver structure, dev->board_ptr contains that
281 static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
283 struct comedi_subdevice *s;
287 /* int subdev_channel = 0; */
289 printk("comedi%d: s526: ", dev->minor);
291 iobase = it->options[0];
292 if (!iobase || !request_region(iobase, S526_IOSIZE, thisboard->name)) {
293 comedi_error(dev, "I/O port conflict");
296 dev->iobase = iobase;
298 printk("iobase=0x%lx\n", dev->iobase);
300 /*** make it a little quieter, exw, 8/29/06
301 for (i = 0; i < S526_NUM_PORTS; i++) {
302 printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i])));
307 * Initialize dev->board_name. Note that we can use the "thisboard"
308 * macro now, since we just initialized it in the last line.
310 dev->board_ptr = &s526_boards[0];
312 dev->board_name = thisboard->name;
315 * Allocate the private structure area. alloc_private() is a
316 * convenient macro defined in comedidev.h.
318 if (alloc_private(dev, sizeof(struct s526_private)) < 0)
322 * Allocate the subdevice structures. alloc_subdevice() is a
323 * convenient macro defined in comedidev.h.
325 dev->n_subdevices = 4;
326 if (alloc_subdevices(dev, dev->n_subdevices) < 0)
329 s = dev->subdevices + 0;
330 /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
331 s->type = COMEDI_SUBD_COUNTER;
332 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
333 /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */
334 s->n_chan = thisboard->gpct_chans;
335 s->maxdata = 0x00ffffff; /* 24 bit counter */
336 s->insn_read = s526_gpct_rinsn;
337 s->insn_config = s526_gpct_insn_config;
338 s->insn_write = s526_gpct_winsn;
340 /* Command are not implemented yet, however they are necessary to
341 allocate the necessary memory for the comedi_async struct (used
342 to trigger the GPCT in case of pulsegenerator function */
343 /* s->do_cmd = s526_gpct_cmd; */
344 /* s->do_cmdtest = s526_gpct_cmdtest; */
345 /* s->cancel = s526_gpct_cancel; */
347 s = dev->subdevices + 1;
348 /* dev->read_subdev=s; */
349 /* analog input subdevice */
350 s->type = COMEDI_SUBD_AI;
351 /* we support differential */
352 s->subdev_flags = SDF_READABLE | SDF_DIFF;
353 /* channels 0 to 7 are the regular differential inputs */
354 /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
357 s->range_table = &range_bipolar10;
358 s->len_chanlist = 16; /* This is the maximum chanlist length that
359 the board can handle */
360 s->insn_read = s526_ai_rinsn;
361 s->insn_config = s526_ai_insn_config;
363 s = dev->subdevices + 2;
364 /* analog output subdevice */
365 s->type = COMEDI_SUBD_AO;
366 s->subdev_flags = SDF_WRITABLE;
369 s->range_table = &range_bipolar10;
370 s->insn_write = s526_ao_winsn;
371 s->insn_read = s526_ao_rinsn;
373 s = dev->subdevices + 3;
374 /* digital i/o subdevice */
375 if (thisboard->have_dio) {
376 s->type = COMEDI_SUBD_DIO;
377 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
380 s->range_table = &range_digital;
381 s->insn_bits = s526_dio_insn_bits;
382 s->insn_config = s526_dio_insn_config;
384 s->type = COMEDI_SUBD_UNUSED;
387 printk("attached\n");
392 /* Example of Counter Application */
393 /* One-shot (software trigger) */
394 cmReg.reg.coutSource = 0; /* out RCAP */
395 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
396 cmReg.reg.autoLoadResetRcap = 1; /* Auto load 0:disabled, 1:enabled */
397 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
398 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
399 cmReg.reg.clockSource = 2; /* Internal */
400 cmReg.reg.countDir = 1; /* Down */
401 cmReg.reg.countDirCtrl = 1; /* Software */
402 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
403 cmReg.reg.preloadRegSel = 0; /* PR0 */
404 cmReg.reg.reserved = 0;
406 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
408 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
409 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
411 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */
412 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */
414 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset RCAP (fires one-shot) */
418 /* Set Counter Mode Register */
419 cmReg.reg.coutSource = 0; /* out RCAP */
420 cmReg.reg.coutPolarity = 0; /* Polarity inverted */
421 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */
422 cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */
423 cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */
424 cmReg.reg.clockSource = 3; /* x4 */
425 cmReg.reg.countDir = 0; /* up */
426 cmReg.reg.countDirCtrl = 0; /* quadrature */
427 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
428 cmReg.reg.preloadRegSel = 0; /* PR0 */
429 cmReg.reg.reserved = 0;
432 printk("Mode reg=0x%04x, 0x%04lx\n", cmReg.value, ADDR_CHAN_REG(REG_C0M,
434 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
436 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n)));
438 /* Load the pre-laod register high word */
439 /* value = (short) (0x55); */
440 /* outw(value, ADDR_CHAN_REG(REG_C0H, n)); */
442 /* Load the pre-laod register low word */
443 /* value = (short)(0xaa55); */
444 /* outw(value, ADDR_CHAN_REG(REG_C0L, n)); */
446 /* Write the Counter Control Register */
447 /* outw(value, ADDR_CHAN_REG(REG_C0C, 0)); */
449 /* Reset the counter if it is software preload */
450 if (cmReg.reg.autoLoadResetRcap == 0) {
451 outw(0x8000, ADDR_CHAN_REG(REG_C0C, n)); /* Reset the counter */
452 outw(0x4000, ADDR_CHAN_REG(REG_C0C, n)); /* Load the counter from PR0 */
455 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
457 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n)));
460 printk("Current registres:\n");
462 for (i = 0; i < S526_NUM_PORTS; i++) {
463 printk("0x%02lx: 0x%04x\n", ADDR_REG(s526_ports[i]),
464 inw(ADDR_REG(s526_ports[i])));
470 * _detach is called to deconfigure a device. It should deallocate
472 * This function is also called when _attach() fails, so it should be
473 * careful not to release resources that were not necessarily
474 * allocated by _attach(). dev->private and dev->subdevices are
475 * deallocated automatically by the core.
477 static int s526_detach(struct comedi_device *dev)
479 printk("comedi%d: s526: remove\n", dev->minor);
482 release_region(dev->iobase, S526_IOSIZE);
487 static int s526_gpct_rinsn(struct comedi_device *dev,
488 struct comedi_subdevice *s, struct comedi_insn *insn,
491 int i; /* counts the Data */
492 int counter_channel = CR_CHAN(insn->chanspec);
493 unsigned short datalow;
494 unsigned short datahigh;
496 /* Check if (n > 0) */
498 printk("s526: INSN_READ: n should be > 0\n");
501 /* Read the low word first */
502 for (i = 0; i < insn->n; i++) {
503 datalow = inw(ADDR_CHAN_REG(REG_C0L, counter_channel));
504 datahigh = inw(ADDR_CHAN_REG(REG_C0H, counter_channel));
505 data[i] = (int)(datahigh & 0x00FF);
506 data[i] = (data[i] << 16) | (datalow & 0xFFFF);
507 /* printk("s526 GPCT[%d]: %x(0x%04x, 0x%04x)\n", counter_channel, data[i], datahigh, datalow); */
512 static int s526_gpct_insn_config(struct comedi_device *dev,
513 struct comedi_subdevice *s,
514 struct comedi_insn *insn, unsigned int *data)
516 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
520 /* printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n", subdev_channel); */
522 for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++) {
523 devpriv->s526_gpct_config[subdev_channel].data[i] =
525 /* printk("data[%d]=%x\n", i, insn->data[i]); */
528 /* Check what type of Counter the user requested, data[0] contains */
529 /* the Application type */
530 switch (insn->data[0]) {
531 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
533 data[0]: Application Type
534 data[1]: Counter Mode Register Value
535 data[2]: Pre-load Register Value
536 data[3]: Conter Control Register
538 printk("s526: GPCT_INSN_CONFIG: Configuring Encoder\n");
539 devpriv->s526_gpct_config[subdev_channel].app =
543 /* Example of Counter Application */
544 /* One-shot (software trigger) */
545 cmReg.reg.coutSource = 0; /* out RCAP */
546 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
547 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */
548 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
549 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
550 cmReg.reg.clockSource = 2; /* Internal */
551 cmReg.reg.countDir = 1; /* Down */
552 cmReg.reg.countDirCtrl = 1; /* Software */
553 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
554 cmReg.reg.preloadRegSel = 0; /* PR0 */
555 cmReg.reg.reserved = 0;
557 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
559 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
560 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
562 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */
563 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */
565 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset RCAP (fires one-shot) */
570 /* Set Counter Mode Register */
571 cmReg.reg.coutSource = 0; /* out RCAP */
572 cmReg.reg.coutPolarity = 0; /* Polarity inverted */
573 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */
574 cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */
575 cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */
576 cmReg.reg.clockSource = 3; /* x4 */
577 cmReg.reg.countDir = 0; /* up */
578 cmReg.reg.countDirCtrl = 0; /* quadrature */
579 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
580 cmReg.reg.preloadRegSel = 0; /* PR0 */
581 cmReg.reg.reserved = 0;
583 /* Set Counter Mode Register */
584 /* printk("s526: Counter Mode register=%x\n", cmReg.value); */
585 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
587 /* Reset the counter if it is software preload */
588 if (cmReg.reg.autoLoadResetRcap == 0) {
589 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */
590 /* outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); Load the counter from PR0 */
593 cmReg.reg.countDirCtrl = 0; /* 0 quadrature, 1 software control */
595 /* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
596 if (insn->data[1] == GPCT_X2) {
597 cmReg.reg.clockSource = 1;
598 } else if (insn->data[1] == GPCT_X4) {
599 cmReg.reg.clockSource = 2;
601 cmReg.reg.clockSource = 0;
604 /* When to take into account the indexpulse: */
605 if (insn->data[2] == GPCT_IndexPhaseLowLow) {
606 } else if (insn->data[2] == GPCT_IndexPhaseLowHigh) {
607 } else if (insn->data[2] == GPCT_IndexPhaseHighLow) {
608 } else if (insn->data[2] == GPCT_IndexPhaseHighHigh) {
610 /* Take into account the index pulse? */
611 if (insn->data[3] == GPCT_RESET_COUNTER_ON_INDEX)
612 cmReg.reg.autoLoadResetRcap = 4; /* Auto load with INDEX^ */
614 /* Set Counter Mode Register */
615 cmReg.value = (short)(insn->data[1] & 0xFFFF);
616 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
618 /* Load the pre-laod register high word */
619 value = (short)((insn->data[2] >> 16) & 0xFFFF);
620 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
622 /* Load the pre-laod register low word */
623 value = (short)(insn->data[2] & 0xFFFF);
624 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
626 /* Write the Counter Control Register */
627 if (insn->data[3] != 0) {
628 value = (short)(insn->data[3] & 0xFFFF);
629 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
631 /* Reset the counter if it is software preload */
632 if (cmReg.reg.autoLoadResetRcap == 0) {
633 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */
634 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */
639 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
641 data[0]: Application Type
642 data[1]: Counter Mode Register Value
643 data[2]: Pre-load Register 0 Value
644 data[3]: Pre-load Register 1 Value
645 data[4]: Conter Control Register
647 printk("s526: GPCT_INSN_CONFIG: Configuring SPG\n");
648 devpriv->s526_gpct_config[subdev_channel].app =
649 SinglePulseGeneration;
651 /* Set Counter Mode Register */
652 cmReg.value = (short)(insn->data[1] & 0xFFFF);
653 cmReg.reg.preloadRegSel = 0; /* PR0 */
654 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
656 /* Load the pre-laod register 0 high word */
657 value = (short)((insn->data[2] >> 16) & 0xFFFF);
658 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
660 /* Load the pre-laod register 0 low word */
661 value = (short)(insn->data[2] & 0xFFFF);
662 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
664 /* Set Counter Mode Register */
665 cmReg.value = (short)(insn->data[1] & 0xFFFF);
666 cmReg.reg.preloadRegSel = 1; /* PR1 */
667 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
669 /* Load the pre-laod register 1 high word */
670 value = (short)((insn->data[3] >> 16) & 0xFFFF);
671 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
673 /* Load the pre-laod register 1 low word */
674 value = (short)(insn->data[3] & 0xFFFF);
675 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
677 /* Write the Counter Control Register */
678 if (insn->data[3] != 0) {
679 value = (short)(insn->data[3] & 0xFFFF);
680 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
684 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
686 data[0]: Application Type
687 data[1]: Counter Mode Register Value
688 data[2]: Pre-load Register 0 Value
689 data[3]: Pre-load Register 1 Value
690 data[4]: Conter Control Register
692 printk("s526: GPCT_INSN_CONFIG: Configuring PTG\n");
693 devpriv->s526_gpct_config[subdev_channel].app =
694 PulseTrainGeneration;
696 /* Set Counter Mode Register */
697 cmReg.value = (short)(insn->data[1] & 0xFFFF);
698 cmReg.reg.preloadRegSel = 0; /* PR0 */
699 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
701 /* Load the pre-laod register 0 high word */
702 value = (short)((insn->data[2] >> 16) & 0xFFFF);
703 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
705 /* Load the pre-laod register 0 low word */
706 value = (short)(insn->data[2] & 0xFFFF);
707 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
709 /* Set Counter Mode Register */
710 cmReg.value = (short)(insn->data[1] & 0xFFFF);
711 cmReg.reg.preloadRegSel = 1; /* PR1 */
712 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
714 /* Load the pre-laod register 1 high word */
715 value = (short)((insn->data[3] >> 16) & 0xFFFF);
716 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
718 /* Load the pre-laod register 1 low word */
719 value = (short)(insn->data[3] & 0xFFFF);
720 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
722 /* Write the Counter Control Register */
723 if (insn->data[3] != 0) {
724 value = (short)(insn->data[3] & 0xFFFF);
725 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
730 printk("s526: unsupported GPCT_insn_config\n");
738 static int s526_gpct_winsn(struct comedi_device *dev,
739 struct comedi_subdevice *s, struct comedi_insn *insn,
742 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
745 printk("s526: GPCT_INSN_WRITE on channel %d\n", subdev_channel);
746 cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel));
747 printk("s526: Counter Mode Register: %x\n", cmReg.value);
748 /* Check what Application of Counter this channel is configured for */
749 switch (devpriv->s526_gpct_config[subdev_channel].app) {
750 case PositionMeasurement:
751 printk("S526: INSN_WRITE: PM\n");
752 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
754 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
757 case SinglePulseGeneration:
758 printk("S526: INSN_WRITE: SPG\n");
759 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
761 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
764 case PulseTrainGeneration:
765 /* data[0] contains the PULSE_WIDTH
766 data[1] contains the PULSE_PERIOD
767 @pre PULSE_PERIOD > PULSE_WIDTH > 0
768 The above periods must be expressed as a multiple of the
769 pulse frequency on the selected source
771 printk("S526: INSN_WRITE: PTG\n");
772 if ((insn->data[1] > insn->data[0]) && (insn->data[0] > 0)) {
773 (devpriv->s526_gpct_config[subdev_channel]).data[0] =
775 (devpriv->s526_gpct_config[subdev_channel]).data[1] =
778 printk("%d \t %d\n", insn->data[1], insn->data[2]);
780 ("s526: INSN_WRITE: PTG: Problem with Pulse params\n");
784 value = (short)((*data >> 16) & 0xFFFF);
785 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
786 value = (short)(*data & 0xFFFF);
787 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
789 default: /* Impossible */
791 ("s526: INSN_WRITE: Functionality %d not implemented yet\n",
792 devpriv->s526_gpct_config[subdev_channel].app);
796 /* return the number of samples written */
800 #define ISR_ADC_DONE 0x4
801 static int s526_ai_insn_config(struct comedi_device *dev,
802 struct comedi_subdevice *s,
803 struct comedi_insn *insn, unsigned int *data)
805 int result = -EINVAL;
812 /* data[0] : channels was set in relevant bits.
815 /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
816 * enable channels here. The channel should be enabled in the
817 * INSN_READ handler. */
819 /* Enable ADC interrupt */
820 outw(ISR_ADC_DONE, ADDR_REG(REG_IER));
821 /* printk("s526: ADC current value: 0x%04x\n", inw(ADDR_REG(REG_ADC))); */
822 devpriv->s526_ai_config = (data[0] & 0x3FF) << 5;
824 devpriv->s526_ai_config |= 0x8000; /* set the delay */
826 devpriv->s526_ai_config |= 0x0001; /* ADC start bit. */
832 * "instructions" read/write data in "one-shot" or "software-triggered"
835 static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
836 struct comedi_insn *insn, unsigned int *data)
839 int chan = CR_CHAN(insn->chanspec);
840 unsigned short value;
844 /* Set configured delay, enable channel for this channel only,
845 * select "ADC read" channel, set "ADC start" bit. */
846 value = (devpriv->s526_ai_config & 0x8000) |
847 ((1 << 5) << chan) | (chan << 1) | 0x0001;
849 /* convert n samples */
850 for (n = 0; n < insn->n; n++) {
851 /* trigger conversion */
852 outw(value, ADDR_REG(REG_ADC));
853 /* printk("s526: Wrote 0x%04x to ADC\n", value); */
854 /* printk("s526: ADC reg=0x%04x\n", inw(ADDR_REG(REG_ADC))); */
857 /* wait for conversion to end */
858 for (i = 0; i < TIMEOUT; i++) {
859 status = inw(ADDR_REG(REG_ISR));
860 if (status & ISR_ADC_DONE) {
861 outw(ISR_ADC_DONE, ADDR_REG(REG_ISR));
866 /* printk() should be used instead of printk()
867 * whenever the code can be called from real-time. */
868 printk("s526: ADC(0x%04x) timeout\n",
869 inw(ADDR_REG(REG_ISR)));
874 d = inw(ADDR_REG(REG_ADD));
875 /* printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF)); */
878 data[n] = d ^ 0x8000;
881 /* return the number of samples read/written */
885 static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
886 struct comedi_insn *insn, unsigned int *data)
889 int chan = CR_CHAN(insn->chanspec);
892 /* printk("s526_ao_winsn\n"); */
894 /* outw(val, dev->iobase + REG_DAC); */
895 outw(val, ADDR_REG(REG_DAC));
897 /* Writing a list of values to an AO channel is probably not
898 * very useful, but that's how the interface is defined. */
899 for (i = 0; i < insn->n; i++) {
900 /* a typical programming sequence */
901 /* outw(data[i], dev->iobase + REG_ADD); write the data to preload register */
902 outw(data[i], ADDR_REG(REG_ADD)); /* write the data to preload register */
903 devpriv->ao_readback[chan] = data[i];
904 /* outw(val + 1, dev->iobase + REG_DAC); starts the D/A conversion. */
905 outw(val + 1, ADDR_REG(REG_DAC)); /* starts the D/A conversion. */
908 /* return the number of samples read/written */
912 /* AO subdevices should have a read insn as well as a write insn.
913 * Usually this means copying a value stored in devpriv. */
914 static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
915 struct comedi_insn *insn, unsigned int *data)
918 int chan = CR_CHAN(insn->chanspec);
920 for (i = 0; i < insn->n; i++)
921 data[i] = devpriv->ao_readback[chan];
926 /* DIO devices are slightly special. Although it is possible to
927 * implement the insn_read/insn_write interface, it is much more
928 * useful to applications if you implement the insn_bits interface.
929 * This allows packed reading/writing of the DIO channels. The
930 * comedi core can convert between insn_bits and insn_read/write */
931 static int s526_dio_insn_bits(struct comedi_device *dev,
932 struct comedi_subdevice *s,
933 struct comedi_insn *insn, unsigned int *data)
938 /* The insn data is a mask in data[0] and the new data
939 * in data[1], each channel cooresponding to a bit. */
941 s->state &= ~data[0];
942 s->state |= data[0] & data[1];
943 /* Write out the new digital output lines */
944 outw(s->state, ADDR_REG(REG_DIO));
947 /* on return, data[1] contains the value of the digital
948 * input and output lines. */
949 data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF; /* low 8 bits are the data */
950 /* or we could just return the software copy of the output values if
951 * it was a purely digital output subdevice */
952 /* data[1]=s->state; */
957 static int s526_dio_insn_config(struct comedi_device *dev,
958 struct comedi_subdevice *s,
959 struct comedi_insn *insn, unsigned int *data)
961 int chan = CR_CHAN(insn->chanspec);
964 printk("S526 DIO insn_config\n");
969 value = inw(ADDR_REG(REG_DIO));
971 /* The input or output configuration of each digital line is
972 * configured by a special insn_config instruction. chanspec
973 * contains the channel to be changed, and data[0] contains the
974 * value COMEDI_INPUT or COMEDI_OUTPUT. */
976 if (data[0] == COMEDI_OUTPUT) {
977 value |= 1 << (chan + 10); /* bit 10/11 set the group 1/2's mode */
978 s->io_bits |= (0xF << chan);
980 value &= ~(1 << (chan + 10)); /* 1 is output, 0 is input. */
981 s->io_bits &= ~(0xF << chan);
983 outw(value, ADDR_REG(REG_DIO));
989 * A convenient macro that defines init_module() and cleanup_module(),
992 COMEDI_INITCLEANUP(driver_s526);