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
 233static int s526_gpct_rinsn(struct comedi_device *dev,
 234                           struct comedi_subdevice *s, struct comedi_insn *insn,
 235                           unsigned int *data)
 236{
 237        int i;                  /*  counts the Data */
 238        int counter_channel = CR_CHAN(insn->chanspec);
 239        unsigned short datalow;
 240        unsigned short datahigh;
 241
 242        /*  Check if (n > 0) */
 243        if (insn->n <= 0) {
 244                printk(KERN_ERR "s526: INSN_READ: n should be > 0\n");
 245                return -EINVAL;
 246        }
 247        /*  Read the low word first */
 248        for (i = 0; i < insn->n; i++) {
 249                datalow = inw(ADDR_CHAN_REG(REG_C0L, counter_channel));
 250                datahigh = inw(ADDR_CHAN_REG(REG_C0H, counter_channel));
 251                data[i] = (int)(datahigh & 0x00FF);
 252                data[i] = (data[i] << 16) | (datalow & 0xFFFF);
 253                /* printk("s526 GPCT[%d]: %x(0x%04x, 0x%04x)\n",
 254                   counter_channel, data[i], datahigh, datalow); */
 255        }
 256        return i;
 257}
 258
 259static int s526_gpct_insn_config(struct comedi_device *dev,
 260                                 struct comedi_subdevice *s,
 261                                 struct comedi_insn *insn, unsigned int *data)
 262{
 263        int subdev_channel = CR_CHAN(insn->chanspec);   /*  Unpack chanspec */
 264        int i;
 265        short value;
 266        union cmReg cmReg;
 267
 268        /* printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n",
 269                                                subdev_channel); */
 270
 271        for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++) {
 272                devpriv->s526_gpct_config[subdev_channel].data[i] =
 273                    insn->data[i];
 274/* printk("data[%d]=%x\n", i, insn->data[i]); */
 275        }
 276
 277        /*  Check what type of Counter the user requested, data[0] contains */
 278        /*  the Application type */
 279        switch (insn->data[0]) {
 280        case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
 281                /*
 282                   data[0]: Application Type
 283                   data[1]: Counter Mode Register Value
 284                   data[2]: Pre-load Register Value
 285                   data[3]: Conter Control Register
 286                 */
 287                printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring Encoder\n");
 288                devpriv->s526_gpct_config[subdev_channel].app =
 289                    PositionMeasurement;
 290
 291#if 0
 292                /*  Example of Counter Application */
 293                /* One-shot (software trigger) */
 294                cmReg.reg.coutSource = 0;       /*  out RCAP */
 295                cmReg.reg.coutPolarity = 1;     /*  Polarity inverted */
 296                cmReg.reg.autoLoadResetRcap = 0;/*  Auto load disabled */
 297                cmReg.reg.hwCtEnableSource = 3; /*  NOT RCAP */
 298                cmReg.reg.ctEnableCtrl = 2;     /*  Hardware */
 299                cmReg.reg.clockSource = 2;      /*  Internal */
 300                cmReg.reg.countDir = 1; /*  Down */
 301                cmReg.reg.countDirCtrl = 1;     /*  Software */
 302                cmReg.reg.outputRegLatchCtrl = 0;       /*  latch on read */
 303                cmReg.reg.preloadRegSel = 0;    /*  PR0 */
 304                cmReg.reg.reserved = 0;
 305
 306                outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
 307
 308                outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
 309                outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
 310
 311                /*  Reset the counter */
 312                outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
 313                /*  Load the counter from PR0 */
 314                outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
 315
 316                /*  Reset RCAP (fires one-shot) */
 317                outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel));
 318
 319#endif
 320
 321#if 1
 322                /*  Set Counter Mode Register */
 323                cmReg.value = insn->data[1] & 0xFFFF;
 324
 325/* printk("s526: Counter Mode register=%x\n", cmReg.value); */
 326                outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
 327
 328                /*  Reset the counter if it is software preload */
 329                if (cmReg.reg.autoLoadResetRcap == 0) {
 330                        /*  Reset the counter */
 331                        outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
 332                        /* Load the counter from PR0
 333                         * outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
 334                         */
 335                }
 336#else
 337                /*  0 quadrature, 1 software control */
 338                cmReg.reg.countDirCtrl = 0;
 339
 340                /*  data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
 341                if (insn->data[1] == GPCT_X2)
 342                        cmReg.reg.clockSource = 1;
 343                else if (insn->data[1] == GPCT_X4)
 344                        cmReg.reg.clockSource = 2;
 345                else
 346                        cmReg.reg.clockSource = 0;
 347
 348                /*  When to take into account the indexpulse: */
 349                /*if (insn->data[2] == GPCT_IndexPhaseLowLow) {
 350                } else if (insn->data[2] == GPCT_IndexPhaseLowHigh) {
 351                } else if (insn->data[2] == GPCT_IndexPhaseHighLow) {
 352                } else if (insn->data[2] == GPCT_IndexPhaseHighHigh) {
 353                }*/
 354                /*  Take into account the index pulse? */
 355                if (insn->data[3] == GPCT_RESET_COUNTER_ON_INDEX)
 356                        /*  Auto load with INDEX^ */
 357                        cmReg.reg.autoLoadResetRcap = 4;
 358
 359                /*  Set Counter Mode Register */
 360                cmReg.value = (short)(insn->data[1] & 0xFFFF);
 361                outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
 362
 363                /*  Load the pre-load register high word */
 364                value = (short)((insn->data[2] >> 16) & 0xFFFF);
 365                outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
 366
 367                /*  Load the pre-load register low word */
 368                value = (short)(insn->data[2] & 0xFFFF);
 369                outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
 370
 371                /*  Write the Counter Control Register */
 372                if (insn->data[3] != 0) {
 373                        value = (short)(insn->data[3] & 0xFFFF);
 374                        outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
 375                }
 376                /*  Reset the counter if it is software preload */
 377                if (cmReg.reg.autoLoadResetRcap == 0) {
 378                        /*  Reset the counter */
 379                        outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
 380                        /*  Load the counter from PR0 */
 381                        outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
 382                }
 383#endif
 384                break;
 385
 386        case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
 387                /*
 388                   data[0]: Application Type
 389                   data[1]: Counter Mode Register Value
 390                   data[2]: Pre-load Register 0 Value
 391                   data[3]: Pre-load Register 1 Value
 392                   data[4]: Conter Control Register
 393                 */
 394                printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring SPG\n");
 395                devpriv->s526_gpct_config[subdev_channel].app =
 396                    SinglePulseGeneration;
 397
 398                /*  Set Counter Mode Register */
 399                cmReg.value = (short)(insn->data[1] & 0xFFFF);
 400                cmReg.reg.preloadRegSel = 0;    /*  PR0 */
 401                outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
 402
 403                /*  Load the pre-load register 0 high word */
 404                value = (short)((insn->data[2] >> 16) & 0xFFFF);
 405                outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
 406
 407                /*  Load the pre-load register 0 low word */
 408                value = (short)(insn->data[2] & 0xFFFF);
 409                outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
 410
 411                /*  Set Counter Mode Register */
 412                cmReg.value = (short)(insn->data[1] & 0xFFFF);
 413                cmReg.reg.preloadRegSel = 1;    /*  PR1 */
 414                outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
 415
 416                /*  Load the pre-load register 1 high word */
 417                value = (short)((insn->data[3] >> 16) & 0xFFFF);
 418                outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
 419
 420                /*  Load the pre-load register 1 low word */
 421                value = (short)(insn->data[3] & 0xFFFF);
 422                outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
 423
 424                /*  Write the Counter Control Register */
 425                if (insn->data[4] != 0) {
 426                        value = (short)(insn->data[4] & 0xFFFF);
 427                        outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
 428                }
 429                break;
 430
 431        case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
 432                /*
 433                   data[0]: Application Type
 434                   data[1]: Counter Mode Register Value
 435                   data[2]: Pre-load Register 0 Value
 436                   data[3]: Pre-load Register 1 Value
 437                   data[4]: Conter Control Register
 438                 */
 439                printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring PTG\n");
 440                devpriv->s526_gpct_config[subdev_channel].app =
 441                    PulseTrainGeneration;
 442
 443                /*  Set Counter Mode Register */
 444                cmReg.value = (short)(insn->data[1] & 0xFFFF);
 445                cmReg.reg.preloadRegSel = 0;    /*  PR0 */
 446                outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
 447
 448                /*  Load the pre-load register 0 high word */
 449                value = (short)((insn->data[2] >> 16) & 0xFFFF);
 450                outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
 451
 452                /*  Load the pre-load register 0 low word */
 453                value = (short)(insn->data[2] & 0xFFFF);
 454                outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
 455
 456                /*  Set Counter Mode Register */
 457                cmReg.value = (short)(insn->data[1] & 0xFFFF);
 458                cmReg.reg.preloadRegSel = 1;    /*  PR1 */
 459                outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
 460
 461                /*  Load the pre-load register 1 high word */
 462                value = (short)((insn->data[3] >> 16) & 0xFFFF);
 463                outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
 464
 465                /*  Load the pre-load register 1 low word */
 466                value = (short)(insn->data[3] & 0xFFFF);
 467                outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
 468
 469                /*  Write the Counter Control Register */
 470                if (insn->data[4] != 0) {
 471                        value = (short)(insn->data[4] & 0xFFFF);
 472                        outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
 473                }
 474                break;
 475
 476        default:
 477                printk(KERN_ERR "s526: unsupported GPCT_insn_config\n");
 478                return -EINVAL;
 479                break;
 480        }
 481
 482        return insn->n;
 483}
 484
 485static int s526_gpct_winsn(struct comedi_device *dev,
 486                           struct comedi_subdevice *s, struct comedi_insn *insn,
 487                           unsigned int *data)
 488{
 489        int subdev_channel = CR_CHAN(insn->chanspec);   /*  Unpack chanspec */
 490        short value;
 491        union cmReg cmReg;
 492
 493        printk(KERN_INFO "s526: GPCT_INSN_WRITE on channel %d\n",
 494                                        subdev_channel);
 495        cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel));
 496        printk(KERN_INFO "s526: Counter Mode Register: %x\n", cmReg.value);
 497        /*  Check what Application of Counter this channel is configured for */
 498        switch (devpriv->s526_gpct_config[subdev_channel].app) {
 499        case PositionMeasurement:
 500                printk(KERN_INFO "S526: INSN_WRITE: PM\n");
 501                outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
 502                                                             subdev_channel));
 503                outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
 504                break;
 505
 506        case SinglePulseGeneration:
 507                printk(KERN_INFO "S526: INSN_WRITE: SPG\n");
 508                outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
 509                                                             subdev_channel));
 510                outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
 511                break;
 512
 513        case PulseTrainGeneration:
 514                /* data[0] contains the PULSE_WIDTH
 515                   data[1] contains the PULSE_PERIOD
 516                   @pre PULSE_PERIOD > PULSE_WIDTH > 0
 517                   The above periods must be expressed as a multiple of the
 518                   pulse frequency on the selected source
 519                 */
 520                printk(KERN_INFO "S526: INSN_WRITE: PTG\n");
 521                if ((insn->data[1] > insn->data[0]) && (insn->data[0] > 0)) {
 522                        (devpriv->s526_gpct_config[subdev_channel]).data[0] =
 523                            insn->data[0];
 524                        (devpriv->s526_gpct_config[subdev_channel]).data[1] =
 525                            insn->data[1];
 526                } else {
 527                        printk(KERN_ERR "s526: INSN_WRITE: PTG: Problem with Pulse params -> %d %d\n",
 528                                insn->data[0], insn->data[1]);
 529                        return -EINVAL;
 530                }
 531
 532                value = (short)((*data >> 16) & 0xFFFF);
 533                outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
 534                value = (short)(*data & 0xFFFF);
 535                outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
 536                break;
 537        default:                /*  Impossible */
 538                printk
 539                    ("s526: INSN_WRITE: Functionality %d not implemented yet\n",
 540                     devpriv->s526_gpct_config[subdev_channel].app);
 541                return -EINVAL;
 542                break;
 543        }
 544        /*  return the number of samples written */
 545        return insn->n;
 546}
 547
 548#define ISR_ADC_DONE 0x4
 549static int s526_ai_insn_config(struct comedi_device *dev,
 550                               struct comedi_subdevice *s,
 551                               struct comedi_insn *insn, unsigned int *data)
 552{
 553        int result = -EINVAL;
 554
 555        if (insn->n < 1)
 556                return result;
 557
 558        result = insn->n;
 559
 560        /* data[0] : channels was set in relevant bits.
 561           data[1] : delay
 562         */
 563        /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
 564         * enable channels here.  The channel should be enabled in the
 565         * INSN_READ handler. */
 566
 567        /*  Enable ADC interrupt */
 568        outw(ISR_ADC_DONE, ADDR_REG(REG_IER));
 569/* printk("s526: ADC current value: 0x%04x\n", inw(ADDR_REG(REG_ADC))); */
 570        devpriv->s526_ai_config = (data[0] & 0x3FF) << 5;
 571        if (data[1] > 0)
 572                devpriv->s526_ai_config |= 0x8000;      /* set the delay */
 573
 574        devpriv->s526_ai_config |= 0x0001;      /*  ADC start bit. */
 575
 576        return result;
 577}
 578
 579/*
 580 * "instructions" read/write data in "one-shot" or "software-triggered"
 581 * mode.
 582 */
 583static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
 584                         struct comedi_insn *insn, unsigned int *data)
 585{
 586        int n, i;
 587        int chan = CR_CHAN(insn->chanspec);
 588        unsigned short value;
 589        unsigned int d;
 590        unsigned int status;
 591
 592        /* Set configured delay, enable channel for this channel only,
 593         * select "ADC read" channel, set "ADC start" bit. */
 594        value = (devpriv->s526_ai_config & 0x8000) |
 595            ((1 << 5) << chan) | (chan << 1) | 0x0001;
 596
 597        /* convert n samples */
 598        for (n = 0; n < insn->n; n++) {
 599                /* trigger conversion */
 600                outw(value, ADDR_REG(REG_ADC));
 601/* printk("s526: Wrote 0x%04x to ADC\n", value); */
 602/* printk("s526: ADC reg=0x%04x\n", inw(ADDR_REG(REG_ADC))); */
 603
 604#define TIMEOUT 100
 605                /* wait for conversion to end */
 606                for (i = 0; i < TIMEOUT; i++) {
 607                        status = inw(ADDR_REG(REG_ISR));
 608                        if (status & ISR_ADC_DONE) {
 609                                outw(ISR_ADC_DONE, ADDR_REG(REG_ISR));
 610                                break;
 611                        }
 612                }
 613                if (i == TIMEOUT) {
 614                        /* printk() should be used instead of printk()
 615                         * whenever the code can be called from real-time. */
 616                        printk(KERN_ERR "s526: ADC(0x%04x) timeout\n",
 617                               inw(ADDR_REG(REG_ISR)));
 618                        return -ETIMEDOUT;
 619                }
 620
 621                /* read data */
 622                d = inw(ADDR_REG(REG_ADD));
 623/* printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF)); */
 624
 625                /* munge data */
 626                data[n] = d ^ 0x8000;
 627        }
 628
 629        /* return the number of samples read/written */
 630        return n;
 631}
 632
 633static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
 634                         struct comedi_insn *insn, unsigned int *data)
 635{
 636        int i;
 637        int chan = CR_CHAN(insn->chanspec);
 638        unsigned short val;
 639
 640/* printk("s526_ao_winsn\n"); */
 641        val = chan << 1;
 642/* outw(val, dev->iobase + REG_DAC); */
 643        outw(val, ADDR_REG(REG_DAC));
 644
 645        /* Writing a list of values to an AO channel is probably not
 646         * very useful, but that's how the interface is defined. */
 647        for (i = 0; i < insn->n; i++) {
 648                /* a typical programming sequence */
 649                /* write the data to preload register
 650                 * outw(data[i], dev->iobase + REG_ADD);
 651                 */
 652                /* write the data to preload register */
 653                outw(data[i], ADDR_REG(REG_ADD));
 654                devpriv->ao_readback[chan] = data[i];
 655/* outw(val + 1, dev->iobase + REG_DAC);  starts the D/A conversion. */
 656                outw(val + 1, ADDR_REG(REG_DAC)); /*starts the D/A conversion.*/
 657        }
 658
 659        /* return the number of samples read/written */
 660        return i;
 661}
 662
 663/* AO subdevices should have a read insn as well as a write insn.
 664 * Usually this means copying a value stored in devpriv. */
 665static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
 666                         struct comedi_insn *insn, unsigned int *data)
 667{
 668        int i;
 669        int chan = CR_CHAN(insn->chanspec);
 670
 671        for (i = 0; i < insn->n; i++)
 672                data[i] = devpriv->ao_readback[chan];
 673
 674        return i;
 675}
 676
 677/* DIO devices are slightly special.  Although it is possible to
 678 * implement the insn_read/insn_write interface, it is much more
 679 * useful to applications if you implement the insn_bits interface.
 680 * This allows packed reading/writing of the DIO channels.  The
 681 * comedi core can convert between insn_bits and insn_read/write */
 682static int s526_dio_insn_bits(struct comedi_device *dev,
 683                              struct comedi_subdevice *s,
 684                              struct comedi_insn *insn, unsigned int *data)
 685{
 686        if (insn->n != 2)
 687                return -EINVAL;
 688
 689        /* The insn data is a mask in data[0] and the new data
 690         * in data[1], each channel cooresponding to a bit. */
 691        if (data[0]) {
 692                s->state &= ~data[0];
 693                s->state |= data[0] & data[1];
 694                /* Write out the new digital output lines */
 695                outw(s->state, ADDR_REG(REG_DIO));
 696        }
 697
 698        /* on return, data[1] contains the value of the digital
 699         * input and output lines. */
 700        data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF; /* low 8 bits are the data */
 701        /* or we could just return the software copy of the output values if
 702         * it was a purely digital output subdevice */
 703        /* data[1]=s->state & 0xFF; */
 704
 705        return 2;
 706}
 707
 708static int s526_dio_insn_config(struct comedi_device *dev,
 709                                struct comedi_subdevice *s,
 710                                struct comedi_insn *insn, unsigned int *data)
 711{
 712        int chan = CR_CHAN(insn->chanspec);
 713        int group, mask;
 714
 715        printk(KERN_INFO "S526 DIO insn_config\n");
 716
 717        /* The input or output configuration of each digital line is
 718         * configured by a special insn_config instruction.  chanspec
 719         * contains the channel to be changed, and data[0] contains the
 720         * value COMEDI_INPUT or COMEDI_OUTPUT. */
 721
 722        group = chan >> 2;
 723        mask = 0xF << (group << 2);
 724        switch (data[0]) {
 725        case INSN_CONFIG_DIO_OUTPUT:
 726                /* bit 10/11 set the group 1/2's mode */
 727                s->state |= 1 << (group + 10);
 728                s->io_bits |= mask;
 729                break;
 730        case INSN_CONFIG_DIO_INPUT:
 731                s->state &= ~(1 << (group + 10)); /* 1 is output, 0 is input. */
 732                s->io_bits &= ~mask;
 733                break;
 734        case INSN_CONFIG_DIO_QUERY:
 735                data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
 736                return insn->n;
 737        default:
 738                return -EINVAL;
 739        }
 740        outw(s->state, ADDR_REG(REG_DIO));
 741
 742        return 1;
 743}
 744
 745static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 746{
 747        struct comedi_subdevice *s;
 748        int iobase;
 749        int i, n;
 750/* short value; */
 751/* int subdev_channel = 0; */
 752        union cmReg cmReg;
 753
 754        printk(KERN_INFO "comedi%d: s526: ", dev->minor);
 755
 756        iobase = it->options[0];
 757        if (!iobase || !request_region(iobase, S526_IOSIZE, thisboard->name)) {
 758                comedi_error(dev, "I/O port conflict");
 759                return -EIO;
 760        }
 761        dev->iobase = iobase;
 762
 763        printk("iobase=0x%lx\n", dev->iobase);
 764
 765        /*** make it a little quieter, exw, 8/29/06
 766        for (i = 0; i < S526_NUM_PORTS; i++) {
 767                printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]),
 768                                inw(ADDR_REG(s526_ports[i])));
 769        }
 770        ***/
 771
 772/*
 773 * Initialize dev->board_name.  Note that we can use the "thisboard"
 774 * macro now, since we just initialized it in the last line.
 775 */
 776        dev->board_ptr = &s526_boards[0];
 777
 778        dev->board_name = thisboard->name;
 779
 780/*
 781 * Allocate the private structure area.  alloc_private() is a
 782 * convenient macro defined in comedidev.h.
 783 */
 784        if (alloc_private(dev, sizeof(struct s526_private)) < 0)
 785                return -ENOMEM;
 786
 787/*
 788 * Allocate the subdevice structures.  alloc_subdevice() is a
 789 * convenient macro defined in comedidev.h.
 790 */
 791        dev->n_subdevices = 4;
 792        if (alloc_subdevices(dev, dev->n_subdevices) < 0)
 793                return -ENOMEM;
 794
 795        s = dev->subdevices + 0;
 796        /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
 797        s->type = COMEDI_SUBD_COUNTER;
 798        s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
 799        /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */
 800        s->n_chan = thisboard->gpct_chans;
 801        s->maxdata = 0x00ffffff;        /* 24 bit counter */
 802        s->insn_read = s526_gpct_rinsn;
 803        s->insn_config = s526_gpct_insn_config;
 804        s->insn_write = s526_gpct_winsn;
 805
 806        /* Command are not implemented yet, however they are necessary to
 807           allocate the necessary memory for the comedi_async struct (used
 808           to trigger the GPCT in case of pulsegenerator function */
 809        /* s->do_cmd = s526_gpct_cmd; */
 810        /* s->do_cmdtest = s526_gpct_cmdtest; */
 811        /* s->cancel = s526_gpct_cancel; */
 812
 813        s = dev->subdevices + 1;
 814        /* dev->read_subdev=s; */
 815        /* analog input subdevice */
 816        s->type = COMEDI_SUBD_AI;
 817        /* we support differential */
 818        s->subdev_flags = SDF_READABLE | SDF_DIFF;
 819        /* channels 0 to 7 are the regular differential inputs */
 820        /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
 821        s->n_chan = 10;
 822        s->maxdata = 0xffff;
 823        s->range_table = &range_bipolar10;
 824        s->len_chanlist = 16;   /* This is the maximum chanlist length that
 825                                   the board can handle */
 826        s->insn_read = s526_ai_rinsn;
 827        s->insn_config = s526_ai_insn_config;
 828
 829        s = dev->subdevices + 2;
 830        /* analog output subdevice */
 831        s->type = COMEDI_SUBD_AO;
 832        s->subdev_flags = SDF_WRITABLE;
 833        s->n_chan = 4;
 834        s->maxdata = 0xffff;
 835        s->range_table = &range_bipolar10;
 836        s->insn_write = s526_ao_winsn;
 837        s->insn_read = s526_ao_rinsn;
 838
 839        s = dev->subdevices + 3;
 840        /* digital i/o subdevice */
 841        if (thisboard->have_dio) {
 842                s->type = COMEDI_SUBD_DIO;
 843                s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 844                s->n_chan = 8;
 845                s->maxdata = 1;
 846                s->range_table = &range_digital;
 847                s->insn_bits = s526_dio_insn_bits;
 848                s->insn_config = s526_dio_insn_config;
 849        } else {
 850                s->type = COMEDI_SUBD_UNUSED;
 851        }
 852
 853        printk(KERN_INFO "attached\n");
 854
 855        return 1;
 856
 857#if 0
 858        /*  Example of Counter Application */
 859        /* One-shot (software trigger) */
 860        cmReg.reg.coutSource = 0;       /*  out RCAP */
 861        cmReg.reg.coutPolarity = 1;     /*  Polarity inverted */
 862        cmReg.reg.autoLoadResetRcap = 1;/*  Auto load 0:disabled, 1:enabled */
 863        cmReg.reg.hwCtEnableSource = 3; /*  NOT RCAP */
 864        cmReg.reg.ctEnableCtrl = 2;     /*  Hardware */
 865        cmReg.reg.clockSource = 2;      /*  Internal */
 866        cmReg.reg.countDir = 1; /*  Down */
 867        cmReg.reg.countDirCtrl = 1;     /*  Software */
 868        cmReg.reg.outputRegLatchCtrl = 0;       /*  latch on read */
 869        cmReg.reg.preloadRegSel = 0;    /*  PR0 */
 870        cmReg.reg.reserved = 0;
 871
 872        outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
 873
 874        outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
 875        outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
 876
 877        /*  Reset the counter */
 878        outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
 879        /*  Load the counter from PR0 */
 880        outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
 881        /*  Reset RCAP (fires one-shot) */
 882        outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel));
 883
 884#else
 885
 886        /*  Set Counter Mode Register */
 887        cmReg.reg.coutSource = 0;       /*  out RCAP */
 888        cmReg.reg.coutPolarity = 0;     /*  Polarity inverted */
 889        cmReg.reg.autoLoadResetRcap = 0;        /*  Auto load disabled */
 890        cmReg.reg.hwCtEnableSource = 2; /*  NOT RCAP */
 891        cmReg.reg.ctEnableCtrl = 1;     /*  1: Software,  >1 : Hardware */
 892        cmReg.reg.clockSource = 3;      /*  x4 */
 893        cmReg.reg.countDir = 0; /*  up */
 894        cmReg.reg.countDirCtrl = 0;     /*  quadrature */
 895        cmReg.reg.outputRegLatchCtrl = 0;       /*  latch on read */
 896        cmReg.reg.preloadRegSel = 0;    /*  PR0 */
 897        cmReg.reg.reserved = 0;
 898
 899        n = 0;
 900        printk(KERN_INFO "Mode reg=0x%04x, 0x%04lx\n",
 901                cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
 902        outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
 903        udelay(1000);
 904        printk(KERN_INFO "Read back mode reg=0x%04x\n",
 905                inw(ADDR_CHAN_REG(REG_C0M, n)));
 906
 907        /*  Load the pre-load register high word */
 908/* value = (short) (0x55); */
 909/* outw(value, ADDR_CHAN_REG(REG_C0H, n)); */
 910
 911        /*  Load the pre-load register low word */
 912/* value = (short)(0xaa55); */
 913/* outw(value, ADDR_CHAN_REG(REG_C0L, n)); */
 914
 915        /*  Write the Counter Control Register */
 916/* outw(value, ADDR_CHAN_REG(REG_C0C, 0)); */
 917
 918        /*  Reset the counter if it is software preload */
 919        if (cmReg.reg.autoLoadResetRcap == 0) {
 920                /*  Reset the counter */
 921                outw(0x8000, ADDR_CHAN_REG(REG_C0C, n));
 922                /*  Load the counter from PR0 */
 923                outw(0x4000, ADDR_CHAN_REG(REG_C0C, n));
 924        }
 925
 926        outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
 927        udelay(1000);
 928        printk(KERN_INFO "Read back mode reg=0x%04x\n",
 929                        inw(ADDR_CHAN_REG(REG_C0M, n)));
 930
 931#endif
 932        printk(KERN_INFO "Current registres:\n");
 933
 934        for (i = 0; i < S526_NUM_PORTS; i++) {
 935                printk(KERN_INFO "0x%02lx: 0x%04x\n",
 936                        ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i])));
 937        }
 938        return 1;
 939}
 940
 941static void s526_detach(struct comedi_device *dev)
 942{
 943        if (dev->iobase > 0)
 944                release_region(dev->iobase, S526_IOSIZE);
 945}
 946
 947static struct comedi_driver s526_driver = {
 948        .driver_name    = "s526",
 949        .module         = THIS_MODULE,
 950        .attach         = s526_attach,
 951        .detach         = s526_detach,
 952        .board_name     = &s526_boards[0].name,
 953        .offset         = sizeof(struct s526_board),
 954        .num_names      = ARRAY_SIZE(s526_boards),
 955};
 956module_comedi_driver(s526_driver);
 957
 958MODULE_AUTHOR("Comedi http://www.comedi.org");
 959MODULE_DESCRIPTION("Comedi low-level driver");
 960MODULE_LICENSE("GPL");
 961