summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Breathitt Gray <vilhelm.gray@gmail.com>2016-09-28 14:00:01 -0400
committerJonathan Cameron <jic23@kernel.org>2016-10-01 12:06:21 -0400
commit28e5d3bb0325e71ef9b53a9cb4242cdfb55fd8c5 (patch)
tree91bae3ac0c7da7137a7dc2f94cb6dcafc3b9fd81
parent1a8f324aa1f2237caef1c6633734785bbdcffeed (diff)
iio: 104-quad-8: Add IIO support for the ACCES 104-QUAD-8
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 a counter's preset attribute. 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@gmail.com> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r--Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8125
-rw-r--r--MAINTAINERS6
-rw-r--r--drivers/iio/Kconfig1
-rw-r--r--drivers/iio/Makefile1
-rw-r--r--drivers/iio/counter/104-quad-8.c593
-rw-r--r--drivers/iio/counter/Kconfig24
-rw-r--r--drivers/iio/counter/Makefile7
7 files changed, 757 insertions, 0 deletions
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 000000000000..ba676520b953
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8
@@ -0,0 +1,125 @@
1What: /sys/bus/iio/devices/iio:deviceX/in_count_count_direction_available
2What: /sys/bus/iio/devices/iio:deviceX/in_count_count_mode_available
3What: /sys/bus/iio/devices/iio:deviceX/in_count_noise_error_available
4What: /sys/bus/iio/devices/iio:deviceX/in_count_quadrature_mode_available
5What: /sys/bus/iio/devices/iio:deviceX/in_index_index_polarity_available
6What: /sys/bus/iio/devices/iio:deviceX/in_index_synchronous_mode_available
7KernelVersion: 4.9
8Contact: linux-iio@vger.kernel.org
9Description:
10 Discrete set of available values for the respective counter
11 configuration are listed in this file.
12
13What: /sys/bus/iio/devices/iio:deviceX/in_countY_count_direction
14KernelVersion: 4.9
15Contact: linux-iio@vger.kernel.org
16Description:
17 Read-only attribute that indicates whether the counter for
18 channel Y is counting up or down.
19
20What: /sys/bus/iio/devices/iio:deviceX/in_countY_count_mode
21KernelVersion: 4.9
22Contact: linux-iio@vger.kernel.org
23Description:
24 Count mode for channel Y. Four count modes are available:
25 normal, range limit, non-recycle, and modulo-n. The preset value
26 for channel Y is used by the count mode where required.
27
28 Normal:
29 Counting is continuous in either direction.
30
31 Range Limit:
32 An upper or lower limit is set, mimicking limit switches
33 in the mechanical counterpart. The upper limit is set to
34 the preset value, while the lower limit is set to 0. The
35 counter freezes at count = preset when counting up, and
36 at count = 0 when counting down. At either of these
37 limits, the counting is resumed only when the count
38 direction is reversed.
39
40 Non-recycle:
41 Counter is disabled whenever a 24-bit count overflow or
42 underflow takes place. The counter is re-enabled when a
43 new count value is loaded to the counter via a preset
44 operation or write to raw.
45
46 Modulo-N:
47 A count boundary is set between 0 and the preset value.
48 The counter is reset to 0 at count = preset when
49 counting up, while the counter is set to the preset
50 value at count = 0 when counting down; the counter does
51 not freeze at the bundary points, but counts
52 continuously throughout.
53
54What: /sys/bus/iio/devices/iio:deviceX/in_countY_noise_error
55KernelVersion: 4.9
56Contact: linux-iio@vger.kernel.org
57Description:
58 Read-only attribute that indicates whether excessive noise is
59 present at the channel Y count inputs in quadrature clock mode;
60 irrelevant in non-quadrature clock mode.
61
62What: /sys/bus/iio/devices/iio:deviceX/in_countY_preset
63KernelVersion: 4.9
64Contact: linux-iio@vger.kernel.org
65Description:
66 If the counter device supports preset registers, the preset
67 count for channel Y is provided by this attribute.
68
69What: /sys/bus/iio/devices/iio:deviceX/in_countY_quadrature_mode
70KernelVersion: 4.9
71Contact: linux-iio@vger.kernel.org
72Description:
73 Configure channel Y counter for non-quadrature or quadrature
74 clock mode. Selecting non-quadrature clock mode will disable
75 synchronous load mode. In quadrature clock mode, the channel Y
76 scale attribute selects the encoder phase division (scale of 1
77 selects full-cycle, scale of 0.5 selects half-cycle, scale of
78 0.25 selects quarter-cycle) processed by the channel Y counter.
79
80 Non-quadrature:
81 The filter and decoder circuit are bypassed. Encoder A
82 input serves as the count input and B as the UP/DOWN
83 direction control input, with B = 1 selecting UP Count
84 mode and B = 0 selecting Down Count mode.
85
86 Quadrature:
87 Encoder A and B inputs are digitally filtered and
88 decoded for UP/DN clock.
89
90What: /sys/bus/iio/devices/iio:deviceX/in_countY_set_to_preset_on_index
91KernelVersion: 4.9
92Contact: linux-iio@vger.kernel.org
93Description:
94 Whether to set channel Y counter with channel Y preset value
95 when channel Y index input is active, or continuously count.
96 Valid attribute values are boolean.
97
98What: /sys/bus/iio/devices/iio:deviceX/in_indexY_index_polarity
99KernelVersion: 4.9
100Contact: linux-iio@vger.kernel.org
101Description:
102 Active level of channel Y index input; irrelevant in
103 non-synchronous load mode.
104
105What: /sys/bus/iio/devices/iio:deviceX/in_indexY_synchronous_mode
106KernelVersion: 4.9
107Contact: linux-iio@vger.kernel.org
108Description:
109 Configure channel Y counter for non-synchronous or synchronous
110 load mode. Synchronous load mode cannot be selected in
111 non-quadrature clock mode.
112
113 Non-synchronous:
114 A logic low level is the active level at this index
115 input. The index function (as enabled via
116 set_to_preset_on_index) is performed directly on the
117 active level of the index input.
118
119 Synchronous:
120 Intended for interfacing with encoder Index output in
121 quadrature clock mode. The active level is configured
122 via index_polarity. The index function (as enabled via
123 set_to_preset_on_index) is performed synchronously with
124 the quadrature clock on the active level of the index
125 input.
diff --git a/MAINTAINERS b/MAINTAINERS
index e013c2be6d23..982dff301045 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -255,6 +255,12 @@ L: linux-gpio@vger.kernel.org
255S: Maintained 255S: Maintained
256F: drivers/gpio/gpio-104-idio-16.c 256F: drivers/gpio/gpio-104-idio-16.c
257 257
258ACCES 104-QUAD-8 IIO DRIVER
259M: William Breathitt Gray <vilhelm.gray@gmail.com>
260L: linux-iio@vger.kernel.org
261S: Maintained
262F: drivers/iio/counter/104-quad-8.c
263
258ACENIC DRIVER 264ACENIC DRIVER
259M: Jes Sorensen <jes@trained-monkey.org> 265M: Jes Sorensen <jes@trained-monkey.org>
260L: linux-acenic@sunsite.dk 266L: linux-acenic@sunsite.dk
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index a31a8cf2c330..a918270d6f54 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -73,6 +73,7 @@ source "drivers/iio/adc/Kconfig"
73source "drivers/iio/amplifiers/Kconfig" 73source "drivers/iio/amplifiers/Kconfig"
74source "drivers/iio/chemical/Kconfig" 74source "drivers/iio/chemical/Kconfig"
75source "drivers/iio/common/Kconfig" 75source "drivers/iio/common/Kconfig"
76source "drivers/iio/counter/Kconfig"
76source "drivers/iio/dac/Kconfig" 77source "drivers/iio/dac/Kconfig"
77source "drivers/iio/dummy/Kconfig" 78source "drivers/iio/dummy/Kconfig"
78source "drivers/iio/frequency/Kconfig" 79source "drivers/iio/frequency/Kconfig"
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 2b6e2762a886..33fa4026f92c 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -18,6 +18,7 @@ obj-y += amplifiers/
18obj-y += buffer/ 18obj-y += buffer/
19obj-y += chemical/ 19obj-y += chemical/
20obj-y += common/ 20obj-y += common/
21obj-y += counter/
21obj-y += dac/ 22obj-y += dac/
22obj-y += dummy/ 23obj-y += dummy/
23obj-y += gyro/ 24obj-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 000000000000..2d2ee353dde7
--- /dev/null
+++ b/drivers/iio/counter/104-quad-8.c
@@ -0,0 +1,593 @@
1/*
2 * IIO driver for the ACCES 104-QUAD-8
3 * Copyright (C) 2016 William Breathitt Gray
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License, version 2, as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * This driver supports the ACCES 104-QUAD-8 and ACCES 104-QUAD-4.
15 */
16#include <linux/bitops.h>
17#include <linux/device.h>
18#include <linux/errno.h>
19#include <linux/iio/iio.h>
20#include <linux/iio/types.h>
21#include <linux/io.h>
22#include <linux/ioport.h>
23#include <linux/isa.h>
24#include <linux/kernel.h>
25#include <linux/module.h>
26#include <linux/moduleparam.h>
27#include <linux/types.h>
28
29#define QUAD8_EXTENT 32
30
31static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)];
32static unsigned int num_quad8;
33module_param_array(base, uint, &num_quad8, 0);
34MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
35
36#define QUAD8_NUM_COUNTERS 8
37
38/**
39 * struct quad8_iio - IIO device private data structure
40 * @preset: array of preset values
41 * @count_mode: array of count mode configurations
42 * @quadrature_mode: array of quadrature mode configurations
43 * @quadrature_scale: array of quadrature mode scale configurations
44 * @ab_enable: array of A and B inputs enable configurations
45 * @preset_enable: array of set_to_preset_on_index attribute configurations
46 * @synchronous_mode: array of index function synchronous mode configurations
47 * @index_polarity: array of index function polarity configurations
48 * @base: base port address of the IIO device
49 */
50struct quad8_iio {
51 unsigned int preset[QUAD8_NUM_COUNTERS];
52 unsigned int count_mode[QUAD8_NUM_COUNTERS];
53 unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
54 unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
55 unsigned int ab_enable[QUAD8_NUM_COUNTERS];
56 unsigned int preset_enable[QUAD8_NUM_COUNTERS];
57 unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
58 unsigned int index_polarity[QUAD8_NUM_COUNTERS];
59 unsigned int base;
60};
61
62static int quad8_read_raw(struct iio_dev *indio_dev,
63 struct iio_chan_spec const *chan, int *val, int *val2, long mask)
64{
65 struct quad8_iio *const priv = iio_priv(indio_dev);
66 const int base_offset = priv->base + 2 * chan->channel;
67 unsigned int flags;
68 unsigned int borrow;
69 unsigned int carry;
70 int i;
71
72 switch (mask) {
73 case IIO_CHAN_INFO_RAW:
74 if (chan->type == IIO_INDEX) {
75 *val = !!(inb(priv->base + 0x16) & BIT(chan->channel));
76 return IIO_VAL_INT;
77 }
78
79 flags = inb(base_offset);
80 borrow = flags & BIT(0);
81 carry = !!(flags & BIT(1));
82
83 /* Borrow XOR Carry effectively doubles count range */
84 *val = (borrow ^ carry) << 24;
85
86 /* Reset Byte Pointer; transfer Counter to Output Latch */
87 outb(0x11, base_offset + 1);
88
89 for (i = 0; i < 3; i++)
90 *val |= (unsigned int)inb(base_offset) << (8 * i);
91
92 return IIO_VAL_INT;
93 case IIO_CHAN_INFO_ENABLE:
94 *val = priv->ab_enable[chan->channel];
95 return IIO_VAL_INT;
96 case IIO_CHAN_INFO_SCALE:
97 *val = 1;
98 *val2 = priv->quadrature_scale[chan->channel];
99 return IIO_VAL_FRACTIONAL_LOG2;
100 }
101
102 return -EINVAL;
103}
104
105static int quad8_write_raw(struct iio_dev *indio_dev,
106 struct iio_chan_spec const *chan, int val, int val2, long mask)
107{
108 struct quad8_iio *const priv = iio_priv(indio_dev);
109 const int base_offset = priv->base + 2 * chan->channel;
110 int i;
111 unsigned int ior_cfg;
112
113 switch (mask) {
114 case IIO_CHAN_INFO_RAW:
115 if (chan->type == IIO_INDEX)
116 return -EINVAL;
117
118 /* Only 24-bit values are supported */
119 if ((unsigned int)val > 0xFFFFFF)
120 return -EINVAL;
121
122 /* Reset Byte Pointer */
123 outb(0x01, base_offset + 1);
124
125 /* Counter can only be set via Preset Register */
126 for (i = 0; i < 3; i++)
127 outb(val >> (8 * i), base_offset);
128
129 /* Transfer Preset Register to Counter */
130 outb(0x08, base_offset + 1);
131
132 /* Reset Byte Pointer */
133 outb(0x01, base_offset + 1);
134
135 /* Set Preset Register back to original value */
136 val = priv->preset[chan->channel];
137 for (i = 0; i < 3; i++)
138 outb(val >> (8 * i), base_offset);
139
140 /* Reset Borrow, Carry, Compare, and Sign flags */
141 outb(0x02, base_offset + 1);
142 /* Reset Error flag */
143 outb(0x06, base_offset + 1);
144
145 return 0;
146 case IIO_CHAN_INFO_ENABLE:
147 /* only boolean values accepted */
148 if (val < 0 || val > 1)
149 return -EINVAL;
150
151 priv->ab_enable[chan->channel] = val;
152
153 ior_cfg = val | priv->preset_enable[chan->channel] << 1;
154
155 /* Load I/O control configuration */
156 outb(0x40 | ior_cfg, base_offset);
157
158 return 0;
159 case IIO_CHAN_INFO_SCALE:
160 /* Quadrature scaling only available in quadrature mode */
161 if (!priv->quadrature_mode[chan->channel] && (val2 || val != 1))
162 return -EINVAL;
163
164 /* Only three gain states (1, 0.5, 0.25) */
165 if (val == 1 && !val2)
166 priv->quadrature_scale[chan->channel] = 0;
167 else if (!val)
168 switch (val2) {
169 case 500000:
170 priv->quadrature_scale[chan->channel] = 1;
171 break;
172 case 250000:
173 priv->quadrature_scale[chan->channel] = 2;
174 break;
175 default:
176 return -EINVAL;
177 }
178 else
179 return -EINVAL;
180
181 return 0;
182 }
183
184 return -EINVAL;
185}
186
187static const struct iio_info quad8_info = {
188 .driver_module = THIS_MODULE,
189 .read_raw = quad8_read_raw,
190 .write_raw = quad8_write_raw
191};
192
193static ssize_t quad8_read_preset(struct iio_dev *indio_dev, uintptr_t private,
194 const struct iio_chan_spec *chan, char *buf)
195{
196 const struct quad8_iio *const priv = iio_priv(indio_dev);
197
198 return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset[chan->channel]);
199}
200
201static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
202 const struct iio_chan_spec *chan, const char *buf, size_t len)
203{
204 struct quad8_iio *const priv = iio_priv(indio_dev);
205 const int base_offset = priv->base + 2 * chan->channel;
206 unsigned int preset;
207 int ret;
208 int i;
209
210 ret = kstrtouint(buf, 0, &preset);
211 if (ret)
212 return ret;
213
214 /* Only 24-bit values are supported */
215 if (preset > 0xFFFFFF)
216 return -EINVAL;
217
218 priv->preset[chan->channel] = preset;
219
220 /* Reset Byte Pointer */
221 outb(0x01, base_offset + 1);
222
223 /* Set Preset Register */
224 for (i = 0; i < 3; i++)
225 outb(preset >> (8 * i), base_offset);
226
227 return len;
228}
229
230static ssize_t quad8_read_set_to_preset_on_index(struct iio_dev *indio_dev,
231 uintptr_t private, const struct iio_chan_spec *chan, char *buf)
232{
233 const struct quad8_iio *const priv = iio_priv(indio_dev);
234
235 return snprintf(buf, PAGE_SIZE, "%u\n",
236 priv->preset_enable[chan->channel]);
237}
238
239static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev,
240 uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
241 size_t len)
242{
243 struct quad8_iio *const priv = iio_priv(indio_dev);
244 const int base_offset = priv->base + 2 * chan->channel;
245 bool preset_enable;
246 int ret;
247 unsigned int ior_cfg;
248
249 ret = kstrtobool(buf, &preset_enable);
250 if (ret)
251 return ret;
252
253 priv->preset_enable[chan->channel] = preset_enable;
254
255 ior_cfg = priv->ab_enable[chan->channel] |
256 (unsigned int)preset_enable << 1;
257
258 /* Load I/O control configuration to Input / Output Control Register */
259 outb(0x40 | ior_cfg, base_offset);
260
261 return len;
262}
263
264static const char *const quad8_noise_error_states[] = {
265 "No excessive noise is present at the count inputs",
266 "Excessive noise is present at the count inputs"
267};
268
269static int quad8_get_noise_error(struct iio_dev *indio_dev,
270 const struct iio_chan_spec *chan)
271{
272 struct quad8_iio *const priv = iio_priv(indio_dev);
273 const int base_offset = priv->base + 2 * chan->channel + 1;
274
275 return !!(inb(base_offset) & BIT(4));
276}
277
278static const struct iio_enum quad8_noise_error_enum = {
279 .items = quad8_noise_error_states,
280 .num_items = ARRAY_SIZE(quad8_noise_error_states),
281 .get = quad8_get_noise_error
282};
283
284static const char *const quad8_count_direction_states[] = {
285 "down",
286 "up"
287};
288
289static int quad8_get_count_direction(struct iio_dev *indio_dev,
290 const struct iio_chan_spec *chan)
291{
292 struct quad8_iio *const priv = iio_priv(indio_dev);
293 const int base_offset = priv->base + 2 * chan->channel + 1;
294
295 return !!(inb(base_offset) & BIT(5));
296}
297
298static const struct iio_enum quad8_count_direction_enum = {
299 .items = quad8_count_direction_states,
300 .num_items = ARRAY_SIZE(quad8_count_direction_states),
301 .get = quad8_get_count_direction
302};
303
304static const char *const quad8_count_modes[] = {
305 "normal",
306 "range limit",
307 "non-recycle",
308 "modulo-n"
309};
310
311static int quad8_set_count_mode(struct iio_dev *indio_dev,
312 const struct iio_chan_spec *chan, unsigned int count_mode)
313{
314 struct quad8_iio *const priv = iio_priv(indio_dev);
315 unsigned int mode_cfg = count_mode << 1;
316 const int base_offset = priv->base + 2 * chan->channel + 1;
317
318 priv->count_mode[chan->channel] = count_mode;
319
320 /* Add quadrature mode configuration */
321 if (priv->quadrature_mode[chan->channel])
322 mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3;
323
324 /* Load mode configuration to Counter Mode Register */
325 outb(0x20 | mode_cfg, base_offset);
326
327 return 0;
328}
329
330static int quad8_get_count_mode(struct iio_dev *indio_dev,
331 const struct iio_chan_spec *chan)
332{
333 const struct quad8_iio *const priv = iio_priv(indio_dev);
334
335 return priv->count_mode[chan->channel];
336}
337
338static const struct iio_enum quad8_count_mode_enum = {
339 .items = quad8_count_modes,
340 .num_items = ARRAY_SIZE(quad8_count_modes),
341 .set = quad8_set_count_mode,
342 .get = quad8_get_count_mode
343};
344
345static const char *const quad8_synchronous_modes[] = {
346 "non-synchronous",
347 "synchronous"
348};
349
350static int quad8_set_synchronous_mode(struct iio_dev *indio_dev,
351 const struct iio_chan_spec *chan, unsigned int synchronous_mode)
352{
353 struct quad8_iio *const priv = iio_priv(indio_dev);
354 const unsigned int idr_cfg = synchronous_mode |
355 priv->index_polarity[chan->channel] << 1;
356 const int base_offset = priv->base + 2 * chan->channel + 1;
357
358 /* Index function must be non-synchronous in non-quadrature mode */
359 if (synchronous_mode && !priv->quadrature_mode[chan->channel])
360 return -EINVAL;
361
362 priv->synchronous_mode[chan->channel] = synchronous_mode;
363
364 /* Load Index Control configuration to Index Control Register */
365 outb(0x40 | idr_cfg, base_offset);
366
367 return 0;
368}
369
370static int quad8_get_synchronous_mode(struct iio_dev *indio_dev,
371 const struct iio_chan_spec *chan)
372{
373 const struct quad8_iio *const priv = iio_priv(indio_dev);
374
375 return priv->synchronous_mode[chan->channel];
376}
377
378static const struct iio_enum quad8_synchronous_mode_enum = {
379 .items = quad8_synchronous_modes,
380 .num_items = ARRAY_SIZE(quad8_synchronous_modes),
381 .set = quad8_set_synchronous_mode,
382 .get = quad8_get_synchronous_mode
383};
384
385static const char *const quad8_quadrature_modes[] = {
386 "non-quadrature",
387 "quadrature"
388};
389
390static int quad8_set_quadrature_mode(struct iio_dev *indio_dev,
391 const struct iio_chan_spec *chan, unsigned int quadrature_mode)
392{
393 struct quad8_iio *const priv = iio_priv(indio_dev);
394 unsigned int mode_cfg = priv->count_mode[chan->channel] << 1;
395 const int base_offset = priv->base + 2 * chan->channel + 1;
396
397 if (quadrature_mode)
398 mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3;
399 else {
400 /* Quadrature scaling only available in quadrature mode */
401 priv->quadrature_scale[chan->channel] = 0;
402
403 /* Synchronous function not supported in non-quadrature mode */
404 if (priv->synchronous_mode[chan->channel])
405 quad8_set_synchronous_mode(indio_dev, chan, 0);
406 }
407
408 priv->quadrature_mode[chan->channel] = quadrature_mode;
409
410 /* Load mode configuration to Counter Mode Register */
411 outb(0x20 | mode_cfg, base_offset);
412
413 return 0;
414}
415
416static int quad8_get_quadrature_mode(struct iio_dev *indio_dev,
417 const struct iio_chan_spec *chan)
418{
419 const struct quad8_iio *const priv = iio_priv(indio_dev);
420
421 return priv->quadrature_mode[chan->channel];
422}
423
424static const struct iio_enum quad8_quadrature_mode_enum = {
425 .items = quad8_quadrature_modes,
426 .num_items = ARRAY_SIZE(quad8_quadrature_modes),
427 .set = quad8_set_quadrature_mode,
428 .get = quad8_get_quadrature_mode
429};
430
431static const char *const quad8_index_polarity_modes[] = {
432 "negative",
433 "positive"
434};
435
436static int quad8_set_index_polarity(struct iio_dev *indio_dev,
437 const struct iio_chan_spec *chan, unsigned int index_polarity)
438{
439 struct quad8_iio *const priv = iio_priv(indio_dev);
440 const unsigned int idr_cfg = priv->synchronous_mode[chan->channel] |
441 index_polarity << 1;
442 const int base_offset = priv->base + 2 * chan->channel + 1;
443
444 priv->index_polarity[chan->channel] = index_polarity;
445
446 /* Load Index Control configuration to Index Control Register */
447 outb(0x40 | idr_cfg, base_offset);
448
449 return 0;
450}
451
452static int quad8_get_index_polarity(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->index_polarity[chan->channel];
458}
459
460static const struct iio_enum quad8_index_polarity_enum = {
461 .items = quad8_index_polarity_modes,
462 .num_items = ARRAY_SIZE(quad8_index_polarity_modes),
463 .set = quad8_set_index_polarity,
464 .get = quad8_get_index_polarity
465};
466
467static const struct iio_chan_spec_ext_info quad8_count_ext_info[] = {
468 {
469 .name = "preset",
470 .shared = IIO_SEPARATE,
471 .read = quad8_read_preset,
472 .write = quad8_write_preset
473 },
474 {
475 .name = "set_to_preset_on_index",
476 .shared = IIO_SEPARATE,
477 .read = quad8_read_set_to_preset_on_index,
478 .write = quad8_write_set_to_preset_on_index
479 },
480 IIO_ENUM("noise_error", IIO_SEPARATE, &quad8_noise_error_enum),
481 IIO_ENUM_AVAILABLE("noise_error", &quad8_noise_error_enum),
482 IIO_ENUM("count_direction", IIO_SEPARATE, &quad8_count_direction_enum),
483 IIO_ENUM_AVAILABLE("count_direction", &quad8_count_direction_enum),
484 IIO_ENUM("count_mode", IIO_SEPARATE, &quad8_count_mode_enum),
485 IIO_ENUM_AVAILABLE("count_mode", &quad8_count_mode_enum),
486 IIO_ENUM("quadrature_mode", IIO_SEPARATE, &quad8_quadrature_mode_enum),
487 IIO_ENUM_AVAILABLE("quadrature_mode", &quad8_quadrature_mode_enum),
488 {}
489};
490
491static const struct iio_chan_spec_ext_info quad8_index_ext_info[] = {
492 IIO_ENUM("synchronous_mode", IIO_SEPARATE,
493 &quad8_synchronous_mode_enum),
494 IIO_ENUM_AVAILABLE("synchronous_mode", &quad8_synchronous_mode_enum),
495 IIO_ENUM("index_polarity", IIO_SEPARATE, &quad8_index_polarity_enum),
496 IIO_ENUM_AVAILABLE("index_polarity", &quad8_index_polarity_enum),
497 {}
498};
499
500#define QUAD8_COUNT_CHAN(_chan) { \
501 .type = IIO_COUNT, \
502 .channel = (_chan), \
503 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
504 BIT(IIO_CHAN_INFO_ENABLE) | BIT(IIO_CHAN_INFO_SCALE), \
505 .ext_info = quad8_count_ext_info, \
506 .indexed = 1 \
507}
508
509#define QUAD8_INDEX_CHAN(_chan) { \
510 .type = IIO_INDEX, \
511 .channel = (_chan), \
512 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
513 .ext_info = quad8_index_ext_info, \
514 .indexed = 1 \
515}
516
517static const struct iio_chan_spec quad8_channels[] = {
518 QUAD8_COUNT_CHAN(0), QUAD8_INDEX_CHAN(0),
519 QUAD8_COUNT_CHAN(1), QUAD8_INDEX_CHAN(1),
520 QUAD8_COUNT_CHAN(2), QUAD8_INDEX_CHAN(2),
521 QUAD8_COUNT_CHAN(3), QUAD8_INDEX_CHAN(3),
522 QUAD8_COUNT_CHAN(4), QUAD8_INDEX_CHAN(4),
523 QUAD8_COUNT_CHAN(5), QUAD8_INDEX_CHAN(5),
524 QUAD8_COUNT_CHAN(6), QUAD8_INDEX_CHAN(6),
525 QUAD8_COUNT_CHAN(7), QUAD8_INDEX_CHAN(7)
526};
527
528static int quad8_probe(struct device *dev, unsigned int id)
529{
530 struct iio_dev *indio_dev;
531 struct quad8_iio *priv;
532 int i, j;
533 unsigned int base_offset;
534
535 indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
536 if (!indio_dev)
537 return -ENOMEM;
538
539 if (!devm_request_region(dev, base[id], QUAD8_EXTENT,
540 dev_name(dev))) {
541 dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
542 base[id], base[id] + QUAD8_EXTENT);
543 return -EBUSY;
544 }
545
546 indio_dev->info = &quad8_info;
547 indio_dev->modes = INDIO_DIRECT_MODE;
548 indio_dev->num_channels = ARRAY_SIZE(quad8_channels);
549 indio_dev->channels = quad8_channels;
550 indio_dev->name = dev_name(dev);
551
552 priv = iio_priv(indio_dev);
553 priv->base = base[id];
554
555 /* Reset all counters and disable interrupt function */
556 outb(0x01, base[id] + 0x11);
557 /* Set initial configuration for all counters */
558 for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
559 base_offset = base[id] + 2 * i;
560 /* Reset Byte Pointer */
561 outb(0x01, base_offset + 1);
562 /* Reset Preset Register */
563 for (j = 0; j < 3; j++)
564 outb(0x00, base_offset);
565 /* Reset Borrow, Carry, Compare, and Sign flags */
566 outb(0x04, base_offset + 1);
567 /* Reset Error flag */
568 outb(0x06, base_offset + 1);
569 /* Binary encoding; Normal count; non-quadrature mode */
570 outb(0x20, base_offset + 1);
571 /* Disable A and B inputs; preset on index; FLG1 as Carry */
572 outb(0x40, base_offset + 1);
573 /* Disable index function; negative index polarity */
574 outb(0x60, base_offset + 1);
575 }
576 /* Enable all counters */
577 outb(0x00, base[id] + 0x11);
578
579 return devm_iio_device_register(dev, indio_dev);
580}
581
582static struct isa_driver quad8_driver = {
583 .probe = quad8_probe,
584 .driver = {
585 .name = "104-quad-8"
586 }
587};
588
589module_isa_driver(quad8_driver, num_quad8);
590
591MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
592MODULE_DESCRIPTION("ACCES 104-QUAD-8 IIO driver");
593MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
new file mode 100644
index 000000000000..44627f6e4861
--- /dev/null
+++ b/drivers/iio/counter/Kconfig
@@ -0,0 +1,24 @@
1#
2# Counter devices
3#
4# When adding new entries keep the list in alphabetical order
5
6menu "Counters"
7
8config 104_QUAD_8
9 tristate "ACCES 104-QUAD-8 driver"
10 depends on X86 && ISA_BUS_API
11 help
12 Say yes here to build support for the ACCES 104-QUAD-8 quadrature
13 encoder counter/interface device family (104-QUAD-8, 104-QUAD-4).
14
15 Performing a write to a counter's IIO_CHAN_INFO_RAW sets the counter and
16 also clears the counter's respective error flag. Although the counters
17 have a 25-bit range, only the lower 24 bits may be set, either directly
18 or via a counter's preset attribute. Interrupts are not supported by
19 this driver.
20
21 The base port addresses for the devices may be configured via the base
22 array module parameter.
23
24endmenu
diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
new file mode 100644
index 000000000000..007e88411648
--- /dev/null
+++ b/drivers/iio/counter/Makefile
@@ -0,0 +1,7 @@
1#
2# Makefile for IIO counter devices
3#
4
5# When adding new entries keep the list in alphabetical order
6
7obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o