linux/drivers/counter/104-quad-8.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Counter driver for the ACCES 104-QUAD-8
   4 * Copyright (C) 2016 William Breathitt Gray
   5 *
   6 * This driver supports the ACCES 104-QUAD-8 and ACCES 104-QUAD-4.
   7 */
   8#include <linux/bitops.h>
   9#include <linux/counter.h>
  10#include <linux/device.h>
  11#include <linux/errno.h>
  12#include <linux/iio/iio.h>
  13#include <linux/iio/types.h>
  14#include <linux/io.h>
  15#include <linux/ioport.h>
  16#include <linux/isa.h>
  17#include <linux/kernel.h>
  18#include <linux/module.h>
  19#include <linux/moduleparam.h>
  20#include <linux/types.h>
  21
  22#define QUAD8_EXTENT 32
  23
  24static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)];
  25static unsigned int num_quad8;
  26module_param_array(base, uint, &num_quad8, 0);
  27MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
  28
  29#define QUAD8_NUM_COUNTERS 8
  30
  31/**
  32 * struct quad8_iio - IIO device private data structure
  33 * @counter:            instance of the counter_device
  34 * @preset:             array of preset values
  35 * @count_mode:         array of count mode configurations
  36 * @quadrature_mode:    array of quadrature mode configurations
  37 * @quadrature_scale:   array of quadrature mode scale configurations
  38 * @ab_enable:          array of A and B inputs enable configurations
  39 * @preset_enable:      array of set_to_preset_on_index attribute configurations
  40 * @synchronous_mode:   array of index function synchronous mode configurations
  41 * @index_polarity:     array of index function polarity configurations
  42 * @base:               base port address of the IIO device
  43 */
  44struct quad8_iio {
  45        struct counter_device counter;
  46        unsigned int preset[QUAD8_NUM_COUNTERS];
  47        unsigned int count_mode[QUAD8_NUM_COUNTERS];
  48        unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
  49        unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
  50        unsigned int ab_enable[QUAD8_NUM_COUNTERS];
  51        unsigned int preset_enable[QUAD8_NUM_COUNTERS];
  52        unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
  53        unsigned int index_polarity[QUAD8_NUM_COUNTERS];
  54        unsigned int base;
  55};
  56
  57#define QUAD8_REG_CHAN_OP 0x11
  58#define QUAD8_REG_INDEX_INPUT_LEVELS 0x16
  59/* Borrow Toggle flip-flop */
  60#define QUAD8_FLAG_BT BIT(0)
  61/* Carry Toggle flip-flop */
  62#define QUAD8_FLAG_CT BIT(1)
  63/* Error flag */
  64#define QUAD8_FLAG_E BIT(4)
  65/* Up/Down flag */
  66#define QUAD8_FLAG_UD BIT(5)
  67/* Reset and Load Signal Decoders */
  68#define QUAD8_CTR_RLD 0x00
  69/* Counter Mode Register */
  70#define QUAD8_CTR_CMR 0x20
  71/* Input / Output Control Register */
  72#define QUAD8_CTR_IOR 0x40
  73/* Index Control Register */
  74#define QUAD8_CTR_IDR 0x60
  75/* Reset Byte Pointer (three byte data pointer) */
  76#define QUAD8_RLD_RESET_BP 0x01
  77/* Reset Counter */
  78#define QUAD8_RLD_RESET_CNTR 0x02
  79/* Reset Borrow Toggle, Carry Toggle, Compare Toggle, and Sign flags */
  80#define QUAD8_RLD_RESET_FLAGS 0x04
  81/* Reset Error flag */
  82#define QUAD8_RLD_RESET_E 0x06
  83/* Preset Register to Counter */
  84#define QUAD8_RLD_PRESET_CNTR 0x08
  85/* Transfer Counter to Output Latch */
  86#define QUAD8_RLD_CNTR_OUT 0x10
  87#define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00
  88#define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
  89#define QUAD8_CMR_QUADRATURE_X1 0x08
  90#define QUAD8_CMR_QUADRATURE_X2 0x10
  91#define QUAD8_CMR_QUADRATURE_X4 0x18
  92
  93
  94static int quad8_read_raw(struct iio_dev *indio_dev,
  95        struct iio_chan_spec const *chan, int *val, int *val2, long mask)
  96{
  97        struct quad8_iio *const priv = iio_priv(indio_dev);
  98        const int base_offset = priv->base + 2 * chan->channel;
  99        unsigned int flags;
 100        unsigned int borrow;
 101        unsigned int carry;
 102        int i;
 103
 104        switch (mask) {
 105        case IIO_CHAN_INFO_RAW:
 106                if (chan->type == IIO_INDEX) {
 107                        *val = !!(inb(priv->base + QUAD8_REG_INDEX_INPUT_LEVELS)
 108                                & BIT(chan->channel));
 109                        return IIO_VAL_INT;
 110                }
 111
 112                flags = inb(base_offset + 1);
 113                borrow = flags & QUAD8_FLAG_BT;
 114                carry = !!(flags & QUAD8_FLAG_CT);
 115
 116                /* Borrow XOR Carry effectively doubles count range */
 117                *val = (borrow ^ carry) << 24;
 118
 119                /* Reset Byte Pointer; transfer Counter to Output Latch */
 120                outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
 121                     base_offset + 1);
 122
 123                for (i = 0; i < 3; i++)
 124                        *val |= (unsigned int)inb(base_offset) << (8 * i);
 125
 126                return IIO_VAL_INT;
 127        case IIO_CHAN_INFO_ENABLE:
 128                *val = priv->ab_enable[chan->channel];
 129                return IIO_VAL_INT;
 130        case IIO_CHAN_INFO_SCALE:
 131                *val = 1;
 132                *val2 = priv->quadrature_scale[chan->channel];
 133                return IIO_VAL_FRACTIONAL_LOG2;
 134        }
 135
 136        return -EINVAL;
 137}
 138
 139static int quad8_write_raw(struct iio_dev *indio_dev,
 140        struct iio_chan_spec const *chan, int val, int val2, long mask)
 141{
 142        struct quad8_iio *const priv = iio_priv(indio_dev);
 143        const int base_offset = priv->base + 2 * chan->channel;
 144        int i;
 145        unsigned int ior_cfg;
 146
 147        switch (mask) {
 148        case IIO_CHAN_INFO_RAW:
 149                if (chan->type == IIO_INDEX)
 150                        return -EINVAL;
 151
 152                /* Only 24-bit values are supported */
 153                if ((unsigned int)val > 0xFFFFFF)
 154                        return -EINVAL;
 155
 156                /* Reset Byte Pointer */
 157                outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
 158
 159                /* Counter can only be set via Preset Register */
 160                for (i = 0; i < 3; i++)
 161                        outb(val >> (8 * i), base_offset);
 162
 163                /* Transfer Preset Register to Counter */
 164                outb(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, base_offset + 1);
 165
 166                /* Reset Byte Pointer */
 167                outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
 168
 169                /* Set Preset Register back to original value */
 170                val = priv->preset[chan->channel];
 171                for (i = 0; i < 3; i++)
 172                        outb(val >> (8 * i), base_offset);
 173
 174                /* Reset Borrow, Carry, Compare, and Sign flags */
 175                outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
 176                /* Reset Error flag */
 177                outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
 178
 179                return 0;
 180        case IIO_CHAN_INFO_ENABLE:
 181                /* only boolean values accepted */
 182                if (val < 0 || val > 1)
 183                        return -EINVAL;
 184
 185                priv->ab_enable[chan->channel] = val;
 186
 187                ior_cfg = val | priv->preset_enable[chan->channel] << 1;
 188
 189                /* Load I/O control configuration */
 190                outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
 191
 192                return 0;
 193        case IIO_CHAN_INFO_SCALE:
 194                /* Quadrature scaling only available in quadrature mode */
 195                if (!priv->quadrature_mode[chan->channel] && (val2 || val != 1))
 196                        return -EINVAL;
 197
 198                /* Only three gain states (1, 0.5, 0.25) */
 199                if (val == 1 && !val2)
 200                        priv->quadrature_scale[chan->channel] = 0;
 201                else if (!val)
 202                        switch (val2) {
 203                        case 500000:
 204                                priv->quadrature_scale[chan->channel] = 1;
 205                                break;
 206                        case 250000:
 207                                priv->quadrature_scale[chan->channel] = 2;
 208                                break;
 209                        default:
 210                                return -EINVAL;
 211                        }
 212                else
 213                        return -EINVAL;
 214
 215                return 0;
 216        }
 217
 218        return -EINVAL;
 219}
 220
 221static const struct iio_info quad8_info = {
 222        .read_raw = quad8_read_raw,
 223        .write_raw = quad8_write_raw
 224};
 225
 226static ssize_t quad8_read_preset(struct iio_dev *indio_dev, uintptr_t private,
 227        const struct iio_chan_spec *chan, char *buf)
 228{
 229        const struct quad8_iio *const priv = iio_priv(indio_dev);
 230
 231        return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset[chan->channel]);
 232}
 233
 234static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
 235        const struct iio_chan_spec *chan, const char *buf, size_t len)
 236{
 237        struct quad8_iio *const priv = iio_priv(indio_dev);
 238        const int base_offset = priv->base + 2 * chan->channel;
 239        unsigned int preset;
 240        int ret;
 241        int i;
 242
 243        ret = kstrtouint(buf, 0, &preset);
 244        if (ret)
 245                return ret;
 246
 247        /* Only 24-bit values are supported */
 248        if (preset > 0xFFFFFF)
 249                return -EINVAL;
 250
 251        priv->preset[chan->channel] = preset;
 252
 253        /* Reset Byte Pointer */
 254        outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
 255
 256        /* Set Preset Register */
 257        for (i = 0; i < 3; i++)
 258                outb(preset >> (8 * i), base_offset);
 259
 260        return len;
 261}
 262
 263static ssize_t quad8_read_set_to_preset_on_index(struct iio_dev *indio_dev,
 264        uintptr_t private, const struct iio_chan_spec *chan, char *buf)
 265{
 266        const struct quad8_iio *const priv = iio_priv(indio_dev);
 267
 268        return snprintf(buf, PAGE_SIZE, "%u\n",
 269                !priv->preset_enable[chan->channel]);
 270}
 271
 272static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev,
 273        uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
 274        size_t len)
 275{
 276        struct quad8_iio *const priv = iio_priv(indio_dev);
 277        const int base_offset = priv->base + 2 * chan->channel + 1;
 278        bool preset_enable;
 279        int ret;
 280        unsigned int ior_cfg;
 281
 282        ret = kstrtobool(buf, &preset_enable);
 283        if (ret)
 284                return ret;
 285
 286        /* Preset enable is active low in Input/Output Control register */
 287        preset_enable = !preset_enable;
 288
 289        priv->preset_enable[chan->channel] = preset_enable;
 290
 291        ior_cfg = priv->ab_enable[chan->channel] |
 292                (unsigned int)preset_enable << 1;
 293
 294        /* Load I/O control configuration to Input / Output Control Register */
 295        outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
 296
 297        return len;
 298}
 299
 300static const char *const quad8_noise_error_states[] = {
 301        "No excessive noise is present at the count inputs",
 302        "Excessive noise is present at the count inputs"
 303};
 304
 305static int quad8_get_noise_error(struct iio_dev *indio_dev,
 306        const struct iio_chan_spec *chan)
 307{
 308        struct quad8_iio *const priv = iio_priv(indio_dev);
 309        const int base_offset = priv->base + 2 * chan->channel + 1;
 310
 311        return !!(inb(base_offset) & QUAD8_FLAG_E);
 312}
 313
 314static const struct iio_enum quad8_noise_error_enum = {
 315        .items = quad8_noise_error_states,
 316        .num_items = ARRAY_SIZE(quad8_noise_error_states),
 317        .get = quad8_get_noise_error
 318};
 319
 320static const char *const quad8_count_direction_states[] = {
 321        "down",
 322        "up"
 323};
 324
 325static int quad8_get_count_direction(struct iio_dev *indio_dev,
 326        const struct iio_chan_spec *chan)
 327{
 328        struct quad8_iio *const priv = iio_priv(indio_dev);
 329        const int base_offset = priv->base + 2 * chan->channel + 1;
 330
 331        return !!(inb(base_offset) & QUAD8_FLAG_UD);
 332}
 333
 334static const struct iio_enum quad8_count_direction_enum = {
 335        .items = quad8_count_direction_states,
 336        .num_items = ARRAY_SIZE(quad8_count_direction_states),
 337        .get = quad8_get_count_direction
 338};
 339
 340static const char *const quad8_count_modes[] = {
 341        "normal",
 342        "range limit",
 343        "non-recycle",
 344        "modulo-n"
 345};
 346
 347static int quad8_set_count_mode(struct iio_dev *indio_dev,
 348        const struct iio_chan_spec *chan, unsigned int cnt_mode)
 349{
 350        struct quad8_iio *const priv = iio_priv(indio_dev);
 351        unsigned int mode_cfg = cnt_mode << 1;
 352        const int base_offset = priv->base + 2 * chan->channel + 1;
 353
 354        priv->count_mode[chan->channel] = cnt_mode;
 355
 356        /* Add quadrature mode configuration */
 357        if (priv->quadrature_mode[chan->channel])
 358                mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3;
 359
 360        /* Load mode configuration to Counter Mode Register */
 361        outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 362
 363        return 0;
 364}
 365
 366static int quad8_get_count_mode(struct iio_dev *indio_dev,
 367        const struct iio_chan_spec *chan)
 368{
 369        const struct quad8_iio *const priv = iio_priv(indio_dev);
 370
 371        return priv->count_mode[chan->channel];
 372}
 373
 374static const struct iio_enum quad8_count_mode_enum = {
 375        .items = quad8_count_modes,
 376        .num_items = ARRAY_SIZE(quad8_count_modes),
 377        .set = quad8_set_count_mode,
 378        .get = quad8_get_count_mode
 379};
 380
 381static const char *const quad8_synchronous_modes[] = {
 382        "non-synchronous",
 383        "synchronous"
 384};
 385
 386static int quad8_set_synchronous_mode(struct iio_dev *indio_dev,
 387        const struct iio_chan_spec *chan, unsigned int synchronous_mode)
 388{
 389        struct quad8_iio *const priv = iio_priv(indio_dev);
 390        const unsigned int idr_cfg = synchronous_mode |
 391                priv->index_polarity[chan->channel] << 1;
 392        const int base_offset = priv->base + 2 * chan->channel + 1;
 393
 394        /* Index function must be non-synchronous in non-quadrature mode */
 395        if (synchronous_mode && !priv->quadrature_mode[chan->channel])
 396                return -EINVAL;
 397
 398        priv->synchronous_mode[chan->channel] = synchronous_mode;
 399
 400        /* Load Index Control configuration to Index Control Register */
 401        outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 402
 403        return 0;
 404}
 405
 406static int quad8_get_synchronous_mode(struct iio_dev *indio_dev,
 407        const struct iio_chan_spec *chan)
 408{
 409        const struct quad8_iio *const priv = iio_priv(indio_dev);
 410
 411        return priv->synchronous_mode[chan->channel];
 412}
 413
 414static const struct iio_enum quad8_synchronous_mode_enum = {
 415        .items = quad8_synchronous_modes,
 416        .num_items = ARRAY_SIZE(quad8_synchronous_modes),
 417        .set = quad8_set_synchronous_mode,
 418        .get = quad8_get_synchronous_mode
 419};
 420
 421static const char *const quad8_quadrature_modes[] = {
 422        "non-quadrature",
 423        "quadrature"
 424};
 425
 426static int quad8_set_quadrature_mode(struct iio_dev *indio_dev,
 427        const struct iio_chan_spec *chan, unsigned int quadrature_mode)
 428{
 429        struct quad8_iio *const priv = iio_priv(indio_dev);
 430        unsigned int mode_cfg = priv->count_mode[chan->channel] << 1;
 431        const int base_offset = priv->base + 2 * chan->channel + 1;
 432
 433        if (quadrature_mode)
 434                mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3;
 435        else {
 436                /* Quadrature scaling only available in quadrature mode */
 437                priv->quadrature_scale[chan->channel] = 0;
 438
 439                /* Synchronous function not supported in non-quadrature mode */
 440                if (priv->synchronous_mode[chan->channel])
 441                        quad8_set_synchronous_mode(indio_dev, chan, 0);
 442        }
 443
 444        priv->quadrature_mode[chan->channel] = quadrature_mode;
 445
 446        /* Load mode configuration to Counter Mode Register */
 447        outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 448
 449        return 0;
 450}
 451
 452static int quad8_get_quadrature_mode(struct iio_dev *indio_dev,
 453        const struct iio_chan_spec *chan)
 454{
 455        const struct quad8_iio *const priv = iio_priv(indio_dev);
 456
 457        return priv->quadrature_mode[chan->channel];
 458}
 459
 460static const struct iio_enum quad8_quadrature_mode_enum = {
 461        .items = quad8_quadrature_modes,
 462        .num_items = ARRAY_SIZE(quad8_quadrature_modes),
 463        .set = quad8_set_quadrature_mode,
 464        .get = quad8_get_quadrature_mode
 465};
 466
 467static const char *const quad8_index_polarity_modes[] = {
 468        "negative",
 469        "positive"
 470};
 471
 472static int quad8_set_index_polarity(struct iio_dev *indio_dev,
 473        const struct iio_chan_spec *chan, unsigned int index_polarity)
 474{
 475        struct quad8_iio *const priv = iio_priv(indio_dev);
 476        const unsigned int idr_cfg = priv->synchronous_mode[chan->channel] |
 477                index_polarity << 1;
 478        const int base_offset = priv->base + 2 * chan->channel + 1;
 479
 480        priv->index_polarity[chan->channel] = index_polarity;
 481
 482        /* Load Index Control configuration to Index Control Register */
 483        outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 484
 485        return 0;
 486}
 487
 488static int quad8_get_index_polarity(struct iio_dev *indio_dev,
 489        const struct iio_chan_spec *chan)
 490{
 491        const struct quad8_iio *const priv = iio_priv(indio_dev);
 492
 493        return priv->index_polarity[chan->channel];
 494}
 495
 496static const struct iio_enum quad8_index_polarity_enum = {
 497        .items = quad8_index_polarity_modes,
 498        .num_items = ARRAY_SIZE(quad8_index_polarity_modes),
 499        .set = quad8_set_index_polarity,
 500        .get = quad8_get_index_polarity
 501};
 502
 503static const struct iio_chan_spec_ext_info quad8_count_ext_info[] = {
 504        {
 505                .name = "preset",
 506                .shared = IIO_SEPARATE,
 507                .read = quad8_read_preset,
 508                .write = quad8_write_preset
 509        },
 510        {
 511                .name = "set_to_preset_on_index",
 512                .shared = IIO_SEPARATE,
 513                .read = quad8_read_set_to_preset_on_index,
 514                .write = quad8_write_set_to_preset_on_index
 515        },
 516        IIO_ENUM("noise_error", IIO_SEPARATE, &quad8_noise_error_enum),
 517        IIO_ENUM_AVAILABLE("noise_error", &quad8_noise_error_enum),
 518        IIO_ENUM("count_direction", IIO_SEPARATE, &quad8_count_direction_enum),
 519        IIO_ENUM_AVAILABLE("count_direction", &quad8_count_direction_enum),
 520        IIO_ENUM("count_mode", IIO_SEPARATE, &quad8_count_mode_enum),
 521        IIO_ENUM_AVAILABLE("count_mode", &quad8_count_mode_enum),
 522        IIO_ENUM("quadrature_mode", IIO_SEPARATE, &quad8_quadrature_mode_enum),
 523        IIO_ENUM_AVAILABLE("quadrature_mode", &quad8_quadrature_mode_enum),
 524        {}
 525};
 526
 527static const struct iio_chan_spec_ext_info quad8_index_ext_info[] = {
 528        IIO_ENUM("synchronous_mode", IIO_SEPARATE,
 529                &quad8_synchronous_mode_enum),
 530        IIO_ENUM_AVAILABLE("synchronous_mode", &quad8_synchronous_mode_enum),
 531        IIO_ENUM("index_polarity", IIO_SEPARATE, &quad8_index_polarity_enum),
 532        IIO_ENUM_AVAILABLE("index_polarity", &quad8_index_polarity_enum),
 533        {}
 534};
 535
 536#define QUAD8_COUNT_CHAN(_chan) {                                       \
 537        .type = IIO_COUNT,                                              \
 538        .channel = (_chan),                                             \
 539        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |                  \
 540                BIT(IIO_CHAN_INFO_ENABLE) | BIT(IIO_CHAN_INFO_SCALE),   \
 541        .ext_info = quad8_count_ext_info,                               \
 542        .indexed = 1                                                    \
 543}
 544
 545#define QUAD8_INDEX_CHAN(_chan) {                       \
 546        .type = IIO_INDEX,                              \
 547        .channel = (_chan),                             \
 548        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),   \
 549        .ext_info = quad8_index_ext_info,               \
 550        .indexed = 1                                    \
 551}
 552
 553static const struct iio_chan_spec quad8_channels[] = {
 554        QUAD8_COUNT_CHAN(0), QUAD8_INDEX_CHAN(0),
 555        QUAD8_COUNT_CHAN(1), QUAD8_INDEX_CHAN(1),
 556        QUAD8_COUNT_CHAN(2), QUAD8_INDEX_CHAN(2),
 557        QUAD8_COUNT_CHAN(3), QUAD8_INDEX_CHAN(3),
 558        QUAD8_COUNT_CHAN(4), QUAD8_INDEX_CHAN(4),
 559        QUAD8_COUNT_CHAN(5), QUAD8_INDEX_CHAN(5),
 560        QUAD8_COUNT_CHAN(6), QUAD8_INDEX_CHAN(6),
 561        QUAD8_COUNT_CHAN(7), QUAD8_INDEX_CHAN(7)
 562};
 563
 564static int quad8_signal_read(struct counter_device *counter,
 565        struct counter_signal *signal, struct counter_signal_read_value *val)
 566{
 567        const struct quad8_iio *const priv = counter->priv;
 568        unsigned int state;
 569        enum counter_signal_level level;
 570
 571        /* Only Index signal levels can be read */
 572        if (signal->id < 16)
 573                return -EINVAL;
 574
 575        state = inb(priv->base + QUAD8_REG_INDEX_INPUT_LEVELS)
 576                & BIT(signal->id - 16);
 577
 578        level = (state) ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
 579
 580        counter_signal_read_value_set(val, COUNTER_SIGNAL_LEVEL, &level);
 581
 582        return 0;
 583}
 584
 585static int quad8_count_read(struct counter_device *counter,
 586        struct counter_count *count, struct counter_count_read_value *val)
 587{
 588        const struct quad8_iio *const priv = counter->priv;
 589        const int base_offset = priv->base + 2 * count->id;
 590        unsigned int flags;
 591        unsigned int borrow;
 592        unsigned int carry;
 593        unsigned long position;
 594        int i;
 595
 596        flags = inb(base_offset + 1);
 597        borrow = flags & QUAD8_FLAG_BT;
 598        carry = !!(flags & QUAD8_FLAG_CT);
 599
 600        /* Borrow XOR Carry effectively doubles count range */
 601        position = (unsigned long)(borrow ^ carry) << 24;
 602
 603        /* Reset Byte Pointer; transfer Counter to Output Latch */
 604        outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
 605             base_offset + 1);
 606
 607        for (i = 0; i < 3; i++)
 608                position |= (unsigned long)inb(base_offset) << (8 * i);
 609
 610        counter_count_read_value_set(val, COUNTER_COUNT_POSITION, &position);
 611
 612        return 0;
 613}
 614
 615static int quad8_count_write(struct counter_device *counter,
 616        struct counter_count *count, struct counter_count_write_value *val)
 617{
 618        const struct quad8_iio *const priv = counter->priv;
 619        const int base_offset = priv->base + 2 * count->id;
 620        int err;
 621        unsigned long position;
 622        int i;
 623
 624        err = counter_count_write_value_get(&position, COUNTER_COUNT_POSITION,
 625                                            val);
 626        if (err)
 627                return err;
 628
 629        /* Only 24-bit values are supported */
 630        if (position > 0xFFFFFF)
 631                return -EINVAL;
 632
 633        /* Reset Byte Pointer */
 634        outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
 635
 636        /* Counter can only be set via Preset Register */
 637        for (i = 0; i < 3; i++)
 638                outb(position >> (8 * i), base_offset);
 639
 640        /* Transfer Preset Register to Counter */
 641        outb(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, base_offset + 1);
 642
 643        /* Reset Byte Pointer */
 644        outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
 645
 646        /* Set Preset Register back to original value */
 647        position = priv->preset[count->id];
 648        for (i = 0; i < 3; i++)
 649                outb(position >> (8 * i), base_offset);
 650
 651        /* Reset Borrow, Carry, Compare, and Sign flags */
 652        outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
 653        /* Reset Error flag */
 654        outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
 655
 656        return 0;
 657}
 658
 659enum quad8_count_function {
 660        QUAD8_COUNT_FUNCTION_PULSE_DIRECTION = 0,
 661        QUAD8_COUNT_FUNCTION_QUADRATURE_X1,
 662        QUAD8_COUNT_FUNCTION_QUADRATURE_X2,
 663        QUAD8_COUNT_FUNCTION_QUADRATURE_X4
 664};
 665
 666static enum counter_count_function quad8_count_functions_list[] = {
 667        [QUAD8_COUNT_FUNCTION_PULSE_DIRECTION] = COUNTER_COUNT_FUNCTION_PULSE_DIRECTION,
 668        [QUAD8_COUNT_FUNCTION_QUADRATURE_X1] = COUNTER_COUNT_FUNCTION_QUADRATURE_X1_A,
 669        [QUAD8_COUNT_FUNCTION_QUADRATURE_X2] = COUNTER_COUNT_FUNCTION_QUADRATURE_X2_A,
 670        [QUAD8_COUNT_FUNCTION_QUADRATURE_X4] = COUNTER_COUNT_FUNCTION_QUADRATURE_X4
 671};
 672
 673static int quad8_function_get(struct counter_device *counter,
 674        struct counter_count *count, size_t *function)
 675{
 676        const struct quad8_iio *const priv = counter->priv;
 677        const int id = count->id;
 678        const unsigned int quadrature_mode = priv->quadrature_mode[id];
 679        const unsigned int scale = priv->quadrature_scale[id];
 680
 681        if (quadrature_mode)
 682                switch (scale) {
 683                case 0:
 684                        *function = QUAD8_COUNT_FUNCTION_QUADRATURE_X1;
 685                        break;
 686                case 1:
 687                        *function = QUAD8_COUNT_FUNCTION_QUADRATURE_X2;
 688                        break;
 689                case 2:
 690                        *function = QUAD8_COUNT_FUNCTION_QUADRATURE_X4;
 691                        break;
 692                }
 693        else
 694                *function = QUAD8_COUNT_FUNCTION_PULSE_DIRECTION;
 695
 696        return 0;
 697}
 698
 699static int quad8_function_set(struct counter_device *counter,
 700        struct counter_count *count, size_t function)
 701{
 702        struct quad8_iio *const priv = counter->priv;
 703        const int id = count->id;
 704        unsigned int *const quadrature_mode = priv->quadrature_mode + id;
 705        unsigned int *const scale = priv->quadrature_scale + id;
 706        unsigned int mode_cfg = priv->count_mode[id] << 1;
 707        unsigned int *const synchronous_mode = priv->synchronous_mode + id;
 708        const unsigned int idr_cfg = priv->index_polarity[id] << 1;
 709        const int base_offset = priv->base + 2 * id + 1;
 710
 711        if (function == QUAD8_COUNT_FUNCTION_PULSE_DIRECTION) {
 712                *quadrature_mode = 0;
 713
 714                /* Quadrature scaling only available in quadrature mode */
 715                *scale = 0;
 716
 717                /* Synchronous function not supported in non-quadrature mode */
 718                if (*synchronous_mode) {
 719                        *synchronous_mode = 0;
 720                        /* Disable synchronous function mode */
 721                        outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 722                }
 723        } else {
 724                *quadrature_mode = 1;
 725
 726                switch (function) {
 727                case QUAD8_COUNT_FUNCTION_QUADRATURE_X1:
 728                        *scale = 0;
 729                        mode_cfg |= QUAD8_CMR_QUADRATURE_X1;
 730                        break;
 731                case QUAD8_COUNT_FUNCTION_QUADRATURE_X2:
 732                        *scale = 1;
 733                        mode_cfg |= QUAD8_CMR_QUADRATURE_X2;
 734                        break;
 735                case QUAD8_COUNT_FUNCTION_QUADRATURE_X4:
 736                        *scale = 2;
 737                        mode_cfg |= QUAD8_CMR_QUADRATURE_X4;
 738                        break;
 739                }
 740        }
 741
 742        /* Load mode configuration to Counter Mode Register */
 743        outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 744
 745        return 0;
 746}
 747
 748static void quad8_direction_get(struct counter_device *counter,
 749        struct counter_count *count, enum counter_count_direction *direction)
 750{
 751        const struct quad8_iio *const priv = counter->priv;
 752        unsigned int ud_flag;
 753        const unsigned int flag_addr = priv->base + 2 * count->id + 1;
 754
 755        /* U/D flag: nonzero = up, zero = down */
 756        ud_flag = inb(flag_addr) & QUAD8_FLAG_UD;
 757
 758        *direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD :
 759                COUNTER_COUNT_DIRECTION_BACKWARD;
 760}
 761
 762enum quad8_synapse_action {
 763        QUAD8_SYNAPSE_ACTION_NONE = 0,
 764        QUAD8_SYNAPSE_ACTION_RISING_EDGE,
 765        QUAD8_SYNAPSE_ACTION_FALLING_EDGE,
 766        QUAD8_SYNAPSE_ACTION_BOTH_EDGES
 767};
 768
 769static enum counter_synapse_action quad8_index_actions_list[] = {
 770        [QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
 771        [QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE
 772};
 773
 774static enum counter_synapse_action quad8_synapse_actions_list[] = {
 775        [QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
 776        [QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
 777        [QUAD8_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
 778        [QUAD8_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES
 779};
 780
 781static int quad8_action_get(struct counter_device *counter,
 782        struct counter_count *count, struct counter_synapse *synapse,
 783        size_t *action)
 784{
 785        struct quad8_iio *const priv = counter->priv;
 786        int err;
 787        size_t function = 0;
 788        const size_t signal_a_id = count->synapses[0].signal->id;
 789        enum counter_count_direction direction;
 790
 791        /* Handle Index signals */
 792        if (synapse->signal->id >= 16) {
 793                if (priv->preset_enable[count->id])
 794                        *action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
 795                else
 796                        *action = QUAD8_SYNAPSE_ACTION_NONE;
 797
 798                return 0;
 799        }
 800
 801        err = quad8_function_get(counter, count, &function);
 802        if (err)
 803                return err;
 804
 805        /* Default action mode */
 806        *action = QUAD8_SYNAPSE_ACTION_NONE;
 807
 808        /* Determine action mode based on current count function mode */
 809        switch (function) {
 810        case QUAD8_COUNT_FUNCTION_PULSE_DIRECTION:
 811                if (synapse->signal->id == signal_a_id)
 812                        *action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
 813                break;
 814        case QUAD8_COUNT_FUNCTION_QUADRATURE_X1:
 815                if (synapse->signal->id == signal_a_id) {
 816                        quad8_direction_get(counter, count, &direction);
 817
 818                        if (direction == COUNTER_COUNT_DIRECTION_FORWARD)
 819                                *action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
 820                        else
 821                                *action = QUAD8_SYNAPSE_ACTION_FALLING_EDGE;
 822                }
 823                break;
 824        case QUAD8_COUNT_FUNCTION_QUADRATURE_X2:
 825                if (synapse->signal->id == signal_a_id)
 826                        *action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES;
 827                break;
 828        case QUAD8_COUNT_FUNCTION_QUADRATURE_X4:
 829                *action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES;
 830                break;
 831        }
 832
 833        return 0;
 834}
 835
 836const struct counter_ops quad8_ops = {
 837        .signal_read = quad8_signal_read,
 838        .count_read = quad8_count_read,
 839        .count_write = quad8_count_write,
 840        .function_get = quad8_function_get,
 841        .function_set = quad8_function_set,
 842        .action_get = quad8_action_get
 843};
 844
 845static int quad8_index_polarity_get(struct counter_device *counter,
 846        struct counter_signal *signal, size_t *index_polarity)
 847{
 848        const struct quad8_iio *const priv = counter->priv;
 849        const size_t channel_id = signal->id - 16;
 850
 851        *index_polarity = priv->index_polarity[channel_id];
 852
 853        return 0;
 854}
 855
 856static int quad8_index_polarity_set(struct counter_device *counter,
 857        struct counter_signal *signal, size_t index_polarity)
 858{
 859        struct quad8_iio *const priv = counter->priv;
 860        const size_t channel_id = signal->id - 16;
 861        const unsigned int idr_cfg = priv->synchronous_mode[channel_id] |
 862                index_polarity << 1;
 863        const int base_offset = priv->base + 2 * channel_id + 1;
 864
 865        priv->index_polarity[channel_id] = index_polarity;
 866
 867        /* Load Index Control configuration to Index Control Register */
 868        outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 869
 870        return 0;
 871}
 872
 873static struct counter_signal_enum_ext quad8_index_pol_enum = {
 874        .items = quad8_index_polarity_modes,
 875        .num_items = ARRAY_SIZE(quad8_index_polarity_modes),
 876        .get = quad8_index_polarity_get,
 877        .set = quad8_index_polarity_set
 878};
 879
 880static int quad8_synchronous_mode_get(struct counter_device *counter,
 881        struct counter_signal *signal, size_t *synchronous_mode)
 882{
 883        const struct quad8_iio *const priv = counter->priv;
 884        const size_t channel_id = signal->id - 16;
 885
 886        *synchronous_mode = priv->synchronous_mode[channel_id];
 887
 888        return 0;
 889}
 890
 891static int quad8_synchronous_mode_set(struct counter_device *counter,
 892        struct counter_signal *signal, size_t synchronous_mode)
 893{
 894        struct quad8_iio *const priv = counter->priv;
 895        const size_t channel_id = signal->id - 16;
 896        const unsigned int idr_cfg = synchronous_mode |
 897                priv->index_polarity[channel_id] << 1;
 898        const int base_offset = priv->base + 2 * channel_id + 1;
 899
 900        /* Index function must be non-synchronous in non-quadrature mode */
 901        if (synchronous_mode && !priv->quadrature_mode[channel_id])
 902                return -EINVAL;
 903
 904        priv->synchronous_mode[channel_id] = synchronous_mode;
 905
 906        /* Load Index Control configuration to Index Control Register */
 907        outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 908
 909        return 0;
 910}
 911
 912static struct counter_signal_enum_ext quad8_syn_mode_enum = {
 913        .items = quad8_synchronous_modes,
 914        .num_items = ARRAY_SIZE(quad8_synchronous_modes),
 915        .get = quad8_synchronous_mode_get,
 916        .set = quad8_synchronous_mode_set
 917};
 918
 919static ssize_t quad8_count_floor_read(struct counter_device *counter,
 920        struct counter_count *count, void *private, char *buf)
 921{
 922        /* Only a floor of 0 is supported */
 923        return sprintf(buf, "0\n");
 924}
 925
 926static int quad8_count_mode_get(struct counter_device *counter,
 927        struct counter_count *count, size_t *cnt_mode)
 928{
 929        const struct quad8_iio *const priv = counter->priv;
 930
 931        /* Map 104-QUAD-8 count mode to Generic Counter count mode */
 932        switch (priv->count_mode[count->id]) {
 933        case 0:
 934                *cnt_mode = COUNTER_COUNT_MODE_NORMAL;
 935                break;
 936        case 1:
 937                *cnt_mode = COUNTER_COUNT_MODE_RANGE_LIMIT;
 938                break;
 939        case 2:
 940                *cnt_mode = COUNTER_COUNT_MODE_NON_RECYCLE;
 941                break;
 942        case 3:
 943                *cnt_mode = COUNTER_COUNT_MODE_MODULO_N;
 944                break;
 945        }
 946
 947        return 0;
 948}
 949
 950static int quad8_count_mode_set(struct counter_device *counter,
 951        struct counter_count *count, size_t cnt_mode)
 952{
 953        struct quad8_iio *const priv = counter->priv;
 954        unsigned int mode_cfg;
 955        const int base_offset = priv->base + 2 * count->id + 1;
 956
 957        /* Map Generic Counter count mode to 104-QUAD-8 count mode */
 958        switch (cnt_mode) {
 959        case COUNTER_COUNT_MODE_NORMAL:
 960                cnt_mode = 0;
 961                break;
 962        case COUNTER_COUNT_MODE_RANGE_LIMIT:
 963                cnt_mode = 1;
 964                break;
 965        case COUNTER_COUNT_MODE_NON_RECYCLE:
 966                cnt_mode = 2;
 967                break;
 968        case COUNTER_COUNT_MODE_MODULO_N:
 969                cnt_mode = 3;
 970                break;
 971        }
 972
 973        priv->count_mode[count->id] = cnt_mode;
 974
 975        /* Set count mode configuration value */
 976        mode_cfg = cnt_mode << 1;
 977
 978        /* Add quadrature mode configuration */
 979        if (priv->quadrature_mode[count->id])
 980                mode_cfg |= (priv->quadrature_scale[count->id] + 1) << 3;
 981
 982        /* Load mode configuration to Counter Mode Register */
 983        outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 984
 985        return 0;
 986}
 987
 988static struct counter_count_enum_ext quad8_cnt_mode_enum = {
 989        .items = counter_count_mode_str,
 990        .num_items = ARRAY_SIZE(counter_count_mode_str),
 991        .get = quad8_count_mode_get,
 992        .set = quad8_count_mode_set
 993};
 994
 995static ssize_t quad8_count_direction_read(struct counter_device *counter,
 996        struct counter_count *count, void *priv, char *buf)
 997{
 998        enum counter_count_direction dir;
 999
1000        quad8_direction_get(counter, count, &dir);
1001
1002        return sprintf(buf, "%s\n", counter_count_direction_str[dir]);
1003}
1004
1005static ssize_t quad8_count_enable_read(struct counter_device *counter,
1006        struct counter_count *count, void *private, char *buf)
1007{
1008        const struct quad8_iio *const priv = counter->priv;
1009
1010        return sprintf(buf, "%u\n", priv->ab_enable[count->id]);
1011}
1012
1013static ssize_t quad8_count_enable_write(struct counter_device *counter,
1014        struct counter_count *count, void *private, const char *buf, size_t len)
1015{
1016        struct quad8_iio *const priv = counter->priv;
1017        const int base_offset = priv->base + 2 * count->id;
1018        int err;
1019        bool ab_enable;
1020        unsigned int ior_cfg;
1021
1022        err = kstrtobool(buf, &ab_enable);
1023        if (err)
1024                return err;
1025
1026        priv->ab_enable[count->id] = ab_enable;
1027
1028        ior_cfg = ab_enable | priv->preset_enable[count->id] << 1;
1029
1030        /* Load I/O control configuration */
1031        outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
1032
1033        return len;
1034}
1035
1036static int quad8_error_noise_get(struct counter_device *counter,
1037        struct counter_count *count, size_t *noise_error)
1038{
1039        const struct quad8_iio *const priv = counter->priv;
1040        const int base_offset = priv->base + 2 * count->id + 1;
1041
1042        *noise_error = !!(inb(base_offset) & QUAD8_FLAG_E);
1043
1044        return 0;
1045}
1046
1047static struct counter_count_enum_ext quad8_error_noise_enum = {
1048        .items = quad8_noise_error_states,
1049        .num_items = ARRAY_SIZE(quad8_noise_error_states),
1050        .get = quad8_error_noise_get
1051};
1052
1053static ssize_t quad8_count_preset_read(struct counter_device *counter,
1054        struct counter_count *count, void *private, char *buf)
1055{
1056        const struct quad8_iio *const priv = counter->priv;
1057
1058        return sprintf(buf, "%u\n", priv->preset[count->id]);
1059}
1060
1061static ssize_t quad8_count_preset_write(struct counter_device *counter,
1062        struct counter_count *count, void *private, const char *buf, size_t len)
1063{
1064        struct quad8_iio *const priv = counter->priv;
1065        const int base_offset = priv->base + 2 * count->id;
1066        unsigned int preset;
1067        int ret;
1068        int i;
1069
1070        ret = kstrtouint(buf, 0, &preset);
1071        if (ret)
1072                return ret;
1073
1074        /* Only 24-bit values are supported */
1075        if (preset > 0xFFFFFF)
1076                return -EINVAL;
1077
1078        priv->preset[count->id] = preset;
1079
1080        /* Reset Byte Pointer */
1081        outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
1082
1083        /* Set Preset Register */
1084        for (i = 0; i < 3; i++)
1085                outb(preset >> (8 * i), base_offset);
1086
1087        return len;
1088}
1089
1090static ssize_t quad8_count_ceiling_read(struct counter_device *counter,
1091        struct counter_count *count, void *private, char *buf)
1092{
1093        const struct quad8_iio *const priv = counter->priv;
1094
1095        /* Range Limit and Modulo-N count modes use preset value as ceiling */
1096        switch (priv->count_mode[count->id]) {
1097        case 1:
1098        case 3:
1099                return quad8_count_preset_read(counter, count, private, buf);
1100        }
1101
1102        /* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
1103        return sprintf(buf, "33554431\n");
1104}
1105
1106static ssize_t quad8_count_ceiling_write(struct counter_device *counter,
1107        struct counter_count *count, void *private, const char *buf, size_t len)
1108{
1109        struct quad8_iio *const priv = counter->priv;
1110
1111        /* Range Limit and Modulo-N count modes use preset value as ceiling */
1112        switch (priv->count_mode[count->id]) {
1113        case 1:
1114        case 3:
1115                return quad8_count_preset_write(counter, count, private, buf,
1116                                                len);
1117        }
1118
1119        return len;
1120}
1121
1122static ssize_t quad8_count_preset_enable_read(struct counter_device *counter,
1123        struct counter_count *count, void *private, char *buf)
1124{
1125        const struct quad8_iio *const priv = counter->priv;
1126
1127        return sprintf(buf, "%u\n", !priv->preset_enable[count->id]);
1128}
1129
1130static ssize_t quad8_count_preset_enable_write(struct counter_device *counter,
1131        struct counter_count *count, void *private, const char *buf, size_t len)
1132{
1133        struct quad8_iio *const priv = counter->priv;
1134        const int base_offset = priv->base + 2 * count->id + 1;
1135        bool preset_enable;
1136        int ret;
1137        unsigned int ior_cfg;
1138
1139        ret = kstrtobool(buf, &preset_enable);
1140        if (ret)
1141                return ret;
1142
1143        /* Preset enable is active low in Input/Output Control register */
1144        preset_enable = !preset_enable;
1145
1146        priv->preset_enable[count->id] = preset_enable;
1147
1148        ior_cfg = priv->ab_enable[count->id] | (unsigned int)preset_enable << 1;
1149
1150        /* Load I/O control configuration to Input / Output Control Register */
1151        outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
1152
1153        return len;
1154}
1155
1156static const struct counter_signal_ext quad8_index_ext[] = {
1157        COUNTER_SIGNAL_ENUM("index_polarity", &quad8_index_pol_enum),
1158        COUNTER_SIGNAL_ENUM_AVAILABLE("index_polarity", &quad8_index_pol_enum),
1159        COUNTER_SIGNAL_ENUM("synchronous_mode", &quad8_syn_mode_enum),
1160        COUNTER_SIGNAL_ENUM_AVAILABLE("synchronous_mode", &quad8_syn_mode_enum)
1161};
1162
1163#define QUAD8_QUAD_SIGNAL(_id, _name) { \
1164        .id = (_id),                    \
1165        .name = (_name)                 \
1166}
1167
1168#define QUAD8_INDEX_SIGNAL(_id, _name) {        \
1169        .id = (_id),                            \
1170        .name = (_name),                        \
1171        .ext = quad8_index_ext,                 \
1172        .num_ext = ARRAY_SIZE(quad8_index_ext)  \
1173}
1174
1175static struct counter_signal quad8_signals[] = {
1176        QUAD8_QUAD_SIGNAL(0, "Channel 1 Quadrature A"),
1177        QUAD8_QUAD_SIGNAL(1, "Channel 1 Quadrature B"),
1178        QUAD8_QUAD_SIGNAL(2, "Channel 2 Quadrature A"),
1179        QUAD8_QUAD_SIGNAL(3, "Channel 2 Quadrature B"),
1180        QUAD8_QUAD_SIGNAL(4, "Channel 3 Quadrature A"),
1181        QUAD8_QUAD_SIGNAL(5, "Channel 3 Quadrature B"),
1182        QUAD8_QUAD_SIGNAL(6, "Channel 4 Quadrature A"),
1183        QUAD8_QUAD_SIGNAL(7, "Channel 4 Quadrature B"),
1184        QUAD8_QUAD_SIGNAL(8, "Channel 5 Quadrature A"),
1185        QUAD8_QUAD_SIGNAL(9, "Channel 5 Quadrature B"),
1186        QUAD8_QUAD_SIGNAL(10, "Channel 6 Quadrature A"),
1187        QUAD8_QUAD_SIGNAL(11, "Channel 6 Quadrature B"),
1188        QUAD8_QUAD_SIGNAL(12, "Channel 7 Quadrature A"),
1189        QUAD8_QUAD_SIGNAL(13, "Channel 7 Quadrature B"),
1190        QUAD8_QUAD_SIGNAL(14, "Channel 8 Quadrature A"),
1191        QUAD8_QUAD_SIGNAL(15, "Channel 8 Quadrature B"),
1192        QUAD8_INDEX_SIGNAL(16, "Channel 1 Index"),
1193        QUAD8_INDEX_SIGNAL(17, "Channel 2 Index"),
1194        QUAD8_INDEX_SIGNAL(18, "Channel 3 Index"),
1195        QUAD8_INDEX_SIGNAL(19, "Channel 4 Index"),
1196        QUAD8_INDEX_SIGNAL(20, "Channel 5 Index"),
1197        QUAD8_INDEX_SIGNAL(21, "Channel 6 Index"),
1198        QUAD8_INDEX_SIGNAL(22, "Channel 7 Index"),
1199        QUAD8_INDEX_SIGNAL(23, "Channel 8 Index")
1200};
1201
1202#define QUAD8_COUNT_SYNAPSES(_id) {                                     \
1203        {                                                               \
1204                .actions_list = quad8_synapse_actions_list,             \
1205                .num_actions = ARRAY_SIZE(quad8_synapse_actions_list),  \
1206                .signal = quad8_signals + 2 * (_id)                     \
1207        },                                                              \
1208        {                                                               \
1209                .actions_list = quad8_synapse_actions_list,             \
1210                .num_actions = ARRAY_SIZE(quad8_synapse_actions_list),  \
1211                .signal = quad8_signals + 2 * (_id) + 1                 \
1212        },                                                              \
1213        {                                                               \
1214                .actions_list = quad8_index_actions_list,               \
1215                .num_actions = ARRAY_SIZE(quad8_index_actions_list),    \
1216                .signal = quad8_signals + 2 * (_id) + 16                \
1217        }                                                               \
1218}
1219
1220static struct counter_synapse quad8_count_synapses[][3] = {
1221        QUAD8_COUNT_SYNAPSES(0), QUAD8_COUNT_SYNAPSES(1),
1222        QUAD8_COUNT_SYNAPSES(2), QUAD8_COUNT_SYNAPSES(3),
1223        QUAD8_COUNT_SYNAPSES(4), QUAD8_COUNT_SYNAPSES(5),
1224        QUAD8_COUNT_SYNAPSES(6), QUAD8_COUNT_SYNAPSES(7)
1225};
1226
1227static const struct counter_count_ext quad8_count_ext[] = {
1228        {
1229                .name = "ceiling",
1230                .read = quad8_count_ceiling_read,
1231                .write = quad8_count_ceiling_write
1232        },
1233        {
1234                .name = "floor",
1235                .read = quad8_count_floor_read
1236        },
1237        COUNTER_COUNT_ENUM("count_mode", &quad8_cnt_mode_enum),
1238        COUNTER_COUNT_ENUM_AVAILABLE("count_mode", &quad8_cnt_mode_enum),
1239        {
1240                .name = "direction",
1241                .read = quad8_count_direction_read
1242        },
1243        {
1244                .name = "enable",
1245                .read = quad8_count_enable_read,
1246                .write = quad8_count_enable_write
1247        },
1248        COUNTER_COUNT_ENUM("error_noise", &quad8_error_noise_enum),
1249        COUNTER_COUNT_ENUM_AVAILABLE("error_noise", &quad8_error_noise_enum),
1250        {
1251                .name = "preset",
1252                .read = quad8_count_preset_read,
1253                .write = quad8_count_preset_write
1254        },
1255        {
1256                .name = "preset_enable",
1257                .read = quad8_count_preset_enable_read,
1258                .write = quad8_count_preset_enable_write
1259        }
1260};
1261
1262#define QUAD8_COUNT(_id, _cntname) {                                    \
1263        .id = (_id),                                                    \
1264        .name = (_cntname),                                             \
1265        .functions_list = quad8_count_functions_list,                   \
1266        .num_functions = ARRAY_SIZE(quad8_count_functions_list),        \
1267        .synapses = quad8_count_synapses[(_id)],                        \
1268        .num_synapses = 2,                                              \
1269        .ext = quad8_count_ext,                                         \
1270        .num_ext = ARRAY_SIZE(quad8_count_ext)                          \
1271}
1272
1273static struct counter_count quad8_counts[] = {
1274        QUAD8_COUNT(0, "Channel 1 Count"),
1275        QUAD8_COUNT(1, "Channel 2 Count"),
1276        QUAD8_COUNT(2, "Channel 3 Count"),
1277        QUAD8_COUNT(3, "Channel 4 Count"),
1278        QUAD8_COUNT(4, "Channel 5 Count"),
1279        QUAD8_COUNT(5, "Channel 6 Count"),
1280        QUAD8_COUNT(6, "Channel 7 Count"),
1281        QUAD8_COUNT(7, "Channel 8 Count")
1282};
1283
1284static int quad8_probe(struct device *dev, unsigned int id)
1285{
1286        struct iio_dev *indio_dev;
1287        struct quad8_iio *quad8iio;
1288        int i, j;
1289        unsigned int base_offset;
1290        int err;
1291
1292        if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
1293                dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
1294                        base[id], base[id] + QUAD8_EXTENT);
1295                return -EBUSY;
1296        }
1297
1298        /* Allocate IIO device; this also allocates driver data structure */
1299        indio_dev = devm_iio_device_alloc(dev, sizeof(*quad8iio));
1300        if (!indio_dev)
1301                return -ENOMEM;
1302
1303        /* Initialize IIO device */
1304        indio_dev->info = &quad8_info;
1305        indio_dev->modes = INDIO_DIRECT_MODE;
1306        indio_dev->num_channels = ARRAY_SIZE(quad8_channels);
1307        indio_dev->channels = quad8_channels;
1308        indio_dev->name = dev_name(dev);
1309        indio_dev->dev.parent = dev;
1310
1311        /* Initialize Counter device and driver data */
1312        quad8iio = iio_priv(indio_dev);
1313        quad8iio->counter.name = dev_name(dev);
1314        quad8iio->counter.parent = dev;
1315        quad8iio->counter.ops = &quad8_ops;
1316        quad8iio->counter.counts = quad8_counts;
1317        quad8iio->counter.num_counts = ARRAY_SIZE(quad8_counts);
1318        quad8iio->counter.signals = quad8_signals;
1319        quad8iio->counter.num_signals = ARRAY_SIZE(quad8_signals);
1320        quad8iio->counter.priv = quad8iio;
1321        quad8iio->base = base[id];
1322
1323        /* Reset all counters and disable interrupt function */
1324        outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
1325        /* Set initial configuration for all counters */
1326        for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
1327                base_offset = base[id] + 2 * i;
1328                /* Reset Byte Pointer */
1329                outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
1330                /* Reset Preset Register */
1331                for (j = 0; j < 3; j++)
1332                        outb(0x00, base_offset);
1333                /* Reset Borrow, Carry, Compare, and Sign flags */
1334                outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
1335                /* Reset Error flag */
1336                outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
1337                /* Binary encoding; Normal count; non-quadrature mode */
1338                outb(QUAD8_CTR_CMR, base_offset + 1);
1339                /* Disable A and B inputs; preset on index; FLG1 as Carry */
1340                outb(QUAD8_CTR_IOR, base_offset + 1);
1341                /* Disable index function; negative index polarity */
1342                outb(QUAD8_CTR_IDR, base_offset + 1);
1343        }
1344        /* Enable all counters */
1345        outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
1346
1347        /* Register IIO device */
1348        err = devm_iio_device_register(dev, indio_dev);
1349        if (err)
1350                return err;
1351
1352        /* Register Counter device */
1353        return devm_counter_register(dev, &quad8iio->counter);
1354}
1355
1356static struct isa_driver quad8_driver = {
1357        .probe = quad8_probe,
1358        .driver = {
1359                .name = "104-quad-8"
1360        }
1361};
1362
1363module_isa_driver(quad8_driver, num_quad8);
1364
1365MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
1366MODULE_DESCRIPTION("ACCES 104-QUAD-8 IIO driver");
1367MODULE_LICENSE("GPL v2");
1368