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