Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.2-rc5 1367 lines 40 kB view raw
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");