root/drivers/staging/comedi/drivers/s526.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. s526_gpct_write
  2. s526_gpct_read
  3. s526_gpct_rinsn
  4. s526_gpct_insn_config
  5. s526_gpct_winsn
  6. s526_eoc
  7. s526_ai_insn_read
  8. s526_ao_insn_write
  9. s526_dio_insn_bits
  10. s526_dio_insn_config
  11. s526_attach

   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 
 170 struct s526_private {
 171         unsigned int gpct_config[4];
 172         unsigned short ai_ctrl;
 173 };
 174 
 175 static 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 
 183 static 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 
 195 static 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 
 209 static 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 
 380 static 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                 /* fall through */
 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 
 416 static 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 
 432 static 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 
 472 static 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 
 501 static 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 
 514 static 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 
 550 static 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 
 619 static struct comedi_driver s526_driver = {
 620         .driver_name    = "s526",
 621         .module         = THIS_MODULE,
 622         .attach         = s526_attach,
 623         .detach         = comedi_legacy_detach,
 624 };
 625 module_comedi_driver(s526_driver);
 626 
 627 MODULE_AUTHOR("Comedi http://www.comedi.org");
 628 MODULE_DESCRIPTION("Comedi low-level driver");
 629 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */