On Sat, Sep 24, 2016 at 06:24:00PM +0100, Jonathan Cameron wrote: >On 21/09/16 21:16, William Breathitt Gray wrote: >> The ACCES 104-QUAD-8 is a general purpose quadrature encoder >> counter/interface board. The 104-QUAD-8 is capable of monitoring the >> outputs of eight encoders via four on-board LSI/CSI LS7266R1 24-bit >> dual-axis quadrature counter chips. Core functions handled by the >> LS7266R1, such as direction and total count, are available. >> >> Performing a write to a counter's IIO_CHAN_INFO_RAW sets the counter and >> also clears the counter's respective error flag. Although the counters >> have a 25-bit range, only the lower 24 bits may be set, either directly >> or via IIO_CHAN_INFO_PRESET. Interrupts are not supported by this >> driver. >> >> This driver adds IIO support for the ACCES 104-QUAD-8 and ACCES >> 104-QUAD-4. The base port addresses for the devices may be configured >> via the base array module parameter. >> >> Signed-off-by: William Breathitt Gray <vilhelm.gray@xxxxxxxxx> >Looking at the fact that preset is rather device specific, I'd be tempted to >not put it info_mask yet, but keep it as an ext_info element until we >get a feel for how it is used in more devices. I suspect some form of preset registers would be rather popular on counter devices in general, however I agree that for now we should probably keep the preset as an ext_info local to the 104-QUAD-8 driver. As more counter device drivers are added, the usages of preset should become more apparent, at which point more we can reconsider adding preset as an IIO constant; we should have a better outlook on how it may fit appropriately into the counter ABI. Until then, I'll reimplement it as an ext_info. > >Good docs on the whole. > >This new ABI was always going to take a few rounds to >come to an agreement! > >Thanks, > >Jonathan >> --- >> .../ABI/testing/sysfs-bus-iio-counter-104-quad-8 | 124 +++++ >> MAINTAINERS | 6 + >> drivers/iio/Kconfig | 1 + >> drivers/iio/Makefile | 1 + >> drivers/iio/counter/104-quad-8.c | 582 +++++++++++++++++++++ >> drivers/iio/counter/Kconfig | 24 + >> drivers/iio/counter/Makefile | 7 + >> 7 files changed, 745 insertions(+) >> create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 >> create mode 100644 drivers/iio/counter/104-quad-8.c >> create mode 100644 drivers/iio/counter/Kconfig >> create mode 100644 drivers/iio/counter/Makefile >> >> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 b/Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 >> new file mode 100644 >> index 0000000..d1b3f15 >> --- /dev/null >> +++ b/Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 >> @@ -0,0 +1,124 @@ >> +What: /sys/bus/iio/devices/iio:deviceX/in_count_ab_enable_available >> +What: /sys/bus/iio/devices/iio:deviceX/in_count_count_direction_available >> +What: /sys/bus/iio/devices/iio:deviceX/in_count_count_mode_available >> +What: /sys/bus/iio/devices/iio:deviceX/in_count_noise_error_available >> +What: /sys/bus/iio/devices/iio:deviceX/in_count_preset_enable_available >> +What: /sys/bus/iio/devices/iio:deviceX/in_count_quadrature_mode_available >> +What: /sys/bus/iio/devices/iio:deviceX/in_index_index_polarity_available >> +What: /sys/bus/iio/devices/iio:deviceX/in_index_synchronous_mode_available >> +KernelVersion: 4.9 >> +Contact: linux-iio@xxxxxxxxxxxxxxx >> +Description: >> + Discrete set of available values for the respective counter >> + configuration are listed in this file. >> + >> +What: /sys/bus/iio/devices/iio:deviceX/in_countY_ab_enable >> +KernelVersion: 4.9 >> +Contact: linux-iio@xxxxxxxxxxxxxxx >> +Description: >> + Enable or disable the encoder A and B inputs for channel Y. >State what values it can take. Actually I'd make this a conventional boolean >and use the fuzzy stuff in str_to_bool. >(so basically 0 or 1) > >I'm still confused on what this is actually doing. What are the A and B inputs? Using the strtobool is a good idea since the only possible states are "enabled" and "disabled." The A and B inputs are the two raw quadrature clock signals. On the 104-QUAD-8, this attribute effectively enables or disables the counter from counting. Perhaps renaming this attribute to "counter_enable" would make the interface more intuitive; or perhaps reimplement as a IIO_CHAN_INFO_ENABLE. What would you suggest? >> + >> +What: /sys/bus/iio/devices/iio:deviceX/in_countY_count_direction >> +KernelVersion: 4.9 >> +Contact: linux-iio@xxxxxxxxxxxxxxx >> +Description: >> + Read-only attribute that indicates whether the counter for >> + channel Y is counting up or down. >> + >> +What: /sys/bus/iio/devices/iio:deviceX/in_countY_count_mode >> +KernelVersion: 4.9 >> +Contact: linux-iio@xxxxxxxxxxxxxxx >> +Description: >> + Count mode for channel Y. Four count modes are available: >> + normal, range limit, non-recycle, and modulo-n. The preset value >> + for channel Y is used by the count mode where required. >> + >> + Normal: >> + Counting is continuous in either direction. >> + >> + Range Limit: >> + An upper or lower limit is set, mimicking limit switches >> + in the mechanical counterpart. The upper limit is set to >> + the preset value, while the lower limit is set to 0. The >> + counter freezes at count = preset when counting up, and >> + at count = 0 when counting down. At either of these >> + limits, the counting is resumed only when the count >> + direction is reversed. >Presumably this of for cases with phsical limits and a system that will slip. >(i.e. stepper that is ramming into an end stop). Horrible ;) >> + >> + Non-recycle: >> + Counter is disabled whenever a 24-bit count overflow or >> + underflow takes place. The counter is re-enabled when a >> + new count value is loaded to the counter via a preset >> + operation or write to raw. >> + >> + Modulo-N: >> + A count boundary is set between 0 and the preset value. >> + The counter is reset to 0 at count = preset when >> + counting up, while the counter is set to the preset >> + value at count = 0 when counting down; the counter does >> + not freeze at the bundary points, but counts continously >> + throughout. >> + >> +What: /sys/bus/iio/devices/iio:deviceX/in_countY_noise_error >> +KernelVersion: 4.9 >> +Contact: linux-iio@xxxxxxxxxxxxxxx >> +Description: >> + Read-only attribute that indicates whether excessive noise is >> + present at the channel Y count inputs in quadrature clock mode; >> + irrelevant in non-quadrature clock mode. >> + >> +What: /sys/bus/iio/devices/iio:deviceX/in_countY_preset_enable >> +KernelVersion: 4.9 >> +Contact: linux-iio@xxxxxxxxxxxxxxx >> +Description: >> + Whether to set channel Y counter with channel Y preset value >> + when channel Y index input is active, or continously count. >Perhaps we can have a more informative name, afterall preset is used for all >sorts of things depending on mode. > >in_countY_set_to_preset_on_index (wordy but makes it clear I think?) I'll change this name to something more informative since you are right. In fact, despite the 104-QUAD-8 only supporting a single index function operation, the LS7266R1 chip does have three others that may pop up in another counter device in the future. William Breathitt Gray >> + >> +What: /sys/bus/iio/devices/iio:deviceX/in_countY_quadrature_mode >> +KernelVersion: 4.9 >> +Contact: linux-iio@xxxxxxxxxxxxxxx >> +Description: >> + Configure channel Y counter for non-quadrature or quadrature >> + clock mode. Selecting non-quadrature clock mode will disable >> + synchronous load mode. In quadrature clock mode, the channel Y >> + scale attribute selects the encoder phase division >> + (1 = full-cycle, 2 = half-cycle, 4 = quarter-cycle) processed by >> + the channel Y counter. >> + >> + Non-quadrature: >> + The filter and decoder circuit are bypassed. Encoder A >> + input serves as the count input and B as the UP/DOWN >> + direction control input, with B = 1 selecting UP Count >> + mode and B = 0 selecting Down Count mode. >> + >> + Quadrature: >> + Encoder A and B inputs are digitally filtered and >> + decoded for UP/DN clock. >> + >> +What: /sys/bus/iio/devices/iio:deviceX/in_indexY_index_polarity >> +KernelVersion: 4.9 >> +Contact: linux-iio@xxxxxxxxxxxxxxx >> +Description: >> + Active level of channel Y index input; irrelevant in >> + non-synchronous load mode. >> + >> +What: /sys/bus/iio/devices/iio:deviceX/in_indexY_synchronous_mode >> +KernelVersion: 4.9 >> +Contact: linux-iio@xxxxxxxxxxxxxxx >> +Description: >> + Configure channel Y counter for non-synchronous or synchronous >> + load mode. Synchronous load mode cannot be selected in >> + non-quadrature clock mode. >> + >> + Non-synchronous: >> + A logic low level is the active level at this index >> + input. The index function (as configured via >> + preset_enable) is performed directly on the active >> + level of the index input. >> + >> + Synchronous: >> + Intended for interfacing with encoder Index output in >> + quadrature clock mode. The active level is configured >> + via index_polarity. The index function (as configured >> + via preset_enable) is performed synchronously with the >> + quadrature clock on the active level of the index input. >> diff --git a/MAINTAINERS b/MAINTAINERS >> index 1b65477..2780a3f 100644 >> --- a/MAINTAINERS >> +++ b/MAINTAINERS >> @@ -255,6 +255,12 @@ L: linux-gpio@xxxxxxxxxxxxxxx >> S: Maintained >> F: drivers/gpio/gpio-104-idio-16.c >> >> +ACCES 104-QUAD-8 IIO DRIVER >> +M: William Breathitt Gray <vilhelm.gray@xxxxxxxxx> >> +L: linux-iio@xxxxxxxxxxxxxxx >> +S: Maintained >> +F: drivers/iio/counter/104-quad-8.c >> + >> ACENIC DRIVER >> M: Jes Sorensen <jes@xxxxxxxxxxxxxxxxxx> >> L: linux-acenic@xxxxxxxxxx >> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig >> index 6743b18..574d1fb 100644 >> --- a/drivers/iio/Kconfig >> +++ b/drivers/iio/Kconfig >> @@ -73,6 +73,7 @@ source "drivers/iio/adc/Kconfig" >> source "drivers/iio/amplifiers/Kconfig" >> source "drivers/iio/chemical/Kconfig" >> source "drivers/iio/common/Kconfig" >> +source "drivers/iio/counter/Kconfig" >> source "drivers/iio/dac/Kconfig" >> source "drivers/iio/dummy/Kconfig" >> source "drivers/iio/frequency/Kconfig" >> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile >> index 87e4c43..77b2000 100644 >> --- a/drivers/iio/Makefile >> +++ b/drivers/iio/Makefile >> @@ -18,6 +18,7 @@ obj-y += amplifiers/ >> obj-y += buffer/ >> obj-y += chemical/ >> obj-y += common/ >> +obj-y += counter/ >> obj-y += dac/ >> obj-y += dummy/ >> obj-y += gyro/ >> diff --git a/drivers/iio/counter/104-quad-8.c b/drivers/iio/counter/104-quad-8.c >> new file mode 100644 >> index 0000000..9a287f9 >> --- /dev/null >> +++ b/drivers/iio/counter/104-quad-8.c >> @@ -0,0 +1,582 @@ >> +/* >> + * IIO driver for the ACCES 104-QUAD-8 >> + * Copyright (C) 2016 William Breathitt Gray >> + * >> + * This program is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License, version 2, as >> + * published by the Free Software Foundation. >> + * >> + * This program is distributed in the hope that it will be useful, but >> + * WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> + * General Public License for more details. >> + * >> + * This driver supports the ACCES 104-QUAD-8 and ACCES 104-QUAD-4. >> + */ >> +#include <linux/bitops.h> >> +#include <linux/device.h> >> +#include <linux/errno.h> >> +#include <linux/iio/iio.h> >> +#include <linux/iio/types.h> >> +#include <linux/io.h> >> +#include <linux/ioport.h> >> +#include <linux/kernel.h> >> +#include <linux/isa.h> >> +#include <linux/module.h> >> +#include <linux/moduleparam.h> >> + >> +#define QUAD8_EXTENT 32 >> + >> +static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)]; >> +static unsigned int num_quad8; >> +module_param_array(base, uint, &num_quad8, 0); >> +MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses"); >> + >> +#define QUAD8_NUM_COUNTERS 8 >> + >> +/** >> + * struct quad8_iio - IIO device private data structure >> + * @preset: array of preset values >> + * @count_mode: array of count mode configurations >> + * @quadrature_mode: array of quadrature mode configurations >> + * @quadrature_scale: array of quadrature mode scale configurations >> + * @ab_enable: array of A and B inputs enable configurations >> + * @preset_enable: array of preset enable configurations >> + * @synchronous_mode: array of index function synchronous mode configurations >> + * @index_polarity: array of index function polarity configurations >> + * @base: base port address of the IIO device >> + */ >> +struct quad8_iio { >> + unsigned int preset[QUAD8_NUM_COUNTERS]; >> + unsigned int count_mode[QUAD8_NUM_COUNTERS]; >> + unsigned int quadrature_mode[QUAD8_NUM_COUNTERS]; >> + unsigned int quadrature_scale[QUAD8_NUM_COUNTERS]; >> + unsigned int ab_enable[QUAD8_NUM_COUNTERS]; >> + unsigned int preset_enable[QUAD8_NUM_COUNTERS]; >> + unsigned int synchronous_mode[QUAD8_NUM_COUNTERS]; >> + unsigned int index_polarity[QUAD8_NUM_COUNTERS]; >> + unsigned int base; >> +}; >> + >> +static int quad8_read_raw(struct iio_dev *indio_dev, >> + struct iio_chan_spec const *chan, int *val, int *val2, long mask) >> +{ >> + struct quad8_iio *const priv = iio_priv(indio_dev); >> + const int base_offset = priv->base + 2 * chan->channel; >> + unsigned int flags; >> + unsigned int borrow; >> + unsigned int carry; >> + int i; >> + >> + switch (mask) { >> + case IIO_CHAN_INFO_RAW: >> + if (chan->type == IIO_INDEX) { >> + *val = !!(inb(priv->base + 0x16) & BIT(chan->channel)); >> + return IIO_VAL_INT; >> + } >> + >> + flags = inb(base_offset); >> + borrow = flags & BIT(0); >> + carry = !!(flags & BIT(1)); >> + >> + /* Borrow XOR Carry effectively doubles count range */ >> + *val = (borrow ^ carry) << 24; >> + >> + /* Reset Byte Pointer; transfer Counter to Output Latch */ >> + outb(0x11, base_offset + 1); >> + >> + for (i = 0; i < 3; i++) >> + *val |= (unsigned int)inb(base_offset) << (8 * i); >> + >> + return IIO_VAL_INT; >> + case IIO_CHAN_INFO_PRESET: >> + *val = priv->preset[chan->channel]; >> + return IIO_VAL_INT; >> + case IIO_CHAN_INFO_SCALE: >> + *val = 1 << priv->quadrature_scale[chan->channel]; >> + return IIO_VAL_INT; >> + } >> + >> + return -EINVAL; >> +} >> + >> +static int quad8_write_raw(struct iio_dev *indio_dev, >> + struct iio_chan_spec const *chan, int val, int val2, long mask) >> +{ >> + struct quad8_iio *const priv = iio_priv(indio_dev); >> + const int base_offset = priv->base + 2 * chan->channel; >> + int i; >> + >> + switch (mask) { >> + case IIO_CHAN_INFO_RAW: >> + if (chan->type == IIO_INDEX) >> + return -EINVAL; >> + >> + /* Only 24-bit values are supported */ >> + if ((unsigned int)val > 0xFFFFFF) >> + return -EINVAL; >> + >> + /* Reset Byte Pointer */ >> + outb(0x01, base_offset + 1); >> + >> + /* Counter can only be set via Preset Register */ >> + for (i = 0; i < 3; i++) >> + outb(val >> (8 * i), base_offset); >> + >> + /* Transfer Preset Register to Counter */ >> + outb(0x08, base_offset + 1); >> + >> + /* Reset Byte Pointer */ >> + outb(0x01, base_offset + 1); >> + >> + /* Set Preset Register back to original value */ >> + val = priv->preset[chan->channel]; >> + for (i = 0; i < 3; i++) >> + outb(val >> (8 * i), base_offset); >> + >> + /* Reset Borrow, Carry, Compare, and Sign flags */ >> + outb(0x02, base_offset + 1); >> + /* Reset Error flag */ >> + outb(0x06, base_offset + 1); >> + >> + return 0; >> + case IIO_CHAN_INFO_PRESET: >> + /* Only 24-bit values are supported */ >> + if ((unsigned int)val > 0xFFFFFF) >> + return -EINVAL; >> + >> + priv->preset[chan->channel] = val; >> + >> + /* Reset Byte Pointer */ >> + outb(0x01, base_offset + 1); >> + >> + /* Set Preset Register */ >> + for (i = 0; i < 3; i++) >> + outb(val >> (8 * i), base_offset); >> + >> + return 0; >> + case IIO_CHAN_INFO_SCALE: >> + /* Quadrature scaling only available in quadrature mode */ >> + if (!priv->quadrature_mode[chan->channel] && val != 1) >> + return -EINVAL; >> + >> + /* Only three gain states (x1, x2, x4) */ >> + switch (val) { >> + case 1: >> + priv->quadrature_scale[chan->channel] = 0; >> + break; >> + case 2: >> + priv->quadrature_scale[chan->channel] = 1; >> + break; >> + case 4: >> + priv->quadrature_scale[chan->channel] = 2; >> + break; >> + default: >> + return -EINVAL; >> + } >> + >> + return 0; >> + } >> + >> + return -EINVAL; >> +} >> + >> +static const struct iio_info quad8_info = { >> + .driver_module = THIS_MODULE, >> + .read_raw = quad8_read_raw, >> + .write_raw = quad8_write_raw >> +}; >> + >> +static const char *const quad8_noise_error_states[] = { >> + "No excessive noise is present at the count inputs", >> + "Excessive noise is present at the count inputs" >> +}; >> + >> +static int quad8_get_noise_error(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan) >> +{ >> + struct quad8_iio *const priv = iio_priv(indio_dev); >> + const int base_offset = priv->base + 2 * chan->channel + 1; >> + >> + return !!(inb(base_offset) & BIT(4)); >> +} >> + >> +static const struct iio_enum quad8_noise_error_enum = { >> + .items = quad8_noise_error_states, >> + .num_items = ARRAY_SIZE(quad8_noise_error_states), >> + .get = quad8_get_noise_error >> +}; >> + >> +static const char *const quad8_count_direction_states[] = { >> + "down", >> + "up" >> +}; >> + >> +static int quad8_get_count_direction(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan) >> +{ >> + struct quad8_iio *const priv = iio_priv(indio_dev); >> + const int base_offset = priv->base + 2 * chan->channel + 1; >> + >> + return !!(inb(base_offset) & BIT(5)); >> +} >> + >> +static const struct iio_enum quad8_count_direction_enum = { >> + .items = quad8_count_direction_states, >> + .num_items = ARRAY_SIZE(quad8_count_direction_states), >> + .get = quad8_get_count_direction >> +}; >> + >> +static const char *const quad8_count_modes[] = { >> + "normal", >> + "range limit", >> + "non-recycle", >> + "modulo-n" >> +}; >> + >> +static int quad8_set_count_mode(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan, unsigned int count_mode) >> +{ >> + struct quad8_iio *const priv = iio_priv(indio_dev); >> + unsigned int mode_cfg = count_mode << 1; >> + const int base_offset = priv->base + 2 * chan->channel + 1; >> + >> + priv->count_mode[chan->channel] = count_mode; >> + >> + /* Add quadrature mode configuration */ >> + if (priv->quadrature_mode[chan->channel]) >> + mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3; >> + >> + /* Load mode configuration to Counter Mode Register */ >> + outb(0x20 | mode_cfg, base_offset); >> + >> + return 0; >> +} >> + >> +static int quad8_get_count_mode(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan) >> +{ >> + const struct quad8_iio *const priv = iio_priv(indio_dev); >> + >> + return priv->count_mode[chan->channel]; >> +} >> + >> +static const struct iio_enum quad8_count_mode_enum = { >> + .items = quad8_count_modes, >> + .num_items = ARRAY_SIZE(quad8_count_modes), >> + .set = quad8_set_count_mode, >> + .get = quad8_get_count_mode >> +}; >> + >> +static const char *const quad8_synchronous_modes[] = { >> + "non-synchronous", >> + "synchronous" >> +}; >> + >> +static int quad8_set_synchronous_mode(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan, unsigned int synchronous_mode) >> +{ >> + struct quad8_iio *const priv = iio_priv(indio_dev); >> + const unsigned int idr_cfg = synchronous_mode | >> + priv->index_polarity[chan->channel] << 1; >> + const int base_offset = priv->base + 2 * chan->channel + 1; >> + >> + /* Index function must be non-synchronous in non-quadrature mode */ >> + if (synchronous_mode && !priv->quadrature_mode[chan->channel]) >> + return -EINVAL; >> + >> + priv->synchronous_mode[chan->channel] = synchronous_mode; >> + >> + /* Load Index Control configuration to Index Control Register */ >> + outb(0x40 | idr_cfg, base_offset); >> + >> + return 0; >> +} >> + >> +static int quad8_get_synchronous_mode(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan) >> +{ >> + const struct quad8_iio *const priv = iio_priv(indio_dev); >> + >> + return priv->synchronous_mode[chan->channel]; >> +} >> + >> +static const struct iio_enum quad8_synchronous_mode_enum = { >> + .items = quad8_synchronous_modes, >> + .num_items = ARRAY_SIZE(quad8_synchronous_modes), >> + .set = quad8_set_synchronous_mode, >> + .get = quad8_get_synchronous_mode >> +}; >> + >> +static const char *const quad8_quadrature_modes[] = { >> + "non-quadrature", >> + "quadrature" >> +}; >> + >> +static int quad8_set_quadrature_mode(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan, unsigned int quadrature_mode) >> +{ >> + struct quad8_iio *const priv = iio_priv(indio_dev); >> + unsigned int mode_cfg = priv->count_mode[chan->channel] << 1; >> + const int base_offset = priv->base + 2 * chan->channel + 1; >> + >> + if (quadrature_mode) >> + mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3; >> + else { >> + /* Quadrature scaling only available in quadrature mode */ >> + priv->quadrature_scale[chan->channel] = 0; >> + >> + /* Index function must be non-synchronous in non-quadrature mode */ >> + if (priv->synchronous_mode[chan->channel]) >> + quad8_set_synchronous_mode(indio_dev, chan, 0); >> + } >> + >> + priv->quadrature_mode[chan->channel] = quadrature_mode; >> + >> + /* Load mode configuration to Counter Mode Register */ >> + outb(0x20 | mode_cfg, base_offset); >> + >> + return 0; >> +} >> + >> +static int quad8_get_quadrature_mode(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan) >> +{ >> + const struct quad8_iio *const priv = iio_priv(indio_dev); >> + >> + return priv->quadrature_mode[chan->channel]; >> +} >> + >> +static const struct iio_enum quad8_quadrature_mode_enum = { >> + .items = quad8_quadrature_modes, >> + .num_items = ARRAY_SIZE(quad8_quadrature_modes), >> + .set = quad8_set_quadrature_mode, >> + .get = quad8_get_quadrature_mode >> +}; >> + >> +static const char *const quad8_ab_enable_modes[] = { >> + "disabled", >> + "enabled" >> +}; >> + >> +static int quad8_set_ab_enable(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan, unsigned int ab_enable) >> +{ >> + struct quad8_iio *const priv = iio_priv(indio_dev); >> + const unsigned int ior_cfg = ab_enable | >> + priv->preset_enable[chan->channel] << 1; >> + const int base_offset = priv->base + 2 * chan->channel + 1; >> + >> + priv->ab_enable[chan->channel] = ab_enable; >> + >> + /* Load I/O control configuration to Input / Output Control Register */ >> + outb(0x40 | ior_cfg, base_offset); >> + >> + return 0; >> +} >> + >> +static int quad8_get_ab_enable(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan) >> +{ >> + const struct quad8_iio *const priv = iio_priv(indio_dev); >> + >> + return priv->ab_enable[chan->channel]; >> +} >> + >> +static const struct iio_enum quad8_ab_enable_enum = { >> + .items = quad8_ab_enable_modes, >> + .num_items = ARRAY_SIZE(quad8_ab_enable_modes), >> + .set = quad8_set_ab_enable, >> + .get = quad8_get_ab_enable >> +}; >> + >> +static const char *const quad8_preset_enable_modes[] = { >> + "Preset counter on index active", >> + "Continuously count" >Change the naming as suggested above and this can become >a boolean (0,1). We pretty much always want to avoid >strings if the meaning can be conveyed as well by an >appropriate boolean or numeric value. > >> +}; >> + >> +static int quad8_set_preset_enable(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan, unsigned int preset_enable) >> +{ >> + struct quad8_iio *const priv = iio_priv(indio_dev); >> + const unsigned int ior_cfg = priv->ab_enable[chan->channel] | >> + preset_enable << 1; >> + const int base_offset = priv->base + 2 * chan->channel + 1; >> + >> + priv->preset_enable[chan->channel] = preset_enable; >> + >> + /* Load I/O control configuration to Input / Output Control Register */ >> + outb(0x40 | ior_cfg, base_offset); >> + >> + return 0; >> +} >> + >> +static int quad8_get_preset_enable(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan) >> +{ >> + const struct quad8_iio *const priv = iio_priv(indio_dev); >> + >> + return priv->preset_enable[chan->channel]; >> +} >> + >> +static const struct iio_enum quad8_preset_enable_enum = { >> + .items = quad8_preset_enable_modes, >> + .num_items = ARRAY_SIZE(quad8_preset_enable_modes), >> + .set = quad8_set_preset_enable, >> + .get = quad8_get_preset_enable >> +}; >> + >> +static const char *const quad8_index_polarity_modes[] = { >> + "negative", >> + "positive" >> +}; >> + >> +static int quad8_set_index_polarity(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan, unsigned int index_polarity) >> +{ >> + struct quad8_iio *const priv = iio_priv(indio_dev); >> + const unsigned int idr_cfg = priv->synchronous_mode[chan->channel] | >> + index_polarity << 1; >> + const int base_offset = priv->base + 2 * chan->channel + 1; >> + >> + priv->index_polarity[chan->channel] = index_polarity; >> + >> + /* Load Index Control configuration to Index Control Register */ >> + outb(0x40 | idr_cfg, base_offset); >> + >> + return 0; >> +} >> + >> +static int quad8_get_index_polarity(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan) >> +{ >> + const struct quad8_iio *const priv = iio_priv(indio_dev); >> + >> + return priv->index_polarity[chan->channel]; >> +} >> + >> +static const struct iio_enum quad8_index_polarity_enum = { >> + .items = quad8_index_polarity_modes, >> + .num_items = ARRAY_SIZE(quad8_index_polarity_modes), >> + .set = quad8_set_index_polarity, >> + .get = quad8_get_index_polarity >> +}; >> + >> +static const struct iio_chan_spec_ext_info quad8_count_ext_info[] = { >> + IIO_ENUM("noise_error", IIO_SEPARATE, &quad8_noise_error_enum), >> + IIO_ENUM_AVAILABLE("noise_error", &quad8_noise_error_enum), >> + IIO_ENUM("count_direction", IIO_SEPARATE, &quad8_count_direction_enum), >> + IIO_ENUM_AVAILABLE("count_direction", &quad8_count_direction_enum), >> + IIO_ENUM("count_mode", IIO_SEPARATE, &quad8_count_mode_enum), >> + IIO_ENUM_AVAILABLE("count_mode", &quad8_count_mode_enum), >> + IIO_ENUM("quadrature_mode", IIO_SEPARATE, &quad8_quadrature_mode_enum), >> + IIO_ENUM_AVAILABLE("quadrature_mode", &quad8_quadrature_mode_enum), >> + IIO_ENUM("ab_enable", IIO_SEPARATE, &quad8_ab_enable_enum), >> + IIO_ENUM_AVAILABLE("ab_enable", &quad8_ab_enable_enum), >> + IIO_ENUM("preset_enable", IIO_SEPARATE, &quad8_preset_enable_enum), >> + IIO_ENUM_AVAILABLE("preset_enable", &quad8_preset_enable_enum), >> + {} >> +}; >> + >> +static const struct iio_chan_spec_ext_info quad8_index_ext_info[] = { >> + IIO_ENUM("synchronous_mode", IIO_SEPARATE, &quad8_synchronous_mode_enum), >> + IIO_ENUM_AVAILABLE("synchronous_mode", &quad8_synchronous_mode_enum), >> + IIO_ENUM("index_polarity", IIO_SEPARATE, &quad8_index_polarity_enum), >> + IIO_ENUM_AVAILABLE("index_polarity", &quad8_index_polarity_enum), >> + {} >> +}; >> + >> +#define QUAD8_COUNT_CHAN(_chan) { \ >> + .type = IIO_COUNT, \ >> + .channel = (_chan), \ >> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ >> + BIT(IIO_CHAN_INFO_PRESET) | BIT(IIO_CHAN_INFO_SCALE), \ >> + .ext_info = quad8_count_ext_info, \ >> + .indexed = 1 \ >> +} >> + >> +#define QUAD8_INDEX_CHAN(_chan) { \ >> + .type = IIO_INDEX, \ >> + .channel = (_chan), \ >> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ >> + .ext_info = quad8_index_ext_info, \ >> + .indexed = 1 \ >> +} >> + >> +static const struct iio_chan_spec quad8_channels[] = { >> + QUAD8_COUNT_CHAN(0), QUAD8_INDEX_CHAN(0), >> + QUAD8_COUNT_CHAN(1), QUAD8_INDEX_CHAN(1), >> + QUAD8_COUNT_CHAN(2), QUAD8_INDEX_CHAN(2), >> + QUAD8_COUNT_CHAN(3), QUAD8_INDEX_CHAN(3), >> + QUAD8_COUNT_CHAN(4), QUAD8_INDEX_CHAN(4), >> + QUAD8_COUNT_CHAN(5), QUAD8_INDEX_CHAN(5), >> + QUAD8_COUNT_CHAN(6), QUAD8_INDEX_CHAN(6), >> + QUAD8_COUNT_CHAN(7), QUAD8_INDEX_CHAN(7) >> +}; >> + >> +static int quad8_probe(struct device *dev, unsigned int id) >> +{ >> + struct iio_dev *indio_dev; >> + struct quad8_iio *priv; >> + int i, j; >> + unsigned int base_offset; >> + >> + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); >> + if (!indio_dev) >> + return -ENOMEM; >> + >> + if (!devm_request_region(dev, base[id], QUAD8_EXTENT, >> + dev_name(dev))) { >> + dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", >> + base[id], base[id] + QUAD8_EXTENT); >> + return -EBUSY; >> + } >> + >> + indio_dev->info = &quad8_info; >> + indio_dev->modes = INDIO_DIRECT_MODE; >> + indio_dev->num_channels = ARRAY_SIZE(quad8_channels); >> + indio_dev->channels = quad8_channels; >> + indio_dev->name = dev_name(dev); >> + >> + priv = iio_priv(indio_dev); >> + priv->base = base[id]; >> + >> + /* Reset all counters and disable interrupt function */ >> + outb(0x01, base[id] + 0x11); >> + /* Set initial configuration for all counters */ >> + for (i = 0; i < QUAD8_NUM_COUNTERS; i++) { >> + base_offset = base[id] + 2 * i; >> + /* Reset Byte Pointer */ >> + outb(0x01, base_offset + 1); >> + /* Reset Preset Register */ >> + for (j = 0; j < 3; j++) >> + outb(0x00, base_offset); >> + /* Reset Borrow, Carry, Compare, and Sign flags */ >> + outb(0x04, base_offset + 1); >> + /* Reset Error flag */ >> + outb(0x06, base_offset + 1); >> + /* Binary encoding; Normal count; non-quadrature mode */ >> + outb(0x20, base_offset + 1); >> + /* Disable A and B inputs; preset on index; FLG1 as Carry */ >> + outb(0x40, base_offset + 1); >> + /* Disable index function; negative index polarity */ >> + outb(0x60, base_offset + 1); >> + } >> + /* Enable all counters */ >> + outb(0x00, base[id] + 0x11); >> + >> + return devm_iio_device_register(dev, indio_dev); >> +} >> + >> +static struct isa_driver quad8_driver = { >> + .probe = quad8_probe, >> + .driver = { >> + .name = "104-quad-8" >> + } >> +}; >> + >> +module_isa_driver(quad8_driver, num_quad8); >> + >> +MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@xxxxxxxxx>"); >> +MODULE_DESCRIPTION("ACCES 104-QUAD-8 IIO driver"); >> +MODULE_LICENSE("GPL v2"); >> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig >> new file mode 100644 >> index 0000000..0ed8f46 >> --- /dev/null >> +++ b/drivers/iio/counter/Kconfig >> @@ -0,0 +1,24 @@ >> +# >> +# Counter devices >> +# >> +# When adding new entries keep the list in alphabetical order >> + >> +menu "Counters" >> + >> +config 104_QUAD_8 >> + tristate "ACCES 104-QUAD-8 driver" >> + depends on X86 && ISA_BUS_API >> + help >> + Say yes here to build support for the ACCES 104-QUAD-8 quadrature >> + encoder counter/interface device family (104-QUAD-8, 104-QUAD-4). >> + >> + Performing a write to a counter's IIO_CHAN_INFO_RAW sets the counter and >> + also clears the counter's respective error flag. Although the counters >> + have a 25-bit range, only the lower 24 bits may be set, either directly >> + or via IIO_CHAN_INFO_PRESET. Interrupts are not supported by this >> + driver. >> + >> + The base port addresses for the devices may be configured via the base >> + array module parameter. >> + >> +endmenu >> diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile >> new file mode 100644 >> index 0000000..007e884 >> --- /dev/null >> +++ b/drivers/iio/counter/Makefile >> @@ -0,0 +1,7 @@ >> +# >> +# Makefile for IIO counter devices >> +# >> + >> +# When adding new entries keep the list in alphabetical order >> + >> +obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o >> > -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html