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
  86struct counter_mode_register_t {
  87#if defined(__LITTLE_ENDIAN_BITFIELD)
  88        unsigned short coutSource:1;
  89        unsigned short coutPolarity:1;
  90        unsigned short autoLoadResetRcap:3;
  91        unsigned short hwCtEnableSource:2;
  92        unsigned short ctEnableCtrl:2;
  93        unsigned short clockSource:2;
  94        unsigned short countDir:1;
  95        unsigned short countDirCtrl:1;
  96        unsigned short outputRegLatchCtrl:1;
  97        unsigned short preloadRegSel:1;
  98        unsigned short reserved:1;
  99 #elif defined(__BIG_ENDIAN_BITFIELD)
 100        unsigned short reserved:1;
 101        unsigned short preloadRegSel:1;
 102        unsigned short outputRegLatchCtrl:1;
 103        unsigned short countDirCtrl:1;
 104        unsigned short countDir:1;
 105        unsigned short clockSource:2;
 106        unsigned short ctEnableCtrl:2;
 107        unsigned short hwCtEnableSource:2;
 108        unsigned short autoLoadResetRcap:3;
 109        unsigned short coutPolarity:1;
 110        unsigned short coutSource:1;
 111#else
 112#error Unknown bit field order
 113#endif
 114};
 115
 116union cmReg {
 117        struct counter_mode_register_t reg;
 118        unsigned short value;
 119};
 120
 121struct s526_private {
 122        unsigned int ao_readback[2];
 123        unsigned int gpct_config[4];
 124        unsigned short ai_config;
 125};
 126
 127static int s526_gpct_rinsn(struct comedi_device *dev,
 128                           struct comedi_subdevice *s,
 129                           struct comedi_insn *insn,
 130                           unsigned int *data)
 131{
 132        unsigned int chan = CR_CHAN(insn->chanspec);
 133        unsigned long chan_iobase = dev->iobase + chan * 8;
 134        unsigned int lo;
 135        unsigned int hi;
 136        int i;
 137
 138        for (i = 0; i < insn->n; i++) {
 139                /* Read the low word first */
 140                lo = inw(chan_iobase + REG_C0L) & 0xffff;
 141                hi = inw(chan_iobase + REG_C0H) & 0xff;
 142
 143                data[i] = (hi << 16) | lo;
 144        }
 145
 146        return insn->n;
 147}
 148
 149static int s526_gpct_insn_config(struct comedi_device *dev,
 150                                 struct comedi_subdevice *s,
 151                                 struct comedi_insn *insn,
 152                                 unsigned int *data)
 153{
 154        struct s526_private *devpriv = dev->private;
 155        unsigned int chan = CR_CHAN(insn->chanspec);
 156        unsigned long chan_iobase = dev->iobase + chan * 8;
 157        unsigned int val;
 158        union cmReg cmReg;
 159
 160        /*  Check what type of Counter the user requested, data[0] contains */
 161        /*  the Application type */
 162        switch (data[0]) {
 163        case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
 164                /*
 165                   data[0]: Application Type
 166                   data[1]: Counter Mode Register Value
 167                   data[2]: Pre-load Register Value
 168                   data[3]: Conter Control Register
 169                 */
 170                devpriv->gpct_config[chan] = data[0];
 171
 172#if 0
 173                /*  Example of Counter Application */
 174                /* One-shot (software trigger) */
 175                cmReg.reg.coutSource = 0;       /*  out RCAP */
 176                cmReg.reg.coutPolarity = 1;     /*  Polarity inverted */
 177                cmReg.reg.autoLoadResetRcap = 0;/*  Auto load disabled */
 178                cmReg.reg.hwCtEnableSource = 3; /*  NOT RCAP */
 179                cmReg.reg.ctEnableCtrl = 2;     /*  Hardware */
 180                cmReg.reg.clockSource = 2;      /*  Internal */
 181                cmReg.reg.countDir = 1; /*  Down */
 182                cmReg.reg.countDirCtrl = 1;     /*  Software */
 183                cmReg.reg.outputRegLatchCtrl = 0;       /*  latch on read */
 184                cmReg.reg.preloadRegSel = 0;    /*  PR0 */
 185                cmReg.reg.reserved = 0;
 186
 187                outw(cmReg.value, chan_iobase + REG_C0M);
 188
 189                outw(0x0001, chan_iobase + REG_C0H);
 190                outw(0x3C68, chan_iobase + REG_C0L);
 191
 192                /*  Reset the counter */
 193                outw(0x8000, chan_iobase + REG_C0C);
 194                /*  Load the counter from PR0 */
 195                outw(0x4000, chan_iobase + REG_C0C);
 196
 197                /*  Reset RCAP (fires one-shot) */
 198                outw(0x0008, chan_iobase + REG_C0C);
 199
 200#endif
 201
 202#if 1
 203                /*  Set Counter Mode Register */
 204                cmReg.value = data[1] & 0xffff;
 205                outw(cmReg.value, chan_iobase + REG_C0M);
 206
 207                /*  Reset the counter if it is software preload */
 208                if (cmReg.reg.autoLoadResetRcap == 0) {
 209                        /*  Reset the counter */
 210                        outw(0x8000, chan_iobase + REG_C0C);
 211                        /* Load the counter from PR0
 212                         * outw(0x4000, chan_iobase + REG_C0C);
 213                         */
 214                }
 215#else
 216                /*  0 quadrature, 1 software control */
 217                cmReg.reg.countDirCtrl = 0;
 218
 219                /*  data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
 220                if (data[1] == GPCT_X2)
 221                        cmReg.reg.clockSource = 1;
 222                else if (data[1] == GPCT_X4)
 223                        cmReg.reg.clockSource = 2;
 224                else
 225                        cmReg.reg.clockSource = 0;
 226
 227                /*  When to take into account the indexpulse: */
 228                /*if (data[2] == GPCT_IndexPhaseLowLow) {
 229                } else if (data[2] == GPCT_IndexPhaseLowHigh) {
 230                } else if (data[2] == GPCT_IndexPhaseHighLow) {
 231                } else if (data[2] == GPCT_IndexPhaseHighHigh) {
 232                }*/
 233                /*  Take into account the index pulse? */
 234                if (data[3] == GPCT_RESET_COUNTER_ON_INDEX)
 235                        /*  Auto load with INDEX^ */
 236                        cmReg.reg.autoLoadResetRcap = 4;
 237
 238                /*  Set Counter Mode Register */
 239                cmReg.value = data[1] & 0xffff;
 240                outw(cmReg.value, chan_iobase + REG_C0M);
 241
 242                /*  Load the pre-load register high word */
 243                val = (data[2] >> 16) & 0xffff;
 244                outw(val, chan_iobase + REG_C0H);
 245
 246                /*  Load the pre-load register low word */
 247                val = data[2] & 0xffff;
 248                outw(val, chan_iobase + REG_C0L);
 249
 250                /*  Write the Counter Control Register */
 251                if (data[3]) {
 252                        val = data[3] & 0xffff;
 253                        outw(val, chan_iobase + REG_C0C);
 254                }
 255                /*  Reset the counter if it is software preload */
 256                if (cmReg.reg.autoLoadResetRcap == 0) {
 257                        /*  Reset the counter */
 258                        outw(0x8000, chan_iobase + REG_C0C);
 259                        /*  Load the counter from PR0 */
 260                        outw(0x4000, chan_iobase + REG_C0C);
 261                }
 262#endif
 263                break;
 264
 265        case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
 266                /*
 267                   data[0]: Application Type
 268                   data[1]: Counter Mode Register Value
 269                   data[2]: Pre-load Register 0 Value
 270                   data[3]: Pre-load Register 1 Value
 271                   data[4]: Conter Control Register
 272                 */
 273                devpriv->gpct_config[chan] = data[0];
 274
 275                /*  Set Counter Mode Register */
 276                cmReg.value = data[1] & 0xffff;
 277                cmReg.reg.preloadRegSel = 0;    /*  PR0 */
 278                outw(cmReg.value, chan_iobase + REG_C0M);
 279
 280                /*  Load the pre-load register 0 high word */
 281                val = (data[2] >> 16) & 0xffff;
 282                outw(val, chan_iobase + REG_C0H);
 283
 284                /*  Load the pre-load register 0 low word */
 285                val = data[2] & 0xffff;
 286                outw(val, chan_iobase + REG_C0L);
 287
 288                /*  Set Counter Mode Register */
 289                cmReg.value = data[1] & 0xffff;
 290                cmReg.reg.preloadRegSel = 1;    /*  PR1 */
 291                outw(cmReg.value, chan_iobase + REG_C0M);
 292
 293                /*  Load the pre-load register 1 high word */
 294                val = (data[3] >> 16) & 0xffff;
 295                outw(val, chan_iobase + REG_C0H);
 296
 297                /*  Load the pre-load register 1 low word */
 298                val = data[3] & 0xffff;
 299                outw(val, chan_iobase + REG_C0L);
 300
 301                /*  Write the Counter Control Register */
 302                if (data[4]) {
 303                        val = data[4] & 0xffff;
 304                        outw(val, chan_iobase + REG_C0C);
 305                }
 306                break;
 307
 308        case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
 309                /*
 310                   data[0]: Application Type
 311                   data[1]: Counter Mode Register Value
 312                   data[2]: Pre-load Register 0 Value
 313                   data[3]: Pre-load Register 1 Value
 314                   data[4]: Conter Control Register
 315                 */
 316                devpriv->gpct_config[chan] = data[0];
 317
 318                /*  Set Counter Mode Register */
 319                cmReg.value = data[1] & 0xffff;
 320                cmReg.reg.preloadRegSel = 0;    /*  PR0 */
 321                outw(cmReg.value, chan_iobase + REG_C0M);
 322
 323                /*  Load the pre-load register 0 high word */
 324                val = (data[2] >> 16) & 0xffff;
 325                outw(val, chan_iobase + REG_C0H);
 326
 327                /*  Load the pre-load register 0 low word */
 328                val = data[2] & 0xffff;
 329                outw(val, chan_iobase + REG_C0L);
 330
 331                /*  Set Counter Mode Register */
 332                cmReg.value = data[1] & 0xffff;
 333                cmReg.reg.preloadRegSel = 1;    /*  PR1 */
 334                outw(cmReg.value, chan_iobase + REG_C0M);
 335
 336                /*  Load the pre-load register 1 high word */
 337                val = (data[3] >> 16) & 0xffff;
 338                outw(val, chan_iobase + REG_C0H);
 339
 340                /*  Load the pre-load register 1 low word */
 341                val = data[3] & 0xffff;
 342                outw(val, chan_iobase + REG_C0L);
 343
 344                /*  Write the Counter Control Register */
 345                if (data[4]) {
 346                        val = data[4] & 0xffff;
 347                        outw(val, chan_iobase + REG_C0C);
 348                }
 349                break;
 350
 351        default:
 352                return -EINVAL;
 353                break;
 354        }
 355
 356        return insn->n;
 357}
 358
 359static int s526_gpct_winsn(struct comedi_device *dev,
 360                           struct comedi_subdevice *s,
 361                           struct comedi_insn *insn,
 362                           unsigned int *data)
 363{
 364        struct s526_private *devpriv = dev->private;
 365        unsigned int chan = CR_CHAN(insn->chanspec);
 366        unsigned long chan_iobase = dev->iobase + chan * 8;
 367
 368        inw(chan_iobase + REG_C0M);     /* Is this read required? */
 369
 370        /*  Check what Application of Counter this channel is configured for */
 371        switch (devpriv->gpct_config[chan]) {
 372        case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
 373                /* data[0] contains the PULSE_WIDTH
 374                   data[1] contains the PULSE_PERIOD
 375                   @pre PULSE_PERIOD > PULSE_WIDTH > 0
 376                   The above periods must be expressed as a multiple of the
 377                   pulse frequency on the selected source
 378                 */
 379                if ((data[1] <= data[0]) || !data[0])
 380                        return -EINVAL;
 381
 382                /* Fall thru to write the PULSE_WIDTH */
 383
 384        case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
 385        case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
 386                outw((data[0] >> 16) & 0xffff, chan_iobase + REG_C0H);
 387                outw(data[0] & 0xffff, chan_iobase + REG_C0L);
 388                break;
 389
 390        default:
 391                return -EINVAL;
 392        }
 393
 394        return insn->n;
 395}
 396
 397#define ISR_ADC_DONE 0x4
 398static int s526_ai_insn_config(struct comedi_device *dev,
 399                               struct comedi_subdevice *s,
 400                               struct comedi_insn *insn, unsigned int *data)
 401{
 402        struct s526_private *devpriv = dev->private;
 403        int result = -EINVAL;
 404
 405        if (insn->n < 1)
 406                return result;
 407
 408        result = insn->n;
 409
 410        /* data[0] : channels was set in relevant bits.
 411           data[1] : delay
 412         */
 413        /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
 414         * enable channels here.  The channel should be enabled in the
 415         * INSN_READ handler. */
 416
 417        /*  Enable ADC interrupt */
 418        outw(ISR_ADC_DONE, dev->iobase + REG_IER);
 419        devpriv->ai_config = (data[0] & 0x3ff) << 5;
 420        if (data[1] > 0)
 421                devpriv->ai_config |= 0x8000;   /* set the delay */
 422
 423        devpriv->ai_config |= 0x0001;           /* ADC start bit */
 424
 425        return result;
 426}
 427
 428static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
 429                         struct comedi_insn *insn, unsigned int *data)
 430{
 431        struct s526_private *devpriv = dev->private;
 432        unsigned int chan = CR_CHAN(insn->chanspec);
 433        int n, i;
 434        unsigned short value;
 435        unsigned int d;
 436        unsigned int status;
 437
 438        /* Set configured delay, enable channel for this channel only,
 439         * select "ADC read" channel, set "ADC start" bit. */
 440        value = (devpriv->ai_config & 0x8000) |
 441                ((1 << 5) << chan) | (chan << 1) | 0x0001;
 442
 443        /* convert n samples */
 444        for (n = 0; n < insn->n; n++) {
 445                /* trigger conversion */
 446                outw(value, dev->iobase + REG_ADC);
 447
 448#define TIMEOUT 100
 449                /* wait for conversion to end */
 450                for (i = 0; i < TIMEOUT; i++) {
 451                        status = inw(dev->iobase + REG_ISR);
 452                        if (status & ISR_ADC_DONE) {
 453                                outw(ISR_ADC_DONE, dev->iobase + REG_ISR);
 454                                break;
 455                        }
 456                }
 457                if (i == TIMEOUT)
 458                        return -ETIMEDOUT;
 459
 460                /* read data */
 461                d = inw(dev->iobase + REG_ADD);
 462
 463                /* munge data */
 464                data[n] = d ^ 0x8000;
 465        }
 466
 467        /* return the number of samples read/written */
 468        return n;
 469}
 470
 471static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
 472                         struct comedi_insn *insn, unsigned int *data)
 473{
 474        struct s526_private *devpriv = dev->private;
 475        unsigned int chan = CR_CHAN(insn->chanspec);
 476        unsigned short val;
 477        int i;
 478
 479        val = chan << 1;
 480        outw(val, dev->iobase + REG_DAC);
 481
 482        for (i = 0; i < insn->n; i++) {
 483                outw(data[i], dev->iobase + REG_ADD);
 484                devpriv->ao_readback[chan] = data[i];
 485                /* starts the D/A conversion */
 486                outw(val + 1, dev->iobase + REG_DAC);
 487        }
 488
 489        return i;
 490}
 491
 492static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
 493                         struct comedi_insn *insn, unsigned int *data)
 494{
 495        struct s526_private *devpriv = dev->private;
 496        unsigned int chan = CR_CHAN(insn->chanspec);
 497        int i;
 498
 499        for (i = 0; i < insn->n; i++)
 500                data[i] = devpriv->ao_readback[chan];
 501
 502        return i;
 503}
 504
 505static int s526_dio_insn_bits(struct comedi_device *dev,
 506                              struct comedi_subdevice *s,
 507                              struct comedi_insn *insn, unsigned int *data)
 508{
 509        if (data[0]) {
 510                s->state &= ~data[0];
 511                s->state |= data[0] & data[1];
 512
 513                outw(s->state, dev->iobase + REG_DIO);
 514        }
 515
 516        data[1] = inw(dev->iobase + REG_DIO) & 0xff;
 517
 518        return insn->n;
 519}
 520
 521static int s526_dio_insn_config(struct comedi_device *dev,
 522                                struct comedi_subdevice *s,
 523                                struct comedi_insn *insn, unsigned int *data)
 524{
 525        unsigned int chan = CR_CHAN(insn->chanspec);
 526        int group, mask;
 527
 528        group = chan >> 2;
 529        mask = 0xF << (group << 2);
 530        switch (data[0]) {
 531        case INSN_CONFIG_DIO_OUTPUT:
 532                /* bit 10/11 set the group 1/2's mode */
 533                s->state |= 1 << (group + 10);
 534                s->io_bits |= mask;
 535                break;
 536        case INSN_CONFIG_DIO_INPUT:
 537                s->state &= ~(1 << (group + 10)); /* 1 is output, 0 is input. */
 538                s->io_bits &= ~mask;
 539                break;
 540        case INSN_CONFIG_DIO_QUERY:
 541                data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
 542                return insn->n;
 543        default:
 544                return -EINVAL;
 545        }
 546        outw(s->state, dev->iobase + REG_DIO);
 547
 548        return 1;
 549}
 550
 551static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 552{
 553        struct s526_private *devpriv;
 554        struct comedi_subdevice *s;
 555        int ret;
 556
 557        ret = comedi_request_region(dev, it->options[0], S526_IOSIZE);
 558        if (ret)
 559                return ret;
 560
 561        devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
 562        if (!devpriv)
 563                return -ENOMEM;
 564        dev->private = devpriv;
 565
 566        ret = comedi_alloc_subdevices(dev, 4);
 567        if (ret)
 568                return ret;
 569
 570        s = &dev->subdevices[0];
 571        /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
 572        s->type = COMEDI_SUBD_COUNTER;
 573        s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
 574        s->n_chan = 4;
 575        s->maxdata = 0x00ffffff;        /* 24 bit counter */
 576        s->insn_read = s526_gpct_rinsn;
 577        s->insn_config = s526_gpct_insn_config;
 578        s->insn_write = s526_gpct_winsn;
 579
 580        s = &dev->subdevices[1];
 581        /* analog input subdevice */
 582        s->type = COMEDI_SUBD_AI;
 583        s->subdev_flags = SDF_READABLE | SDF_DIFF;
 584        /* channels 0 to 7 are the regular differential inputs */
 585        /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
 586        s->n_chan = 10;
 587        s->maxdata = 0xffff;
 588        s->range_table = &range_bipolar10;
 589        s->len_chanlist = 16;
 590        s->insn_read = s526_ai_rinsn;
 591        s->insn_config = s526_ai_insn_config;
 592
 593        s = &dev->subdevices[2];
 594        /* analog output subdevice */
 595        s->type = COMEDI_SUBD_AO;
 596        s->subdev_flags = SDF_WRITABLE;
 597        s->n_chan = 4;
 598        s->maxdata = 0xffff;
 599        s->range_table = &range_bipolar10;
 600        s->insn_write = s526_ao_winsn;
 601        s->insn_read = s526_ao_rinsn;
 602
 603        s = &dev->subdevices[3];
 604        /* digital i/o subdevice */
 605        s->type = COMEDI_SUBD_DIO;
 606        s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 607        s->n_chan = 8;
 608        s->maxdata = 1;
 609        s->range_table = &range_digital;
 610        s->insn_bits = s526_dio_insn_bits;
 611        s->insn_config = s526_dio_insn_config;
 612
 613        return 1;
 614}
 615
 616static struct comedi_driver s526_driver = {
 617        .driver_name    = "s526",
 618        .module         = THIS_MODULE,
 619        .attach         = s526_attach,
 620        .detach         = comedi_legacy_detach,
 621};
 622module_comedi_driver(s526_driver);
 623
 624MODULE_AUTHOR("Comedi http://www.comedi.org");
 625MODULE_DESCRIPTION("Comedi low-level driver");
 626MODULE_LICENSE("GPL");
 627