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

counter: 104-quad-8: Add Generic Counter interface support

This patch adds support for the Generic Counter interface to the
104-QUAD-8 driver. The existing 104-QUAD-8 device interface should not
be affected by this patch; all changes are intended as supplemental
additions as perceived by the user.

Generic Counter Counts are created for the eight quadrature channel
counts, as well as their respective quadrature A and B Signals (which
are associated via respective Synapse structures) and respective index
Signals.

The new Generic Counter interface sysfs attributes are intended to
expose the same functionality and data available via the existing
104-QUAD-8 IIO device interface; the Generic Counter interface serves
to provide the respective functionality and data in a standard way
expected of counter devices.

Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

William Breathitt Gray and committed by
Greg Kroah-Hartman
f1d8a071 e854bac9

+1392 -643
+2 -2
MAINTAINERS
··· 268 268 S: Maintained 269 269 F: drivers/gpio/gpio-104-idio-16.c 270 270 271 - ACCES 104-QUAD-8 IIO DRIVER 271 + ACCES 104-QUAD-8 DRIVER 272 272 M: William Breathitt Gray <vilhelm.gray@gmail.com> 273 273 L: linux-iio@vger.kernel.org 274 274 S: Maintained 275 275 F: Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 276 - F: drivers/iio/counter/104-quad-8.c 276 + F: drivers/counter/104-quad-8.c 277 277 278 278 ACCES PCI-IDIO-16 GPIO DRIVER 279 279 M: William Breathitt Gray <vilhelm.gray@gmail.com>
+1367
drivers/counter/104-quad-8.c
··· 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 + 24 + static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)]; 25 + static unsigned int num_quad8; 26 + module_param_array(base, uint, &num_quad8, 0); 27 + MODULE_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 + */ 44 + struct 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 + 94 + static 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 + 139 + static 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 + 221 + static const struct iio_info quad8_info = { 222 + .read_raw = quad8_read_raw, 223 + .write_raw = quad8_write_raw 224 + }; 225 + 226 + static 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 + 234 + static 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 + 263 + static 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 + 272 + static 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 + 300 + static 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 + 305 + static 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 + 314 + static 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 + 320 + static const char *const quad8_count_direction_states[] = { 321 + "down", 322 + "up" 323 + }; 324 + 325 + static 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 + 334 + static 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 + 340 + static const char *const quad8_count_modes[] = { 341 + "normal", 342 + "range limit", 343 + "non-recycle", 344 + "modulo-n" 345 + }; 346 + 347 + static 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 + 366 + static 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 + 374 + static 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 + 381 + static const char *const quad8_synchronous_modes[] = { 382 + "non-synchronous", 383 + "synchronous" 384 + }; 385 + 386 + static 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 + 406 + static 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 + 414 + static 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 + 421 + static const char *const quad8_quadrature_modes[] = { 422 + "non-quadrature", 423 + "quadrature" 424 + }; 425 + 426 + static 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 + 452 + static 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 + 460 + static 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 + 467 + static const char *const quad8_index_polarity_modes[] = { 468 + "negative", 469 + "positive" 470 + }; 471 + 472 + static 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 + 488 + static 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 + 496 + static 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 + 503 + static 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 + 527 + static 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 + 553 + static 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 + 564 + static 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 + 585 + static 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 + 615 + static 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 + 659 + enum 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 + 666 + static 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 + 673 + static 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 + 699 + static 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 + 748 + static 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 + 762 + enum 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 + 769 + static 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 + 774 + static 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 + 781 + static 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 + 836 + const 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 + 845 + static 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 + 856 + static 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 + 873 + static 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 + 880 + static 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 + 891 + static 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 + 912 + static 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 + 919 + static 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 + 926 + static 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 + 950 + static 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 + 988 + static 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 + 995 + static 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 + 1005 + static 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 + 1013 + static 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 + 1036 + static 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 + 1047 + static 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 + 1053 + static 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 + 1061 + static 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 + 1090 + static 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 + 1106 + static 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 + 1122 + static 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 + 1130 + static 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 + 1156 + static 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 + 1175 + static 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 + 1220 + static 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 + 1227 + static 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 + 1273 + static 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 + 1284 + static 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 + 1356 + static struct isa_driver quad8_driver = { 1357 + .probe = quad8_probe, 1358 + .driver = { 1359 + .name = "104-quad-8" 1360 + } 1361 + }; 1362 + 1363 + module_isa_driver(quad8_driver, num_quad8); 1364 + 1365 + MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); 1366 + MODULE_DESCRIPTION("ACCES 104-QUAD-8 IIO driver"); 1367 + MODULE_LICENSE("GPL v2");
+21
drivers/counter/Kconfig
··· 8 8 This enables counter device support through the Generic Counter 9 9 interface. You only need to enable this, if you also want to enable 10 10 one or more of the counter device drivers below. 11 + 12 + if COUNTER 13 + 14 + config 104_QUAD_8 15 + tristate "ACCES 104-QUAD-8 driver" 16 + depends on PC104 && X86 && IIO 17 + select ISA_BUS_API 18 + help 19 + Say yes here to build support for the ACCES 104-QUAD-8 quadrature 20 + encoder counter/interface device family (104-QUAD-8, 104-QUAD-4). 21 + 22 + A counter's respective error flag may be cleared by performing a write 23 + operation on the respective count value attribute. Although the 24 + 104-QUAD-8 counters have a 25-bit range, only the lower 24 bits may be 25 + set, either directly or via the counter's preset attribute. Interrupts 26 + are not supported by this driver. 27 + 28 + The base port addresses for the devices may be configured via the base 29 + array module parameter. 30 + 31 + endif # COUNTER
+2
drivers/counter/Makefile
··· 3 3 # 4 4 5 5 obj-$(CONFIG_COUNTER) += counter.o 6 + 7 + obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
-623
drivers/iio/counter/104-quad-8.c
··· 1 - // SPDX-License-Identifier: GPL-2.0 2 - /* 3 - * IIO 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/device.h> 10 - #include <linux/errno.h> 11 - #include <linux/iio/iio.h> 12 - #include <linux/iio/types.h> 13 - #include <linux/io.h> 14 - #include <linux/ioport.h> 15 - #include <linux/isa.h> 16 - #include <linux/kernel.h> 17 - #include <linux/module.h> 18 - #include <linux/moduleparam.h> 19 - #include <linux/types.h> 20 - 21 - #define QUAD8_EXTENT 32 22 - 23 - static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)]; 24 - static unsigned int num_quad8; 25 - module_param_array(base, uint, &num_quad8, 0); 26 - MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses"); 27 - 28 - #define QUAD8_NUM_COUNTERS 8 29 - 30 - /** 31 - * struct quad8_iio - IIO device private data structure 32 - * @preset: array of preset values 33 - * @count_mode: array of count mode configurations 34 - * @quadrature_mode: array of quadrature mode configurations 35 - * @quadrature_scale: array of quadrature mode scale configurations 36 - * @ab_enable: array of A and B inputs enable configurations 37 - * @preset_enable: array of set_to_preset_on_index attribute configurations 38 - * @synchronous_mode: array of index function synchronous mode configurations 39 - * @index_polarity: array of index function polarity configurations 40 - * @base: base port address of the IIO device 41 - */ 42 - struct quad8_iio { 43 - unsigned int preset[QUAD8_NUM_COUNTERS]; 44 - unsigned int count_mode[QUAD8_NUM_COUNTERS]; 45 - unsigned int quadrature_mode[QUAD8_NUM_COUNTERS]; 46 - unsigned int quadrature_scale[QUAD8_NUM_COUNTERS]; 47 - unsigned int ab_enable[QUAD8_NUM_COUNTERS]; 48 - unsigned int preset_enable[QUAD8_NUM_COUNTERS]; 49 - unsigned int synchronous_mode[QUAD8_NUM_COUNTERS]; 50 - unsigned int index_polarity[QUAD8_NUM_COUNTERS]; 51 - unsigned int base; 52 - }; 53 - 54 - #define QUAD8_REG_CHAN_OP 0x11 55 - #define QUAD8_REG_INDEX_INPUT_LEVELS 0x16 56 - /* Borrow Toggle flip-flop */ 57 - #define QUAD8_FLAG_BT BIT(0) 58 - /* Carry Toggle flip-flop */ 59 - #define QUAD8_FLAG_CT BIT(1) 60 - /* Error flag */ 61 - #define QUAD8_FLAG_E BIT(4) 62 - /* Up/Down flag */ 63 - #define QUAD8_FLAG_UD BIT(5) 64 - /* Reset and Load Signal Decoders */ 65 - #define QUAD8_CTR_RLD 0x00 66 - /* Counter Mode Register */ 67 - #define QUAD8_CTR_CMR 0x20 68 - /* Input / Output Control Register */ 69 - #define QUAD8_CTR_IOR 0x40 70 - /* Index Control Register */ 71 - #define QUAD8_CTR_IDR 0x60 72 - /* Reset Byte Pointer (three byte data pointer) */ 73 - #define QUAD8_RLD_RESET_BP 0x01 74 - /* Reset Counter */ 75 - #define QUAD8_RLD_RESET_CNTR 0x02 76 - /* Reset Borrow Toggle, Carry Toggle, Compare Toggle, and Sign flags */ 77 - #define QUAD8_RLD_RESET_FLAGS 0x04 78 - /* Reset Error flag */ 79 - #define QUAD8_RLD_RESET_E 0x06 80 - /* Preset Register to Counter */ 81 - #define QUAD8_RLD_PRESET_CNTR 0x08 82 - /* Transfer Counter to Output Latch */ 83 - #define QUAD8_RLD_CNTR_OUT 0x10 84 - #define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00 85 - #define QUAD8_CHAN_OP_RESET_COUNTERS 0x01 86 - 87 - static int quad8_read_raw(struct iio_dev *indio_dev, 88 - struct iio_chan_spec const *chan, int *val, int *val2, long mask) 89 - { 90 - struct quad8_iio *const priv = iio_priv(indio_dev); 91 - const int base_offset = priv->base + 2 * chan->channel; 92 - unsigned int flags; 93 - unsigned int borrow; 94 - unsigned int carry; 95 - int i; 96 - 97 - switch (mask) { 98 - case IIO_CHAN_INFO_RAW: 99 - if (chan->type == IIO_INDEX) { 100 - *val = !!(inb(priv->base + QUAD8_REG_INDEX_INPUT_LEVELS) 101 - & BIT(chan->channel)); 102 - return IIO_VAL_INT; 103 - } 104 - 105 - flags = inb(base_offset + 1); 106 - borrow = flags & QUAD8_FLAG_BT; 107 - carry = !!(flags & QUAD8_FLAG_CT); 108 - 109 - /* Borrow XOR Carry effectively doubles count range */ 110 - *val = (borrow ^ carry) << 24; 111 - 112 - /* Reset Byte Pointer; transfer Counter to Output Latch */ 113 - outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT, 114 - base_offset + 1); 115 - 116 - for (i = 0; i < 3; i++) 117 - *val |= (unsigned int)inb(base_offset) << (8 * i); 118 - 119 - return IIO_VAL_INT; 120 - case IIO_CHAN_INFO_ENABLE: 121 - *val = priv->ab_enable[chan->channel]; 122 - return IIO_VAL_INT; 123 - case IIO_CHAN_INFO_SCALE: 124 - *val = 1; 125 - *val2 = priv->quadrature_scale[chan->channel]; 126 - return IIO_VAL_FRACTIONAL_LOG2; 127 - } 128 - 129 - return -EINVAL; 130 - } 131 - 132 - static int quad8_write_raw(struct iio_dev *indio_dev, 133 - struct iio_chan_spec const *chan, int val, int val2, long mask) 134 - { 135 - struct quad8_iio *const priv = iio_priv(indio_dev); 136 - const int base_offset = priv->base + 2 * chan->channel; 137 - int i; 138 - unsigned int ior_cfg; 139 - 140 - switch (mask) { 141 - case IIO_CHAN_INFO_RAW: 142 - if (chan->type == IIO_INDEX) 143 - return -EINVAL; 144 - 145 - /* Only 24-bit values are supported */ 146 - if ((unsigned int)val > 0xFFFFFF) 147 - return -EINVAL; 148 - 149 - /* Reset Byte Pointer */ 150 - outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); 151 - 152 - /* Counter can only be set via Preset Register */ 153 - for (i = 0; i < 3; i++) 154 - outb(val >> (8 * i), base_offset); 155 - 156 - /* Transfer Preset Register to Counter */ 157 - outb(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, base_offset + 1); 158 - 159 - /* Reset Byte Pointer */ 160 - outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); 161 - 162 - /* Set Preset Register back to original value */ 163 - val = priv->preset[chan->channel]; 164 - for (i = 0; i < 3; i++) 165 - outb(val >> (8 * i), base_offset); 166 - 167 - /* Reset Borrow, Carry, Compare, and Sign flags */ 168 - outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1); 169 - /* Reset Error flag */ 170 - outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1); 171 - 172 - return 0; 173 - case IIO_CHAN_INFO_ENABLE: 174 - /* only boolean values accepted */ 175 - if (val < 0 || val > 1) 176 - return -EINVAL; 177 - 178 - priv->ab_enable[chan->channel] = val; 179 - 180 - ior_cfg = val | priv->preset_enable[chan->channel] << 1; 181 - 182 - /* Load I/O control configuration */ 183 - outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1); 184 - 185 - return 0; 186 - case IIO_CHAN_INFO_SCALE: 187 - /* Quadrature scaling only available in quadrature mode */ 188 - if (!priv->quadrature_mode[chan->channel] && (val2 || val != 1)) 189 - return -EINVAL; 190 - 191 - /* Only three gain states (1, 0.5, 0.25) */ 192 - if (val == 1 && !val2) 193 - priv->quadrature_scale[chan->channel] = 0; 194 - else if (!val) 195 - switch (val2) { 196 - case 500000: 197 - priv->quadrature_scale[chan->channel] = 1; 198 - break; 199 - case 250000: 200 - priv->quadrature_scale[chan->channel] = 2; 201 - break; 202 - default: 203 - return -EINVAL; 204 - } 205 - else 206 - return -EINVAL; 207 - 208 - return 0; 209 - } 210 - 211 - return -EINVAL; 212 - } 213 - 214 - static const struct iio_info quad8_info = { 215 - .read_raw = quad8_read_raw, 216 - .write_raw = quad8_write_raw 217 - }; 218 - 219 - static ssize_t quad8_read_preset(struct iio_dev *indio_dev, uintptr_t private, 220 - const struct iio_chan_spec *chan, char *buf) 221 - { 222 - const struct quad8_iio *const priv = iio_priv(indio_dev); 223 - 224 - return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset[chan->channel]); 225 - } 226 - 227 - static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private, 228 - const struct iio_chan_spec *chan, const char *buf, size_t len) 229 - { 230 - struct quad8_iio *const priv = iio_priv(indio_dev); 231 - const int base_offset = priv->base + 2 * chan->channel; 232 - unsigned int preset; 233 - int ret; 234 - int i; 235 - 236 - ret = kstrtouint(buf, 0, &preset); 237 - if (ret) 238 - return ret; 239 - 240 - /* Only 24-bit values are supported */ 241 - if (preset > 0xFFFFFF) 242 - return -EINVAL; 243 - 244 - priv->preset[chan->channel] = preset; 245 - 246 - /* Reset Byte Pointer */ 247 - outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); 248 - 249 - /* Set Preset Register */ 250 - for (i = 0; i < 3; i++) 251 - outb(preset >> (8 * i), base_offset); 252 - 253 - return len; 254 - } 255 - 256 - static ssize_t quad8_read_set_to_preset_on_index(struct iio_dev *indio_dev, 257 - uintptr_t private, const struct iio_chan_spec *chan, char *buf) 258 - { 259 - const struct quad8_iio *const priv = iio_priv(indio_dev); 260 - 261 - return snprintf(buf, PAGE_SIZE, "%u\n", 262 - !priv->preset_enable[chan->channel]); 263 - } 264 - 265 - static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev, 266 - uintptr_t private, const struct iio_chan_spec *chan, const char *buf, 267 - size_t len) 268 - { 269 - struct quad8_iio *const priv = iio_priv(indio_dev); 270 - const int base_offset = priv->base + 2 * chan->channel + 1; 271 - bool preset_enable; 272 - int ret; 273 - unsigned int ior_cfg; 274 - 275 - ret = kstrtobool(buf, &preset_enable); 276 - if (ret) 277 - return ret; 278 - 279 - /* Preset enable is active low in Input/Output Control register */ 280 - preset_enable = !preset_enable; 281 - 282 - priv->preset_enable[chan->channel] = preset_enable; 283 - 284 - ior_cfg = priv->ab_enable[chan->channel] | 285 - (unsigned int)preset_enable << 1; 286 - 287 - /* Load I/O control configuration to Input / Output Control Register */ 288 - outb(QUAD8_CTR_IOR | ior_cfg, base_offset); 289 - 290 - return len; 291 - } 292 - 293 - static const char *const quad8_noise_error_states[] = { 294 - "No excessive noise is present at the count inputs", 295 - "Excessive noise is present at the count inputs" 296 - }; 297 - 298 - static int quad8_get_noise_error(struct iio_dev *indio_dev, 299 - const struct iio_chan_spec *chan) 300 - { 301 - struct quad8_iio *const priv = iio_priv(indio_dev); 302 - const int base_offset = priv->base + 2 * chan->channel + 1; 303 - 304 - return !!(inb(base_offset) & QUAD8_FLAG_E); 305 - } 306 - 307 - static const struct iio_enum quad8_noise_error_enum = { 308 - .items = quad8_noise_error_states, 309 - .num_items = ARRAY_SIZE(quad8_noise_error_states), 310 - .get = quad8_get_noise_error 311 - }; 312 - 313 - static const char *const quad8_count_direction_states[] = { 314 - "down", 315 - "up" 316 - }; 317 - 318 - static int quad8_get_count_direction(struct iio_dev *indio_dev, 319 - const struct iio_chan_spec *chan) 320 - { 321 - struct quad8_iio *const priv = iio_priv(indio_dev); 322 - const int base_offset = priv->base + 2 * chan->channel + 1; 323 - 324 - return !!(inb(base_offset) & QUAD8_FLAG_UD); 325 - } 326 - 327 - static const struct iio_enum quad8_count_direction_enum = { 328 - .items = quad8_count_direction_states, 329 - .num_items = ARRAY_SIZE(quad8_count_direction_states), 330 - .get = quad8_get_count_direction 331 - }; 332 - 333 - static const char *const quad8_count_modes[] = { 334 - "normal", 335 - "range limit", 336 - "non-recycle", 337 - "modulo-n" 338 - }; 339 - 340 - static int quad8_set_count_mode(struct iio_dev *indio_dev, 341 - const struct iio_chan_spec *chan, unsigned int count_mode) 342 - { 343 - struct quad8_iio *const priv = iio_priv(indio_dev); 344 - unsigned int mode_cfg = count_mode << 1; 345 - const int base_offset = priv->base + 2 * chan->channel + 1; 346 - 347 - priv->count_mode[chan->channel] = count_mode; 348 - 349 - /* Add quadrature mode configuration */ 350 - if (priv->quadrature_mode[chan->channel]) 351 - mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3; 352 - 353 - /* Load mode configuration to Counter Mode Register */ 354 - outb(QUAD8_CTR_CMR | mode_cfg, base_offset); 355 - 356 - return 0; 357 - } 358 - 359 - static int quad8_get_count_mode(struct iio_dev *indio_dev, 360 - const struct iio_chan_spec *chan) 361 - { 362 - const struct quad8_iio *const priv = iio_priv(indio_dev); 363 - 364 - return priv->count_mode[chan->channel]; 365 - } 366 - 367 - static const struct iio_enum quad8_count_mode_enum = { 368 - .items = quad8_count_modes, 369 - .num_items = ARRAY_SIZE(quad8_count_modes), 370 - .set = quad8_set_count_mode, 371 - .get = quad8_get_count_mode 372 - }; 373 - 374 - static const char *const quad8_synchronous_modes[] = { 375 - "non-synchronous", 376 - "synchronous" 377 - }; 378 - 379 - static int quad8_set_synchronous_mode(struct iio_dev *indio_dev, 380 - const struct iio_chan_spec *chan, unsigned int synchronous_mode) 381 - { 382 - struct quad8_iio *const priv = iio_priv(indio_dev); 383 - const unsigned int idr_cfg = synchronous_mode | 384 - priv->index_polarity[chan->channel] << 1; 385 - const int base_offset = priv->base + 2 * chan->channel + 1; 386 - 387 - /* Index function must be non-synchronous in non-quadrature mode */ 388 - if (synchronous_mode && !priv->quadrature_mode[chan->channel]) 389 - return -EINVAL; 390 - 391 - priv->synchronous_mode[chan->channel] = synchronous_mode; 392 - 393 - /* Load Index Control configuration to Index Control Register */ 394 - outb(QUAD8_CTR_IDR | idr_cfg, base_offset); 395 - 396 - return 0; 397 - } 398 - 399 - static int quad8_get_synchronous_mode(struct iio_dev *indio_dev, 400 - const struct iio_chan_spec *chan) 401 - { 402 - const struct quad8_iio *const priv = iio_priv(indio_dev); 403 - 404 - return priv->synchronous_mode[chan->channel]; 405 - } 406 - 407 - static const struct iio_enum quad8_synchronous_mode_enum = { 408 - .items = quad8_synchronous_modes, 409 - .num_items = ARRAY_SIZE(quad8_synchronous_modes), 410 - .set = quad8_set_synchronous_mode, 411 - .get = quad8_get_synchronous_mode 412 - }; 413 - 414 - static const char *const quad8_quadrature_modes[] = { 415 - "non-quadrature", 416 - "quadrature" 417 - }; 418 - 419 - static int quad8_set_quadrature_mode(struct iio_dev *indio_dev, 420 - const struct iio_chan_spec *chan, unsigned int quadrature_mode) 421 - { 422 - struct quad8_iio *const priv = iio_priv(indio_dev); 423 - unsigned int mode_cfg = priv->count_mode[chan->channel] << 1; 424 - const int base_offset = priv->base + 2 * chan->channel + 1; 425 - 426 - if (quadrature_mode) 427 - mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3; 428 - else { 429 - /* Quadrature scaling only available in quadrature mode */ 430 - priv->quadrature_scale[chan->channel] = 0; 431 - 432 - /* Synchronous function not supported in non-quadrature mode */ 433 - if (priv->synchronous_mode[chan->channel]) 434 - quad8_set_synchronous_mode(indio_dev, chan, 0); 435 - } 436 - 437 - priv->quadrature_mode[chan->channel] = quadrature_mode; 438 - 439 - /* Load mode configuration to Counter Mode Register */ 440 - outb(QUAD8_CTR_CMR | mode_cfg, base_offset); 441 - 442 - return 0; 443 - } 444 - 445 - static int quad8_get_quadrature_mode(struct iio_dev *indio_dev, 446 - const struct iio_chan_spec *chan) 447 - { 448 - const struct quad8_iio *const priv = iio_priv(indio_dev); 449 - 450 - return priv->quadrature_mode[chan->channel]; 451 - } 452 - 453 - static const struct iio_enum quad8_quadrature_mode_enum = { 454 - .items = quad8_quadrature_modes, 455 - .num_items = ARRAY_SIZE(quad8_quadrature_modes), 456 - .set = quad8_set_quadrature_mode, 457 - .get = quad8_get_quadrature_mode 458 - }; 459 - 460 - static const char *const quad8_index_polarity_modes[] = { 461 - "negative", 462 - "positive" 463 - }; 464 - 465 - static int quad8_set_index_polarity(struct iio_dev *indio_dev, 466 - const struct iio_chan_spec *chan, unsigned int index_polarity) 467 - { 468 - struct quad8_iio *const priv = iio_priv(indio_dev); 469 - const unsigned int idr_cfg = priv->synchronous_mode[chan->channel] | 470 - index_polarity << 1; 471 - const int base_offset = priv->base + 2 * chan->channel + 1; 472 - 473 - priv->index_polarity[chan->channel] = index_polarity; 474 - 475 - /* Load Index Control configuration to Index Control Register */ 476 - outb(QUAD8_CTR_IDR | idr_cfg, base_offset); 477 - 478 - return 0; 479 - } 480 - 481 - static int quad8_get_index_polarity(struct iio_dev *indio_dev, 482 - const struct iio_chan_spec *chan) 483 - { 484 - const struct quad8_iio *const priv = iio_priv(indio_dev); 485 - 486 - return priv->index_polarity[chan->channel]; 487 - } 488 - 489 - static const struct iio_enum quad8_index_polarity_enum = { 490 - .items = quad8_index_polarity_modes, 491 - .num_items = ARRAY_SIZE(quad8_index_polarity_modes), 492 - .set = quad8_set_index_polarity, 493 - .get = quad8_get_index_polarity 494 - }; 495 - 496 - static const struct iio_chan_spec_ext_info quad8_count_ext_info[] = { 497 - { 498 - .name = "preset", 499 - .shared = IIO_SEPARATE, 500 - .read = quad8_read_preset, 501 - .write = quad8_write_preset 502 - }, 503 - { 504 - .name = "set_to_preset_on_index", 505 - .shared = IIO_SEPARATE, 506 - .read = quad8_read_set_to_preset_on_index, 507 - .write = quad8_write_set_to_preset_on_index 508 - }, 509 - IIO_ENUM("noise_error", IIO_SEPARATE, &quad8_noise_error_enum), 510 - IIO_ENUM_AVAILABLE("noise_error", &quad8_noise_error_enum), 511 - IIO_ENUM("count_direction", IIO_SEPARATE, &quad8_count_direction_enum), 512 - IIO_ENUM_AVAILABLE("count_direction", &quad8_count_direction_enum), 513 - IIO_ENUM("count_mode", IIO_SEPARATE, &quad8_count_mode_enum), 514 - IIO_ENUM_AVAILABLE("count_mode", &quad8_count_mode_enum), 515 - IIO_ENUM("quadrature_mode", IIO_SEPARATE, &quad8_quadrature_mode_enum), 516 - IIO_ENUM_AVAILABLE("quadrature_mode", &quad8_quadrature_mode_enum), 517 - {} 518 - }; 519 - 520 - static const struct iio_chan_spec_ext_info quad8_index_ext_info[] = { 521 - IIO_ENUM("synchronous_mode", IIO_SEPARATE, 522 - &quad8_synchronous_mode_enum), 523 - IIO_ENUM_AVAILABLE("synchronous_mode", &quad8_synchronous_mode_enum), 524 - IIO_ENUM("index_polarity", IIO_SEPARATE, &quad8_index_polarity_enum), 525 - IIO_ENUM_AVAILABLE("index_polarity", &quad8_index_polarity_enum), 526 - {} 527 - }; 528 - 529 - #define QUAD8_COUNT_CHAN(_chan) { \ 530 - .type = IIO_COUNT, \ 531 - .channel = (_chan), \ 532 - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ 533 - BIT(IIO_CHAN_INFO_ENABLE) | BIT(IIO_CHAN_INFO_SCALE), \ 534 - .ext_info = quad8_count_ext_info, \ 535 - .indexed = 1 \ 536 - } 537 - 538 - #define QUAD8_INDEX_CHAN(_chan) { \ 539 - .type = IIO_INDEX, \ 540 - .channel = (_chan), \ 541 - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 542 - .ext_info = quad8_index_ext_info, \ 543 - .indexed = 1 \ 544 - } 545 - 546 - static const struct iio_chan_spec quad8_channels[] = { 547 - QUAD8_COUNT_CHAN(0), QUAD8_INDEX_CHAN(0), 548 - QUAD8_COUNT_CHAN(1), QUAD8_INDEX_CHAN(1), 549 - QUAD8_COUNT_CHAN(2), QUAD8_INDEX_CHAN(2), 550 - QUAD8_COUNT_CHAN(3), QUAD8_INDEX_CHAN(3), 551 - QUAD8_COUNT_CHAN(4), QUAD8_INDEX_CHAN(4), 552 - QUAD8_COUNT_CHAN(5), QUAD8_INDEX_CHAN(5), 553 - QUAD8_COUNT_CHAN(6), QUAD8_INDEX_CHAN(6), 554 - QUAD8_COUNT_CHAN(7), QUAD8_INDEX_CHAN(7) 555 - }; 556 - 557 - static int quad8_probe(struct device *dev, unsigned int id) 558 - { 559 - struct iio_dev *indio_dev; 560 - struct quad8_iio *priv; 561 - int i, j; 562 - unsigned int base_offset; 563 - 564 - indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); 565 - if (!indio_dev) 566 - return -ENOMEM; 567 - 568 - if (!devm_request_region(dev, base[id], QUAD8_EXTENT, 569 - dev_name(dev))) { 570 - dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", 571 - base[id], base[id] + QUAD8_EXTENT); 572 - return -EBUSY; 573 - } 574 - 575 - indio_dev->info = &quad8_info; 576 - indio_dev->modes = INDIO_DIRECT_MODE; 577 - indio_dev->num_channels = ARRAY_SIZE(quad8_channels); 578 - indio_dev->channels = quad8_channels; 579 - indio_dev->name = dev_name(dev); 580 - indio_dev->dev.parent = dev; 581 - 582 - priv = iio_priv(indio_dev); 583 - priv->base = base[id]; 584 - 585 - /* Reset all counters and disable interrupt function */ 586 - outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP); 587 - /* Set initial configuration for all counters */ 588 - for (i = 0; i < QUAD8_NUM_COUNTERS; i++) { 589 - base_offset = base[id] + 2 * i; 590 - /* Reset Byte Pointer */ 591 - outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); 592 - /* Reset Preset Register */ 593 - for (j = 0; j < 3; j++) 594 - outb(0x00, base_offset); 595 - /* Reset Borrow, Carry, Compare, and Sign flags */ 596 - outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1); 597 - /* Reset Error flag */ 598 - outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1); 599 - /* Binary encoding; Normal count; non-quadrature mode */ 600 - outb(QUAD8_CTR_CMR, base_offset + 1); 601 - /* Disable A and B inputs; preset on index; FLG1 as Carry */ 602 - outb(QUAD8_CTR_IOR, base_offset + 1); 603 - /* Disable index function; negative index polarity */ 604 - outb(QUAD8_CTR_IDR, base_offset + 1); 605 - } 606 - /* Enable all counters */ 607 - outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP); 608 - 609 - return devm_iio_device_register(dev, indio_dev); 610 - } 611 - 612 - static struct isa_driver quad8_driver = { 613 - .probe = quad8_probe, 614 - .driver = { 615 - .name = "104-quad-8" 616 - } 617 - }; 618 - 619 - module_isa_driver(quad8_driver, num_quad8); 620 - 621 - MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); 622 - MODULE_DESCRIPTION("ACCES 104-QUAD-8 IIO driver"); 623 - MODULE_LICENSE("GPL v2");
-17
drivers/iio/counter/Kconfig
··· 5 5 6 6 menu "Counters" 7 7 8 - config 104_QUAD_8 9 - tristate "ACCES 104-QUAD-8 driver" 10 - depends on PC104 && X86 11 - select ISA_BUS_API 12 - help 13 - Say yes here to build support for the ACCES 104-QUAD-8 quadrature 14 - encoder counter/interface device family (104-QUAD-8, 104-QUAD-4). 15 - 16 - Performing a write to a counter's IIO_CHAN_INFO_RAW sets the counter and 17 - also clears the counter's respective error flag. Although the counters 18 - have a 25-bit range, only the lower 24 bits may be set, either directly 19 - or via a counter's preset attribute. Interrupts are not supported by 20 - this driver. 21 - 22 - The base port addresses for the devices may be configured via the base 23 - array module parameter. 24 - 25 8 config STM32_LPTIMER_CNT 26 9 tristate "STM32 LP Timer encoder counter driver" 27 10 depends on MFD_STM32_LPTIMER || COMPILE_TEST
-1
drivers/iio/counter/Makefile
··· 4 4 5 5 # When adding new entries keep the list in alphabetical order 6 6 7 - obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o 8 7 obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o