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/io.h>
  13#include <linux/ioport.h>
  14#include <linux/isa.h>
  15#include <linux/kernel.h>
  16#include <linux/module.h>
  17#include <linux/moduleparam.h>
  18#include <linux/types.h>
  19
  20#define QUAD8_EXTENT 32
  21
  22static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)];
  23static unsigned int num_quad8;
  24module_param_hw_array(base, uint, ioport, &num_quad8, 0);
  25MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
  26
  27#define QUAD8_NUM_COUNTERS 8
  28
  29/**
  30 * struct quad8 - device private data structure
  31 * @lock:               lock to prevent clobbering device states during R/W ops
  32 * @counter:            instance of the counter_device
  33 * @fck_prescaler:      array of filter clock prescaler configurations
  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 * @cable_fault_enable: differential encoder cable status enable configurations
  43 * @base:               base port address of the device
  44 */
  45struct quad8 {
  46        struct mutex lock;
  47        struct counter_device counter;
  48        unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
  49        unsigned int preset[QUAD8_NUM_COUNTERS];
  50        unsigned int count_mode[QUAD8_NUM_COUNTERS];
  51        unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
  52        unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
  53        unsigned int ab_enable[QUAD8_NUM_COUNTERS];
  54        unsigned int preset_enable[QUAD8_NUM_COUNTERS];
  55        unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
  56        unsigned int index_polarity[QUAD8_NUM_COUNTERS];
  57        unsigned int cable_fault_enable;
  58        unsigned int base;
  59};
  60
  61#define QUAD8_REG_CHAN_OP 0x11
  62#define QUAD8_REG_INDEX_INPUT_LEVELS 0x16
  63#define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17
  64/* Borrow Toggle flip-flop */
  65#define QUAD8_FLAG_BT BIT(0)
  66/* Carry Toggle flip-flop */
  67#define QUAD8_FLAG_CT BIT(1)
  68/* Error flag */
  69#define QUAD8_FLAG_E BIT(4)
  70/* Up/Down flag */
  71#define QUAD8_FLAG_UD BIT(5)
  72/* Reset and Load Signal Decoders */
  73#define QUAD8_CTR_RLD 0x00
  74/* Counter Mode Register */
  75#define QUAD8_CTR_CMR 0x20
  76/* Input / Output Control Register */
  77#define QUAD8_CTR_IOR 0x40
  78/* Index Control Register */
  79#define QUAD8_CTR_IDR 0x60
  80/* Reset Byte Pointer (three byte data pointer) */
  81#define QUAD8_RLD_RESET_BP 0x01
  82/* Reset Counter */
  83#define QUAD8_RLD_RESET_CNTR 0x02
  84/* Reset Borrow Toggle, Carry Toggle, Compare Toggle, and Sign flags */
  85#define QUAD8_RLD_RESET_FLAGS 0x04
  86/* Reset Error flag */
  87#define QUAD8_RLD_RESET_E 0x06
  88/* Preset Register to Counter */
  89#define QUAD8_RLD_PRESET_CNTR 0x08
  90/* Transfer Counter to Output Latch */
  91#define QUAD8_RLD_CNTR_OUT 0x10
  92/* Transfer Preset Register LSB to FCK Prescaler */
  93#define QUAD8_RLD_PRESET_PSC 0x18
  94#define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00
  95#define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
  96#define QUAD8_CMR_QUADRATURE_X1 0x08
  97#define QUAD8_CMR_QUADRATURE_X2 0x10
  98#define QUAD8_CMR_QUADRATURE_X4 0x18
  99
 100static int quad8_signal_read(struct counter_device *counter,
 101                             struct counter_signal *signal,
 102                             enum counter_signal_level *level)
 103{
 104        const struct quad8 *const priv = counter->priv;
 105        unsigned int state;
 106
 107        /* Only Index signal levels can be read */
 108        if (signal->id < 16)
 109                return -EINVAL;
 110
 111        state = inb(priv->base + QUAD8_REG_INDEX_INPUT_LEVELS)
 112                & BIT(signal->id - 16);
 113
 114        *level = (state) ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
 115
 116        return 0;
 117}
 118
 119static int quad8_count_read(struct counter_device *counter,
 120        struct counter_count *count, unsigned long *val)
 121{
 122        struct quad8 *const priv = counter->priv;
 123        const int base_offset = priv->base + 2 * count->id;
 124        unsigned int flags;
 125        unsigned int borrow;
 126        unsigned int carry;
 127        int i;
 128
 129        flags = inb(base_offset + 1);
 130        borrow = flags & QUAD8_FLAG_BT;
 131        carry = !!(flags & QUAD8_FLAG_CT);
 132
 133        /* Borrow XOR Carry effectively doubles count range */
 134        *val = (unsigned long)(borrow ^ carry) << 24;
 135
 136        mutex_lock(&priv->lock);
 137
 138        /* Reset Byte Pointer; transfer Counter to Output Latch */
 139        outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
 140             base_offset + 1);
 141
 142        for (i = 0; i < 3; i++)
 143                *val |= (unsigned long)inb(base_offset) << (8 * i);
 144
 145        mutex_unlock(&priv->lock);
 146
 147        return 0;
 148}
 149
 150static int quad8_count_write(struct counter_device *counter,
 151        struct counter_count *count, unsigned long val)
 152{
 153        struct quad8 *const priv = counter->priv;
 154        const int base_offset = priv->base + 2 * count->id;
 155        int i;
 156
 157        /* Only 24-bit values are supported */
 158        if (val > 0xFFFFFF)
 159                return -ERANGE;
 160
 161        mutex_lock(&priv->lock);
 162
 163        /* Reset Byte Pointer */
 164        outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
 165
 166        /* Counter can only be set via Preset Register */
 167        for (i = 0; i < 3; i++)
 168                outb(val >> (8 * i), base_offset);
 169
 170        /* Transfer Preset Register to Counter */
 171        outb(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, base_offset + 1);
 172
 173        /* Reset Byte Pointer */
 174        outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
 175
 176        /* Set Preset Register back to original value */
 177        val = priv->preset[count->id];
 178        for (i = 0; i < 3; i++)
 179                outb(val >> (8 * i), base_offset);
 180
 181        /* Reset Borrow, Carry, Compare, and Sign flags */
 182        outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
 183        /* Reset Error flag */
 184        outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
 185
 186        mutex_unlock(&priv->lock);
 187
 188        return 0;
 189}
 190
 191enum quad8_count_function {
 192        QUAD8_COUNT_FUNCTION_PULSE_DIRECTION = 0,
 193        QUAD8_COUNT_FUNCTION_QUADRATURE_X1,
 194        QUAD8_COUNT_FUNCTION_QUADRATURE_X2,
 195        QUAD8_COUNT_FUNCTION_QUADRATURE_X4
 196};
 197
 198static const enum counter_function quad8_count_functions_list[] = {
 199        [QUAD8_COUNT_FUNCTION_PULSE_DIRECTION] = COUNTER_FUNCTION_PULSE_DIRECTION,
 200        [QUAD8_COUNT_FUNCTION_QUADRATURE_X1] = COUNTER_FUNCTION_QUADRATURE_X1_A,
 201        [QUAD8_COUNT_FUNCTION_QUADRATURE_X2] = COUNTER_FUNCTION_QUADRATURE_X2_A,
 202        [QUAD8_COUNT_FUNCTION_QUADRATURE_X4] = COUNTER_FUNCTION_QUADRATURE_X4
 203};
 204
 205static int quad8_function_get(struct counter_device *counter,
 206        struct counter_count *count, size_t *function)
 207{
 208        struct quad8 *const priv = counter->priv;
 209        const int id = count->id;
 210
 211        mutex_lock(&priv->lock);
 212
 213        if (priv->quadrature_mode[id])
 214                switch (priv->quadrature_scale[id]) {
 215                case 0:
 216                        *function = QUAD8_COUNT_FUNCTION_QUADRATURE_X1;
 217                        break;
 218                case 1:
 219                        *function = QUAD8_COUNT_FUNCTION_QUADRATURE_X2;
 220                        break;
 221                case 2:
 222                        *function = QUAD8_COUNT_FUNCTION_QUADRATURE_X4;
 223                        break;
 224                }
 225        else
 226                *function = QUAD8_COUNT_FUNCTION_PULSE_DIRECTION;
 227
 228        mutex_unlock(&priv->lock);
 229
 230        return 0;
 231}
 232
 233static int quad8_function_set(struct counter_device *counter,
 234        struct counter_count *count, size_t function)
 235{
 236        struct quad8 *const priv = counter->priv;
 237        const int id = count->id;
 238        unsigned int *const quadrature_mode = priv->quadrature_mode + id;
 239        unsigned int *const scale = priv->quadrature_scale + id;
 240        unsigned int *const synchronous_mode = priv->synchronous_mode + id;
 241        const int base_offset = priv->base + 2 * id + 1;
 242        unsigned int mode_cfg;
 243        unsigned int idr_cfg;
 244
 245        mutex_lock(&priv->lock);
 246
 247        mode_cfg = priv->count_mode[id] << 1;
 248        idr_cfg = priv->index_polarity[id] << 1;
 249
 250        if (function == QUAD8_COUNT_FUNCTION_PULSE_DIRECTION) {
 251                *quadrature_mode = 0;
 252
 253                /* Quadrature scaling only available in quadrature mode */
 254                *scale = 0;
 255
 256                /* Synchronous function not supported in non-quadrature mode */
 257                if (*synchronous_mode) {
 258                        *synchronous_mode = 0;
 259                        /* Disable synchronous function mode */
 260                        outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 261                }
 262        } else {
 263                *quadrature_mode = 1;
 264
 265                switch (function) {
 266                case QUAD8_COUNT_FUNCTION_QUADRATURE_X1:
 267                        *scale = 0;
 268                        mode_cfg |= QUAD8_CMR_QUADRATURE_X1;
 269                        break;
 270                case QUAD8_COUNT_FUNCTION_QUADRATURE_X2:
 271                        *scale = 1;
 272                        mode_cfg |= QUAD8_CMR_QUADRATURE_X2;
 273                        break;
 274                case QUAD8_COUNT_FUNCTION_QUADRATURE_X4:
 275                        *scale = 2;
 276                        mode_cfg |= QUAD8_CMR_QUADRATURE_X4;
 277                        break;
 278                default:
 279                        /* should never reach this path */
 280                        mutex_unlock(&priv->lock);
 281                        return -EINVAL;
 282                }
 283        }
 284
 285        /* Load mode configuration to Counter Mode Register */
 286        outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 287
 288        mutex_unlock(&priv->lock);
 289
 290        return 0;
 291}
 292
 293static void quad8_direction_get(struct counter_device *counter,
 294        struct counter_count *count, enum counter_count_direction *direction)
 295{
 296        const struct quad8 *const priv = counter->priv;
 297        unsigned int ud_flag;
 298        const unsigned int flag_addr = priv->base + 2 * count->id + 1;
 299
 300        /* U/D flag: nonzero = up, zero = down */
 301        ud_flag = inb(flag_addr) & QUAD8_FLAG_UD;
 302
 303        *direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD :
 304                COUNTER_COUNT_DIRECTION_BACKWARD;
 305}
 306
 307enum quad8_synapse_action {
 308        QUAD8_SYNAPSE_ACTION_NONE = 0,
 309        QUAD8_SYNAPSE_ACTION_RISING_EDGE,
 310        QUAD8_SYNAPSE_ACTION_FALLING_EDGE,
 311        QUAD8_SYNAPSE_ACTION_BOTH_EDGES
 312};
 313
 314static const enum counter_synapse_action quad8_index_actions_list[] = {
 315        [QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
 316        [QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE
 317};
 318
 319static const enum counter_synapse_action quad8_synapse_actions_list[] = {
 320        [QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
 321        [QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
 322        [QUAD8_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
 323        [QUAD8_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES
 324};
 325
 326static int quad8_action_get(struct counter_device *counter,
 327        struct counter_count *count, struct counter_synapse *synapse,
 328        size_t *action)
 329{
 330        struct quad8 *const priv = counter->priv;
 331        int err;
 332        size_t function = 0;
 333        const size_t signal_a_id = count->synapses[0].signal->id;
 334        enum counter_count_direction direction;
 335
 336        /* Handle Index signals */
 337        if (synapse->signal->id >= 16) {
 338                if (priv->preset_enable[count->id])
 339                        *action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
 340                else
 341                        *action = QUAD8_SYNAPSE_ACTION_NONE;
 342
 343                return 0;
 344        }
 345
 346        err = quad8_function_get(counter, count, &function);
 347        if (err)
 348                return err;
 349
 350        /* Default action mode */
 351        *action = QUAD8_SYNAPSE_ACTION_NONE;
 352
 353        /* Determine action mode based on current count function mode */
 354        switch (function) {
 355        case QUAD8_COUNT_FUNCTION_PULSE_DIRECTION:
 356                if (synapse->signal->id == signal_a_id)
 357                        *action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
 358                return 0;
 359        case QUAD8_COUNT_FUNCTION_QUADRATURE_X1:
 360                if (synapse->signal->id == signal_a_id) {
 361                        quad8_direction_get(counter, count, &direction);
 362
 363                        if (direction == COUNTER_COUNT_DIRECTION_FORWARD)
 364                                *action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
 365                        else
 366                                *action = QUAD8_SYNAPSE_ACTION_FALLING_EDGE;
 367                }
 368                return 0;
 369        case QUAD8_COUNT_FUNCTION_QUADRATURE_X2:
 370                if (synapse->signal->id == signal_a_id)
 371                        *action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES;
 372                return 0;
 373        case QUAD8_COUNT_FUNCTION_QUADRATURE_X4:
 374                *action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES;
 375                return 0;
 376        default:
 377                /* should never reach this path */
 378                return -EINVAL;
 379        }
 380}
 381
 382static const struct counter_ops quad8_ops = {
 383        .signal_read = quad8_signal_read,
 384        .count_read = quad8_count_read,
 385        .count_write = quad8_count_write,
 386        .function_get = quad8_function_get,
 387        .function_set = quad8_function_set,
 388        .action_get = quad8_action_get
 389};
 390
 391static const char *const quad8_index_polarity_modes[] = {
 392        "negative",
 393        "positive"
 394};
 395
 396static int quad8_index_polarity_get(struct counter_device *counter,
 397        struct counter_signal *signal, size_t *index_polarity)
 398{
 399        const struct quad8 *const priv = counter->priv;
 400        const size_t channel_id = signal->id - 16;
 401
 402        *index_polarity = priv->index_polarity[channel_id];
 403
 404        return 0;
 405}
 406
 407static int quad8_index_polarity_set(struct counter_device *counter,
 408        struct counter_signal *signal, size_t index_polarity)
 409{
 410        struct quad8 *const priv = counter->priv;
 411        const size_t channel_id = signal->id - 16;
 412        const int base_offset = priv->base + 2 * channel_id + 1;
 413        unsigned int idr_cfg = index_polarity << 1;
 414
 415        mutex_lock(&priv->lock);
 416
 417        idr_cfg |= priv->synchronous_mode[channel_id];
 418
 419        priv->index_polarity[channel_id] = index_polarity;
 420
 421        /* Load Index Control configuration to Index Control Register */
 422        outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 423
 424        mutex_unlock(&priv->lock);
 425
 426        return 0;
 427}
 428
 429static struct counter_signal_enum_ext quad8_index_pol_enum = {
 430        .items = quad8_index_polarity_modes,
 431        .num_items = ARRAY_SIZE(quad8_index_polarity_modes),
 432        .get = quad8_index_polarity_get,
 433        .set = quad8_index_polarity_set
 434};
 435
 436static const char *const quad8_synchronous_modes[] = {
 437        "non-synchronous",
 438        "synchronous"
 439};
 440
 441static int quad8_synchronous_mode_get(struct counter_device *counter,
 442        struct counter_signal *signal, size_t *synchronous_mode)
 443{
 444        const struct quad8 *const priv = counter->priv;
 445        const size_t channel_id = signal->id - 16;
 446
 447        *synchronous_mode = priv->synchronous_mode[channel_id];
 448
 449        return 0;
 450}
 451
 452static int quad8_synchronous_mode_set(struct counter_device *counter,
 453        struct counter_signal *signal, size_t synchronous_mode)
 454{
 455        struct quad8 *const priv = counter->priv;
 456        const size_t channel_id = signal->id - 16;
 457        const int base_offset = priv->base + 2 * channel_id + 1;
 458        unsigned int idr_cfg = synchronous_mode;
 459
 460        mutex_lock(&priv->lock);
 461
 462        idr_cfg |= priv->index_polarity[channel_id] << 1;
 463
 464        /* Index function must be non-synchronous in non-quadrature mode */
 465        if (synchronous_mode && !priv->quadrature_mode[channel_id]) {
 466                mutex_unlock(&priv->lock);
 467                return -EINVAL;
 468        }
 469
 470        priv->synchronous_mode[channel_id] = synchronous_mode;
 471
 472        /* Load Index Control configuration to Index Control Register */
 473        outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 474
 475        mutex_unlock(&priv->lock);
 476
 477        return 0;
 478}
 479
 480static struct counter_signal_enum_ext quad8_syn_mode_enum = {
 481        .items = quad8_synchronous_modes,
 482        .num_items = ARRAY_SIZE(quad8_synchronous_modes),
 483        .get = quad8_synchronous_mode_get,
 484        .set = quad8_synchronous_mode_set
 485};
 486
 487static ssize_t quad8_count_floor_read(struct counter_device *counter,
 488        struct counter_count *count, void *private, char *buf)
 489{
 490        /* Only a floor of 0 is supported */
 491        return sprintf(buf, "0\n");
 492}
 493
 494static int quad8_count_mode_get(struct counter_device *counter,
 495        struct counter_count *count, size_t *cnt_mode)
 496{
 497        const struct quad8 *const priv = counter->priv;
 498
 499        /* Map 104-QUAD-8 count mode to Generic Counter count mode */
 500        switch (priv->count_mode[count->id]) {
 501        case 0:
 502                *cnt_mode = COUNTER_COUNT_MODE_NORMAL;
 503                break;
 504        case 1:
 505                *cnt_mode = COUNTER_COUNT_MODE_RANGE_LIMIT;
 506                break;
 507        case 2:
 508                *cnt_mode = COUNTER_COUNT_MODE_NON_RECYCLE;
 509                break;
 510        case 3:
 511                *cnt_mode = COUNTER_COUNT_MODE_MODULO_N;
 512                break;
 513        }
 514
 515        return 0;
 516}
 517
 518static int quad8_count_mode_set(struct counter_device *counter,
 519        struct counter_count *count, size_t cnt_mode)
 520{
 521        struct quad8 *const priv = counter->priv;
 522        unsigned int mode_cfg;
 523        const int base_offset = priv->base + 2 * count->id + 1;
 524
 525        /* Map Generic Counter count mode to 104-QUAD-8 count mode */
 526        switch (cnt_mode) {
 527        case COUNTER_COUNT_MODE_NORMAL:
 528                cnt_mode = 0;
 529                break;
 530        case COUNTER_COUNT_MODE_RANGE_LIMIT:
 531                cnt_mode = 1;
 532                break;
 533        case COUNTER_COUNT_MODE_NON_RECYCLE:
 534                cnt_mode = 2;
 535                break;
 536        case COUNTER_COUNT_MODE_MODULO_N:
 537                cnt_mode = 3;
 538                break;
 539        default:
 540                /* should never reach this path */
 541                return -EINVAL;
 542        }
 543
 544        mutex_lock(&priv->lock);
 545
 546        priv->count_mode[count->id] = cnt_mode;
 547
 548        /* Set count mode configuration value */
 549        mode_cfg = cnt_mode << 1;
 550
 551        /* Add quadrature mode configuration */
 552        if (priv->quadrature_mode[count->id])
 553                mode_cfg |= (priv->quadrature_scale[count->id] + 1) << 3;
 554
 555        /* Load mode configuration to Counter Mode Register */
 556        outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 557
 558        mutex_unlock(&priv->lock);
 559
 560        return 0;
 561}
 562
 563static struct counter_count_enum_ext quad8_cnt_mode_enum = {
 564        .items = counter_count_mode_str,
 565        .num_items = ARRAY_SIZE(counter_count_mode_str),
 566        .get = quad8_count_mode_get,
 567        .set = quad8_count_mode_set
 568};
 569
 570static ssize_t quad8_count_direction_read(struct counter_device *counter,
 571        struct counter_count *count, void *priv, char *buf)
 572{
 573        enum counter_count_direction dir;
 574
 575        quad8_direction_get(counter, count, &dir);
 576
 577        return sprintf(buf, "%s\n", counter_count_direction_str[dir]);
 578}
 579
 580static ssize_t quad8_count_enable_read(struct counter_device *counter,
 581        struct counter_count *count, void *private, char *buf)
 582{
 583        const struct quad8 *const priv = counter->priv;
 584
 585        return sprintf(buf, "%u\n", priv->ab_enable[count->id]);
 586}
 587
 588static ssize_t quad8_count_enable_write(struct counter_device *counter,
 589        struct counter_count *count, void *private, const char *buf, size_t len)
 590{
 591        struct quad8 *const priv = counter->priv;
 592        const int base_offset = priv->base + 2 * count->id;
 593        int err;
 594        bool ab_enable;
 595        unsigned int ior_cfg;
 596
 597        err = kstrtobool(buf, &ab_enable);
 598        if (err)
 599                return err;
 600
 601        mutex_lock(&priv->lock);
 602
 603        priv->ab_enable[count->id] = ab_enable;
 604
 605        ior_cfg = ab_enable | priv->preset_enable[count->id] << 1;
 606
 607        /* Load I/O control configuration */
 608        outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
 609
 610        mutex_unlock(&priv->lock);
 611
 612        return len;
 613}
 614
 615static const char *const quad8_noise_error_states[] = {
 616        "No excessive noise is present at the count inputs",
 617        "Excessive noise is present at the count inputs"
 618};
 619
 620static int quad8_error_noise_get(struct counter_device *counter,
 621        struct counter_count *count, size_t *noise_error)
 622{
 623        const struct quad8 *const priv = counter->priv;
 624        const int base_offset = priv->base + 2 * count->id + 1;
 625
 626        *noise_error = !!(inb(base_offset) & QUAD8_FLAG_E);
 627
 628        return 0;
 629}
 630
 631static struct counter_count_enum_ext quad8_error_noise_enum = {
 632        .items = quad8_noise_error_states,
 633        .num_items = ARRAY_SIZE(quad8_noise_error_states),
 634        .get = quad8_error_noise_get
 635};
 636
 637static ssize_t quad8_count_preset_read(struct counter_device *counter,
 638        struct counter_count *count, void *private, char *buf)
 639{
 640        const struct quad8 *const priv = counter->priv;
 641
 642        return sprintf(buf, "%u\n", priv->preset[count->id]);
 643}
 644
 645static void quad8_preset_register_set(struct quad8 *const priv, const int id,
 646                                      const unsigned int preset)
 647{
 648        const unsigned int base_offset = priv->base + 2 * id;
 649        int i;
 650
 651        priv->preset[id] = preset;
 652
 653        /* Reset Byte Pointer */
 654        outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
 655
 656        /* Set Preset Register */
 657        for (i = 0; i < 3; i++)
 658                outb(preset >> (8 * i), base_offset);
 659}
 660
 661static ssize_t quad8_count_preset_write(struct counter_device *counter,
 662        struct counter_count *count, void *private, const char *buf, size_t len)
 663{
 664        struct quad8 *const priv = counter->priv;
 665        unsigned int preset;
 666        int ret;
 667
 668        ret = kstrtouint(buf, 0, &preset);
 669        if (ret)
 670                return ret;
 671
 672        /* Only 24-bit values are supported */
 673        if (preset > 0xFFFFFF)
 674                return -ERANGE;
 675
 676        mutex_lock(&priv->lock);
 677
 678        quad8_preset_register_set(priv, count->id, preset);
 679
 680        mutex_unlock(&priv->lock);
 681
 682        return len;
 683}
 684
 685static ssize_t quad8_count_ceiling_read(struct counter_device *counter,
 686        struct counter_count *count, void *private, char *buf)
 687{
 688        struct quad8 *const priv = counter->priv;
 689
 690        mutex_lock(&priv->lock);
 691
 692        /* Range Limit and Modulo-N count modes use preset value as ceiling */
 693        switch (priv->count_mode[count->id]) {
 694        case 1:
 695        case 3:
 696                mutex_unlock(&priv->lock);
 697                return sprintf(buf, "%u\n", priv->preset[count->id]);
 698        }
 699
 700        mutex_unlock(&priv->lock);
 701
 702        /* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
 703        return sprintf(buf, "33554431\n");
 704}
 705
 706static ssize_t quad8_count_ceiling_write(struct counter_device *counter,
 707        struct counter_count *count, void *private, const char *buf, size_t len)
 708{
 709        struct quad8 *const priv = counter->priv;
 710        unsigned int ceiling;
 711        int ret;
 712
 713        ret = kstrtouint(buf, 0, &ceiling);
 714        if (ret)
 715                return ret;
 716
 717        /* Only 24-bit values are supported */
 718        if (ceiling > 0xFFFFFF)
 719                return -ERANGE;
 720
 721        mutex_lock(&priv->lock);
 722
 723        /* Range Limit and Modulo-N count modes use preset value as ceiling */
 724        switch (priv->count_mode[count->id]) {
 725        case 1:
 726        case 3:
 727                quad8_preset_register_set(priv, count->id, ceiling);
 728                mutex_unlock(&priv->lock);
 729                return len;
 730        }
 731
 732        mutex_unlock(&priv->lock);
 733
 734        return -EINVAL;
 735}
 736
 737static ssize_t quad8_count_preset_enable_read(struct counter_device *counter,
 738        struct counter_count *count, void *private, char *buf)
 739{
 740        const struct quad8 *const priv = counter->priv;
 741
 742        return sprintf(buf, "%u\n", !priv->preset_enable[count->id]);
 743}
 744
 745static ssize_t quad8_count_preset_enable_write(struct counter_device *counter,
 746        struct counter_count *count, void *private, const char *buf, size_t len)
 747{
 748        struct quad8 *const priv = counter->priv;
 749        const int base_offset = priv->base + 2 * count->id + 1;
 750        bool preset_enable;
 751        int ret;
 752        unsigned int ior_cfg;
 753
 754        ret = kstrtobool(buf, &preset_enable);
 755        if (ret)
 756                return ret;
 757
 758        /* Preset enable is active low in Input/Output Control register */
 759        preset_enable = !preset_enable;
 760
 761        mutex_lock(&priv->lock);
 762
 763        priv->preset_enable[count->id] = preset_enable;
 764
 765        ior_cfg = priv->ab_enable[count->id] | (unsigned int)preset_enable << 1;
 766
 767        /* Load I/O control configuration to Input / Output Control Register */
 768        outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
 769
 770        mutex_unlock(&priv->lock);
 771
 772        return len;
 773}
 774
 775static ssize_t quad8_signal_cable_fault_read(struct counter_device *counter,
 776                                             struct counter_signal *signal,
 777                                             void *private, char *buf)
 778{
 779        struct quad8 *const priv = counter->priv;
 780        const size_t channel_id = signal->id / 2;
 781        bool disabled;
 782        unsigned int status;
 783        unsigned int fault;
 784
 785        mutex_lock(&priv->lock);
 786
 787        disabled = !(priv->cable_fault_enable & BIT(channel_id));
 788
 789        if (disabled) {
 790                mutex_unlock(&priv->lock);
 791                return -EINVAL;
 792        }
 793
 794        /* Logic 0 = cable fault */
 795        status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
 796
 797        mutex_unlock(&priv->lock);
 798
 799        /* Mask respective channel and invert logic */
 800        fault = !(status & BIT(channel_id));
 801
 802        return sprintf(buf, "%u\n", fault);
 803}
 804
 805static ssize_t quad8_signal_cable_fault_enable_read(
 806        struct counter_device *counter, struct counter_signal *signal,
 807        void *private, char *buf)
 808{
 809        const struct quad8 *const priv = counter->priv;
 810        const size_t channel_id = signal->id / 2;
 811        const unsigned int enb = !!(priv->cable_fault_enable & BIT(channel_id));
 812
 813        return sprintf(buf, "%u\n", enb);
 814}
 815
 816static ssize_t quad8_signal_cable_fault_enable_write(
 817        struct counter_device *counter, struct counter_signal *signal,
 818        void *private, const char *buf, size_t len)
 819{
 820        struct quad8 *const priv = counter->priv;
 821        const size_t channel_id = signal->id / 2;
 822        bool enable;
 823        int ret;
 824        unsigned int cable_fault_enable;
 825
 826        ret = kstrtobool(buf, &enable);
 827        if (ret)
 828                return ret;
 829
 830        mutex_lock(&priv->lock);
 831
 832        if (enable)
 833                priv->cable_fault_enable |= BIT(channel_id);
 834        else
 835                priv->cable_fault_enable &= ~BIT(channel_id);
 836
 837        /* Enable is active low in Differential Encoder Cable Status register */
 838        cable_fault_enable = ~priv->cable_fault_enable;
 839
 840        outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
 841
 842        mutex_unlock(&priv->lock);
 843
 844        return len;
 845}
 846
 847static ssize_t quad8_signal_fck_prescaler_read(struct counter_device *counter,
 848        struct counter_signal *signal, void *private, char *buf)
 849{
 850        const struct quad8 *const priv = counter->priv;
 851        const size_t channel_id = signal->id / 2;
 852
 853        return sprintf(buf, "%u\n", priv->fck_prescaler[channel_id]);
 854}
 855
 856static ssize_t quad8_signal_fck_prescaler_write(struct counter_device *counter,
 857        struct counter_signal *signal, void *private, const char *buf,
 858        size_t len)
 859{
 860        struct quad8 *const priv = counter->priv;
 861        const size_t channel_id = signal->id / 2;
 862        const int base_offset = priv->base + 2 * channel_id;
 863        u8 prescaler;
 864        int ret;
 865
 866        ret = kstrtou8(buf, 0, &prescaler);
 867        if (ret)
 868                return ret;
 869
 870        mutex_lock(&priv->lock);
 871
 872        priv->fck_prescaler[channel_id] = prescaler;
 873
 874        /* Reset Byte Pointer */
 875        outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
 876
 877        /* Set filter clock factor */
 878        outb(prescaler, base_offset);
 879        outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
 880             base_offset + 1);
 881
 882        mutex_unlock(&priv->lock);
 883
 884        return len;
 885}
 886
 887static const struct counter_signal_ext quad8_signal_ext[] = {
 888        {
 889                .name = "cable_fault",
 890                .read = quad8_signal_cable_fault_read
 891        },
 892        {
 893                .name = "cable_fault_enable",
 894                .read = quad8_signal_cable_fault_enable_read,
 895                .write = quad8_signal_cable_fault_enable_write
 896        },
 897        {
 898                .name = "filter_clock_prescaler",
 899                .read = quad8_signal_fck_prescaler_read,
 900                .write = quad8_signal_fck_prescaler_write
 901        }
 902};
 903
 904static const struct counter_signal_ext quad8_index_ext[] = {
 905        COUNTER_SIGNAL_ENUM("index_polarity", &quad8_index_pol_enum),
 906        COUNTER_SIGNAL_ENUM_AVAILABLE("index_polarity", &quad8_index_pol_enum),
 907        COUNTER_SIGNAL_ENUM("synchronous_mode", &quad8_syn_mode_enum),
 908        COUNTER_SIGNAL_ENUM_AVAILABLE("synchronous_mode", &quad8_syn_mode_enum)
 909};
 910
 911#define QUAD8_QUAD_SIGNAL(_id, _name) {         \
 912        .id = (_id),                            \
 913        .name = (_name),                        \
 914        .ext = quad8_signal_ext,                \
 915        .num_ext = ARRAY_SIZE(quad8_signal_ext) \
 916}
 917
 918#define QUAD8_INDEX_SIGNAL(_id, _name) {        \
 919        .id = (_id),                            \
 920        .name = (_name),                        \
 921        .ext = quad8_index_ext,                 \
 922        .num_ext = ARRAY_SIZE(quad8_index_ext)  \
 923}
 924
 925static struct counter_signal quad8_signals[] = {
 926        QUAD8_QUAD_SIGNAL(0, "Channel 1 Quadrature A"),
 927        QUAD8_QUAD_SIGNAL(1, "Channel 1 Quadrature B"),
 928        QUAD8_QUAD_SIGNAL(2, "Channel 2 Quadrature A"),
 929        QUAD8_QUAD_SIGNAL(3, "Channel 2 Quadrature B"),
 930        QUAD8_QUAD_SIGNAL(4, "Channel 3 Quadrature A"),
 931        QUAD8_QUAD_SIGNAL(5, "Channel 3 Quadrature B"),
 932        QUAD8_QUAD_SIGNAL(6, "Channel 4 Quadrature A"),
 933        QUAD8_QUAD_SIGNAL(7, "Channel 4 Quadrature B"),
 934        QUAD8_QUAD_SIGNAL(8, "Channel 5 Quadrature A"),
 935        QUAD8_QUAD_SIGNAL(9, "Channel 5 Quadrature B"),
 936        QUAD8_QUAD_SIGNAL(10, "Channel 6 Quadrature A"),
 937        QUAD8_QUAD_SIGNAL(11, "Channel 6 Quadrature B"),
 938        QUAD8_QUAD_SIGNAL(12, "Channel 7 Quadrature A"),
 939        QUAD8_QUAD_SIGNAL(13, "Channel 7 Quadrature B"),
 940        QUAD8_QUAD_SIGNAL(14, "Channel 8 Quadrature A"),
 941        QUAD8_QUAD_SIGNAL(15, "Channel 8 Quadrature B"),
 942        QUAD8_INDEX_SIGNAL(16, "Channel 1 Index"),
 943        QUAD8_INDEX_SIGNAL(17, "Channel 2 Index"),
 944        QUAD8_INDEX_SIGNAL(18, "Channel 3 Index"),
 945        QUAD8_INDEX_SIGNAL(19, "Channel 4 Index"),
 946        QUAD8_INDEX_SIGNAL(20, "Channel 5 Index"),
 947        QUAD8_INDEX_SIGNAL(21, "Channel 6 Index"),
 948        QUAD8_INDEX_SIGNAL(22, "Channel 7 Index"),
 949        QUAD8_INDEX_SIGNAL(23, "Channel 8 Index")
 950};
 951
 952#define QUAD8_COUNT_SYNAPSES(_id) {                                     \
 953        {                                                               \
 954                .actions_list = quad8_synapse_actions_list,             \
 955                .num_actions = ARRAY_SIZE(quad8_synapse_actions_list),  \
 956                .signal = quad8_signals + 2 * (_id)                     \
 957        },                                                              \
 958        {                                                               \
 959                .actions_list = quad8_synapse_actions_list,             \
 960                .num_actions = ARRAY_SIZE(quad8_synapse_actions_list),  \
 961                .signal = quad8_signals + 2 * (_id) + 1                 \
 962        },                                                              \
 963        {                                                               \
 964                .actions_list = quad8_index_actions_list,               \
 965                .num_actions = ARRAY_SIZE(quad8_index_actions_list),    \
 966                .signal = quad8_signals + 2 * (_id) + 16                \
 967        }                                                               \
 968}
 969
 970static struct counter_synapse quad8_count_synapses[][3] = {
 971        QUAD8_COUNT_SYNAPSES(0), QUAD8_COUNT_SYNAPSES(1),
 972        QUAD8_COUNT_SYNAPSES(2), QUAD8_COUNT_SYNAPSES(3),
 973        QUAD8_COUNT_SYNAPSES(4), QUAD8_COUNT_SYNAPSES(5),
 974        QUAD8_COUNT_SYNAPSES(6), QUAD8_COUNT_SYNAPSES(7)
 975};
 976
 977static const struct counter_count_ext quad8_count_ext[] = {
 978        {
 979                .name = "ceiling",
 980                .read = quad8_count_ceiling_read,
 981                .write = quad8_count_ceiling_write
 982        },
 983        {
 984                .name = "floor",
 985                .read = quad8_count_floor_read
 986        },
 987        COUNTER_COUNT_ENUM("count_mode", &quad8_cnt_mode_enum),
 988        COUNTER_COUNT_ENUM_AVAILABLE("count_mode", &quad8_cnt_mode_enum),
 989        {
 990                .name = "direction",
 991                .read = quad8_count_direction_read
 992        },
 993        {
 994                .name = "enable",
 995                .read = quad8_count_enable_read,
 996                .write = quad8_count_enable_write
 997        },
 998        COUNTER_COUNT_ENUM("error_noise", &quad8_error_noise_enum),
 999        COUNTER_COUNT_ENUM_AVAILABLE("error_noise", &quad8_error_noise_enum),
1000        {
1001                .name = "preset",
1002                .read = quad8_count_preset_read,
1003                .write = quad8_count_preset_write
1004        },
1005        {
1006                .name = "preset_enable",
1007                .read = quad8_count_preset_enable_read,
1008                .write = quad8_count_preset_enable_write
1009        }
1010};
1011
1012#define QUAD8_COUNT(_id, _cntname) {                                    \
1013        .id = (_id),                                                    \
1014        .name = (_cntname),                                             \
1015        .functions_list = quad8_count_functions_list,                   \
1016        .num_functions = ARRAY_SIZE(quad8_count_functions_list),        \
1017        .synapses = quad8_count_synapses[(_id)],                        \
1018        .num_synapses = 2,                                              \
1019        .ext = quad8_count_ext,                                         \
1020        .num_ext = ARRAY_SIZE(quad8_count_ext)                          \
1021}
1022
1023static struct counter_count quad8_counts[] = {
1024        QUAD8_COUNT(0, "Channel 1 Count"),
1025        QUAD8_COUNT(1, "Channel 2 Count"),
1026        QUAD8_COUNT(2, "Channel 3 Count"),
1027        QUAD8_COUNT(3, "Channel 4 Count"),
1028        QUAD8_COUNT(4, "Channel 5 Count"),
1029        QUAD8_COUNT(5, "Channel 6 Count"),
1030        QUAD8_COUNT(6, "Channel 7 Count"),
1031        QUAD8_COUNT(7, "Channel 8 Count")
1032};
1033
1034static int quad8_probe(struct device *dev, unsigned int id)
1035{
1036        struct quad8 *priv;
1037        int i, j;
1038        unsigned int base_offset;
1039
1040        if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
1041                dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
1042                        base[id], base[id] + QUAD8_EXTENT);
1043                return -EBUSY;
1044        }
1045
1046        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
1047        if (!priv)
1048                return -ENOMEM;
1049
1050        /* Initialize Counter device and driver data */
1051        priv->counter.name = dev_name(dev);
1052        priv->counter.parent = dev;
1053        priv->counter.ops = &quad8_ops;
1054        priv->counter.counts = quad8_counts;
1055        priv->counter.num_counts = ARRAY_SIZE(quad8_counts);
1056        priv->counter.signals = quad8_signals;
1057        priv->counter.num_signals = ARRAY_SIZE(quad8_signals);
1058        priv->counter.priv = priv;
1059        priv->base = base[id];
1060
1061        /* Initialize mutex */
1062        mutex_init(&priv->lock);
1063
1064        /* Reset all counters and disable interrupt function */
1065        outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
1066        /* Set initial configuration for all counters */
1067        for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
1068                base_offset = base[id] + 2 * i;
1069                /* Reset Byte Pointer */
1070                outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
1071                /* Reset filter clock factor */
1072                outb(0, base_offset);
1073                outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
1074                     base_offset + 1);
1075                /* Reset Byte Pointer */
1076                outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
1077                /* Reset Preset Register */
1078                for (j = 0; j < 3; j++)
1079                        outb(0x00, base_offset);
1080                /* Reset Borrow, Carry, Compare, and Sign flags */
1081                outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
1082                /* Reset Error flag */
1083                outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
1084                /* Binary encoding; Normal count; non-quadrature mode */
1085                outb(QUAD8_CTR_CMR, base_offset + 1);
1086                /* Disable A and B inputs; preset on index; FLG1 as Carry */
1087                outb(QUAD8_CTR_IOR, base_offset + 1);
1088                /* Disable index function; negative index polarity */
1089                outb(QUAD8_CTR_IDR, base_offset + 1);
1090        }
1091        /* Disable Differential Encoder Cable Status for all channels */
1092        outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS);
1093        /* Enable all counters */
1094        outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
1095
1096        return devm_counter_register(dev, &priv->counter);
1097}
1098
1099static struct isa_driver quad8_driver = {
1100        .probe = quad8_probe,
1101        .driver = {
1102                .name = "104-quad-8"
1103        }
1104};
1105
1106module_isa_driver(quad8_driver, num_quad8);
1107
1108MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
1109MODULE_DESCRIPTION("ACCES 104-QUAD-8 driver");
1110MODULE_LICENSE("GPL v2");
1111