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