linux/drivers/staging/comedi/drivers/s526.c
<<
>>
Prefs
   1/*
   2 * 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
  19/*
  20 * Driver: s526
  21 * Description: Sensoray 526 driver
  22 * Devices: [Sensoray] 526 (s526)
  23 * Author: Richie
  24 *         Everett Wang <everett.wang@everteq.com>
  25 * Updated: Thu, 14 Sep. 2006
  26 * Status: experimental
  27 *
  28 * Encoder works
  29 * Analog input works
  30 * Analog output works
  31 * PWM output works
  32 * Commands are not supported yet.
  33 *
  34 * Configuration Options:
  35 *   [0] - I/O port base address
  36 */
  37
  38#include <linux/module.h>
  39#include "../comedidev.h"
  40
  41/*
  42 * Register I/O map
  43 */
  44#define S526_TIMER_REG          0x00
  45#define S526_TIMER_LOAD(x)      (((x) & 0xff) << 8)
  46#define S526_TIMER_MODE         ((x) << 1)
  47#define S526_TIMER_MANUAL       S526_TIMER_MODE(0)
  48#define S526_TIMER_AUTO         S526_TIMER_MODE(1)
  49#define S526_TIMER_RESTART      BIT(0)
  50#define S526_WDOG_REG           0x02
  51#define S526_WDOG_INVERTED      BIT(4)
  52#define S526_WDOG_ENA           BIT(3)
  53#define S526_WDOG_INTERVAL(x)   (((x) & 0x7) << 0)
  54#define S526_AO_CTRL_REG        0x04
  55#define S526_AO_CTRL_RESET      BIT(3)
  56#define S526_AO_CTRL_CHAN(x)    (((x) & 0x3) << 1)
  57#define S526_AO_CTRL_START      BIT(0)
  58#define S526_AI_CTRL_REG        0x06
  59#define S526_AI_CTRL_DELAY      BIT(15)
  60#define S526_AI_CTRL_CONV(x)    (1 << (5 + ((x) & 0x9)))
  61#define S526_AI_CTRL_READ(x)    (((x) & 0xf) << 1)
  62#define S526_AI_CTRL_START      BIT(0)
  63#define S526_AO_REG             0x08
  64#define S526_AI_REG             0x08
  65#define S526_DIO_CTRL_REG       0x0a
  66#define S526_DIO_CTRL_DIO3_NEG  BIT(15) /* irq on DIO3 neg/pos edge */
  67#define S526_DIO_CTRL_DIO2_NEG  BIT(14) /* irq on DIO2 neg/pos edge */
  68#define S526_DIO_CTRL_DIO1_NEG  BIT(13) /* irq on DIO1 neg/pos edge */
  69#define S526_DIO_CTRL_DIO0_NEG  BIT(12) /* irq on DIO0 neg/pos edge */
  70#define S526_DIO_CTRL_GRP2_OUT  BIT(11)
  71#define S526_DIO_CTRL_GRP1_OUT  BIT(10)
  72#define S526_DIO_CTRL_GRP2_NEG  BIT(8)  /* irq on DIO[4-7] neg/pos edge */
  73#define S526_INT_ENA_REG        0x0c
  74#define S526_INT_STATUS_REG     0x0e
  75#define S526_INT_DIO(x)         BIT(8 + ((x) & 0x7))
  76#define S526_INT_EEPROM         BIT(7)  /* status only */
  77#define S526_INT_CNTR(x)        BIT(3 + (3 - ((x) & 0x3)))
  78#define S526_INT_AI             BIT(2)
  79#define S526_INT_AO             BIT(1)
  80#define S526_INT_TIMER          BIT(0)
  81#define S526_MISC_REG           0x10
  82#define S526_MISC_LED_OFF       BIT(0)
  83#define S526_GPCT_LSB_REG(x)    (0x12 + ((x) * 8))
  84#define S526_GPCT_MSB_REG(x)    (0x14 + ((x) * 8))
  85#define S526_GPCT_MODE_REG(x)   (0x16 + ((x) * 8))
  86#define S526_GPCT_MODE_COUT_SRC(x)      ((x) << 0)
  87#define S526_GPCT_MODE_COUT_SRC_MASK    S526_GPCT_MODE_COUT_SRC(0x1)
  88#define S526_GPCT_MODE_COUT_SRC_RCAP    S526_GPCT_MODE_COUT_SRC(0)
  89#define S526_GPCT_MODE_COUT_SRC_RTGL    S526_GPCT_MODE_COUT_SRC(1)
  90#define S526_GPCT_MODE_COUT_POL(x)      ((x) << 1)
  91#define S526_GPCT_MODE_COUT_POL_MASK    S526_GPCT_MODE_COUT_POL(0x1)
  92#define S526_GPCT_MODE_COUT_POL_NORM    S526_GPCT_MODE_COUT_POL(0)
  93#define S526_GPCT_MODE_COUT_POL_INV     S526_GPCT_MODE_COUT_POL(1)
  94#define S526_GPCT_MODE_AUTOLOAD(x)      ((x) << 2)
  95#define S526_GPCT_MODE_AUTOLOAD_MASK    S526_GPCT_MODE_AUTOLOAD(0x7)
  96#define S526_GPCT_MODE_AUTOLOAD_NONE    S526_GPCT_MODE_AUTOLOAD(0)
  97/* these 3 bits can be OR'ed */
  98#define S526_GPCT_MODE_AUTOLOAD_RO      S526_GPCT_MODE_AUTOLOAD(0x1)
  99#define S526_GPCT_MODE_AUTOLOAD_IXFALL  S526_GPCT_MODE_AUTOLOAD(0x2)
 100#define S526_GPCT_MODE_AUTOLOAD_IXRISE  S526_GPCT_MODE_AUTOLOAD(0x4)
 101#define S526_GPCT_MODE_HWCTEN_SRC(x)    ((x) << 5)
 102#define S526_GPCT_MODE_HWCTEN_SRC_MASK  S526_GPCT_MODE_HWCTEN_SRC(0x3)
 103#define S526_GPCT_MODE_HWCTEN_SRC_CEN   S526_GPCT_MODE_HWCTEN_SRC(0)
 104#define S526_GPCT_MODE_HWCTEN_SRC_IX    S526_GPCT_MODE_HWCTEN_SRC(1)
 105#define S526_GPCT_MODE_HWCTEN_SRC_IXRF  S526_GPCT_MODE_HWCTEN_SRC(2)
 106#define S526_GPCT_MODE_HWCTEN_SRC_NRCAP S526_GPCT_MODE_HWCTEN_SRC(3)
 107#define S526_GPCT_MODE_CTEN_CTRL(x)     ((x) << 7)
 108#define S526_GPCT_MODE_CTEN_CTRL_MASK   S526_GPCT_MODE_CTEN_CTRL(0x3)
 109#define S526_GPCT_MODE_CTEN_CTRL_DIS    S526_GPCT_MODE_CTEN_CTRL(0)
 110#define S526_GPCT_MODE_CTEN_CTRL_ENA    S526_GPCT_MODE_CTEN_CTRL(1)
 111#define S526_GPCT_MODE_CTEN_CTRL_HW     S526_GPCT_MODE_CTEN_CTRL(2)
 112#define S526_GPCT_MODE_CTEN_CTRL_INVHW  S526_GPCT_MODE_CTEN_CTRL(3)
 113#define S526_GPCT_MODE_CLK_SRC(x)       ((x) << 9)
 114#define S526_GPCT_MODE_CLK_SRC_MASK     S526_GPCT_MODE_CLK_SRC(0x3)
 115/* if count direction control set to quadrature */
 116#define S526_GPCT_MODE_CLK_SRC_QUADX1   S526_GPCT_MODE_CLK_SRC(0)
 117#define S526_GPCT_MODE_CLK_SRC_QUADX2   S526_GPCT_MODE_CLK_SRC(1)
 118#define S526_GPCT_MODE_CLK_SRC_QUADX4   S526_GPCT_MODE_CLK_SRC(2)
 119#define S526_GPCT_MODE_CLK_SRC_QUADX4_  S526_GPCT_MODE_CLK_SRC(3)
 120/* if count direction control set to software control */
 121#define S526_GPCT_MODE_CLK_SRC_ARISE    S526_GPCT_MODE_CLK_SRC(0)
 122#define S526_GPCT_MODE_CLK_SRC_AFALL    S526_GPCT_MODE_CLK_SRC(1)
 123#define S526_GPCT_MODE_CLK_SRC_INT      S526_GPCT_MODE_CLK_SRC(2)
 124#define S526_GPCT_MODE_CLK_SRC_INTHALF  S526_GPCT_MODE_CLK_SRC(3)
 125#define S526_GPCT_MODE_CT_DIR(x)        ((x) << 11)
 126#define S526_GPCT_MODE_CT_DIR_MASK      S526_GPCT_MODE_CT_DIR(0x1)
 127/* if count direction control set to software control */
 128#define S526_GPCT_MODE_CT_DIR_UP        S526_GPCT_MODE_CT_DIR(0)
 129#define S526_GPCT_MODE_CT_DIR_DOWN      S526_GPCT_MODE_CT_DIR(1)
 130#define S526_GPCT_MODE_CTDIR_CTRL(x)    ((x) << 12)
 131#define S526_GPCT_MODE_CTDIR_CTRL_MASK  S526_GPCT_MODE_CTDIR_CTRL(0x1)
 132#define S526_GPCT_MODE_CTDIR_CTRL_QUAD  S526_GPCT_MODE_CTDIR_CTRL(0)
 133#define S526_GPCT_MODE_CTDIR_CTRL_SOFT  S526_GPCT_MODE_CTDIR_CTRL(1)
 134#define S526_GPCT_MODE_LATCH_CTRL(x)    ((x) << 13)
 135#define S526_GPCT_MODE_LATCH_CTRL_MASK  S526_GPCT_MODE_LATCH_CTRL(0x1)
 136#define S526_GPCT_MODE_LATCH_CTRL_READ  S526_GPCT_MODE_LATCH_CTRL(0)
 137#define S526_GPCT_MODE_LATCH_CTRL_EVENT S526_GPCT_MODE_LATCH_CTRL(1)
 138#define S526_GPCT_MODE_PR_SELECT(x)     ((x) << 14)
 139#define S526_GPCT_MODE_PR_SELECT_MASK   S526_GPCT_MODE_PR_SELECT(0x1)
 140#define S526_GPCT_MODE_PR_SELECT_PR0    S526_GPCT_MODE_PR_SELECT(0)
 141#define S526_GPCT_MODE_PR_SELECT_PR1    S526_GPCT_MODE_PR_SELECT(1)
 142/* Control/Status - R = readable, W = writeable, C = write 1 to clear */
 143#define S526_GPCT_CTRL_REG(x)   (0x18 + ((x) * 8))
 144#define S526_GPCT_CTRL_EV_STATUS(x)     ((x) << 0)              /* RC */
 145#define S526_GPCT_CTRL_EV_STATUS_MASK   S526_GPCT_EV_STATUS(0xf)
 146#define S526_GPCT_CTRL_EV_STATUS_NONE   S526_GPCT_EV_STATUS(0)
 147/* these 4 bits can be OR'ed */
 148#define S526_GPCT_CTRL_EV_STATUS_ECAP   S526_GPCT_EV_STATUS(0x1)
 149#define S526_GPCT_CTRL_EV_STATUS_ICAPN  S526_GPCT_EV_STATUS(0x2)
 150#define S526_GPCT_CTRL_EV_STATUS_ICAPP  S526_GPCT_EV_STATUS(0x4)
 151#define S526_GPCT_CTRL_EV_STATUS_RCAP   S526_GPCT_EV_STATUS(0x8)
 152#define S526_GPCT_CTRL_COUT_STATUS      BIT(4)                  /* R */
 153#define S526_GPCT_CTRL_INDEX_STATUS     BIT(5)                  /* R */
 154#define S525_GPCT_CTRL_INTEN(x)         ((x) << 6)              /* W */
 155#define S525_GPCT_CTRL_INTEN_MASK       S526_GPCT_CTRL_INTEN(0xf)
 156#define S525_GPCT_CTRL_INTEN_NONE       S526_GPCT_CTRL_INTEN(0)
 157/* these 4 bits can be OR'ed */
 158#define S525_GPCT_CTRL_INTEN_ERROR      S526_GPCT_CTRL_INTEN(0x1)
 159#define S525_GPCT_CTRL_INTEN_IXFALL     S526_GPCT_CTRL_INTEN(0x2)
 160#define S525_GPCT_CTRL_INTEN_IXRISE     S526_GPCT_CTRL_INTEN(0x4)
 161#define S525_GPCT_CTRL_INTEN_RO         S526_GPCT_CTRL_INTEN(0x8)
 162#define S525_GPCT_CTRL_LATCH_SEL(x)     ((x) << 10)             /* W */
 163#define S525_GPCT_CTRL_LATCH_SEL_MASK   S526_GPCT_CTRL_LATCH_SEL(0x7)
 164#define S525_GPCT_CTRL_LATCH_SEL_NONE   S526_GPCT_CTRL_LATCH_SEL(0)
 165/* these 3 bits can be OR'ed */
 166#define S525_GPCT_CTRL_LATCH_SEL_IXFALL S526_GPCT_CTRL_LATCH_SEL(0x1)
 167#define S525_GPCT_CTRL_LATCH_SEL_IXRISE S526_GPCT_CTRL_LATCH_SEL(0x2)
 168#define S525_GPCT_CTRL_LATCH_SEL_ITIMER S526_GPCT_CTRL_LATCH_SEL(0x4)
 169#define S525_GPCT_CTRL_CT_ARM           BIT(13)                 /* W */
 170#define S525_GPCT_CTRL_CT_LOAD          BIT(14)                 /* W */
 171#define S526_GPCT_CTRL_CT_RESET         BIT(15)                 /* W */
 172#define S526_EEPROM_DATA_REG    0x32
 173#define S526_EEPROM_CTRL_REG    0x34
 174#define S526_EEPROM_CTRL_ADDR(x) (((x) & 0x3f) << 3)
 175#define S526_EEPROM_CTRL(x)     (((x) & 0x3) << 1)
 176#define S526_EEPROM_CTRL_READ   S526_EEPROM_CTRL(2)
 177#define S526_EEPROM_CTRL_START  BIT(0)
 178
 179struct s526_private {
 180        unsigned int gpct_config[4];
 181        unsigned short ai_ctrl;
 182};
 183
 184static void s526_gpct_write(struct comedi_device *dev,
 185                            unsigned int chan, unsigned int val)
 186{
 187        /* write high word then low word */
 188        outw((val >> 16) & 0xffff, dev->iobase + S526_GPCT_MSB_REG(chan));
 189        outw(val & 0xffff, dev->iobase + S526_GPCT_LSB_REG(chan));
 190}
 191
 192static unsigned int s526_gpct_read(struct comedi_device *dev,
 193                                   unsigned int chan)
 194{
 195        unsigned int val;
 196
 197        /* read the low word then high word */
 198        val = inw(dev->iobase + S526_GPCT_LSB_REG(chan)) & 0xffff;
 199        val |= (inw(dev->iobase + S526_GPCT_MSB_REG(chan)) & 0xff) << 16;
 200
 201        return val;
 202}
 203
 204static int s526_gpct_rinsn(struct comedi_device *dev,
 205                           struct comedi_subdevice *s,
 206                           struct comedi_insn *insn,
 207                           unsigned int *data)
 208{
 209        unsigned int chan = CR_CHAN(insn->chanspec);
 210        int i;
 211
 212        for (i = 0; i < insn->n; i++)
 213                data[i] = s526_gpct_read(dev, chan);
 214
 215        return insn->n;
 216}
 217
 218static int s526_gpct_insn_config(struct comedi_device *dev,
 219                                 struct comedi_subdevice *s,
 220                                 struct comedi_insn *insn,
 221                                 unsigned int *data)
 222{
 223        struct s526_private *devpriv = dev->private;
 224        unsigned int chan = CR_CHAN(insn->chanspec);
 225        unsigned int val;
 226
 227        /*
 228         * Check what type of Counter the user requested
 229         * data[0] contains the Application type
 230         */
 231        switch (data[0]) {
 232        case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
 233                /*
 234                 * data[0]: Application Type
 235                 * data[1]: Counter Mode Register Value
 236                 * data[2]: Pre-load Register Value
 237                 * data[3]: Conter Control Register
 238                 */
 239                devpriv->gpct_config[chan] = data[0];
 240
 241#if 1
 242                /*  Set Counter Mode Register */
 243                val = data[1] & 0xffff;
 244                outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
 245
 246                /*  Reset the counter if it is software preload */
 247                if ((val & S526_GPCT_MODE_AUTOLOAD_MASK) ==
 248                    S526_GPCT_MODE_AUTOLOAD_NONE) {
 249                        /*  Reset the counter */
 250                        outw(S526_GPCT_CTRL_CT_RESET,
 251                             dev->iobase + S526_GPCT_CTRL_REG(chan));
 252                        /*
 253                         * Load the counter from PR0
 254                         * outw(S526_GPCT_CTRL_CT_LOAD,
 255                         *      dev->iobase + S526_GPCT_CTRL_REG(chan));
 256                         */
 257                }
 258#else
 259                val = S526_GPCT_MODE_CTDIR_CTRL_QUAD;
 260
 261                /*  data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
 262                if (data[1] == GPCT_X2)
 263                        val |= S526_GPCT_MODE_CLK_SRC_QUADX2;
 264                else if (data[1] == GPCT_X4)
 265                        val |= S526_GPCT_MODE_CLK_SRC_QUADX4;
 266                else
 267                        val |= S526_GPCT_MODE_CLK_SRC_QUADX1;
 268
 269                /*  When to take into account the indexpulse: */
 270                /*
 271                 * if (data[2] == GPCT_IndexPhaseLowLow) {
 272                 * } else if (data[2] == GPCT_IndexPhaseLowHigh) {
 273                 * } else if (data[2] == GPCT_IndexPhaseHighLow) {
 274                 * } else if (data[2] == GPCT_IndexPhaseHighHigh) {
 275                 * }
 276                 */
 277                /*  Take into account the index pulse? */
 278                if (data[3] == GPCT_RESET_COUNTER_ON_INDEX) {
 279                        /*  Auto load with INDEX^ */
 280                        val |= S526_GPCT_MODE_AUTOLOAD_IXRISE;
 281                }
 282
 283                /*  Set Counter Mode Register */
 284                val = data[1] & 0xffff;
 285                outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
 286
 287                /*  Load the pre-load register */
 288                s526_gpct_write(dev, chan, data[2]);
 289
 290                /*  Write the Counter Control Register */
 291                if (data[3])
 292                        outw(data[3] & 0xffff,
 293                             dev->iobase + S526_GPCT_CTRL_REG(chan));
 294
 295                /*  Reset the counter if it is software preload */
 296                if ((val & S526_GPCT_MODE_AUTOLOAD_MASK) ==
 297                    S526_GPCT_MODE_AUTOLOAD_NONE) {
 298                        /*  Reset the counter */
 299                        outw(S526_GPCT_CTRL_CT_RESET,
 300                             dev->iobase + S526_GPCT_CTRL_REG(chan));
 301                        /*  Load the counter from PR0 */
 302                        outw(S526_GPCT_CTRL_CT_LOAD,
 303                             dev->iobase + S526_GPCT_CTRL_REG(chan));
 304                }
 305#endif
 306                break;
 307
 308        case INSN_CONFIG_GPCT_SINGLE_PULSE_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                val = data[1] & 0xffff;
 320                /* Select PR0 */
 321                val &= ~S526_GPCT_MODE_PR_SELECT_MASK;
 322                val |= S526_GPCT_MODE_PR_SELECT_PR0;
 323                outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
 324
 325                /* Load the pre-load register 0 */
 326                s526_gpct_write(dev, chan, data[2]);
 327
 328                /*  Set Counter Mode Register */
 329                val = data[1] & 0xffff;
 330                /* Select PR1 */
 331                val &= ~S526_GPCT_MODE_PR_SELECT_MASK;
 332                val |= S526_GPCT_MODE_PR_SELECT_PR1;
 333                outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
 334
 335                /* Load the pre-load register 1 */
 336                s526_gpct_write(dev, chan, data[3]);
 337
 338                /*  Write the Counter Control Register */
 339                if (data[4]) {
 340                        val = data[4] & 0xffff;
 341                        outw(val, dev->iobase + S526_GPCT_CTRL_REG(chan));
 342                }
 343                break;
 344
 345        case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
 346                /*
 347                 * data[0]: Application Type
 348                 * data[1]: Counter Mode Register Value
 349                 * data[2]: Pre-load Register 0 Value
 350                 * data[3]: Pre-load Register 1 Value
 351                 * data[4]: Conter Control Register
 352                 */
 353                devpriv->gpct_config[chan] = data[0];
 354
 355                /*  Set Counter Mode Register */
 356                val = data[1] & 0xffff;
 357                /* Select PR0 */
 358                val &= ~S526_GPCT_MODE_PR_SELECT_MASK;
 359                val |= S526_GPCT_MODE_PR_SELECT_PR0;
 360                outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
 361
 362                /* Load the pre-load register 0 */
 363                s526_gpct_write(dev, chan, data[2]);
 364
 365                /*  Set Counter Mode Register */
 366                val = data[1] & 0xffff;
 367                /* Select PR1 */
 368                val &= ~S526_GPCT_MODE_PR_SELECT_MASK;
 369                val |= S526_GPCT_MODE_PR_SELECT_PR1;
 370                outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
 371
 372                /* Load the pre-load register 1 */
 373                s526_gpct_write(dev, chan, data[3]);
 374
 375                /*  Write the Counter Control Register */
 376                if (data[4]) {
 377                        val = data[4] & 0xffff;
 378                        outw(val, dev->iobase + S526_GPCT_CTRL_REG(chan));
 379                }
 380                break;
 381
 382        default:
 383                return -EINVAL;
 384        }
 385
 386        return insn->n;
 387}
 388
 389static int s526_gpct_winsn(struct comedi_device *dev,
 390                           struct comedi_subdevice *s,
 391                           struct comedi_insn *insn,
 392                           unsigned int *data)
 393{
 394        struct s526_private *devpriv = dev->private;
 395        unsigned int chan = CR_CHAN(insn->chanspec);
 396
 397        inw(dev->iobase + S526_GPCT_MODE_REG(chan));    /* Is this required? */
 398
 399        /*  Check what Application of Counter this channel is configured for */
 400        switch (devpriv->gpct_config[chan]) {
 401        case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
 402                /*
 403                 * data[0] contains the PULSE_WIDTH
 404                 * data[1] contains the PULSE_PERIOD
 405                 * @pre PULSE_PERIOD > PULSE_WIDTH > 0
 406                 * The above periods must be expressed as a multiple of the
 407                 * pulse frequency on the selected source
 408                 */
 409                if ((data[1] <= data[0]) || !data[0])
 410                        return -EINVAL;
 411
 412                /* Fall thru to write the PULSE_WIDTH */
 413
 414        case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
 415        case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
 416                s526_gpct_write(dev, chan, data[0]);
 417                break;
 418
 419        default:
 420                return -EINVAL;
 421        }
 422
 423        return insn->n;
 424}
 425
 426static int s526_eoc(struct comedi_device *dev,
 427                    struct comedi_subdevice *s,
 428                    struct comedi_insn *insn,
 429                    unsigned long context)
 430{
 431        unsigned int status;
 432
 433        status = inw(dev->iobase + S526_INT_STATUS_REG);
 434        if (status & context) {
 435                /* we got our eoc event, clear it */
 436                outw(context, dev->iobase + S526_INT_STATUS_REG);
 437                return 0;
 438        }
 439        return -EBUSY;
 440}
 441
 442static int s526_ai_insn_read(struct comedi_device *dev,
 443                             struct comedi_subdevice *s,
 444                             struct comedi_insn *insn,
 445                             unsigned int *data)
 446{
 447        struct s526_private *devpriv = dev->private;
 448        unsigned int chan = CR_CHAN(insn->chanspec);
 449        unsigned int ctrl;
 450        unsigned int val;
 451        int ret;
 452        int i;
 453
 454        ctrl = S526_AI_CTRL_CONV(chan) | S526_AI_CTRL_READ(chan) |
 455               S526_AI_CTRL_START;
 456        if (ctrl != devpriv->ai_ctrl) {
 457                /*
 458                 * The multiplexor needs to change, enable the 15us
 459                 * delay for the first sample.
 460                 */
 461                devpriv->ai_ctrl = ctrl;
 462                ctrl |= S526_AI_CTRL_DELAY;
 463        }
 464
 465        for (i = 0; i < insn->n; i++) {
 466                /* trigger conversion */
 467                outw(ctrl, dev->iobase + S526_AI_CTRL_REG);
 468                ctrl &= ~S526_AI_CTRL_DELAY;
 469
 470                /* wait for conversion to end */
 471                ret = comedi_timeout(dev, s, insn, s526_eoc, S526_INT_AI);
 472                if (ret)
 473                        return ret;
 474
 475                val = inw(dev->iobase + S526_AI_REG);
 476                data[i] = comedi_offset_munge(s, val);
 477        }
 478
 479        return insn->n;
 480}
 481
 482static int s526_ao_insn_write(struct comedi_device *dev,
 483                              struct comedi_subdevice *s,
 484                              struct comedi_insn *insn,
 485                              unsigned int *data)
 486{
 487        unsigned int chan = CR_CHAN(insn->chanspec);
 488        unsigned int ctrl = S526_AO_CTRL_CHAN(chan);
 489        unsigned int val = s->readback[chan];
 490        int ret;
 491        int i;
 492
 493        outw(ctrl, dev->iobase + S526_AO_CTRL_REG);
 494        ctrl |= S526_AO_CTRL_START;
 495
 496        for (i = 0; i < insn->n; i++) {
 497                val = data[i];
 498                outw(val, dev->iobase + S526_AO_REG);
 499                outw(ctrl, dev->iobase + S526_AO_CTRL_REG);
 500
 501                /* wait for conversion to end */
 502                ret = comedi_timeout(dev, s, insn, s526_eoc, S526_INT_AO);
 503                if (ret)
 504                        return ret;
 505        }
 506        s->readback[chan] = val;
 507
 508        return insn->n;
 509}
 510
 511static int s526_dio_insn_bits(struct comedi_device *dev,
 512                              struct comedi_subdevice *s,
 513                              struct comedi_insn *insn,
 514                              unsigned int *data)
 515{
 516        if (comedi_dio_update_state(s, data))
 517                outw(s->state, dev->iobase + S526_DIO_CTRL_REG);
 518
 519        data[1] = inw(dev->iobase + S526_DIO_CTRL_REG) & 0xff;
 520
 521        return insn->n;
 522}
 523
 524static int s526_dio_insn_config(struct comedi_device *dev,
 525                                struct comedi_subdevice *s,
 526                                struct comedi_insn *insn,
 527                                unsigned int *data)
 528{
 529        unsigned int chan = CR_CHAN(insn->chanspec);
 530        unsigned int mask;
 531        int ret;
 532
 533        /*
 534         * Digital I/O can be configured as inputs or outputs in
 535         * groups of 4; DIO group 1 (DIO0-3) and DIO group 2 (DIO4-7).
 536         */
 537        if (chan < 4)
 538                mask = 0x0f;
 539        else
 540                mask = 0xf0;
 541
 542        ret = comedi_dio_insn_config(dev, s, insn, data, mask);
 543        if (ret)
 544                return ret;
 545
 546        if (s->io_bits & 0x0f)
 547                s->state |= S526_DIO_CTRL_GRP1_OUT;
 548        else
 549                s->state &= ~S526_DIO_CTRL_GRP1_OUT;
 550        if (s->io_bits & 0xf0)
 551                s->state |= S526_DIO_CTRL_GRP2_OUT;
 552        else
 553                s->state &= ~S526_DIO_CTRL_GRP2_OUT;
 554
 555        outw(s->state, dev->iobase + S526_DIO_CTRL_REG);
 556
 557        return insn->n;
 558}
 559
 560static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 561{
 562        struct s526_private *devpriv;
 563        struct comedi_subdevice *s;
 564        int ret;
 565
 566        ret = comedi_request_region(dev, it->options[0], 0x40);
 567        if (ret)
 568                return ret;
 569
 570        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 571        if (!devpriv)
 572                return -ENOMEM;
 573
 574        ret = comedi_alloc_subdevices(dev, 4);
 575        if (ret)
 576                return ret;
 577
 578        /* General-Purpose Counter/Timer (GPCT) */
 579        s = &dev->subdevices[0];
 580        s->type         = COMEDI_SUBD_COUNTER;
 581        s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
 582        s->n_chan       = 4;
 583        s->maxdata      = 0x00ffffff;
 584        s->insn_read    = s526_gpct_rinsn;
 585        s->insn_config  = s526_gpct_insn_config;
 586        s->insn_write   = s526_gpct_winsn;
 587
 588        /*
 589         * Analog Input subdevice
 590         * channels 0 to 7 are the regular differential inputs
 591         * channel 8 is "reference 0" (+10V)
 592         * channel 9 is "reference 1" (0V)
 593         */
 594        s = &dev->subdevices[1];
 595        s->type         = COMEDI_SUBD_AI;
 596        s->subdev_flags = SDF_READABLE | SDF_DIFF;
 597        s->n_chan       = 10;
 598        s->maxdata      = 0xffff;
 599        s->range_table  = &range_bipolar10;
 600        s->len_chanlist = 16;
 601        s->insn_read    = s526_ai_insn_read;
 602
 603        /* Analog Output subdevice */
 604        s = &dev->subdevices[2];
 605        s->type         = COMEDI_SUBD_AO;
 606        s->subdev_flags = SDF_WRITABLE;
 607        s->n_chan       = 4;
 608        s->maxdata      = 0xffff;
 609        s->range_table  = &range_bipolar10;
 610        s->insn_write   = s526_ao_insn_write;
 611
 612        ret = comedi_alloc_subdev_readback(s);
 613        if (ret)
 614                return ret;
 615
 616        /* Digital I/O subdevice */
 617        s = &dev->subdevices[3];
 618        s->type         = COMEDI_SUBD_DIO;
 619        s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 620        s->n_chan       = 8;
 621        s->maxdata      = 1;
 622        s->range_table  = &range_digital;
 623        s->insn_bits    = s526_dio_insn_bits;
 624        s->insn_config  = s526_dio_insn_config;
 625
 626        return 0;
 627}
 628
 629static struct comedi_driver s526_driver = {
 630        .driver_name    = "s526",
 631        .module         = THIS_MODULE,
 632        .attach         = s526_attach,
 633        .detach         = comedi_legacy_detach,
 634};
 635module_comedi_driver(s526_driver);
 636
 637MODULE_AUTHOR("Comedi http://www.comedi.org");
 638MODULE_DESCRIPTION("Comedi low-level driver");
 639MODULE_LICENSE("GPL");
 640