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[] = {
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;
127 } counter_mode_register_t;
130 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! */
139 CountingAndTimeMeasurement,
140 SinglePulseGeneration,
141 PulseTrainGeneration,
144 } S526_GPCT_APP_CLASS;
146 /* Config struct for different GPCT subdevice Application Classes and
149 typedef struct s526GPCTConfig {
150 S526_GPCT_APP_CLASS app;
151 int data[MAX_GPCT_CONFIG_DATA];
152 } s526_gpct_config_t;
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.
159 typedef struct s526_board_struct {
170 static const 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 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 comedi_device struct. */
197 /* would be useful for a PCI device */
198 struct pci_dev *pci_dev;
200 /* Used for AO readback */
201 unsigned int ao_readback[2];
203 s526_gpct_config_t s526_gpct_config[4];
204 unsigned short s526_ai_config;
207 * most drivers define the following macro to make it easy to
208 * access the private structure.
210 #define devpriv ((s526_private *)dev->private)
213 * The comedi_driver structure tells the Comedi core module
214 * which functions to call to configure/deconfigure (attach/detach)
215 * the board, and also about the kernel module that contains
218 static int s526_attach(comedi_device * dev, comedi_devconfig * it);
219 static int s526_detach(comedi_device * dev);
220 static comedi_driver driver_s526 = {
225 /* It is not necessary to implement the following members if you are
226 * writing a driver for a ISA PnP or PCI card */
227 /* Most drivers will support multiple types of boards by
228 * having an array of board structures. These were defined
229 * in s526_boards[] above. Note that the element 'name'
230 * was first in the structure -- Comedi uses this fact to
231 * extract the name of the board without knowing any details
232 * about the structure except for its length.
233 * When a device is attached (by comedi_config), the name
234 * of the device is given to Comedi, and Comedi tries to
235 * match it by going through the list of board names. If
236 * there is a match, the address of the pointer is put
237 * into dev->board_ptr and driver->attach() is called.
239 * Note that these are not necessary if you can determine
240 * the type of board in software. ISA PnP, PCI, and PCMCIA
241 * devices are such boards.
243 board_name:&s526_boards[0].name,
244 offset:sizeof(s526_board),
245 num_names:sizeof(s526_boards) / sizeof(s526_board),
248 static int s526_gpct_rinsn(comedi_device * dev, comedi_subdevice * s,
249 comedi_insn * insn, unsigned int * data);
250 static int s526_gpct_insn_config(comedi_device * dev, comedi_subdevice * s,
251 comedi_insn * insn, unsigned int * data);
252 static int s526_gpct_winsn(comedi_device * dev, comedi_subdevice * s,
253 comedi_insn * insn, unsigned int * data);
254 static int s526_ai_insn_config(comedi_device * dev, comedi_subdevice * s,
255 comedi_insn * insn, unsigned int * data);
256 static int s526_ai_rinsn(comedi_device * dev, comedi_subdevice * s,
257 comedi_insn * insn, unsigned int * data);
258 static int s526_ao_winsn(comedi_device * dev, comedi_subdevice * s,
259 comedi_insn * insn, unsigned int * data);
260 static int s526_ao_rinsn(comedi_device * dev, comedi_subdevice * s,
261 comedi_insn * insn, unsigned int * data);
262 static int s526_dio_insn_bits(comedi_device * dev, comedi_subdevice * s,
263 comedi_insn * insn, unsigned int * data);
264 static int s526_dio_insn_config(comedi_device * dev, comedi_subdevice * s,
265 comedi_insn * insn, unsigned int * data);
268 * Attach is called by the Comedi core to configure the driver
269 * for a particular board. If you specified a board_name array
270 * in the driver structure, dev->board_ptr contains that
273 static int s526_attach(comedi_device * dev, comedi_devconfig * it)
279 // int subdev_channel = 0;
281 printk("comedi%d: s526: ", dev->minor);
283 iobase = it->options[0];
284 if (!iobase || !request_region(iobase, S526_IOSIZE, thisboard->name)) {
285 comedi_error(dev, "I/O port conflict");
288 dev->iobase = iobase;
290 printk("iobase=0x%lx\n", dev->iobase);
292 /*** make it a little quieter, exw, 8/29/06
293 for (i = 0; i < S526_NUM_PORTS; i++) {
294 printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i])));
299 * Initialize dev->board_name. Note that we can use the "thisboard"
300 * macro now, since we just initialized it in the last line.
302 dev->board_ptr = &s526_boards[0];
304 dev->board_name = thisboard->name;
307 * Allocate the private structure area. alloc_private() is a
308 * convenient macro defined in comedidev.h.
310 if (alloc_private(dev, sizeof(s526_private)) < 0)
314 * Allocate the subdevice structures. alloc_subdevice() is a
315 * convenient macro defined in comedidev.h.
317 dev->n_subdevices = 4;
318 if (alloc_subdevices(dev, dev->n_subdevices) < 0)
321 s = dev->subdevices + 0;
322 /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
323 s->type = COMEDI_SUBD_COUNTER;
324 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
325 /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */
326 s->n_chan = thisboard->gpct_chans;
327 s->maxdata = 0x00ffffff; /* 24 bit counter */
328 s->insn_read = s526_gpct_rinsn;
329 s->insn_config = s526_gpct_insn_config;
330 s->insn_write = s526_gpct_winsn;
332 /* Command are not implemented yet, however they are necessary to
333 allocate the necessary memory for the comedi_async struct (used
334 to trigger the GPCT in case of pulsegenerator function */
335 //s->do_cmd = s526_gpct_cmd;
336 //s->do_cmdtest = s526_gpct_cmdtest;
337 //s->cancel = s526_gpct_cancel;
339 s = dev->subdevices + 1;
340 //dev->read_subdev=s;
341 /* analog input subdevice */
342 s->type = COMEDI_SUBD_AI;
343 /* we support differential */
344 s->subdev_flags = SDF_READABLE | SDF_DIFF;
345 /* channels 0 to 7 are the regular differential inputs */
346 /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
349 s->range_table = &range_bipolar10;
350 s->len_chanlist = 16; /* This is the maximum chanlist length that
351 the board can handle */
352 s->insn_read = s526_ai_rinsn;
353 s->insn_config = s526_ai_insn_config;
355 s = dev->subdevices + 2;
356 /* analog output subdevice */
357 s->type = COMEDI_SUBD_AO;
358 s->subdev_flags = SDF_WRITABLE;
361 s->range_table = &range_bipolar10;
362 s->insn_write = s526_ao_winsn;
363 s->insn_read = s526_ao_rinsn;
365 s = dev->subdevices + 3;
366 /* digital i/o subdevice */
367 if (thisboard->have_dio) {
368 s->type = COMEDI_SUBD_DIO;
369 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
372 s->range_table = &range_digital;
373 s->insn_bits = s526_dio_insn_bits;
374 s->insn_config = s526_dio_insn_config;
376 s->type = COMEDI_SUBD_UNUSED;
379 printk("attached\n");
384 // Example of Counter Application
385 //One-shot (software trigger)
386 cmReg.reg.coutSource = 0; // out RCAP
387 cmReg.reg.coutPolarity = 1; // Polarity inverted
388 cmReg.reg.autoLoadResetRcap = 1; // Auto load 0:disabled, 1:enabled
389 cmReg.reg.hwCtEnableSource = 3; // NOT RCAP
390 cmReg.reg.ctEnableCtrl = 2; // Hardware
391 cmReg.reg.clockSource = 2; // Internal
392 cmReg.reg.countDir = 1; // Down
393 cmReg.reg.countDirCtrl = 1; // Software
394 cmReg.reg.outputRegLatchCtrl = 0; // latch on read
395 cmReg.reg.preloadRegSel = 0; // PR0
396 cmReg.reg.reserved = 0;
398 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
400 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
401 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
403 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
404 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
406 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset RCAP (fires one-shot)
410 // Set Counter Mode Register
411 cmReg.reg.coutSource = 0; // out RCAP
412 cmReg.reg.coutPolarity = 0; // Polarity inverted
413 cmReg.reg.autoLoadResetRcap = 0; // Auto load disabled
414 cmReg.reg.hwCtEnableSource = 2; // NOT RCAP
415 cmReg.reg.ctEnableCtrl = 1; // 1: Software, >1 : Hardware
416 cmReg.reg.clockSource = 3; // x4
417 cmReg.reg.countDir = 0; // up
418 cmReg.reg.countDirCtrl = 0; // quadrature
419 cmReg.reg.outputRegLatchCtrl = 0; // latch on read
420 cmReg.reg.preloadRegSel = 0; // PR0
421 cmReg.reg.reserved = 0;
424 printk("Mode reg=0x%04x, 0x%04lx\n", cmReg.value, ADDR_CHAN_REG(REG_C0M,
426 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
428 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n)));
430 // Load the pre-laod register high word
431 // value = (short) (0x55);
432 // outw(value, ADDR_CHAN_REG(REG_C0H, n));
434 // Load the pre-laod register low word
435 // value = (short)(0xaa55);
436 // outw(value, ADDR_CHAN_REG(REG_C0L, n));
438 // Write the Counter Control Register
439 // outw(value, ADDR_CHAN_REG(REG_C0C, 0));
441 // Reset the counter if it is software preload
442 if (cmReg.reg.autoLoadResetRcap == 0) {
443 outw(0x8000, ADDR_CHAN_REG(REG_C0C, n)); // Reset the counter
444 outw(0x4000, ADDR_CHAN_REG(REG_C0C, n)); // Load the counter from PR0
447 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
449 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n)));
452 printk("Current registres:\n");
454 for (i = 0; i < S526_NUM_PORTS; i++) {
455 printk("0x%02lx: 0x%04x\n", ADDR_REG(s526_ports[i]),
456 inw(ADDR_REG(s526_ports[i])));
462 * _detach is called to deconfigure a device. It should deallocate
464 * This function is also called when _attach() fails, so it should be
465 * careful not to release resources that were not necessarily
466 * allocated by _attach(). dev->private and dev->subdevices are
467 * deallocated automatically by the core.
469 static int s526_detach(comedi_device * dev)
471 printk("comedi%d: s526: remove\n", dev->minor);
474 release_region(dev->iobase, S526_IOSIZE);
479 static int s526_gpct_rinsn(comedi_device * dev, comedi_subdevice * s,
480 comedi_insn * insn, unsigned int * data)
482 int i; // counts the Data
483 int counter_channel = CR_CHAN(insn->chanspec);
484 unsigned short datalow;
485 unsigned short datahigh;
489 printk("s526: INSN_READ: n should be > 0\n");
492 // Read the low word first
493 for (i = 0; i < insn->n; i++) {
494 datalow = inw(ADDR_CHAN_REG(REG_C0L, counter_channel));
495 datahigh = inw(ADDR_CHAN_REG(REG_C0H, counter_channel));
496 data[i] = (int)(datahigh & 0x00FF);
497 data[i] = (data[i] << 16) | (datalow & 0xFFFF);
498 // printk("s526 GPCT[%d]: %x(0x%04x, 0x%04x)\n", counter_channel, data[i], datahigh, datalow);
503 static int s526_gpct_insn_config(comedi_device * dev, comedi_subdevice * s,
504 comedi_insn * insn, unsigned int * data)
506 int subdev_channel = CR_CHAN(insn->chanspec); // Unpack chanspec
510 // printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n", subdev_channel);
512 for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++) {
513 devpriv->s526_gpct_config[subdev_channel].data[i] =
515 // printk("data[%d]=%x\n", i, insn->data[i]);
518 // Check what type of Counter the user requested, data[0] contains
519 // the Application type
520 switch (insn->data[0]) {
521 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
523 data[0]: Application Type
524 data[1]: Counter Mode Register Value
525 data[2]: Pre-load Register Value
526 data[3]: Conter Control Register
528 printk("s526: GPCT_INSN_CONFIG: Configuring Encoder\n");
529 devpriv->s526_gpct_config[subdev_channel].app =
533 // Example of Counter Application
534 //One-shot (software trigger)
535 cmReg.reg.coutSource = 0; // out RCAP
536 cmReg.reg.coutPolarity = 1; // Polarity inverted
537 cmReg.reg.autoLoadResetRcap = 0; // Auto load disabled
538 cmReg.reg.hwCtEnableSource = 3; // NOT RCAP
539 cmReg.reg.ctEnableCtrl = 2; // Hardware
540 cmReg.reg.clockSource = 2; // Internal
541 cmReg.reg.countDir = 1; // Down
542 cmReg.reg.countDirCtrl = 1; // Software
543 cmReg.reg.outputRegLatchCtrl = 0; // latch on read
544 cmReg.reg.preloadRegSel = 0; // PR0
545 cmReg.reg.reserved = 0;
547 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
549 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
550 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
552 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
553 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
555 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset RCAP (fires one-shot)
560 // Set Counter Mode Register
561 cmReg.reg.coutSource = 0; // out RCAP
562 cmReg.reg.coutPolarity = 0; // Polarity inverted
563 cmReg.reg.autoLoadResetRcap = 0; // Auto load disabled
564 cmReg.reg.hwCtEnableSource = 2; // NOT RCAP
565 cmReg.reg.ctEnableCtrl = 1; // 1: Software, >1 : Hardware
566 cmReg.reg.clockSource = 3; // x4
567 cmReg.reg.countDir = 0; // up
568 cmReg.reg.countDirCtrl = 0; // quadrature
569 cmReg.reg.outputRegLatchCtrl = 0; // latch on read
570 cmReg.reg.preloadRegSel = 0; // PR0
571 cmReg.reg.reserved = 0;
573 // Set Counter Mode Register
574 // printk("s526: Counter Mode register=%x\n", cmReg.value);
575 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
577 // Reset the counter if it is software preload
578 if (cmReg.reg.autoLoadResetRcap == 0) {
579 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
580 // outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
583 cmReg.reg.countDirCtrl = 0; // 0 quadrature, 1 software control
585 // data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4
586 if (insn->data[1] == GPCT_X2) {
587 cmReg.reg.clockSource = 1;
588 } else if (insn->data[1] == GPCT_X4) {
589 cmReg.reg.clockSource = 2;
591 cmReg.reg.clockSource = 0;
594 // When to take into account the indexpulse:
595 if (insn->data[2] == GPCT_IndexPhaseLowLow) {
596 } else if (insn->data[2] == GPCT_IndexPhaseLowHigh) {
597 } else if (insn->data[2] == GPCT_IndexPhaseHighLow) {
598 } else if (insn->data[2] == GPCT_IndexPhaseHighHigh) {
600 // Take into account the index pulse?
601 if (insn->data[3] == GPCT_RESET_COUNTER_ON_INDEX)
602 cmReg.reg.autoLoadResetRcap = 4; // Auto load with INDEX^
604 // Set Counter Mode Register
605 cmReg.value = (short) (insn->data[1] & 0xFFFF);
606 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
608 // Load the pre-laod register high word
609 value = (short) ((insn->data[2] >> 16) & 0xFFFF);
610 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
612 // Load the pre-laod register low word
613 value = (short) (insn->data[2] & 0xFFFF);
614 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
616 // Write the Counter Control Register
617 if (insn->data[3] != 0) {
618 value = (short) (insn->data[3] & 0xFFFF);
619 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
621 // Reset the counter if it is software preload
622 if (cmReg.reg.autoLoadResetRcap == 0) {
623 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
624 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
629 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
631 data[0]: Application Type
632 data[1]: Counter Mode Register Value
633 data[2]: Pre-load Register 0 Value
634 data[3]: Pre-load Register 1 Value
635 data[4]: Conter Control Register
637 printk("s526: GPCT_INSN_CONFIG: Configuring SPG\n");
638 devpriv->s526_gpct_config[subdev_channel].app =
639 SinglePulseGeneration;
641 // Set Counter Mode Register
642 cmReg.value = (short) (insn->data[1] & 0xFFFF);
643 cmReg.reg.preloadRegSel = 0; // PR0
644 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
646 // Load the pre-laod register 0 high word
647 value = (short) ((insn->data[2] >> 16) & 0xFFFF);
648 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
650 // Load the pre-laod register 0 low word
651 value = (short) (insn->data[2] & 0xFFFF);
652 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
654 // Set Counter Mode Register
655 cmReg.value = (short) (insn->data[1] & 0xFFFF);
656 cmReg.reg.preloadRegSel = 1; // PR1
657 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
659 // Load the pre-laod register 1 high word
660 value = (short) ((insn->data[3] >> 16) & 0xFFFF);
661 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
663 // Load the pre-laod register 1 low word
664 value = (short) (insn->data[3] & 0xFFFF);
665 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
667 // Write the Counter Control Register
668 if (insn->data[3] != 0) {
669 value = (short) (insn->data[3] & 0xFFFF);
670 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
674 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
676 data[0]: Application Type
677 data[1]: Counter Mode Register Value
678 data[2]: Pre-load Register 0 Value
679 data[3]: Pre-load Register 1 Value
680 data[4]: Conter Control Register
682 printk("s526: GPCT_INSN_CONFIG: Configuring PTG\n");
683 devpriv->s526_gpct_config[subdev_channel].app =
684 PulseTrainGeneration;
686 // Set Counter Mode Register
687 cmReg.value = (short) (insn->data[1] & 0xFFFF);
688 cmReg.reg.preloadRegSel = 0; // PR0
689 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
691 // Load the pre-laod register 0 high word
692 value = (short) ((insn->data[2] >> 16) & 0xFFFF);
693 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
695 // Load the pre-laod register 0 low word
696 value = (short) (insn->data[2] & 0xFFFF);
697 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
699 // Set Counter Mode Register
700 cmReg.value = (short) (insn->data[1] & 0xFFFF);
701 cmReg.reg.preloadRegSel = 1; // PR1
702 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
704 // Load the pre-laod register 1 high word
705 value = (short) ((insn->data[3] >> 16) & 0xFFFF);
706 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
708 // Load the pre-laod register 1 low word
709 value = (short) (insn->data[3] & 0xFFFF);
710 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
712 // Write the Counter Control Register
713 if (insn->data[3] != 0) {
714 value = (short) (insn->data[3] & 0xFFFF);
715 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
720 printk("s526: unsupported GPCT_insn_config\n");
728 static int s526_gpct_winsn(comedi_device * dev, comedi_subdevice * s,
729 comedi_insn * insn, unsigned int * data)
731 int subdev_channel = CR_CHAN(insn->chanspec); // Unpack chanspec
734 printk("s526: GPCT_INSN_WRITE on channel %d\n", subdev_channel);
735 cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel));
736 printk("s526: Counter Mode Register: %x\n", cmReg.value);
737 // Check what Application of Counter this channel is configured for
738 switch (devpriv->s526_gpct_config[subdev_channel].app) {
739 case PositionMeasurement:
740 printk("S526: INSN_WRITE: PM\n");
741 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
743 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
746 case SinglePulseGeneration:
747 printk("S526: INSN_WRITE: SPG\n");
748 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
750 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
753 case PulseTrainGeneration:
754 /* data[0] contains the PULSE_WIDTH
755 data[1] contains the PULSE_PERIOD
756 @pre PULSE_PERIOD > PULSE_WIDTH > 0
757 The above periods must be expressed as a multiple of the
758 pulse frequency on the selected source
760 printk("S526: INSN_WRITE: PTG\n");
761 if ((insn->data[1] > insn->data[0]) && (insn->data[0] > 0)) {
762 (devpriv->s526_gpct_config[subdev_channel]).data[0] =
764 (devpriv->s526_gpct_config[subdev_channel]).data[1] =
767 printk("%d \t %d\n", insn->data[1], insn->data[2]);
768 printk("s526: INSN_WRITE: PTG: Problem with Pulse params\n");
772 value = (short) ((*data >> 16) & 0xFFFF);
773 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
774 value = (short) (*data & 0xFFFF);
775 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
777 default: // Impossible
778 printk("s526: INSN_WRITE: Functionality %d not implemented yet\n", devpriv->s526_gpct_config[subdev_channel].app);
782 // return the number of samples written
786 #define ISR_ADC_DONE 0x4
787 static int s526_ai_insn_config(comedi_device * dev, comedi_subdevice * s,
788 comedi_insn * insn, unsigned int * data)
790 int result = -EINVAL;
797 /* data[0] : channels was set in relevant bits.
800 /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
801 * enable channels here. The channel should be enabled in the
802 * INSN_READ handler. */
804 // Enable ADC interrupt
805 outw(ISR_ADC_DONE, ADDR_REG(REG_IER));
806 // printk("s526: ADC current value: 0x%04x\n", inw(ADDR_REG(REG_ADC)));
807 devpriv->s526_ai_config = (data[0] & 0x3FF) << 5;
809 devpriv->s526_ai_config |= 0x8000; //set the delay
811 devpriv->s526_ai_config |= 0x0001; // ADC start bit.
817 * "instructions" read/write data in "one-shot" or "software-triggered"
820 static int s526_ai_rinsn(comedi_device * dev, comedi_subdevice * s,
821 comedi_insn * insn, unsigned int * data)
824 int chan = CR_CHAN(insn->chanspec);
825 unsigned short value;
829 /* Set configured delay, enable channel for this channel only,
830 * select "ADC read" channel, set "ADC start" bit. */
831 value = (devpriv->s526_ai_config & 0x8000) |
832 ((1 << 5) << chan) | (chan << 1) | 0x0001;
834 /* convert n samples */
835 for (n = 0; n < insn->n; n++) {
836 /* trigger conversion */
837 outw(value, ADDR_REG(REG_ADC));
838 // printk("s526: Wrote 0x%04x to ADC\n", value);
839 // printk("s526: ADC reg=0x%04x\n", inw(ADDR_REG(REG_ADC)));
842 /* wait for conversion to end */
843 for (i = 0; i < TIMEOUT; i++) {
844 status = inw(ADDR_REG(REG_ISR));
845 if (status & ISR_ADC_DONE) {
846 outw(ISR_ADC_DONE, ADDR_REG(REG_ISR));
851 /* rt_printk() should be used instead of printk()
852 * whenever the code can be called from real-time. */
853 rt_printk("s526: ADC(0x%04x) timeout\n",
854 inw(ADDR_REG(REG_ISR)));
859 d = inw(ADDR_REG(REG_ADD));
860 // printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF));
863 data[n] = d ^ 0x8000;
866 /* return the number of samples read/written */
870 static int s526_ao_winsn(comedi_device * dev, comedi_subdevice * s,
871 comedi_insn * insn, unsigned int * data)
874 int chan = CR_CHAN(insn->chanspec);
877 // printk("s526_ao_winsn\n");
879 // outw(val, dev->iobase + REG_DAC);
880 outw(val, ADDR_REG(REG_DAC));
882 /* Writing a list of values to an AO channel is probably not
883 * very useful, but that's how the interface is defined. */
884 for (i = 0; i < insn->n; i++) {
885 /* a typical programming sequence */
886 // outw(data[i], dev->iobase + REG_ADD); // write the data to preload register
887 outw(data[i], ADDR_REG(REG_ADD)); // write the data to preload register
888 devpriv->ao_readback[chan] = data[i];
889 // outw(val + 1, dev->iobase + REG_DAC); // starts the D/A conversion.
890 outw(val + 1, ADDR_REG(REG_DAC)); // starts the D/A conversion.
893 /* return the number of samples read/written */
897 /* AO subdevices should have a read insn as well as a write insn.
898 * Usually this means copying a value stored in devpriv. */
899 static int s526_ao_rinsn(comedi_device * dev, comedi_subdevice * s,
900 comedi_insn * insn, unsigned int * data)
903 int chan = CR_CHAN(insn->chanspec);
905 for (i = 0; i < insn->n; i++)
906 data[i] = devpriv->ao_readback[chan];
911 /* DIO devices are slightly special. Although it is possible to
912 * implement the insn_read/insn_write interface, it is much more
913 * useful to applications if you implement the insn_bits interface.
914 * This allows packed reading/writing of the DIO channels. The
915 * comedi core can convert between insn_bits and insn_read/write */
916 static int s526_dio_insn_bits(comedi_device * dev, comedi_subdevice * s,
917 comedi_insn * insn, unsigned int * data)
922 /* The insn data is a mask in data[0] and the new data
923 * in data[1], each channel cooresponding to a bit. */
925 s->state &= ~data[0];
926 s->state |= data[0] & data[1];
927 /* Write out the new digital output lines */
928 outw(s->state, ADDR_REG(REG_DIO));
931 /* on return, data[1] contains the value of the digital
932 * input and output lines. */
933 data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF; // low 8 bits are the data
934 /* or we could just return the software copy of the output values if
935 * it was a purely digital output subdevice */
941 static int s526_dio_insn_config(comedi_device * dev, comedi_subdevice * s,
942 comedi_insn * insn, unsigned int * data)
944 int chan = CR_CHAN(insn->chanspec);
947 printk("S526 DIO insn_config\n");
952 value = inw(ADDR_REG(REG_DIO));
954 /* The input or output configuration of each digital line is
955 * configured by a special insn_config instruction. chanspec
956 * contains the channel to be changed, and data[0] contains the
957 * value COMEDI_INPUT or COMEDI_OUTPUT. */
959 if (data[0] == COMEDI_OUTPUT) {
960 value |= 1 << (chan + 10); // bit 10/11 set the group 1/2's mode
961 s->io_bits |= (0xF << chan);
963 value &= ~(1 << (chan + 10)); // 1 is output, 0 is input.
964 s->io_bits &= ~(0xF << chan);
966 outw(value, ADDR_REG(REG_DIO));
972 * A convenient macro that defines init_module() and cleanup_module(),
975 COMEDI_INITCLEANUP(driver_s526);