linux/drivers/staging/comedi/drivers/s526.c
<<
>>
Prefs
   1/*
   2    comedi/drivers/s526.c
   3    Sensoray s526 Comedi driver
   4
   5    COMEDI - Linux Control and Measurement Device Interface
   6    Copyright (C) 2000 David A. Schleef <ds@schleef.org>
   7
   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.
  12
  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.
  17
  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.
  21
  22*/
  23/*
  24Driver: s526
  25Description: Sensoray 526 driver
  26Devices: [Sensoray] 526 (s526)
  27Author: Richie
  28        Everett Wang <everett.wang@everteq.com>
  29Updated: Thu, 14 Sep. 2006
  30Status: experimental
  31
  32Encoder works
  33Analog input works
  34Analog output works
  35PWM output works
  36Commands are not supported yet.
  37
  38Configuration Options:
  39
  40comedi_config /dev/comedi0 s526 0x2C0,0x3
  41
  42*/
  43
  44#include "../comedidev.h"
  45#include <linux/ioport.h>
  46#include <asm/byteorder.h>
  47
  48#define S526_SIZE 64
  49
  50#define S526_START_AI_CONV      0
  51#define S526_AI_READ            0
  52
  53/* Ports */
  54#define S526_IOSIZE 0x40
  55#define S526_NUM_PORTS 27
  56
  57/* registers */
  58#define REG_TCR 0x00
  59#define REG_WDC 0x02
  60#define REG_DAC 0x04
  61#define REG_ADC 0x06
  62#define REG_ADD 0x08
  63#define REG_DIO 0x0A
  64#define REG_IER 0x0C
  65#define REG_ISR 0x0E
  66#define REG_MSC 0x10
  67#define REG_C0L 0x12
  68#define REG_C0H 0x14
  69#define REG_C0M 0x16
  70#define REG_C0C 0x18
  71#define REG_C1L 0x1A
  72#define REG_C1H 0x1C
  73#define REG_C1M 0x1E
  74#define REG_C1C 0x20
  75#define REG_C2L 0x22
  76#define REG_C2H 0x24
  77#define REG_C2M 0x26
  78#define REG_C2C 0x28
  79#define REG_C3L 0x2A
  80#define REG_C3H 0x2C
  81#define REG_C3M 0x2E
  82#define REG_C3C 0x30
  83#define REG_EED 0x32
  84#define REG_EEC 0x34
  85
  86static const int s526_ports[] = {
  87        REG_TCR,
  88        REG_WDC,
  89        REG_DAC,
  90        REG_ADC,
  91        REG_ADD,
  92        REG_DIO,
  93        REG_IER,
  94        REG_ISR,
  95        REG_MSC,
  96        REG_C0L,
  97        REG_C0H,
  98        REG_C0M,
  99        REG_C0C,
 100        REG_C1L,
 101        REG_C1H,
 102        REG_C1M,
 103        REG_C1C,
 104        REG_C2L,
 105        REG_C2H,
 106        REG_C2M,
 107        REG_C2C,
 108        REG_C3L,
 109        REG_C3H,
 110        REG_C3M,
 111        REG_C3C,
 112        REG_EED,
 113        REG_EEC
 114};
 115
 116struct 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;
 141#else
 142#error Unknown bit field order
 143#endif
 144};
 145
 146union cmReg {
 147        struct counter_mode_register_t reg;
 148        unsigned short value;
 149};
 150
 151#define MAX_GPCT_CONFIG_DATA 6
 152
 153/* Different Application Classes for GPCT Subdevices */
 154/* The list is not exhaustive and needs discussion! */
 155enum S526_GPCT_APP_CLASS {
 156        CountingAndTimeMeasurement,
 157        SinglePulseGeneration,
 158        PulseTrainGeneration,
 159        PositionMeasurement,
 160        Miscellaneous
 161};
 162
 163/* Config struct for different GPCT subdevice Application Classes and
 164   their options
 165*/
 166struct s526GPCTConfig {
 167        enum S526_GPCT_APP_CLASS app;
 168        int data[MAX_GPCT_CONFIG_DATA];
 169};
 170
 171/*
 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.
 175 */
 176struct s526_board {
 177        const char *name;
 178        int gpct_chans;
 179        int gpct_bits;
 180        int ad_chans;
 181        int ad_bits;
 182        int da_chans;
 183        int da_bits;
 184        int have_dio;
 185};
 186
 187static const struct s526_board s526_boards[] = {
 188        {
 189         .name = "s526",
 190         .gpct_chans = 4,
 191         .gpct_bits = 24,
 192         .ad_chans = 8,
 193         .ad_bits = 16,
 194         .da_chans = 4,
 195         .da_bits = 16,
 196         .have_dio = 1,
 197         }
 198};
 199
 200#define ADDR_REG(reg) (dev->iobase + (reg))
 201#define ADDR_CHAN_REG(reg, chan) (dev->iobase + (reg) + (chan) * 8)
 202
 203/*
 204 * Useful for shorthand access to the particular board structure
 205 */
 206#define thisboard ((const struct s526_board *)dev->board_ptr)
 207
 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
 211   struct.
 212*/
 213struct s526_private {
 214
 215        int data;
 216
 217        /* would be useful for a PCI device */
 218        struct pci_dev *pci_dev;
 219
 220        /* Used for AO readback */
 221        unsigned int ao_readback[2];
 222
 223        struct s526GPCTConfig s526_gpct_config[4];
 224        unsigned short s526_ai_config;
 225};
 226
 227/*
 228 * most drivers define the following macro to make it easy to
 229 * access the private structure.
 230 */
 231#define devpriv ((struct s526_private *)dev->private)
 232
 233/*
 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
 237 * the device code.
 238 */
 239static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it);
 240static int s526_detach(struct comedi_device *dev);
 241static 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.
 259         *
 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.
 263         */
 264        .board_name = &s526_boards[0].name,
 265        .offset = sizeof(struct s526_board),
 266        .num_names = ARRAY_SIZE(s526_boards),
 267};
 268
 269static int s526_gpct_rinsn(struct comedi_device *dev,
 270                           struct comedi_subdevice *s, struct comedi_insn *insn,
 271                           unsigned int *data);
 272static int s526_gpct_insn_config(struct comedi_device *dev,
 273                                 struct comedi_subdevice *s,
 274                                 struct comedi_insn *insn, unsigned int *data);
 275static int s526_gpct_winsn(struct comedi_device *dev,
 276                           struct comedi_subdevice *s, struct comedi_insn *insn,
 277                           unsigned int *data);
 278static int s526_ai_insn_config(struct comedi_device *dev,
 279                               struct comedi_subdevice *s,
 280                               struct comedi_insn *insn, unsigned int *data);
 281static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
 282                         struct comedi_insn *insn, unsigned int *data);
 283static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
 284                         struct comedi_insn *insn, unsigned int *data);
 285static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
 286                         struct comedi_insn *insn, unsigned int *data);
 287static int s526_dio_insn_bits(struct comedi_device *dev,
 288                              struct comedi_subdevice *s,
 289                              struct comedi_insn *insn, unsigned int *data);
 290static int s526_dio_insn_config(struct comedi_device *dev,
 291                                struct comedi_subdevice *s,
 292                                struct comedi_insn *insn, unsigned int *data);
 293
 294/*
 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
 298 * address.
 299 */
 300static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 301{
 302        struct comedi_subdevice *s;
 303        int iobase;
 304        int i, n;
 305/* short value; */
 306/* int subdev_channel = 0; */
 307        union cmReg cmReg;
 308
 309        printk(KERN_INFO "comedi%d: s526: ", dev->minor);
 310
 311        iobase = it->options[0];
 312        if (!iobase || !request_region(iobase, S526_IOSIZE, thisboard->name)) {
 313                comedi_error(dev, "I/O port conflict");
 314                return -EIO;
 315        }
 316        dev->iobase = iobase;
 317
 318        printk("iobase=0x%lx\n", dev->iobase);
 319
 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])));
 324        }
 325        ***/
 326
 327/*
 328 * Initialize dev->board_name.  Note that we can use the "thisboard"
 329 * macro now, since we just initialized it in the last line.
 330 */
 331        dev->board_ptr = &s526_boards[0];
 332
 333        dev->board_name = thisboard->name;
 334
 335/*
 336 * Allocate the private structure area.  alloc_private() is a
 337 * convenient macro defined in comedidev.h.
 338 */
 339        if (alloc_private(dev, sizeof(struct s526_private)) < 0)
 340                return -ENOMEM;
 341
 342/*
 343 * Allocate the subdevice structures.  alloc_subdevice() is a
 344 * convenient macro defined in comedidev.h.
 345 */
 346        dev->n_subdevices = 4;
 347        if (alloc_subdevices(dev, dev->n_subdevices) < 0)
 348                return -ENOMEM;
 349
 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;
 360
 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; */
 367
 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) */
 376        s->n_chan = 10;
 377        s->maxdata = 0xffff;
 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;
 383
 384        s = dev->subdevices + 2;
 385        /* analog output subdevice */
 386        s->type = COMEDI_SUBD_AO;
 387        s->subdev_flags = SDF_WRITABLE;
 388        s->n_chan = 4;
 389        s->maxdata = 0xffff;
 390        s->range_table = &range_bipolar10;
 391        s->insn_write = s526_ao_winsn;
 392        s->insn_read = s526_ao_rinsn;
 393
 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;
 399                s->n_chan = 8;
 400                s->maxdata = 1;
 401                s->range_table = &range_digital;
 402                s->insn_bits = s526_dio_insn_bits;
 403                s->insn_config = s526_dio_insn_config;
 404        } else {
 405                s->type = COMEDI_SUBD_UNUSED;
 406        }
 407
 408        printk(KERN_INFO "attached\n");
 409
 410        return 1;
 411
 412#if 0
 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;
 426
 427        outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
 428
 429        outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
 430        outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
 431
 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));
 438
 439#else
 440
 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;
 453
 454        n = 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));
 458        udelay(1000);
 459        printk(KERN_INFO "Read back mode reg=0x%04x\n",
 460                inw(ADDR_CHAN_REG(REG_C0M, n)));
 461
 462        /*  Load the pre-load register high word */
 463/* value = (short) (0x55); */
 464/* outw(value, ADDR_CHAN_REG(REG_C0H, n)); */
 465
 466        /*  Load the pre-load register low word */
 467/* value = (short)(0xaa55); */
 468/* outw(value, ADDR_CHAN_REG(REG_C0L, n)); */
 469
 470        /*  Write the Counter Control Register */
 471/* outw(value, ADDR_CHAN_REG(REG_C0C, 0)); */
 472
 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));
 479        }
 480
 481        outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
 482        udelay(1000);
 483        printk(KERN_INFO "Read back mode reg=0x%04x\n",
 484                        inw(ADDR_CHAN_REG(REG_C0M, n)));
 485
 486#endif
 487        printk(KERN_INFO "Current registres:\n");
 488
 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])));
 492        }
 493        return 1;
 494}
 495
 496/*
 497 * _detach is called to deconfigure a device.  It should deallocate
 498 * resources.
 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.
 503 */
 504static int s526_detach(struct comedi_device *dev)
 505{
 506        printk(KERN_INFO "comedi%d: s526: remove\n", dev->minor);
 507
 508        if (dev->iobase > 0)
 509                release_region(dev->iobase, S526_IOSIZE);
 510
 511        return 0;
 512}
 513
 514static int s526_gpct_rinsn(struct comedi_device *dev,
 515                           struct comedi_subdevice *s, struct comedi_insn *insn,
 516                           unsigned int *data)
 517{
 518        int i;                  /*  counts the Data */
 519        int counter_channel = CR_CHAN(insn->chanspec);
 520        unsigned short datalow;
 521        unsigned short datahigh;
 522
 523        /*  Check if (n > 0) */
 524        if (insn->n <= 0) {
 525                printk(KERN_ERR "s526: INSN_READ: n should be > 0\n");
 526                return -EINVAL;
 527        }
 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); */
 536        }
 537        return i;
 538}
 539
 540static int s526_gpct_insn_config(struct comedi_device *dev,
 541                                 struct comedi_subdevice *s,
 542                                 struct comedi_insn *insn, unsigned int *data)
 543{
 544        int subdev_channel = CR_CHAN(insn->chanspec);   /*  Unpack chanspec */
 545        int i;
 546        short value;
 547        union cmReg cmReg;
 548
 549        /* printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n",
 550                                                subdev_channel); */
 551
 552        for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++) {
 553                devpriv->s526_gpct_config[subdev_channel].data[i] =
 554                    insn->data[i];
 555/* printk("data[%d]=%x\n", i, insn->data[i]); */
 556        }
 557
 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:
 562                /*
 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
 567                 */
 568                printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring Encoder\n");
 569                devpriv->s526_gpct_config[subdev_channel].app =
 570                    PositionMeasurement;
 571
 572#if 0
 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;
 586
 587                outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
 588
 589                outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
 590                outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
 591
 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));
 596
 597                /*  Reset RCAP (fires one-shot) */
 598                outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel));
 599
 600#endif
 601
 602#if 1
 603                /*  Set Counter Mode Register */
 604                cmReg.value = insn->data[1] & 0xFFFF;
 605
 606/* printk("s526: Counter Mode register=%x\n", cmReg.value); */
 607                outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
 608
 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));
 615                         */
 616                }
 617#else
 618                /*  0 quadrature, 1 software control */
 619                cmReg.reg.countDirCtrl = 0;
 620
 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;
 626                else
 627                        cmReg.reg.clockSource = 0;
 628
 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) {
 634                }*/
 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;
 639
 640                /*  Set Counter Mode Register */
 641                cmReg.value = (short)(insn->data[1] & 0xFFFF);
 642                outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
 643
 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));
 647
 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));
 651
 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));
 656                }
 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));
 663                }
 664#endif
 665                break;
 666
 667        case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
 668                /*
 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
 674                 */
 675                printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring SPG\n");
 676                devpriv->s526_gpct_config[subdev_channel].app =
 677                    SinglePulseGeneration;
 678
 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));
 683
 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));
 687
 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));
 691
 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));
 696
 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));
 700
 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));
 704
 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));
 709                }
 710                break;
 711
 712        case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
 713                /*
 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
 719                 */
 720                printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring PTG\n");
 721                devpriv->s526_gpct_config[subdev_channel].app =
 722                    PulseTrainGeneration;
 723
 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));
 728
 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));
 732
 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));
 736
 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));
 741
 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));
 745
 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));
 749
 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));
 754                }
 755                break;
 756
 757        default:
 758                printk(KERN_ERR "s526: unsupported GPCT_insn_config\n");
 759                return -EINVAL;
 760                break;
 761        }
 762
 763        return insn->n;
 764}
 765
 766static int s526_gpct_winsn(struct comedi_device *dev,
 767                           struct comedi_subdevice *s, struct comedi_insn *insn,
 768                           unsigned int *data)
 769{
 770        int subdev_channel = CR_CHAN(insn->chanspec);   /*  Unpack chanspec */
 771        short value;
 772        union cmReg cmReg;
 773
 774        printk(KERN_INFO "s526: GPCT_INSN_WRITE on channel %d\n",
 775                                        subdev_channel);
 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,
 783                                                             subdev_channel));
 784                outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
 785                break;
 786
 787        case SinglePulseGeneration:
 788                printk(KERN_INFO "S526: INSN_WRITE: SPG\n");
 789                outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
 790                                                             subdev_channel));
 791                outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
 792                break;
 793
 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
 800                 */
 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] =
 804                            insn->data[0];
 805                        (devpriv->s526_gpct_config[subdev_channel]).data[1] =
 806                            insn->data[1];
 807                } else {
 808                        printk(KERN_ERR "s526: INSN_WRITE: PTG: Problem with Pulse params -> %d %d\n",
 809                                insn->data[0], insn->data[1]);
 810                        return -EINVAL;
 811                }
 812
 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));
 817                break;
 818        default:                /*  Impossible */
 819                printk
 820                    ("s526: INSN_WRITE: Functionality %d not implemented yet\n",
 821                     devpriv->s526_gpct_config[subdev_channel].app);
 822                return -EINVAL;
 823                break;
 824        }
 825        /*  return the number of samples written */
 826        return insn->n;
 827}
 828
 829#define ISR_ADC_DONE 0x4
 830static int s526_ai_insn_config(struct comedi_device *dev,
 831                               struct comedi_subdevice *s,
 832                               struct comedi_insn *insn, unsigned int *data)
 833{
 834        int result = -EINVAL;
 835
 836        if (insn->n < 1)
 837                return result;
 838
 839        result = insn->n;
 840
 841        /* data[0] : channels was set in relevant bits.
 842           data[1] : delay
 843         */
 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. */
 847
 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;
 852        if (data[1] > 0)
 853                devpriv->s526_ai_config |= 0x8000;      /* set the delay */
 854
 855        devpriv->s526_ai_config |= 0x0001;      /*  ADC start bit. */
 856
 857        return result;
 858}
 859
 860/*
 861 * "instructions" read/write data in "one-shot" or "software-triggered"
 862 * mode.
 863 */
 864static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
 865                         struct comedi_insn *insn, unsigned int *data)
 866{
 867        int n, i;
 868        int chan = CR_CHAN(insn->chanspec);
 869        unsigned short value;
 870        unsigned int d;
 871        unsigned int status;
 872
 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;
 877
 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))); */
 884
 885#define TIMEOUT 100
 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));
 891                                break;
 892                        }
 893                }
 894                if (i == TIMEOUT) {
 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)));
 899                        return -ETIMEDOUT;
 900                }
 901
 902                /* read data */
 903                d = inw(ADDR_REG(REG_ADD));
 904/* printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF)); */
 905
 906                /* munge data */
 907                data[n] = d ^ 0x8000;
 908        }
 909
 910        /* return the number of samples read/written */
 911        return n;
 912}
 913
 914static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
 915                         struct comedi_insn *insn, unsigned int *data)
 916{
 917        int i;
 918        int chan = CR_CHAN(insn->chanspec);
 919        unsigned short val;
 920
 921/* printk("s526_ao_winsn\n"); */
 922        val = chan << 1;
 923/* outw(val, dev->iobase + REG_DAC); */
 924        outw(val, ADDR_REG(REG_DAC));
 925
 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);
 932                 */
 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.*/
 938        }
 939
 940        /* return the number of samples read/written */
 941        return i;
 942}
 943
 944/* AO subdevices should have a read insn as well as a write insn.
 945 * Usually this means copying a value stored in devpriv. */
 946static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
 947                         struct comedi_insn *insn, unsigned int *data)
 948{
 949        int i;
 950        int chan = CR_CHAN(insn->chanspec);
 951
 952        for (i = 0; i < insn->n; i++)
 953                data[i] = devpriv->ao_readback[chan];
 954
 955        return i;
 956}
 957
 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 */
 963static int s526_dio_insn_bits(struct comedi_device *dev,
 964                              struct comedi_subdevice *s,
 965                              struct comedi_insn *insn, unsigned int *data)
 966{
 967        if (insn->n != 2)
 968                return -EINVAL;
 969
 970        /* The insn data is a mask in data[0] and the new data
 971         * in data[1], each channel cooresponding to a bit. */
 972        if (data[0]) {
 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));
 977        }
 978
 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; */
 985
 986        return 2;
 987}
 988
 989static int s526_dio_insn_config(struct comedi_device *dev,
 990                                struct comedi_subdevice *s,
 991                                struct comedi_insn *insn, unsigned int *data)
 992{
 993        int chan = CR_CHAN(insn->chanspec);
 994        int group, mask;
 995
 996        printk(KERN_INFO "S526 DIO insn_config\n");
 997
 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. */
1002
1003        group = chan >> 2;
1004        mask = 0xF << (group << 2);
1005        switch (data[0]) {
1006        case INSN_CONFIG_DIO_OUTPUT:
1007                /* bit 10/11 set the group 1/2's mode */
1008                s->state |= 1 << (group + 10);
1009                s->io_bits |= mask;
1010                break;
1011        case INSN_CONFIG_DIO_INPUT:
1012                s->state &= ~(1 << (group + 10)); /* 1 is output, 0 is input. */
1013                s->io_bits &= ~mask;
1014                break;
1015        case INSN_CONFIG_DIO_QUERY:
1016                data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
1017                return insn->n;
1018        default:
1019                return -EINVAL;
1020        }
1021        outw(s->state, ADDR_REG(REG_DIO));
1022
1023        return 1;
1024}
1025
1026/*
1027 * A convenient macro that defines init_module() and cleanup_module(),
1028 * as necessary.
1029 */
1030static int __init driver_s526_init_module(void)
1031{
1032        return comedi_driver_register(&driver_s526);
1033}
1034
1035static void __exit driver_s526_cleanup_module(void)
1036{
1037        comedi_driver_unregister(&driver_s526);
1038}
1039
1040module_init(driver_s526_init_module);
1041module_exit(driver_s526_cleanup_module);
1042
1043MODULE_AUTHOR("Comedi http://www.comedi.org");
1044MODULE_DESCRIPTION("Comedi low-level driver");
1045MODULE_LICENSE("GPL");
1046