Commit | Line | Data |
---|---|---|
f1d8a071 WBG |
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 | |
954ab5cc | 34 | * @fck_prescaler: array of filter clock prescaler configurations |
f1d8a071 WBG |
35 | * @preset: array of preset values |
36 | * @count_mode: array of count mode configurations | |
37 | * @quadrature_mode: array of quadrature mode configurations | |
38 | * @quadrature_scale: array of quadrature mode scale configurations | |
39 | * @ab_enable: array of A and B inputs enable configurations | |
40 | * @preset_enable: array of set_to_preset_on_index attribute configurations | |
41 | * @synchronous_mode: array of index function synchronous mode configurations | |
42 | * @index_polarity: array of index function polarity configurations | |
954ab5cc | 43 | * @cable_fault_enable: differential encoder cable status enable configurations |
f1d8a071 WBG |
44 | * @base: base port address of the IIO device |
45 | */ | |
46 | struct quad8_iio { | |
fc069262 | 47 | struct mutex lock; |
f1d8a071 | 48 | struct counter_device counter; |
de65d055 | 49 | unsigned int fck_prescaler[QUAD8_NUM_COUNTERS]; |
f1d8a071 WBG |
50 | unsigned int preset[QUAD8_NUM_COUNTERS]; |
51 | unsigned int count_mode[QUAD8_NUM_COUNTERS]; | |
52 | unsigned int quadrature_mode[QUAD8_NUM_COUNTERS]; | |
53 | unsigned int quadrature_scale[QUAD8_NUM_COUNTERS]; | |
54 | unsigned int ab_enable[QUAD8_NUM_COUNTERS]; | |
55 | unsigned int preset_enable[QUAD8_NUM_COUNTERS]; | |
56 | unsigned int synchronous_mode[QUAD8_NUM_COUNTERS]; | |
57 | unsigned int index_polarity[QUAD8_NUM_COUNTERS]; | |
954ab5cc | 58 | unsigned int cable_fault_enable; |
f1d8a071 WBG |
59 | unsigned int base; |
60 | }; | |
61 | ||
62 | #define QUAD8_REG_CHAN_OP 0x11 | |
63 | #define QUAD8_REG_INDEX_INPUT_LEVELS 0x16 | |
954ab5cc | 64 | #define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17 |
f1d8a071 WBG |
65 | /* Borrow Toggle flip-flop */ |
66 | #define QUAD8_FLAG_BT BIT(0) | |
67 | /* Carry Toggle flip-flop */ | |
68 | #define QUAD8_FLAG_CT BIT(1) | |
69 | /* Error flag */ | |
70 | #define QUAD8_FLAG_E BIT(4) | |
71 | /* Up/Down flag */ | |
72 | #define QUAD8_FLAG_UD BIT(5) | |
73 | /* Reset and Load Signal Decoders */ | |
74 | #define QUAD8_CTR_RLD 0x00 | |
75 | /* Counter Mode Register */ | |
76 | #define QUAD8_CTR_CMR 0x20 | |
77 | /* Input / Output Control Register */ | |
78 | #define QUAD8_CTR_IOR 0x40 | |
79 | /* Index Control Register */ | |
80 | #define QUAD8_CTR_IDR 0x60 | |
81 | /* Reset Byte Pointer (three byte data pointer) */ | |
82 | #define QUAD8_RLD_RESET_BP 0x01 | |
83 | /* Reset Counter */ | |
84 | #define QUAD8_RLD_RESET_CNTR 0x02 | |
85 | /* Reset Borrow Toggle, Carry Toggle, Compare Toggle, and Sign flags */ | |
86 | #define QUAD8_RLD_RESET_FLAGS 0x04 | |
87 | /* Reset Error flag */ | |
88 | #define QUAD8_RLD_RESET_E 0x06 | |
89 | /* Preset Register to Counter */ | |
90 | #define QUAD8_RLD_PRESET_CNTR 0x08 | |
91 | /* Transfer Counter to Output Latch */ | |
92 | #define QUAD8_RLD_CNTR_OUT 0x10 | |
de65d055 WBG |
93 | /* Transfer Preset Register LSB to FCK Prescaler */ |
94 | #define QUAD8_RLD_PRESET_PSC 0x18 | |
f1d8a071 WBG |
95 | #define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00 |
96 | #define QUAD8_CHAN_OP_RESET_COUNTERS 0x01 | |
97 | #define QUAD8_CMR_QUADRATURE_X1 0x08 | |
98 | #define QUAD8_CMR_QUADRATURE_X2 0x10 | |
99 | #define QUAD8_CMR_QUADRATURE_X4 0x18 | |
100 | ||
101 | ||
102 | static int quad8_read_raw(struct iio_dev *indio_dev, | |
103 | struct iio_chan_spec const *chan, int *val, int *val2, long mask) | |
104 | { | |
105 | struct quad8_iio *const priv = iio_priv(indio_dev); | |
106 | const int base_offset = priv->base + 2 * chan->channel; | |
107 | unsigned int flags; | |
108 | unsigned int borrow; | |
109 | unsigned int carry; | |
110 | int i; | |
111 | ||
112 | switch (mask) { | |
113 | case IIO_CHAN_INFO_RAW: | |
114 | if (chan->type == IIO_INDEX) { | |
115 | *val = !!(inb(priv->base + QUAD8_REG_INDEX_INPUT_LEVELS) | |
116 | & BIT(chan->channel)); | |
117 | return IIO_VAL_INT; | |
118 | } | |
119 | ||
120 | flags = inb(base_offset + 1); | |
121 | borrow = flags & QUAD8_FLAG_BT; | |
122 | carry = !!(flags & QUAD8_FLAG_CT); | |
123 | ||
124 | /* Borrow XOR Carry effectively doubles count range */ | |
125 | *val = (borrow ^ carry) << 24; | |
126 | ||
fc069262 SNW |
127 | mutex_lock(&priv->lock); |
128 | ||
f1d8a071 WBG |
129 | /* Reset Byte Pointer; transfer Counter to Output Latch */ |
130 | outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT, | |
131 | base_offset + 1); | |
132 | ||
133 | for (i = 0; i < 3; i++) | |
134 | *val |= (unsigned int)inb(base_offset) << (8 * i); | |
135 | ||
fc069262 SNW |
136 | mutex_unlock(&priv->lock); |
137 | ||
f1d8a071 WBG |
138 | return IIO_VAL_INT; |
139 | case IIO_CHAN_INFO_ENABLE: | |
140 | *val = priv->ab_enable[chan->channel]; | |
141 | return IIO_VAL_INT; | |
142 | case IIO_CHAN_INFO_SCALE: | |
143 | *val = 1; | |
144 | *val2 = priv->quadrature_scale[chan->channel]; | |
145 | return IIO_VAL_FRACTIONAL_LOG2; | |
146 | } | |
147 | ||
148 | return -EINVAL; | |
149 | } | |
150 | ||
151 | static int quad8_write_raw(struct iio_dev *indio_dev, | |
152 | struct iio_chan_spec const *chan, int val, int val2, long mask) | |
153 | { | |
154 | struct quad8_iio *const priv = iio_priv(indio_dev); | |
155 | const int base_offset = priv->base + 2 * chan->channel; | |
156 | int i; | |
157 | unsigned int ior_cfg; | |
158 | ||
159 | switch (mask) { | |
160 | case IIO_CHAN_INFO_RAW: | |
161 | if (chan->type == IIO_INDEX) | |
162 | return -EINVAL; | |
163 | ||
164 | /* Only 24-bit values are supported */ | |
165 | if ((unsigned int)val > 0xFFFFFF) | |
166 | return -EINVAL; | |
167 | ||
fc069262 SNW |
168 | mutex_lock(&priv->lock); |
169 | ||
f1d8a071 WBG |
170 | /* Reset Byte Pointer */ |
171 | outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); | |
172 | ||
173 | /* Counter can only be set via Preset Register */ | |
174 | for (i = 0; i < 3; i++) | |
175 | outb(val >> (8 * i), base_offset); | |
176 | ||
177 | /* Transfer Preset Register to Counter */ | |
178 | outb(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, base_offset + 1); | |
179 | ||
180 | /* Reset Byte Pointer */ | |
181 | outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); | |
182 | ||
183 | /* Set Preset Register back to original value */ | |
184 | val = priv->preset[chan->channel]; | |
185 | for (i = 0; i < 3; i++) | |
186 | outb(val >> (8 * i), base_offset); | |
187 | ||
188 | /* Reset Borrow, Carry, Compare, and Sign flags */ | |
189 | outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1); | |
190 | /* Reset Error flag */ | |
191 | outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1); | |
192 | ||
fc069262 SNW |
193 | mutex_unlock(&priv->lock); |
194 | ||
f1d8a071 WBG |
195 | return 0; |
196 | case IIO_CHAN_INFO_ENABLE: | |
197 | /* only boolean values accepted */ | |
198 | if (val < 0 || val > 1) | |
199 | return -EINVAL; | |
200 | ||
fc069262 SNW |
201 | mutex_lock(&priv->lock); |
202 | ||
f1d8a071 WBG |
203 | priv->ab_enable[chan->channel] = val; |
204 | ||
205 | ior_cfg = val | priv->preset_enable[chan->channel] << 1; | |
206 | ||
207 | /* Load I/O control configuration */ | |
208 | outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1); | |
209 | ||
fc069262 SNW |
210 | mutex_unlock(&priv->lock); |
211 | ||
f1d8a071 WBG |
212 | return 0; |
213 | case IIO_CHAN_INFO_SCALE: | |
fc069262 SNW |
214 | mutex_lock(&priv->lock); |
215 | ||
f1d8a071 | 216 | /* Quadrature scaling only available in quadrature mode */ |
fc069262 SNW |
217 | if (!priv->quadrature_mode[chan->channel] && |
218 | (val2 || val != 1)) { | |
219 | mutex_unlock(&priv->lock); | |
f1d8a071 | 220 | return -EINVAL; |
fc069262 | 221 | } |
f1d8a071 WBG |
222 | |
223 | /* Only three gain states (1, 0.5, 0.25) */ | |
224 | if (val == 1 && !val2) | |
225 | priv->quadrature_scale[chan->channel] = 0; | |
226 | else if (!val) | |
227 | switch (val2) { | |
228 | case 500000: | |
229 | priv->quadrature_scale[chan->channel] = 1; | |
230 | break; | |
231 | case 250000: | |
232 | priv->quadrature_scale[chan->channel] = 2; | |
233 | break; | |
234 | default: | |
fc069262 | 235 | mutex_unlock(&priv->lock); |
f1d8a071 WBG |
236 | return -EINVAL; |
237 | } | |
fc069262 SNW |
238 | else { |
239 | mutex_unlock(&priv->lock); | |
f1d8a071 | 240 | return -EINVAL; |
fc069262 | 241 | } |
f1d8a071 | 242 | |
fc069262 | 243 | mutex_unlock(&priv->lock); |
f1d8a071 WBG |
244 | return 0; |
245 | } | |
246 | ||
247 | return -EINVAL; | |
248 | } | |
249 | ||
250 | static const struct iio_info quad8_info = { | |
251 | .read_raw = quad8_read_raw, | |
252 | .write_raw = quad8_write_raw | |
253 | }; | |
254 | ||
255 | static ssize_t quad8_read_preset(struct iio_dev *indio_dev, uintptr_t private, | |
256 | const struct iio_chan_spec *chan, char *buf) | |
257 | { | |
258 | const struct quad8_iio *const priv = iio_priv(indio_dev); | |
259 | ||
260 | return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset[chan->channel]); | |
261 | } | |
262 | ||
263 | static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private, | |
264 | const struct iio_chan_spec *chan, const char *buf, size_t len) | |
265 | { | |
266 | struct quad8_iio *const priv = iio_priv(indio_dev); | |
267 | const int base_offset = priv->base + 2 * chan->channel; | |
268 | unsigned int preset; | |
269 | int ret; | |
270 | int i; | |
271 | ||
272 | ret = kstrtouint(buf, 0, &preset); | |
273 | if (ret) | |
274 | return ret; | |
275 | ||
276 | /* Only 24-bit values are supported */ | |
277 | if (preset > 0xFFFFFF) | |
278 | return -EINVAL; | |
279 | ||
fc069262 SNW |
280 | mutex_lock(&priv->lock); |
281 | ||
f1d8a071 WBG |
282 | priv->preset[chan->channel] = preset; |
283 | ||
284 | /* Reset Byte Pointer */ | |
285 | outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); | |
286 | ||
287 | /* Set Preset Register */ | |
288 | for (i = 0; i < 3; i++) | |
289 | outb(preset >> (8 * i), base_offset); | |
290 | ||
fc069262 SNW |
291 | mutex_unlock(&priv->lock); |
292 | ||
f1d8a071 WBG |
293 | return len; |
294 | } | |
295 | ||
296 | static ssize_t quad8_read_set_to_preset_on_index(struct iio_dev *indio_dev, | |
297 | uintptr_t private, const struct iio_chan_spec *chan, char *buf) | |
298 | { | |
299 | const struct quad8_iio *const priv = iio_priv(indio_dev); | |
300 | ||
301 | return snprintf(buf, PAGE_SIZE, "%u\n", | |
302 | !priv->preset_enable[chan->channel]); | |
303 | } | |
304 | ||
305 | static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev, | |
306 | uintptr_t private, const struct iio_chan_spec *chan, const char *buf, | |
307 | size_t len) | |
308 | { | |
309 | struct quad8_iio *const priv = iio_priv(indio_dev); | |
310 | const int base_offset = priv->base + 2 * chan->channel + 1; | |
311 | bool preset_enable; | |
312 | int ret; | |
313 | unsigned int ior_cfg; | |
314 | ||
315 | ret = kstrtobool(buf, &preset_enable); | |
316 | if (ret) | |
317 | return ret; | |
318 | ||
319 | /* Preset enable is active low in Input/Output Control register */ | |
320 | preset_enable = !preset_enable; | |
321 | ||
fc069262 SNW |
322 | mutex_lock(&priv->lock); |
323 | ||
f1d8a071 WBG |
324 | priv->preset_enable[chan->channel] = preset_enable; |
325 | ||
326 | ior_cfg = priv->ab_enable[chan->channel] | | |
327 | (unsigned int)preset_enable << 1; | |
328 | ||
329 | /* Load I/O control configuration to Input / Output Control Register */ | |
330 | outb(QUAD8_CTR_IOR | ior_cfg, base_offset); | |
331 | ||
fc069262 SNW |
332 | mutex_unlock(&priv->lock); |
333 | ||
f1d8a071 WBG |
334 | return len; |
335 | } | |
336 | ||
337 | static const char *const quad8_noise_error_states[] = { | |
338 | "No excessive noise is present at the count inputs", | |
339 | "Excessive noise is present at the count inputs" | |
340 | }; | |
341 | ||
342 | static int quad8_get_noise_error(struct iio_dev *indio_dev, | |
343 | const struct iio_chan_spec *chan) | |
344 | { | |
345 | struct quad8_iio *const priv = iio_priv(indio_dev); | |
346 | const int base_offset = priv->base + 2 * chan->channel + 1; | |
347 | ||
348 | return !!(inb(base_offset) & QUAD8_FLAG_E); | |
349 | } | |
350 | ||
351 | static const struct iio_enum quad8_noise_error_enum = { | |
352 | .items = quad8_noise_error_states, | |
353 | .num_items = ARRAY_SIZE(quad8_noise_error_states), | |
354 | .get = quad8_get_noise_error | |
355 | }; | |
356 | ||
357 | static const char *const quad8_count_direction_states[] = { | |
358 | "down", | |
359 | "up" | |
360 | }; | |
361 | ||
362 | static int quad8_get_count_direction(struct iio_dev *indio_dev, | |
363 | const struct iio_chan_spec *chan) | |
364 | { | |
365 | struct quad8_iio *const priv = iio_priv(indio_dev); | |
366 | const int base_offset = priv->base + 2 * chan->channel + 1; | |
367 | ||
368 | return !!(inb(base_offset) & QUAD8_FLAG_UD); | |
369 | } | |
370 | ||
371 | static const struct iio_enum quad8_count_direction_enum = { | |
372 | .items = quad8_count_direction_states, | |
373 | .num_items = ARRAY_SIZE(quad8_count_direction_states), | |
374 | .get = quad8_get_count_direction | |
375 | }; | |
376 | ||
377 | static const char *const quad8_count_modes[] = { | |
378 | "normal", | |
379 | "range limit", | |
380 | "non-recycle", | |
381 | "modulo-n" | |
382 | }; | |
383 | ||
384 | static int quad8_set_count_mode(struct iio_dev *indio_dev, | |
385 | const struct iio_chan_spec *chan, unsigned int cnt_mode) | |
386 | { | |
387 | struct quad8_iio *const priv = iio_priv(indio_dev); | |
388 | unsigned int mode_cfg = cnt_mode << 1; | |
389 | const int base_offset = priv->base + 2 * chan->channel + 1; | |
390 | ||
fc069262 SNW |
391 | mutex_lock(&priv->lock); |
392 | ||
f1d8a071 WBG |
393 | priv->count_mode[chan->channel] = cnt_mode; |
394 | ||
395 | /* Add quadrature mode configuration */ | |
396 | if (priv->quadrature_mode[chan->channel]) | |
397 | mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3; | |
398 | ||
399 | /* Load mode configuration to Counter Mode Register */ | |
400 | outb(QUAD8_CTR_CMR | mode_cfg, base_offset); | |
401 | ||
fc069262 SNW |
402 | mutex_unlock(&priv->lock); |
403 | ||
f1d8a071 WBG |
404 | return 0; |
405 | } | |
406 | ||
407 | static int quad8_get_count_mode(struct iio_dev *indio_dev, | |
408 | const struct iio_chan_spec *chan) | |
409 | { | |
410 | const struct quad8_iio *const priv = iio_priv(indio_dev); | |
411 | ||
412 | return priv->count_mode[chan->channel]; | |
413 | } | |
414 | ||
415 | static const struct iio_enum quad8_count_mode_enum = { | |
416 | .items = quad8_count_modes, | |
417 | .num_items = ARRAY_SIZE(quad8_count_modes), | |
418 | .set = quad8_set_count_mode, | |
419 | .get = quad8_get_count_mode | |
420 | }; | |
421 | ||
422 | static const char *const quad8_synchronous_modes[] = { | |
423 | "non-synchronous", | |
424 | "synchronous" | |
425 | }; | |
426 | ||
427 | static int quad8_set_synchronous_mode(struct iio_dev *indio_dev, | |
428 | const struct iio_chan_spec *chan, unsigned int synchronous_mode) | |
429 | { | |
430 | struct quad8_iio *const priv = iio_priv(indio_dev); | |
f1d8a071 | 431 | const int base_offset = priv->base + 2 * chan->channel + 1; |
fc069262 SNW |
432 | unsigned int idr_cfg = synchronous_mode; |
433 | ||
434 | mutex_lock(&priv->lock); | |
435 | ||
436 | idr_cfg |= priv->index_polarity[chan->channel] << 1; | |
f1d8a071 WBG |
437 | |
438 | /* Index function must be non-synchronous in non-quadrature mode */ | |
fc069262 SNW |
439 | if (synchronous_mode && !priv->quadrature_mode[chan->channel]) { |
440 | mutex_unlock(&priv->lock); | |
f1d8a071 | 441 | return -EINVAL; |
fc069262 | 442 | } |
f1d8a071 WBG |
443 | |
444 | priv->synchronous_mode[chan->channel] = synchronous_mode; | |
445 | ||
446 | /* Load Index Control configuration to Index Control Register */ | |
447 | outb(QUAD8_CTR_IDR | idr_cfg, base_offset); | |
448 | ||
fc069262 SNW |
449 | mutex_unlock(&priv->lock); |
450 | ||
f1d8a071 WBG |
451 | return 0; |
452 | } | |
453 | ||
454 | static int quad8_get_synchronous_mode(struct iio_dev *indio_dev, | |
455 | const struct iio_chan_spec *chan) | |
456 | { | |
457 | const struct quad8_iio *const priv = iio_priv(indio_dev); | |
458 | ||
459 | return priv->synchronous_mode[chan->channel]; | |
460 | } | |
461 | ||
462 | static const struct iio_enum quad8_synchronous_mode_enum = { | |
463 | .items = quad8_synchronous_modes, | |
464 | .num_items = ARRAY_SIZE(quad8_synchronous_modes), | |
465 | .set = quad8_set_synchronous_mode, | |
466 | .get = quad8_get_synchronous_mode | |
467 | }; | |
468 | ||
469 | static const char *const quad8_quadrature_modes[] = { | |
470 | "non-quadrature", | |
471 | "quadrature" | |
472 | }; | |
473 | ||
474 | static int quad8_set_quadrature_mode(struct iio_dev *indio_dev, | |
475 | const struct iio_chan_spec *chan, unsigned int quadrature_mode) | |
476 | { | |
477 | struct quad8_iio *const priv = iio_priv(indio_dev); | |
f1d8a071 | 478 | const int base_offset = priv->base + 2 * chan->channel + 1; |
fc069262 SNW |
479 | unsigned int mode_cfg; |
480 | ||
481 | mutex_lock(&priv->lock); | |
482 | ||
483 | mode_cfg = priv->count_mode[chan->channel] << 1; | |
f1d8a071 WBG |
484 | |
485 | if (quadrature_mode) | |
486 | mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3; | |
487 | else { | |
488 | /* Quadrature scaling only available in quadrature mode */ | |
489 | priv->quadrature_scale[chan->channel] = 0; | |
490 | ||
491 | /* Synchronous function not supported in non-quadrature mode */ | |
492 | if (priv->synchronous_mode[chan->channel]) | |
493 | quad8_set_synchronous_mode(indio_dev, chan, 0); | |
494 | } | |
495 | ||
496 | priv->quadrature_mode[chan->channel] = quadrature_mode; | |
497 | ||
498 | /* Load mode configuration to Counter Mode Register */ | |
499 | outb(QUAD8_CTR_CMR | mode_cfg, base_offset); | |
500 | ||
fc069262 SNW |
501 | mutex_unlock(&priv->lock); |
502 | ||
f1d8a071 WBG |
503 | return 0; |
504 | } | |
505 | ||
506 | static int quad8_get_quadrature_mode(struct iio_dev *indio_dev, | |
507 | const struct iio_chan_spec *chan) | |
508 | { | |
509 | const struct quad8_iio *const priv = iio_priv(indio_dev); | |
510 | ||
511 | return priv->quadrature_mode[chan->channel]; | |
512 | } | |
513 | ||
514 | static const struct iio_enum quad8_quadrature_mode_enum = { | |
515 | .items = quad8_quadrature_modes, | |
516 | .num_items = ARRAY_SIZE(quad8_quadrature_modes), | |
517 | .set = quad8_set_quadrature_mode, | |
518 | .get = quad8_get_quadrature_mode | |
519 | }; | |
520 | ||
521 | static const char *const quad8_index_polarity_modes[] = { | |
522 | "negative", | |
523 | "positive" | |
524 | }; | |
525 | ||
526 | static int quad8_set_index_polarity(struct iio_dev *indio_dev, | |
527 | const struct iio_chan_spec *chan, unsigned int index_polarity) | |
528 | { | |
529 | struct quad8_iio *const priv = iio_priv(indio_dev); | |
f1d8a071 | 530 | const int base_offset = priv->base + 2 * chan->channel + 1; |
fc069262 SNW |
531 | unsigned int idr_cfg = index_polarity << 1; |
532 | ||
533 | mutex_lock(&priv->lock); | |
534 | ||
535 | idr_cfg |= priv->synchronous_mode[chan->channel]; | |
f1d8a071 WBG |
536 | |
537 | priv->index_polarity[chan->channel] = index_polarity; | |
538 | ||
539 | /* Load Index Control configuration to Index Control Register */ | |
540 | outb(QUAD8_CTR_IDR | idr_cfg, base_offset); | |
541 | ||
fc069262 SNW |
542 | mutex_unlock(&priv->lock); |
543 | ||
f1d8a071 WBG |
544 | return 0; |
545 | } | |
546 | ||
547 | static int quad8_get_index_polarity(struct iio_dev *indio_dev, | |
548 | const struct iio_chan_spec *chan) | |
549 | { | |
550 | const struct quad8_iio *const priv = iio_priv(indio_dev); | |
551 | ||
552 | return priv->index_polarity[chan->channel]; | |
553 | } | |
554 | ||
555 | static const struct iio_enum quad8_index_polarity_enum = { | |
556 | .items = quad8_index_polarity_modes, | |
557 | .num_items = ARRAY_SIZE(quad8_index_polarity_modes), | |
558 | .set = quad8_set_index_polarity, | |
559 | .get = quad8_get_index_polarity | |
560 | }; | |
561 | ||
562 | static const struct iio_chan_spec_ext_info quad8_count_ext_info[] = { | |
563 | { | |
564 | .name = "preset", | |
565 | .shared = IIO_SEPARATE, | |
566 | .read = quad8_read_preset, | |
567 | .write = quad8_write_preset | |
568 | }, | |
569 | { | |
570 | .name = "set_to_preset_on_index", | |
571 | .shared = IIO_SEPARATE, | |
572 | .read = quad8_read_set_to_preset_on_index, | |
573 | .write = quad8_write_set_to_preset_on_index | |
574 | }, | |
575 | IIO_ENUM("noise_error", IIO_SEPARATE, &quad8_noise_error_enum), | |
576 | IIO_ENUM_AVAILABLE("noise_error", &quad8_noise_error_enum), | |
577 | IIO_ENUM("count_direction", IIO_SEPARATE, &quad8_count_direction_enum), | |
578 | IIO_ENUM_AVAILABLE("count_direction", &quad8_count_direction_enum), | |
579 | IIO_ENUM("count_mode", IIO_SEPARATE, &quad8_count_mode_enum), | |
580 | IIO_ENUM_AVAILABLE("count_mode", &quad8_count_mode_enum), | |
581 | IIO_ENUM("quadrature_mode", IIO_SEPARATE, &quad8_quadrature_mode_enum), | |
582 | IIO_ENUM_AVAILABLE("quadrature_mode", &quad8_quadrature_mode_enum), | |
583 | {} | |
584 | }; | |
585 | ||
586 | static const struct iio_chan_spec_ext_info quad8_index_ext_info[] = { | |
587 | IIO_ENUM("synchronous_mode", IIO_SEPARATE, | |
588 | &quad8_synchronous_mode_enum), | |
589 | IIO_ENUM_AVAILABLE("synchronous_mode", &quad8_synchronous_mode_enum), | |
590 | IIO_ENUM("index_polarity", IIO_SEPARATE, &quad8_index_polarity_enum), | |
591 | IIO_ENUM_AVAILABLE("index_polarity", &quad8_index_polarity_enum), | |
592 | {} | |
593 | }; | |
594 | ||
595 | #define QUAD8_COUNT_CHAN(_chan) { \ | |
596 | .type = IIO_COUNT, \ | |
597 | .channel = (_chan), \ | |
598 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ | |
599 | BIT(IIO_CHAN_INFO_ENABLE) | BIT(IIO_CHAN_INFO_SCALE), \ | |
600 | .ext_info = quad8_count_ext_info, \ | |
601 | .indexed = 1 \ | |
602 | } | |
603 | ||
604 | #define QUAD8_INDEX_CHAN(_chan) { \ | |
605 | .type = IIO_INDEX, \ | |
606 | .channel = (_chan), \ | |
607 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
608 | .ext_info = quad8_index_ext_info, \ | |
609 | .indexed = 1 \ | |
610 | } | |
611 | ||
612 | static const struct iio_chan_spec quad8_channels[] = { | |
613 | QUAD8_COUNT_CHAN(0), QUAD8_INDEX_CHAN(0), | |
614 | QUAD8_COUNT_CHAN(1), QUAD8_INDEX_CHAN(1), | |
615 | QUAD8_COUNT_CHAN(2), QUAD8_INDEX_CHAN(2), | |
616 | QUAD8_COUNT_CHAN(3), QUAD8_INDEX_CHAN(3), | |
617 | QUAD8_COUNT_CHAN(4), QUAD8_INDEX_CHAN(4), | |
618 | QUAD8_COUNT_CHAN(5), QUAD8_INDEX_CHAN(5), | |
619 | QUAD8_COUNT_CHAN(6), QUAD8_INDEX_CHAN(6), | |
620 | QUAD8_COUNT_CHAN(7), QUAD8_INDEX_CHAN(7) | |
621 | }; | |
622 | ||
623 | static int quad8_signal_read(struct counter_device *counter, | |
d49e6ee2 | 624 | struct counter_signal *signal, enum counter_signal_value *val) |
f1d8a071 WBG |
625 | { |
626 | const struct quad8_iio *const priv = counter->priv; | |
627 | unsigned int state; | |
f1d8a071 WBG |
628 | |
629 | /* Only Index signal levels can be read */ | |
630 | if (signal->id < 16) | |
631 | return -EINVAL; | |
632 | ||
633 | state = inb(priv->base + QUAD8_REG_INDEX_INPUT_LEVELS) | |
634 | & BIT(signal->id - 16); | |
635 | ||
d49e6ee2 | 636 | *val = (state) ? COUNTER_SIGNAL_HIGH : COUNTER_SIGNAL_LOW; |
f1d8a071 WBG |
637 | |
638 | return 0; | |
639 | } | |
640 | ||
641 | static int quad8_count_read(struct counter_device *counter, | |
d49e6ee2 | 642 | struct counter_count *count, unsigned long *val) |
f1d8a071 | 643 | { |
fc069262 | 644 | struct quad8_iio *const priv = counter->priv; |
f1d8a071 WBG |
645 | const int base_offset = priv->base + 2 * count->id; |
646 | unsigned int flags; | |
647 | unsigned int borrow; | |
648 | unsigned int carry; | |
f1d8a071 WBG |
649 | int i; |
650 | ||
651 | flags = inb(base_offset + 1); | |
652 | borrow = flags & QUAD8_FLAG_BT; | |
653 | carry = !!(flags & QUAD8_FLAG_CT); | |
654 | ||
655 | /* Borrow XOR Carry effectively doubles count range */ | |
d49e6ee2 | 656 | *val = (unsigned long)(borrow ^ carry) << 24; |
f1d8a071 | 657 | |
fc069262 SNW |
658 | mutex_lock(&priv->lock); |
659 | ||
f1d8a071 WBG |
660 | /* Reset Byte Pointer; transfer Counter to Output Latch */ |
661 | outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT, | |
662 | base_offset + 1); | |
663 | ||
664 | for (i = 0; i < 3; i++) | |
d49e6ee2 | 665 | *val |= (unsigned long)inb(base_offset) << (8 * i); |
f1d8a071 | 666 | |
fc069262 SNW |
667 | mutex_unlock(&priv->lock); |
668 | ||
f1d8a071 WBG |
669 | return 0; |
670 | } | |
671 | ||
672 | static int quad8_count_write(struct counter_device *counter, | |
d49e6ee2 | 673 | struct counter_count *count, unsigned long val) |
f1d8a071 | 674 | { |
fc069262 | 675 | struct quad8_iio *const priv = counter->priv; |
f1d8a071 | 676 | const int base_offset = priv->base + 2 * count->id; |
f1d8a071 WBG |
677 | int i; |
678 | ||
f1d8a071 | 679 | /* Only 24-bit values are supported */ |
d49e6ee2 | 680 | if (val > 0xFFFFFF) |
f1d8a071 WBG |
681 | return -EINVAL; |
682 | ||
fc069262 SNW |
683 | mutex_lock(&priv->lock); |
684 | ||
f1d8a071 WBG |
685 | /* Reset Byte Pointer */ |
686 | outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); | |
687 | ||
688 | /* Counter can only be set via Preset Register */ | |
689 | for (i = 0; i < 3; i++) | |
d49e6ee2 | 690 | outb(val >> (8 * i), base_offset); |
f1d8a071 WBG |
691 | |
692 | /* Transfer Preset Register to Counter */ | |
693 | outb(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, base_offset + 1); | |
694 | ||
695 | /* Reset Byte Pointer */ | |
696 | outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); | |
697 | ||
698 | /* Set Preset Register back to original value */ | |
d49e6ee2 | 699 | val = priv->preset[count->id]; |
f1d8a071 | 700 | for (i = 0; i < 3; i++) |
d49e6ee2 | 701 | outb(val >> (8 * i), base_offset); |
f1d8a071 WBG |
702 | |
703 | /* Reset Borrow, Carry, Compare, and Sign flags */ | |
704 | outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1); | |
705 | /* Reset Error flag */ | |
706 | outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1); | |
707 | ||
fc069262 SNW |
708 | mutex_unlock(&priv->lock); |
709 | ||
f1d8a071 WBG |
710 | return 0; |
711 | } | |
712 | ||
713 | enum quad8_count_function { | |
714 | QUAD8_COUNT_FUNCTION_PULSE_DIRECTION = 0, | |
715 | QUAD8_COUNT_FUNCTION_QUADRATURE_X1, | |
716 | QUAD8_COUNT_FUNCTION_QUADRATURE_X2, | |
717 | QUAD8_COUNT_FUNCTION_QUADRATURE_X4 | |
718 | }; | |
719 | ||
720 | static enum counter_count_function quad8_count_functions_list[] = { | |
721 | [QUAD8_COUNT_FUNCTION_PULSE_DIRECTION] = COUNTER_COUNT_FUNCTION_PULSE_DIRECTION, | |
722 | [QUAD8_COUNT_FUNCTION_QUADRATURE_X1] = COUNTER_COUNT_FUNCTION_QUADRATURE_X1_A, | |
723 | [QUAD8_COUNT_FUNCTION_QUADRATURE_X2] = COUNTER_COUNT_FUNCTION_QUADRATURE_X2_A, | |
724 | [QUAD8_COUNT_FUNCTION_QUADRATURE_X4] = COUNTER_COUNT_FUNCTION_QUADRATURE_X4 | |
725 | }; | |
726 | ||
727 | static int quad8_function_get(struct counter_device *counter, | |
728 | struct counter_count *count, size_t *function) | |
729 | { | |
fc069262 | 730 | struct quad8_iio *const priv = counter->priv; |
f1d8a071 | 731 | const int id = count->id; |
f1d8a071 | 732 | |
fc069262 SNW |
733 | mutex_lock(&priv->lock); |
734 | ||
735 | if (priv->quadrature_mode[id]) | |
736 | switch (priv->quadrature_scale[id]) { | |
f1d8a071 WBG |
737 | case 0: |
738 | *function = QUAD8_COUNT_FUNCTION_QUADRATURE_X1; | |
739 | break; | |
740 | case 1: | |
741 | *function = QUAD8_COUNT_FUNCTION_QUADRATURE_X2; | |
742 | break; | |
743 | case 2: | |
744 | *function = QUAD8_COUNT_FUNCTION_QUADRATURE_X4; | |
745 | break; | |
746 | } | |
747 | else | |
748 | *function = QUAD8_COUNT_FUNCTION_PULSE_DIRECTION; | |
749 | ||
fc069262 SNW |
750 | mutex_unlock(&priv->lock); |
751 | ||
f1d8a071 WBG |
752 | return 0; |
753 | } | |
754 | ||
755 | static int quad8_function_set(struct counter_device *counter, | |
756 | struct counter_count *count, size_t function) | |
757 | { | |
758 | struct quad8_iio *const priv = counter->priv; | |
759 | const int id = count->id; | |
760 | unsigned int *const quadrature_mode = priv->quadrature_mode + id; | |
761 | unsigned int *const scale = priv->quadrature_scale + id; | |
f1d8a071 | 762 | unsigned int *const synchronous_mode = priv->synchronous_mode + id; |
f1d8a071 | 763 | const int base_offset = priv->base + 2 * id + 1; |
fc069262 SNW |
764 | unsigned int mode_cfg; |
765 | unsigned int idr_cfg; | |
766 | ||
767 | mutex_lock(&priv->lock); | |
768 | ||
769 | mode_cfg = priv->count_mode[id] << 1; | |
770 | idr_cfg = priv->index_polarity[id] << 1; | |
f1d8a071 WBG |
771 | |
772 | if (function == QUAD8_COUNT_FUNCTION_PULSE_DIRECTION) { | |
773 | *quadrature_mode = 0; | |
774 | ||
775 | /* Quadrature scaling only available in quadrature mode */ | |
776 | *scale = 0; | |
777 | ||
778 | /* Synchronous function not supported in non-quadrature mode */ | |
779 | if (*synchronous_mode) { | |
780 | *synchronous_mode = 0; | |
781 | /* Disable synchronous function mode */ | |
782 | outb(QUAD8_CTR_IDR | idr_cfg, base_offset); | |
783 | } | |
784 | } else { | |
785 | *quadrature_mode = 1; | |
786 | ||
787 | switch (function) { | |
788 | case QUAD8_COUNT_FUNCTION_QUADRATURE_X1: | |
789 | *scale = 0; | |
790 | mode_cfg |= QUAD8_CMR_QUADRATURE_X1; | |
791 | break; | |
792 | case QUAD8_COUNT_FUNCTION_QUADRATURE_X2: | |
793 | *scale = 1; | |
794 | mode_cfg |= QUAD8_CMR_QUADRATURE_X2; | |
795 | break; | |
796 | case QUAD8_COUNT_FUNCTION_QUADRATURE_X4: | |
797 | *scale = 2; | |
798 | mode_cfg |= QUAD8_CMR_QUADRATURE_X4; | |
799 | break; | |
800 | } | |
801 | } | |
802 | ||
803 | /* Load mode configuration to Counter Mode Register */ | |
804 | outb(QUAD8_CTR_CMR | mode_cfg, base_offset); | |
805 | ||
fc069262 SNW |
806 | mutex_unlock(&priv->lock); |
807 | ||
f1d8a071 WBG |
808 | return 0; |
809 | } | |
810 | ||
811 | static void quad8_direction_get(struct counter_device *counter, | |
812 | struct counter_count *count, enum counter_count_direction *direction) | |
813 | { | |
814 | const struct quad8_iio *const priv = counter->priv; | |
815 | unsigned int ud_flag; | |
816 | const unsigned int flag_addr = priv->base + 2 * count->id + 1; | |
817 | ||
818 | /* U/D flag: nonzero = up, zero = down */ | |
819 | ud_flag = inb(flag_addr) & QUAD8_FLAG_UD; | |
820 | ||
821 | *direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD : | |
822 | COUNTER_COUNT_DIRECTION_BACKWARD; | |
823 | } | |
824 | ||
825 | enum quad8_synapse_action { | |
826 | QUAD8_SYNAPSE_ACTION_NONE = 0, | |
827 | QUAD8_SYNAPSE_ACTION_RISING_EDGE, | |
828 | QUAD8_SYNAPSE_ACTION_FALLING_EDGE, | |
829 | QUAD8_SYNAPSE_ACTION_BOTH_EDGES | |
830 | }; | |
831 | ||
832 | static enum counter_synapse_action quad8_index_actions_list[] = { | |
833 | [QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE, | |
834 | [QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE | |
835 | }; | |
836 | ||
837 | static enum counter_synapse_action quad8_synapse_actions_list[] = { | |
838 | [QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE, | |
839 | [QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE, | |
840 | [QUAD8_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE, | |
841 | [QUAD8_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES | |
842 | }; | |
843 | ||
844 | static int quad8_action_get(struct counter_device *counter, | |
845 | struct counter_count *count, struct counter_synapse *synapse, | |
846 | size_t *action) | |
847 | { | |
848 | struct quad8_iio *const priv = counter->priv; | |
849 | int err; | |
850 | size_t function = 0; | |
851 | const size_t signal_a_id = count->synapses[0].signal->id; | |
852 | enum counter_count_direction direction; | |
853 | ||
854 | /* Handle Index signals */ | |
855 | if (synapse->signal->id >= 16) { | |
856 | if (priv->preset_enable[count->id]) | |
857 | *action = QUAD8_SYNAPSE_ACTION_RISING_EDGE; | |
858 | else | |
859 | *action = QUAD8_SYNAPSE_ACTION_NONE; | |
860 | ||
861 | return 0; | |
862 | } | |
863 | ||
864 | err = quad8_function_get(counter, count, &function); | |
865 | if (err) | |
866 | return err; | |
867 | ||
868 | /* Default action mode */ | |
869 | *action = QUAD8_SYNAPSE_ACTION_NONE; | |
870 | ||
871 | /* Determine action mode based on current count function mode */ | |
872 | switch (function) { | |
873 | case QUAD8_COUNT_FUNCTION_PULSE_DIRECTION: | |
874 | if (synapse->signal->id == signal_a_id) | |
875 | *action = QUAD8_SYNAPSE_ACTION_RISING_EDGE; | |
876 | break; | |
877 | case QUAD8_COUNT_FUNCTION_QUADRATURE_X1: | |
878 | if (synapse->signal->id == signal_a_id) { | |
879 | quad8_direction_get(counter, count, &direction); | |
880 | ||
881 | if (direction == COUNTER_COUNT_DIRECTION_FORWARD) | |
882 | *action = QUAD8_SYNAPSE_ACTION_RISING_EDGE; | |
883 | else | |
884 | *action = QUAD8_SYNAPSE_ACTION_FALLING_EDGE; | |
885 | } | |
886 | break; | |
887 | case QUAD8_COUNT_FUNCTION_QUADRATURE_X2: | |
888 | if (synapse->signal->id == signal_a_id) | |
889 | *action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES; | |
890 | break; | |
891 | case QUAD8_COUNT_FUNCTION_QUADRATURE_X4: | |
892 | *action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES; | |
893 | break; | |
894 | } | |
895 | ||
896 | return 0; | |
897 | } | |
898 | ||
17aa207e | 899 | static const struct counter_ops quad8_ops = { |
f1d8a071 WBG |
900 | .signal_read = quad8_signal_read, |
901 | .count_read = quad8_count_read, | |
902 | .count_write = quad8_count_write, | |
903 | .function_get = quad8_function_get, | |
904 | .function_set = quad8_function_set, | |
905 | .action_get = quad8_action_get | |
906 | }; | |
907 | ||
908 | static int quad8_index_polarity_get(struct counter_device *counter, | |
909 | struct counter_signal *signal, size_t *index_polarity) | |
910 | { | |
911 | const struct quad8_iio *const priv = counter->priv; | |
912 | const size_t channel_id = signal->id - 16; | |
913 | ||
914 | *index_polarity = priv->index_polarity[channel_id]; | |
915 | ||
916 | return 0; | |
917 | } | |
918 | ||
919 | static int quad8_index_polarity_set(struct counter_device *counter, | |
920 | struct counter_signal *signal, size_t index_polarity) | |
921 | { | |
922 | struct quad8_iio *const priv = counter->priv; | |
923 | const size_t channel_id = signal->id - 16; | |
f1d8a071 | 924 | const int base_offset = priv->base + 2 * channel_id + 1; |
fc069262 SNW |
925 | unsigned int idr_cfg = index_polarity << 1; |
926 | ||
927 | mutex_lock(&priv->lock); | |
928 | ||
929 | idr_cfg |= priv->synchronous_mode[channel_id]; | |
f1d8a071 WBG |
930 | |
931 | priv->index_polarity[channel_id] = index_polarity; | |
932 | ||
933 | /* Load Index Control configuration to Index Control Register */ | |
934 | outb(QUAD8_CTR_IDR | idr_cfg, base_offset); | |
935 | ||
fc069262 SNW |
936 | mutex_unlock(&priv->lock); |
937 | ||
f1d8a071 WBG |
938 | return 0; |
939 | } | |
940 | ||
941 | static struct counter_signal_enum_ext quad8_index_pol_enum = { | |
942 | .items = quad8_index_polarity_modes, | |
943 | .num_items = ARRAY_SIZE(quad8_index_polarity_modes), | |
944 | .get = quad8_index_polarity_get, | |
945 | .set = quad8_index_polarity_set | |
946 | }; | |
947 | ||
948 | static int quad8_synchronous_mode_get(struct counter_device *counter, | |
949 | struct counter_signal *signal, size_t *synchronous_mode) | |
950 | { | |
951 | const struct quad8_iio *const priv = counter->priv; | |
952 | const size_t channel_id = signal->id - 16; | |
953 | ||
954 | *synchronous_mode = priv->synchronous_mode[channel_id]; | |
955 | ||
956 | return 0; | |
957 | } | |
958 | ||
959 | static int quad8_synchronous_mode_set(struct counter_device *counter, | |
960 | struct counter_signal *signal, size_t synchronous_mode) | |
961 | { | |
962 | struct quad8_iio *const priv = counter->priv; | |
963 | const size_t channel_id = signal->id - 16; | |
f1d8a071 | 964 | const int base_offset = priv->base + 2 * channel_id + 1; |
fc069262 SNW |
965 | unsigned int idr_cfg = synchronous_mode; |
966 | ||
967 | mutex_lock(&priv->lock); | |
968 | ||
969 | idr_cfg |= priv->index_polarity[channel_id] << 1; | |
f1d8a071 WBG |
970 | |
971 | /* Index function must be non-synchronous in non-quadrature mode */ | |
fc069262 SNW |
972 | if (synchronous_mode && !priv->quadrature_mode[channel_id]) { |
973 | mutex_unlock(&priv->lock); | |
f1d8a071 | 974 | return -EINVAL; |
fc069262 | 975 | } |
f1d8a071 WBG |
976 | |
977 | priv->synchronous_mode[channel_id] = synchronous_mode; | |
978 | ||
979 | /* Load Index Control configuration to Index Control Register */ | |
980 | outb(QUAD8_CTR_IDR | idr_cfg, base_offset); | |
981 | ||
fc069262 SNW |
982 | mutex_unlock(&priv->lock); |
983 | ||
f1d8a071 WBG |
984 | return 0; |
985 | } | |
986 | ||
987 | static struct counter_signal_enum_ext quad8_syn_mode_enum = { | |
988 | .items = quad8_synchronous_modes, | |
989 | .num_items = ARRAY_SIZE(quad8_synchronous_modes), | |
990 | .get = quad8_synchronous_mode_get, | |
991 | .set = quad8_synchronous_mode_set | |
992 | }; | |
993 | ||
994 | static ssize_t quad8_count_floor_read(struct counter_device *counter, | |
995 | struct counter_count *count, void *private, char *buf) | |
996 | { | |
997 | /* Only a floor of 0 is supported */ | |
998 | return sprintf(buf, "0\n"); | |
999 | } | |
1000 | ||
1001 | static int quad8_count_mode_get(struct counter_device *counter, | |
1002 | struct counter_count *count, size_t *cnt_mode) | |
1003 | { | |
1004 | const struct quad8_iio *const priv = counter->priv; | |
1005 | ||
1006 | /* Map 104-QUAD-8 count mode to Generic Counter count mode */ | |
1007 | switch (priv->count_mode[count->id]) { | |
1008 | case 0: | |
1009 | *cnt_mode = COUNTER_COUNT_MODE_NORMAL; | |
1010 | break; | |
1011 | case 1: | |
1012 | *cnt_mode = COUNTER_COUNT_MODE_RANGE_LIMIT; | |
1013 | break; | |
1014 | case 2: | |
1015 | *cnt_mode = COUNTER_COUNT_MODE_NON_RECYCLE; | |
1016 | break; | |
1017 | case 3: | |
1018 | *cnt_mode = COUNTER_COUNT_MODE_MODULO_N; | |
1019 | break; | |
1020 | } | |
1021 | ||
1022 | return 0; | |
1023 | } | |
1024 | ||
1025 | static int quad8_count_mode_set(struct counter_device *counter, | |
1026 | struct counter_count *count, size_t cnt_mode) | |
1027 | { | |
1028 | struct quad8_iio *const priv = counter->priv; | |
1029 | unsigned int mode_cfg; | |
1030 | const int base_offset = priv->base + 2 * count->id + 1; | |
1031 | ||
1032 | /* Map Generic Counter count mode to 104-QUAD-8 count mode */ | |
1033 | switch (cnt_mode) { | |
1034 | case COUNTER_COUNT_MODE_NORMAL: | |
1035 | cnt_mode = 0; | |
1036 | break; | |
1037 | case COUNTER_COUNT_MODE_RANGE_LIMIT: | |
1038 | cnt_mode = 1; | |
1039 | break; | |
1040 | case COUNTER_COUNT_MODE_NON_RECYCLE: | |
1041 | cnt_mode = 2; | |
1042 | break; | |
1043 | case COUNTER_COUNT_MODE_MODULO_N: | |
1044 | cnt_mode = 3; | |
1045 | break; | |
1046 | } | |
1047 | ||
fc069262 SNW |
1048 | mutex_lock(&priv->lock); |
1049 | ||
f1d8a071 WBG |
1050 | priv->count_mode[count->id] = cnt_mode; |
1051 | ||
1052 | /* Set count mode configuration value */ | |
1053 | mode_cfg = cnt_mode << 1; | |
1054 | ||
1055 | /* Add quadrature mode configuration */ | |
1056 | if (priv->quadrature_mode[count->id]) | |
1057 | mode_cfg |= (priv->quadrature_scale[count->id] + 1) << 3; | |
1058 | ||
1059 | /* Load mode configuration to Counter Mode Register */ | |
1060 | outb(QUAD8_CTR_CMR | mode_cfg, base_offset); | |
1061 | ||
fc069262 SNW |
1062 | mutex_unlock(&priv->lock); |
1063 | ||
f1d8a071 WBG |
1064 | return 0; |
1065 | } | |
1066 | ||
1067 | static struct counter_count_enum_ext quad8_cnt_mode_enum = { | |
1068 | .items = counter_count_mode_str, | |
1069 | .num_items = ARRAY_SIZE(counter_count_mode_str), | |
1070 | .get = quad8_count_mode_get, | |
1071 | .set = quad8_count_mode_set | |
1072 | }; | |
1073 | ||
1074 | static ssize_t quad8_count_direction_read(struct counter_device *counter, | |
1075 | struct counter_count *count, void *priv, char *buf) | |
1076 | { | |
1077 | enum counter_count_direction dir; | |
1078 | ||
1079 | quad8_direction_get(counter, count, &dir); | |
1080 | ||
1081 | return sprintf(buf, "%s\n", counter_count_direction_str[dir]); | |
1082 | } | |
1083 | ||
1084 | static ssize_t quad8_count_enable_read(struct counter_device *counter, | |
1085 | struct counter_count *count, void *private, char *buf) | |
1086 | { | |
1087 | const struct quad8_iio *const priv = counter->priv; | |
1088 | ||
1089 | return sprintf(buf, "%u\n", priv->ab_enable[count->id]); | |
1090 | } | |
1091 | ||
1092 | static ssize_t quad8_count_enable_write(struct counter_device *counter, | |
1093 | struct counter_count *count, void *private, const char *buf, size_t len) | |
1094 | { | |
1095 | struct quad8_iio *const priv = counter->priv; | |
1096 | const int base_offset = priv->base + 2 * count->id; | |
1097 | int err; | |
1098 | bool ab_enable; | |
1099 | unsigned int ior_cfg; | |
1100 | ||
1101 | err = kstrtobool(buf, &ab_enable); | |
1102 | if (err) | |
1103 | return err; | |
1104 | ||
fc069262 SNW |
1105 | mutex_lock(&priv->lock); |
1106 | ||
f1d8a071 WBG |
1107 | priv->ab_enable[count->id] = ab_enable; |
1108 | ||
1109 | ior_cfg = ab_enable | priv->preset_enable[count->id] << 1; | |
1110 | ||
1111 | /* Load I/O control configuration */ | |
1112 | outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1); | |
1113 | ||
fc069262 SNW |
1114 | mutex_unlock(&priv->lock); |
1115 | ||
f1d8a071 WBG |
1116 | return len; |
1117 | } | |
1118 | ||
1119 | static int quad8_error_noise_get(struct counter_device *counter, | |
1120 | struct counter_count *count, size_t *noise_error) | |
1121 | { | |
1122 | const struct quad8_iio *const priv = counter->priv; | |
1123 | const int base_offset = priv->base + 2 * count->id + 1; | |
1124 | ||
1125 | *noise_error = !!(inb(base_offset) & QUAD8_FLAG_E); | |
1126 | ||
1127 | return 0; | |
1128 | } | |
1129 | ||
1130 | static struct counter_count_enum_ext quad8_error_noise_enum = { | |
1131 | .items = quad8_noise_error_states, | |
1132 | .num_items = ARRAY_SIZE(quad8_noise_error_states), | |
1133 | .get = quad8_error_noise_get | |
1134 | }; | |
1135 | ||
1136 | static ssize_t quad8_count_preset_read(struct counter_device *counter, | |
1137 | struct counter_count *count, void *private, char *buf) | |
1138 | { | |
1139 | const struct quad8_iio *const priv = counter->priv; | |
1140 | ||
1141 | return sprintf(buf, "%u\n", priv->preset[count->id]); | |
1142 | } | |
1143 | ||
fc069262 SNW |
1144 | static void quad8_preset_register_set(struct quad8_iio *quad8iio, int id, |
1145 | unsigned int preset) | |
1146 | { | |
1147 | const unsigned int base_offset = quad8iio->base + 2 * id; | |
1148 | int i; | |
1149 | ||
1150 | quad8iio->preset[id] = preset; | |
1151 | ||
1152 | /* Reset Byte Pointer */ | |
1153 | outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); | |
1154 | ||
1155 | /* Set Preset Register */ | |
1156 | for (i = 0; i < 3; i++) | |
1157 | outb(preset >> (8 * i), base_offset); | |
1158 | } | |
1159 | ||
f1d8a071 WBG |
1160 | static ssize_t quad8_count_preset_write(struct counter_device *counter, |
1161 | struct counter_count *count, void *private, const char *buf, size_t len) | |
1162 | { | |
1163 | struct quad8_iio *const priv = counter->priv; | |
f1d8a071 WBG |
1164 | unsigned int preset; |
1165 | int ret; | |
f1d8a071 WBG |
1166 | |
1167 | ret = kstrtouint(buf, 0, &preset); | |
1168 | if (ret) | |
1169 | return ret; | |
1170 | ||
1171 | /* Only 24-bit values are supported */ | |
1172 | if (preset > 0xFFFFFF) | |
1173 | return -EINVAL; | |
1174 | ||
fc069262 | 1175 | mutex_lock(&priv->lock); |
f1d8a071 | 1176 | |
fc069262 | 1177 | quad8_preset_register_set(priv, count->id, preset); |
f1d8a071 | 1178 | |
fc069262 | 1179 | mutex_unlock(&priv->lock); |
f1d8a071 WBG |
1180 | |
1181 | return len; | |
1182 | } | |
1183 | ||
1184 | static ssize_t quad8_count_ceiling_read(struct counter_device *counter, | |
1185 | struct counter_count *count, void *private, char *buf) | |
1186 | { | |
fc069262 SNW |
1187 | struct quad8_iio *const priv = counter->priv; |
1188 | ||
1189 | mutex_lock(&priv->lock); | |
f1d8a071 WBG |
1190 | |
1191 | /* Range Limit and Modulo-N count modes use preset value as ceiling */ | |
1192 | switch (priv->count_mode[count->id]) { | |
1193 | case 1: | |
1194 | case 3: | |
fc069262 SNW |
1195 | mutex_unlock(&priv->lock); |
1196 | return sprintf(buf, "%u\n", priv->preset[count->id]); | |
f1d8a071 WBG |
1197 | } |
1198 | ||
fc069262 SNW |
1199 | mutex_unlock(&priv->lock); |
1200 | ||
f1d8a071 WBG |
1201 | /* By default 0x1FFFFFF (25 bits unsigned) is maximum count */ |
1202 | return sprintf(buf, "33554431\n"); | |
1203 | } | |
1204 | ||
1205 | static ssize_t quad8_count_ceiling_write(struct counter_device *counter, | |
1206 | struct counter_count *count, void *private, const char *buf, size_t len) | |
1207 | { | |
1208 | struct quad8_iio *const priv = counter->priv; | |
fc069262 SNW |
1209 | unsigned int ceiling; |
1210 | int ret; | |
1211 | ||
1212 | ret = kstrtouint(buf, 0, &ceiling); | |
1213 | if (ret) | |
1214 | return ret; | |
1215 | ||
1216 | /* Only 24-bit values are supported */ | |
1217 | if (ceiling > 0xFFFFFF) | |
1218 | return -EINVAL; | |
1219 | ||
1220 | mutex_lock(&priv->lock); | |
f1d8a071 WBG |
1221 | |
1222 | /* Range Limit and Modulo-N count modes use preset value as ceiling */ | |
1223 | switch (priv->count_mode[count->id]) { | |
1224 | case 1: | |
1225 | case 3: | |
fc069262 SNW |
1226 | quad8_preset_register_set(priv, count->id, ceiling); |
1227 | break; | |
f1d8a071 WBG |
1228 | } |
1229 | ||
fc069262 SNW |
1230 | mutex_unlock(&priv->lock); |
1231 | ||
f1d8a071 WBG |
1232 | return len; |
1233 | } | |
1234 | ||
1235 | static ssize_t quad8_count_preset_enable_read(struct counter_device *counter, | |
1236 | struct counter_count *count, void *private, char *buf) | |
1237 | { | |
1238 | const struct quad8_iio *const priv = counter->priv; | |
1239 | ||
1240 | return sprintf(buf, "%u\n", !priv->preset_enable[count->id]); | |
1241 | } | |
1242 | ||
1243 | static ssize_t quad8_count_preset_enable_write(struct counter_device *counter, | |
1244 | struct counter_count *count, void *private, const char *buf, size_t len) | |
1245 | { | |
1246 | struct quad8_iio *const priv = counter->priv; | |
1247 | const int base_offset = priv->base + 2 * count->id + 1; | |
1248 | bool preset_enable; | |
1249 | int ret; | |
1250 | unsigned int ior_cfg; | |
1251 | ||
1252 | ret = kstrtobool(buf, &preset_enable); | |
1253 | if (ret) | |
1254 | return ret; | |
1255 | ||
1256 | /* Preset enable is active low in Input/Output Control register */ | |
1257 | preset_enable = !preset_enable; | |
1258 | ||
fc069262 SNW |
1259 | mutex_lock(&priv->lock); |
1260 | ||
f1d8a071 WBG |
1261 | priv->preset_enable[count->id] = preset_enable; |
1262 | ||
1263 | ior_cfg = priv->ab_enable[count->id] | (unsigned int)preset_enable << 1; | |
1264 | ||
1265 | /* Load I/O control configuration to Input / Output Control Register */ | |
1266 | outb(QUAD8_CTR_IOR | ior_cfg, base_offset); | |
1267 | ||
fc069262 SNW |
1268 | mutex_unlock(&priv->lock); |
1269 | ||
f1d8a071 WBG |
1270 | return len; |
1271 | } | |
1272 | ||
954ab5cc WBG |
1273 | static ssize_t quad8_signal_cable_fault_read(struct counter_device *counter, |
1274 | struct counter_signal *signal, | |
1275 | void *private, char *buf) | |
1276 | { | |
708d9893 | 1277 | struct quad8_iio *const priv = counter->priv; |
954ab5cc | 1278 | const size_t channel_id = signal->id / 2; |
708d9893 | 1279 | bool disabled; |
954ab5cc WBG |
1280 | unsigned int status; |
1281 | unsigned int fault; | |
1282 | ||
708d9893 SNW |
1283 | mutex_lock(&priv->lock); |
1284 | ||
1285 | disabled = !(priv->cable_fault_enable & BIT(channel_id)); | |
1286 | ||
1287 | if (disabled) { | |
1288 | mutex_unlock(&priv->lock); | |
954ab5cc | 1289 | return -EINVAL; |
708d9893 | 1290 | } |
954ab5cc WBG |
1291 | |
1292 | /* Logic 0 = cable fault */ | |
1293 | status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS); | |
1294 | ||
708d9893 SNW |
1295 | mutex_unlock(&priv->lock); |
1296 | ||
954ab5cc WBG |
1297 | /* Mask respective channel and invert logic */ |
1298 | fault = !(status & BIT(channel_id)); | |
1299 | ||
1300 | return sprintf(buf, "%u\n", fault); | |
1301 | } | |
1302 | ||
1303 | static ssize_t quad8_signal_cable_fault_enable_read( | |
1304 | struct counter_device *counter, struct counter_signal *signal, | |
1305 | void *private, char *buf) | |
1306 | { | |
1307 | const struct quad8_iio *const priv = counter->priv; | |
1308 | const size_t channel_id = signal->id / 2; | |
1309 | const unsigned int enb = !!(priv->cable_fault_enable & BIT(channel_id)); | |
1310 | ||
1311 | return sprintf(buf, "%u\n", enb); | |
1312 | } | |
1313 | ||
1314 | static ssize_t quad8_signal_cable_fault_enable_write( | |
1315 | struct counter_device *counter, struct counter_signal *signal, | |
1316 | void *private, const char *buf, size_t len) | |
1317 | { | |
1318 | struct quad8_iio *const priv = counter->priv; | |
1319 | const size_t channel_id = signal->id / 2; | |
1320 | bool enable; | |
1321 | int ret; | |
1322 | unsigned int cable_fault_enable; | |
1323 | ||
1324 | ret = kstrtobool(buf, &enable); | |
1325 | if (ret) | |
1326 | return ret; | |
1327 | ||
708d9893 SNW |
1328 | mutex_lock(&priv->lock); |
1329 | ||
954ab5cc WBG |
1330 | if (enable) |
1331 | priv->cable_fault_enable |= BIT(channel_id); | |
1332 | else | |
1333 | priv->cable_fault_enable &= ~BIT(channel_id); | |
1334 | ||
1335 | /* Enable is active low in Differential Encoder Cable Status register */ | |
1336 | cable_fault_enable = ~priv->cable_fault_enable; | |
1337 | ||
1338 | outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS); | |
1339 | ||
708d9893 SNW |
1340 | mutex_unlock(&priv->lock); |
1341 | ||
954ab5cc WBG |
1342 | return len; |
1343 | } | |
1344 | ||
de65d055 WBG |
1345 | static ssize_t quad8_signal_fck_prescaler_read(struct counter_device *counter, |
1346 | struct counter_signal *signal, void *private, char *buf) | |
1347 | { | |
1348 | const struct quad8_iio *const priv = counter->priv; | |
1349 | const size_t channel_id = signal->id / 2; | |
1350 | ||
1351 | return sprintf(buf, "%u\n", priv->fck_prescaler[channel_id]); | |
1352 | } | |
1353 | ||
1354 | static ssize_t quad8_signal_fck_prescaler_write(struct counter_device *counter, | |
1355 | struct counter_signal *signal, void *private, const char *buf, | |
1356 | size_t len) | |
1357 | { | |
1358 | struct quad8_iio *const priv = counter->priv; | |
1359 | const size_t channel_id = signal->id / 2; | |
1360 | const int base_offset = priv->base + 2 * channel_id; | |
1361 | u8 prescaler; | |
1362 | int ret; | |
1363 | ||
1364 | ret = kstrtou8(buf, 0, &prescaler); | |
1365 | if (ret) | |
1366 | return ret; | |
1367 | ||
d5ed76ad SNW |
1368 | mutex_lock(&priv->lock); |
1369 | ||
de65d055 WBG |
1370 | priv->fck_prescaler[channel_id] = prescaler; |
1371 | ||
1372 | /* Reset Byte Pointer */ | |
1373 | outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); | |
1374 | ||
1375 | /* Set filter clock factor */ | |
1376 | outb(prescaler, base_offset); | |
1377 | outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC, | |
1378 | base_offset + 1); | |
1379 | ||
d5ed76ad SNW |
1380 | mutex_unlock(&priv->lock); |
1381 | ||
de65d055 WBG |
1382 | return len; |
1383 | } | |
1384 | ||
1385 | static const struct counter_signal_ext quad8_signal_ext[] = { | |
954ab5cc WBG |
1386 | { |
1387 | .name = "cable_fault", | |
1388 | .read = quad8_signal_cable_fault_read | |
1389 | }, | |
1390 | { | |
1391 | .name = "cable_fault_enable", | |
1392 | .read = quad8_signal_cable_fault_enable_read, | |
1393 | .write = quad8_signal_cable_fault_enable_write | |
1394 | }, | |
de65d055 WBG |
1395 | { |
1396 | .name = "filter_clock_prescaler", | |
1397 | .read = quad8_signal_fck_prescaler_read, | |
1398 | .write = quad8_signal_fck_prescaler_write | |
1399 | } | |
1400 | }; | |
1401 | ||
f1d8a071 WBG |
1402 | static const struct counter_signal_ext quad8_index_ext[] = { |
1403 | COUNTER_SIGNAL_ENUM("index_polarity", &quad8_index_pol_enum), | |
1404 | COUNTER_SIGNAL_ENUM_AVAILABLE("index_polarity", &quad8_index_pol_enum), | |
1405 | COUNTER_SIGNAL_ENUM("synchronous_mode", &quad8_syn_mode_enum), | |
1406 | COUNTER_SIGNAL_ENUM_AVAILABLE("synchronous_mode", &quad8_syn_mode_enum) | |
1407 | }; | |
1408 | ||
de65d055 WBG |
1409 | #define QUAD8_QUAD_SIGNAL(_id, _name) { \ |
1410 | .id = (_id), \ | |
1411 | .name = (_name), \ | |
1412 | .ext = quad8_signal_ext, \ | |
1413 | .num_ext = ARRAY_SIZE(quad8_signal_ext) \ | |
f1d8a071 WBG |
1414 | } |
1415 | ||
1416 | #define QUAD8_INDEX_SIGNAL(_id, _name) { \ | |
1417 | .id = (_id), \ | |
1418 | .name = (_name), \ | |
1419 | .ext = quad8_index_ext, \ | |
1420 | .num_ext = ARRAY_SIZE(quad8_index_ext) \ | |
1421 | } | |
1422 | ||
1423 | static struct counter_signal quad8_signals[] = { | |
1424 | QUAD8_QUAD_SIGNAL(0, "Channel 1 Quadrature A"), | |
1425 | QUAD8_QUAD_SIGNAL(1, "Channel 1 Quadrature B"), | |
1426 | QUAD8_QUAD_SIGNAL(2, "Channel 2 Quadrature A"), | |
1427 | QUAD8_QUAD_SIGNAL(3, "Channel 2 Quadrature B"), | |
1428 | QUAD8_QUAD_SIGNAL(4, "Channel 3 Quadrature A"), | |
1429 | QUAD8_QUAD_SIGNAL(5, "Channel 3 Quadrature B"), | |
1430 | QUAD8_QUAD_SIGNAL(6, "Channel 4 Quadrature A"), | |
1431 | QUAD8_QUAD_SIGNAL(7, "Channel 4 Quadrature B"), | |
1432 | QUAD8_QUAD_SIGNAL(8, "Channel 5 Quadrature A"), | |
1433 | QUAD8_QUAD_SIGNAL(9, "Channel 5 Quadrature B"), | |
1434 | QUAD8_QUAD_SIGNAL(10, "Channel 6 Quadrature A"), | |
1435 | QUAD8_QUAD_SIGNAL(11, "Channel 6 Quadrature B"), | |
1436 | QUAD8_QUAD_SIGNAL(12, "Channel 7 Quadrature A"), | |
1437 | QUAD8_QUAD_SIGNAL(13, "Channel 7 Quadrature B"), | |
1438 | QUAD8_QUAD_SIGNAL(14, "Channel 8 Quadrature A"), | |
1439 | QUAD8_QUAD_SIGNAL(15, "Channel 8 Quadrature B"), | |
1440 | QUAD8_INDEX_SIGNAL(16, "Channel 1 Index"), | |
1441 | QUAD8_INDEX_SIGNAL(17, "Channel 2 Index"), | |
1442 | QUAD8_INDEX_SIGNAL(18, "Channel 3 Index"), | |
1443 | QUAD8_INDEX_SIGNAL(19, "Channel 4 Index"), | |
1444 | QUAD8_INDEX_SIGNAL(20, "Channel 5 Index"), | |
1445 | QUAD8_INDEX_SIGNAL(21, "Channel 6 Index"), | |
1446 | QUAD8_INDEX_SIGNAL(22, "Channel 7 Index"), | |
1447 | QUAD8_INDEX_SIGNAL(23, "Channel 8 Index") | |
1448 | }; | |
1449 | ||
1450 | #define QUAD8_COUNT_SYNAPSES(_id) { \ | |
1451 | { \ | |
1452 | .actions_list = quad8_synapse_actions_list, \ | |
1453 | .num_actions = ARRAY_SIZE(quad8_synapse_actions_list), \ | |
1454 | .signal = quad8_signals + 2 * (_id) \ | |
1455 | }, \ | |
1456 | { \ | |
1457 | .actions_list = quad8_synapse_actions_list, \ | |
1458 | .num_actions = ARRAY_SIZE(quad8_synapse_actions_list), \ | |
1459 | .signal = quad8_signals + 2 * (_id) + 1 \ | |
1460 | }, \ | |
1461 | { \ | |
1462 | .actions_list = quad8_index_actions_list, \ | |
1463 | .num_actions = ARRAY_SIZE(quad8_index_actions_list), \ | |
1464 | .signal = quad8_signals + 2 * (_id) + 16 \ | |
1465 | } \ | |
1466 | } | |
1467 | ||
1468 | static struct counter_synapse quad8_count_synapses[][3] = { | |
1469 | QUAD8_COUNT_SYNAPSES(0), QUAD8_COUNT_SYNAPSES(1), | |
1470 | QUAD8_COUNT_SYNAPSES(2), QUAD8_COUNT_SYNAPSES(3), | |
1471 | QUAD8_COUNT_SYNAPSES(4), QUAD8_COUNT_SYNAPSES(5), | |
1472 | QUAD8_COUNT_SYNAPSES(6), QUAD8_COUNT_SYNAPSES(7) | |
1473 | }; | |
1474 | ||
1475 | static const struct counter_count_ext quad8_count_ext[] = { | |
1476 | { | |
1477 | .name = "ceiling", | |
1478 | .read = quad8_count_ceiling_read, | |
1479 | .write = quad8_count_ceiling_write | |
1480 | }, | |
1481 | { | |
1482 | .name = "floor", | |
1483 | .read = quad8_count_floor_read | |
1484 | }, | |
1485 | COUNTER_COUNT_ENUM("count_mode", &quad8_cnt_mode_enum), | |
1486 | COUNTER_COUNT_ENUM_AVAILABLE("count_mode", &quad8_cnt_mode_enum), | |
1487 | { | |
1488 | .name = "direction", | |
1489 | .read = quad8_count_direction_read | |
1490 | }, | |
1491 | { | |
1492 | .name = "enable", | |
1493 | .read = quad8_count_enable_read, | |
1494 | .write = quad8_count_enable_write | |
1495 | }, | |
1496 | COUNTER_COUNT_ENUM("error_noise", &quad8_error_noise_enum), | |
1497 | COUNTER_COUNT_ENUM_AVAILABLE("error_noise", &quad8_error_noise_enum), | |
1498 | { | |
1499 | .name = "preset", | |
1500 | .read = quad8_count_preset_read, | |
1501 | .write = quad8_count_preset_write | |
1502 | }, | |
1503 | { | |
1504 | .name = "preset_enable", | |
1505 | .read = quad8_count_preset_enable_read, | |
1506 | .write = quad8_count_preset_enable_write | |
1507 | } | |
1508 | }; | |
1509 | ||
1510 | #define QUAD8_COUNT(_id, _cntname) { \ | |
1511 | .id = (_id), \ | |
1512 | .name = (_cntname), \ | |
1513 | .functions_list = quad8_count_functions_list, \ | |
1514 | .num_functions = ARRAY_SIZE(quad8_count_functions_list), \ | |
1515 | .synapses = quad8_count_synapses[(_id)], \ | |
1516 | .num_synapses = 2, \ | |
1517 | .ext = quad8_count_ext, \ | |
1518 | .num_ext = ARRAY_SIZE(quad8_count_ext) \ | |
1519 | } | |
1520 | ||
1521 | static struct counter_count quad8_counts[] = { | |
1522 | QUAD8_COUNT(0, "Channel 1 Count"), | |
1523 | QUAD8_COUNT(1, "Channel 2 Count"), | |
1524 | QUAD8_COUNT(2, "Channel 3 Count"), | |
1525 | QUAD8_COUNT(3, "Channel 4 Count"), | |
1526 | QUAD8_COUNT(4, "Channel 5 Count"), | |
1527 | QUAD8_COUNT(5, "Channel 6 Count"), | |
1528 | QUAD8_COUNT(6, "Channel 7 Count"), | |
1529 | QUAD8_COUNT(7, "Channel 8 Count") | |
1530 | }; | |
1531 | ||
1532 | static int quad8_probe(struct device *dev, unsigned int id) | |
1533 | { | |
1534 | struct iio_dev *indio_dev; | |
1535 | struct quad8_iio *quad8iio; | |
1536 | int i, j; | |
1537 | unsigned int base_offset; | |
1538 | int err; | |
1539 | ||
1540 | if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) { | |
1541 | dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", | |
1542 | base[id], base[id] + QUAD8_EXTENT); | |
1543 | return -EBUSY; | |
1544 | } | |
1545 | ||
1546 | /* Allocate IIO device; this also allocates driver data structure */ | |
1547 | indio_dev = devm_iio_device_alloc(dev, sizeof(*quad8iio)); | |
1548 | if (!indio_dev) | |
1549 | return -ENOMEM; | |
1550 | ||
1551 | /* Initialize IIO device */ | |
1552 | indio_dev->info = &quad8_info; | |
1553 | indio_dev->modes = INDIO_DIRECT_MODE; | |
1554 | indio_dev->num_channels = ARRAY_SIZE(quad8_channels); | |
1555 | indio_dev->channels = quad8_channels; | |
1556 | indio_dev->name = dev_name(dev); | |
f1d8a071 WBG |
1557 | |
1558 | /* Initialize Counter device and driver data */ | |
1559 | quad8iio = iio_priv(indio_dev); | |
1560 | quad8iio->counter.name = dev_name(dev); | |
1561 | quad8iio->counter.parent = dev; | |
1562 | quad8iio->counter.ops = &quad8_ops; | |
1563 | quad8iio->counter.counts = quad8_counts; | |
1564 | quad8iio->counter.num_counts = ARRAY_SIZE(quad8_counts); | |
1565 | quad8iio->counter.signals = quad8_signals; | |
1566 | quad8iio->counter.num_signals = ARRAY_SIZE(quad8_signals); | |
1567 | quad8iio->counter.priv = quad8iio; | |
1568 | quad8iio->base = base[id]; | |
1569 | ||
fc069262 SNW |
1570 | /* Initialize mutex */ |
1571 | mutex_init(&quad8iio->lock); | |
1572 | ||
f1d8a071 WBG |
1573 | /* Reset all counters and disable interrupt function */ |
1574 | outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP); | |
1575 | /* Set initial configuration for all counters */ | |
1576 | for (i = 0; i < QUAD8_NUM_COUNTERS; i++) { | |
1577 | base_offset = base[id] + 2 * i; | |
1578 | /* Reset Byte Pointer */ | |
1579 | outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); | |
de65d055 WBG |
1580 | /* Reset filter clock factor */ |
1581 | outb(0, base_offset); | |
1582 | outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC, | |
1583 | base_offset + 1); | |
1584 | /* Reset Byte Pointer */ | |
1585 | outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); | |
f1d8a071 WBG |
1586 | /* Reset Preset Register */ |
1587 | for (j = 0; j < 3; j++) | |
1588 | outb(0x00, base_offset); | |
1589 | /* Reset Borrow, Carry, Compare, and Sign flags */ | |
1590 | outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1); | |
1591 | /* Reset Error flag */ | |
1592 | outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1); | |
1593 | /* Binary encoding; Normal count; non-quadrature mode */ | |
1594 | outb(QUAD8_CTR_CMR, base_offset + 1); | |
1595 | /* Disable A and B inputs; preset on index; FLG1 as Carry */ | |
1596 | outb(QUAD8_CTR_IOR, base_offset + 1); | |
1597 | /* Disable index function; negative index polarity */ | |
1598 | outb(QUAD8_CTR_IDR, base_offset + 1); | |
1599 | } | |
954ab5cc WBG |
1600 | /* Disable Differential Encoder Cable Status for all channels */ |
1601 | outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS); | |
f1d8a071 WBG |
1602 | /* Enable all counters */ |
1603 | outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP); | |
1604 | ||
1605 | /* Register IIO device */ | |
1606 | err = devm_iio_device_register(dev, indio_dev); | |
1607 | if (err) | |
1608 | return err; | |
1609 | ||
1610 | /* Register Counter device */ | |
1611 | return devm_counter_register(dev, &quad8iio->counter); | |
1612 | } | |
1613 | ||
1614 | static struct isa_driver quad8_driver = { | |
1615 | .probe = quad8_probe, | |
1616 | .driver = { | |
1617 | .name = "104-quad-8" | |
1618 | } | |
1619 | }; | |
1620 | ||
1621 | module_isa_driver(quad8_driver, num_quad8); | |
1622 | ||
1623 | MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); | |
1624 | MODULE_DESCRIPTION("ACCES 104-QUAD-8 IIO driver"); | |
1625 | MODULE_LICENSE("GPL v2"); |