• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/io.h>
13 #include <linux/ioport.h>
14 #include <linux/isa.h>
15 #include <linux/kernel.h>
16 #include <linux/module.h>
17 #include <linux/moduleparam.h>
18 #include <linux/types.h>
19 
20 #define QUAD8_EXTENT 32
21 
22 static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)];
23 static unsigned int num_quad8;
24 module_param_hw_array(base, uint, ioport, &num_quad8, 0);
25 MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
26 
27 #define QUAD8_NUM_COUNTERS 8
28 
29 /**
30  * struct quad8 - device private data structure
31  * @lock:		lock to prevent clobbering device states during R/W ops
32  * @counter:		instance of the counter_device
33  * @fck_prescaler:	array of filter clock prescaler configurations
34  * @preset:		array of preset values
35  * @count_mode:		array of count mode configurations
36  * @quadrature_mode:	array of quadrature mode configurations
37  * @quadrature_scale:	array of quadrature mode scale configurations
38  * @ab_enable:		array of A and B inputs enable configurations
39  * @preset_enable:	array of set_to_preset_on_index attribute configurations
40  * @synchronous_mode:	array of index function synchronous mode configurations
41  * @index_polarity:	array of index function polarity configurations
42  * @cable_fault_enable:	differential encoder cable status enable configurations
43  * @base:		base port address of the device
44  */
45 struct quad8 {
46 	struct mutex lock;
47 	struct counter_device counter;
48 	unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
49 	unsigned int preset[QUAD8_NUM_COUNTERS];
50 	unsigned int count_mode[QUAD8_NUM_COUNTERS];
51 	unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
52 	unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
53 	unsigned int ab_enable[QUAD8_NUM_COUNTERS];
54 	unsigned int preset_enable[QUAD8_NUM_COUNTERS];
55 	unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
56 	unsigned int index_polarity[QUAD8_NUM_COUNTERS];
57 	unsigned int cable_fault_enable;
58 	unsigned int base;
59 };
60 
61 #define QUAD8_REG_CHAN_OP 0x11
62 #define QUAD8_REG_INDEX_INPUT_LEVELS 0x16
63 #define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17
64 /* Error flag */
65 #define QUAD8_FLAG_E BIT(4)
66 /* Up/Down flag */
67 #define QUAD8_FLAG_UD BIT(5)
68 /* Reset and Load Signal Decoders */
69 #define QUAD8_CTR_RLD 0x00
70 /* Counter Mode Register */
71 #define QUAD8_CTR_CMR 0x20
72 /* Input / Output Control Register */
73 #define QUAD8_CTR_IOR 0x40
74 /* Index Control Register */
75 #define QUAD8_CTR_IDR 0x60
76 /* Reset Byte Pointer (three byte data pointer) */
77 #define QUAD8_RLD_RESET_BP 0x01
78 /* Reset Counter */
79 #define QUAD8_RLD_RESET_CNTR 0x02
80 /* Reset Borrow Toggle, Carry Toggle, Compare Toggle, and Sign flags */
81 #define QUAD8_RLD_RESET_FLAGS 0x04
82 /* Reset Error flag */
83 #define QUAD8_RLD_RESET_E 0x06
84 /* Preset Register to Counter */
85 #define QUAD8_RLD_PRESET_CNTR 0x08
86 /* Transfer Counter to Output Latch */
87 #define QUAD8_RLD_CNTR_OUT 0x10
88 /* Transfer Preset Register LSB to FCK Prescaler */
89 #define QUAD8_RLD_PRESET_PSC 0x18
90 #define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00
91 #define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
92 #define QUAD8_CMR_QUADRATURE_X1 0x08
93 #define QUAD8_CMR_QUADRATURE_X2 0x10
94 #define QUAD8_CMR_QUADRATURE_X4 0x18
95 
96 /* Each Counter is 24 bits wide */
97 #define LS7267_CNTR_MAX GENMASK(23, 0)
98 
quad8_signal_read(struct counter_device * counter,struct counter_signal * signal,enum counter_signal_level * level)99 static int quad8_signal_read(struct counter_device *counter,
100 			     struct counter_signal *signal,
101 			     enum counter_signal_level *level)
102 {
103 	const struct quad8 *const priv = counter->priv;
104 	unsigned int state;
105 
106 	/* Only Index signal levels can be read */
107 	if (signal->id < 16)
108 		return -EINVAL;
109 
110 	state = inb(priv->base + QUAD8_REG_INDEX_INPUT_LEVELS)
111 		& BIT(signal->id - 16);
112 
113 	*level = (state) ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
114 
115 	return 0;
116 }
117 
quad8_count_read(struct counter_device * counter,struct counter_count * count,u64 * val)118 static int quad8_count_read(struct counter_device *counter,
119 			    struct counter_count *count, u64 *val)
120 {
121 	struct quad8 *const priv = counter->priv;
122 	const int base_offset = priv->base + 2 * count->id;
123 	int i;
124 
125 	*val = 0;
126 
127 	mutex_lock(&priv->lock);
128 
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 long)inb(base_offset) << (8 * i);
135 
136 	mutex_unlock(&priv->lock);
137 
138 	return 0;
139 }
140 
quad8_count_write(struct counter_device * counter,struct counter_count * count,u64 val)141 static int quad8_count_write(struct counter_device *counter,
142 			     struct counter_count *count, u64 val)
143 {
144 	struct quad8 *const priv = counter->priv;
145 	const int base_offset = priv->base + 2 * count->id;
146 	int i;
147 
148 	if (val > LS7267_CNTR_MAX)
149 		return -ERANGE;
150 
151 	mutex_lock(&priv->lock);
152 
153 	/* Reset Byte Pointer */
154 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
155 
156 	/* Counter can only be set via Preset Register */
157 	for (i = 0; i < 3; i++)
158 		outb(val >> (8 * i), base_offset);
159 
160 	/* Transfer Preset Register to Counter */
161 	outb(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, base_offset + 1);
162 
163 	/* Reset Byte Pointer */
164 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
165 
166 	/* Set Preset Register back to original value */
167 	val = priv->preset[count->id];
168 	for (i = 0; i < 3; i++)
169 		outb(val >> (8 * i), base_offset);
170 
171 	/* Reset Borrow, Carry, Compare, and Sign flags */
172 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
173 	/* Reset Error flag */
174 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
175 
176 	mutex_unlock(&priv->lock);
177 
178 	return 0;
179 }
180 
181 static const enum counter_function quad8_count_functions_list[] = {
182 	COUNTER_FUNCTION_PULSE_DIRECTION,
183 	COUNTER_FUNCTION_QUADRATURE_X1_A,
184 	COUNTER_FUNCTION_QUADRATURE_X2_A,
185 	COUNTER_FUNCTION_QUADRATURE_X4,
186 };
187 
quad8_function_read(struct counter_device * counter,struct counter_count * count,enum counter_function * function)188 static int quad8_function_read(struct counter_device *counter,
189 			       struct counter_count *count,
190 			       enum counter_function *function)
191 {
192 	struct quad8 *const priv = counter->priv;
193 	const int id = count->id;
194 
195 	mutex_lock(&priv->lock);
196 
197 	if (priv->quadrature_mode[id])
198 		switch (priv->quadrature_scale[id]) {
199 		case 0:
200 			*function = COUNTER_FUNCTION_QUADRATURE_X1_A;
201 			break;
202 		case 1:
203 			*function = COUNTER_FUNCTION_QUADRATURE_X2_A;
204 			break;
205 		case 2:
206 			*function = COUNTER_FUNCTION_QUADRATURE_X4;
207 			break;
208 		}
209 	else
210 		*function = COUNTER_FUNCTION_PULSE_DIRECTION;
211 
212 	mutex_unlock(&priv->lock);
213 
214 	return 0;
215 }
216 
quad8_function_write(struct counter_device * counter,struct counter_count * count,enum counter_function function)217 static int quad8_function_write(struct counter_device *counter,
218 				struct counter_count *count,
219 				enum counter_function function)
220 {
221 	struct quad8 *const priv = counter->priv;
222 	const int id = count->id;
223 	unsigned int *const quadrature_mode = priv->quadrature_mode + id;
224 	unsigned int *const scale = priv->quadrature_scale + id;
225 	unsigned int *const synchronous_mode = priv->synchronous_mode + id;
226 	const int base_offset = priv->base + 2 * id + 1;
227 	unsigned int mode_cfg;
228 	unsigned int idr_cfg;
229 
230 	mutex_lock(&priv->lock);
231 
232 	mode_cfg = priv->count_mode[id] << 1;
233 	idr_cfg = priv->index_polarity[id] << 1;
234 
235 	if (function == COUNTER_FUNCTION_PULSE_DIRECTION) {
236 		*quadrature_mode = 0;
237 
238 		/* Quadrature scaling only available in quadrature mode */
239 		*scale = 0;
240 
241 		/* Synchronous function not supported in non-quadrature mode */
242 		if (*synchronous_mode) {
243 			*synchronous_mode = 0;
244 			/* Disable synchronous function mode */
245 			outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
246 		}
247 	} else {
248 		*quadrature_mode = 1;
249 
250 		switch (function) {
251 		case COUNTER_FUNCTION_QUADRATURE_X1_A:
252 			*scale = 0;
253 			mode_cfg |= QUAD8_CMR_QUADRATURE_X1;
254 			break;
255 		case COUNTER_FUNCTION_QUADRATURE_X2_A:
256 			*scale = 1;
257 			mode_cfg |= QUAD8_CMR_QUADRATURE_X2;
258 			break;
259 		case COUNTER_FUNCTION_QUADRATURE_X4:
260 			*scale = 2;
261 			mode_cfg |= QUAD8_CMR_QUADRATURE_X4;
262 			break;
263 		default:
264 			/* should never reach this path */
265 			mutex_unlock(&priv->lock);
266 			return -EINVAL;
267 		}
268 	}
269 
270 	/* Load mode configuration to Counter Mode Register */
271 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
272 
273 	mutex_unlock(&priv->lock);
274 
275 	return 0;
276 }
277 
quad8_direction_read(struct counter_device * counter,struct counter_count * count,enum counter_count_direction * direction)278 static int quad8_direction_read(struct counter_device *counter,
279 				struct counter_count *count,
280 				enum counter_count_direction *direction)
281 {
282 	const struct quad8 *const priv = counter->priv;
283 	unsigned int ud_flag;
284 	const unsigned int flag_addr = priv->base + 2 * count->id + 1;
285 
286 	/* U/D flag: nonzero = up, zero = down */
287 	ud_flag = inb(flag_addr) & QUAD8_FLAG_UD;
288 
289 	*direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD :
290 		COUNTER_COUNT_DIRECTION_BACKWARD;
291 
292 	return 0;
293 }
294 
295 static const enum counter_synapse_action quad8_index_actions_list[] = {
296 	COUNTER_SYNAPSE_ACTION_NONE,
297 	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
298 };
299 
300 static const enum counter_synapse_action quad8_synapse_actions_list[] = {
301 	COUNTER_SYNAPSE_ACTION_NONE,
302 	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
303 	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
304 	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
305 };
306 
quad8_action_read(struct counter_device * counter,struct counter_count * count,struct counter_synapse * synapse,enum counter_synapse_action * action)307 static int quad8_action_read(struct counter_device *counter,
308 			     struct counter_count *count,
309 			     struct counter_synapse *synapse,
310 			     enum counter_synapse_action *action)
311 {
312 	struct quad8 *const priv = counter->priv;
313 	int err;
314 	enum counter_function function;
315 	const size_t signal_a_id = count->synapses[0].signal->id;
316 	enum counter_count_direction direction;
317 
318 	/* Handle Index signals */
319 	if (synapse->signal->id >= 16) {
320 		if (!priv->preset_enable[count->id])
321 			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
322 		else
323 			*action = COUNTER_SYNAPSE_ACTION_NONE;
324 
325 		return 0;
326 	}
327 
328 	err = quad8_function_read(counter, count, &function);
329 	if (err)
330 		return err;
331 
332 	/* Default action mode */
333 	*action = COUNTER_SYNAPSE_ACTION_NONE;
334 
335 	/* Determine action mode based on current count function mode */
336 	switch (function) {
337 	case COUNTER_FUNCTION_PULSE_DIRECTION:
338 		if (synapse->signal->id == signal_a_id)
339 			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
340 		return 0;
341 	case COUNTER_FUNCTION_QUADRATURE_X1_A:
342 		if (synapse->signal->id == signal_a_id) {
343 			err = quad8_direction_read(counter, count, &direction);
344 			if (err)
345 				return err;
346 
347 			if (direction == COUNTER_COUNT_DIRECTION_FORWARD)
348 				*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
349 			else
350 				*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
351 		}
352 		return 0;
353 	case COUNTER_FUNCTION_QUADRATURE_X2_A:
354 		if (synapse->signal->id == signal_a_id)
355 			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
356 		return 0;
357 	case COUNTER_FUNCTION_QUADRATURE_X4:
358 		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
359 		return 0;
360 	default:
361 		/* should never reach this path */
362 		return -EINVAL;
363 	}
364 }
365 
366 static const struct counter_ops quad8_ops = {
367 	.signal_read = quad8_signal_read,
368 	.count_read = quad8_count_read,
369 	.count_write = quad8_count_write,
370 	.function_read = quad8_function_read,
371 	.function_write = quad8_function_write,
372 	.action_read = quad8_action_read
373 };
374 
375 static const char *const quad8_index_polarity_modes[] = {
376 	"negative",
377 	"positive"
378 };
379 
quad8_index_polarity_get(struct counter_device * counter,struct counter_signal * signal,u32 * index_polarity)380 static int quad8_index_polarity_get(struct counter_device *counter,
381 				    struct counter_signal *signal,
382 				    u32 *index_polarity)
383 {
384 	const struct quad8 *const priv = counter->priv;
385 	const size_t channel_id = signal->id - 16;
386 
387 	*index_polarity = priv->index_polarity[channel_id];
388 
389 	return 0;
390 }
391 
quad8_index_polarity_set(struct counter_device * counter,struct counter_signal * signal,u32 index_polarity)392 static int quad8_index_polarity_set(struct counter_device *counter,
393 				    struct counter_signal *signal,
394 				    u32 index_polarity)
395 {
396 	struct quad8 *const priv = counter->priv;
397 	const size_t channel_id = signal->id - 16;
398 	const int base_offset = priv->base + 2 * channel_id + 1;
399 	unsigned int idr_cfg = index_polarity << 1;
400 
401 	mutex_lock(&priv->lock);
402 
403 	idr_cfg |= priv->synchronous_mode[channel_id];
404 
405 	priv->index_polarity[channel_id] = index_polarity;
406 
407 	/* Load Index Control configuration to Index Control Register */
408 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
409 
410 	mutex_unlock(&priv->lock);
411 
412 	return 0;
413 }
414 
415 static const char *const quad8_synchronous_modes[] = {
416 	"non-synchronous",
417 	"synchronous"
418 };
419 
quad8_synchronous_mode_get(struct counter_device * counter,struct counter_signal * signal,u32 * synchronous_mode)420 static int quad8_synchronous_mode_get(struct counter_device *counter,
421 				      struct counter_signal *signal,
422 				      u32 *synchronous_mode)
423 {
424 	const struct quad8 *const priv = counter->priv;
425 	const size_t channel_id = signal->id - 16;
426 
427 	*synchronous_mode = priv->synchronous_mode[channel_id];
428 
429 	return 0;
430 }
431 
quad8_synchronous_mode_set(struct counter_device * counter,struct counter_signal * signal,u32 synchronous_mode)432 static int quad8_synchronous_mode_set(struct counter_device *counter,
433 				      struct counter_signal *signal,
434 				      u32 synchronous_mode)
435 {
436 	struct quad8 *const priv = counter->priv;
437 	const size_t channel_id = signal->id - 16;
438 	const int base_offset = priv->base + 2 * channel_id + 1;
439 	unsigned int idr_cfg = synchronous_mode;
440 
441 	mutex_lock(&priv->lock);
442 
443 	idr_cfg |= priv->index_polarity[channel_id] << 1;
444 
445 	/* Index function must be non-synchronous in non-quadrature mode */
446 	if (synchronous_mode && !priv->quadrature_mode[channel_id]) {
447 		mutex_unlock(&priv->lock);
448 		return -EINVAL;
449 	}
450 
451 	priv->synchronous_mode[channel_id] = synchronous_mode;
452 
453 	/* Load Index Control configuration to Index Control Register */
454 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
455 
456 	mutex_unlock(&priv->lock);
457 
458 	return 0;
459 }
460 
quad8_count_floor_read(struct counter_device * counter,struct counter_count * count,u64 * floor)461 static int quad8_count_floor_read(struct counter_device *counter,
462 				  struct counter_count *count, u64 *floor)
463 {
464 	/* Only a floor of 0 is supported */
465 	*floor = 0;
466 
467 	return 0;
468 }
469 
quad8_count_mode_read(struct counter_device * counter,struct counter_count * count,enum counter_count_mode * cnt_mode)470 static int quad8_count_mode_read(struct counter_device *counter,
471 				 struct counter_count *count,
472 				 enum counter_count_mode *cnt_mode)
473 {
474 	const struct quad8 *const priv = counter->priv;
475 
476 	/* Map 104-QUAD-8 count mode to Generic Counter count mode */
477 	switch (priv->count_mode[count->id]) {
478 	case 0:
479 		*cnt_mode = COUNTER_COUNT_MODE_NORMAL;
480 		break;
481 	case 1:
482 		*cnt_mode = COUNTER_COUNT_MODE_RANGE_LIMIT;
483 		break;
484 	case 2:
485 		*cnt_mode = COUNTER_COUNT_MODE_NON_RECYCLE;
486 		break;
487 	case 3:
488 		*cnt_mode = COUNTER_COUNT_MODE_MODULO_N;
489 		break;
490 	}
491 
492 	return 0;
493 }
494 
quad8_count_mode_write(struct counter_device * counter,struct counter_count * count,enum counter_count_mode cnt_mode)495 static int quad8_count_mode_write(struct counter_device *counter,
496 				  struct counter_count *count,
497 				  enum counter_count_mode cnt_mode)
498 {
499 	struct quad8 *const priv = counter->priv;
500 	unsigned int count_mode;
501 	unsigned int mode_cfg;
502 	const int base_offset = priv->base + 2 * count->id + 1;
503 
504 	/* Map Generic Counter count mode to 104-QUAD-8 count mode */
505 	switch (cnt_mode) {
506 	case COUNTER_COUNT_MODE_NORMAL:
507 		count_mode = 0;
508 		break;
509 	case COUNTER_COUNT_MODE_RANGE_LIMIT:
510 		count_mode = 1;
511 		break;
512 	case COUNTER_COUNT_MODE_NON_RECYCLE:
513 		count_mode = 2;
514 		break;
515 	case COUNTER_COUNT_MODE_MODULO_N:
516 		count_mode = 3;
517 		break;
518 	default:
519 		/* should never reach this path */
520 		return -EINVAL;
521 	}
522 
523 	mutex_lock(&priv->lock);
524 
525 	priv->count_mode[count->id] = count_mode;
526 
527 	/* Set count mode configuration value */
528 	mode_cfg = count_mode << 1;
529 
530 	/* Add quadrature mode configuration */
531 	if (priv->quadrature_mode[count->id])
532 		mode_cfg |= (priv->quadrature_scale[count->id] + 1) << 3;
533 
534 	/* Load mode configuration to Counter Mode Register */
535 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
536 
537 	mutex_unlock(&priv->lock);
538 
539 	return 0;
540 }
541 
quad8_count_enable_read(struct counter_device * counter,struct counter_count * count,u8 * enable)542 static int quad8_count_enable_read(struct counter_device *counter,
543 				   struct counter_count *count, u8 *enable)
544 {
545 	const struct quad8 *const priv = counter->priv;
546 
547 	*enable = priv->ab_enable[count->id];
548 
549 	return 0;
550 }
551 
quad8_count_enable_write(struct counter_device * counter,struct counter_count * count,u8 enable)552 static int quad8_count_enable_write(struct counter_device *counter,
553 				    struct counter_count *count, u8 enable)
554 {
555 	struct quad8 *const priv = counter->priv;
556 	const int base_offset = priv->base + 2 * count->id;
557 	unsigned int ior_cfg;
558 
559 	mutex_lock(&priv->lock);
560 
561 	priv->ab_enable[count->id] = enable;
562 
563 	ior_cfg = enable | priv->preset_enable[count->id] << 1;
564 
565 	/* Load I/O control configuration */
566 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
567 
568 	mutex_unlock(&priv->lock);
569 
570 	return 0;
571 }
572 
573 static const char *const quad8_noise_error_states[] = {
574 	"No excessive noise is present at the count inputs",
575 	"Excessive noise is present at the count inputs"
576 };
577 
quad8_error_noise_get(struct counter_device * counter,struct counter_count * count,u32 * noise_error)578 static int quad8_error_noise_get(struct counter_device *counter,
579 				 struct counter_count *count, u32 *noise_error)
580 {
581 	const struct quad8 *const priv = counter->priv;
582 	const int base_offset = priv->base + 2 * count->id + 1;
583 
584 	*noise_error = !!(inb(base_offset) & QUAD8_FLAG_E);
585 
586 	return 0;
587 }
588 
quad8_count_preset_read(struct counter_device * counter,struct counter_count * count,u64 * preset)589 static int quad8_count_preset_read(struct counter_device *counter,
590 				   struct counter_count *count, u64 *preset)
591 {
592 	const struct quad8 *const priv = counter->priv;
593 
594 	*preset = priv->preset[count->id];
595 
596 	return 0;
597 }
598 
quad8_preset_register_set(struct quad8 * const priv,const int id,const unsigned int preset)599 static void quad8_preset_register_set(struct quad8 *const priv, const int id,
600 				      const unsigned int preset)
601 {
602 	const unsigned int base_offset = priv->base + 2 * id;
603 	int i;
604 
605 	priv->preset[id] = preset;
606 
607 	/* Reset Byte Pointer */
608 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
609 
610 	/* Set Preset Register */
611 	for (i = 0; i < 3; i++)
612 		outb(preset >> (8 * i), base_offset);
613 }
614 
quad8_count_preset_write(struct counter_device * counter,struct counter_count * count,u64 preset)615 static int quad8_count_preset_write(struct counter_device *counter,
616 				    struct counter_count *count, u64 preset)
617 {
618 	struct quad8 *const priv = counter->priv;
619 
620 	if (preset > LS7267_CNTR_MAX)
621 		return -ERANGE;
622 
623 	mutex_lock(&priv->lock);
624 
625 	quad8_preset_register_set(priv, count->id, preset);
626 
627 	mutex_unlock(&priv->lock);
628 
629 	return 0;
630 }
631 
quad8_count_ceiling_read(struct counter_device * counter,struct counter_count * count,u64 * ceiling)632 static int quad8_count_ceiling_read(struct counter_device *counter,
633 				    struct counter_count *count, u64 *ceiling)
634 {
635 	struct quad8 *const priv = counter->priv;
636 
637 	mutex_lock(&priv->lock);
638 
639 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
640 	switch (priv->count_mode[count->id]) {
641 	case 1:
642 	case 3:
643 		*ceiling = priv->preset[count->id];
644 		break;
645 	default:
646 		*ceiling = LS7267_CNTR_MAX;
647 		break;
648 	}
649 
650 	mutex_unlock(&priv->lock);
651 
652 	return 0;
653 }
654 
quad8_count_ceiling_write(struct counter_device * counter,struct counter_count * count,u64 ceiling)655 static int quad8_count_ceiling_write(struct counter_device *counter,
656 				     struct counter_count *count, u64 ceiling)
657 {
658 	struct quad8 *const priv = counter->priv;
659 
660 	if (ceiling > LS7267_CNTR_MAX)
661 		return -ERANGE;
662 
663 	mutex_lock(&priv->lock);
664 
665 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
666 	switch (priv->count_mode[count->id]) {
667 	case 1:
668 	case 3:
669 		quad8_preset_register_set(priv, count->id, ceiling);
670 		mutex_unlock(&priv->lock);
671 		return 0;
672 	}
673 
674 	mutex_unlock(&priv->lock);
675 
676 	return -EINVAL;
677 }
678 
quad8_count_preset_enable_read(struct counter_device * counter,struct counter_count * count,u8 * preset_enable)679 static int quad8_count_preset_enable_read(struct counter_device *counter,
680 					  struct counter_count *count,
681 					  u8 *preset_enable)
682 {
683 	const struct quad8 *const priv = counter->priv;
684 
685 	*preset_enable = !priv->preset_enable[count->id];
686 
687 	return 0;
688 }
689 
quad8_count_preset_enable_write(struct counter_device * counter,struct counter_count * count,u8 preset_enable)690 static int quad8_count_preset_enable_write(struct counter_device *counter,
691 					   struct counter_count *count,
692 					   u8 preset_enable)
693 {
694 	struct quad8 *const priv = counter->priv;
695 	const int base_offset = priv->base + 2 * count->id + 1;
696 	unsigned int ior_cfg;
697 
698 	/* Preset enable is active low in Input/Output Control register */
699 	preset_enable = !preset_enable;
700 
701 	mutex_lock(&priv->lock);
702 
703 	priv->preset_enable[count->id] = preset_enable;
704 
705 	ior_cfg = priv->ab_enable[count->id] | preset_enable << 1;
706 
707 	/* Load I/O control configuration to Input / Output Control Register */
708 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
709 
710 	mutex_unlock(&priv->lock);
711 
712 	return 0;
713 }
714 
quad8_signal_cable_fault_read(struct counter_device * counter,struct counter_signal * signal,u8 * cable_fault)715 static int quad8_signal_cable_fault_read(struct counter_device *counter,
716 					 struct counter_signal *signal,
717 					 u8 *cable_fault)
718 {
719 	struct quad8 *const priv = counter->priv;
720 	const size_t channel_id = signal->id / 2;
721 	bool disabled;
722 	unsigned int status;
723 
724 	mutex_lock(&priv->lock);
725 
726 	disabled = !(priv->cable_fault_enable & BIT(channel_id));
727 
728 	if (disabled) {
729 		mutex_unlock(&priv->lock);
730 		return -EINVAL;
731 	}
732 
733 	/* Logic 0 = cable fault */
734 	status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
735 
736 	mutex_unlock(&priv->lock);
737 
738 	/* Mask respective channel and invert logic */
739 	*cable_fault = !(status & BIT(channel_id));
740 
741 	return 0;
742 }
743 
quad8_signal_cable_fault_enable_read(struct counter_device * counter,struct counter_signal * signal,u8 * enable)744 static int quad8_signal_cable_fault_enable_read(struct counter_device *counter,
745 						struct counter_signal *signal,
746 						u8 *enable)
747 {
748 	const struct quad8 *const priv = counter->priv;
749 	const size_t channel_id = signal->id / 2;
750 
751 	*enable = !!(priv->cable_fault_enable & BIT(channel_id));
752 
753 	return 0;
754 }
755 
quad8_signal_cable_fault_enable_write(struct counter_device * counter,struct counter_signal * signal,u8 enable)756 static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
757 						 struct counter_signal *signal,
758 						 u8 enable)
759 {
760 	struct quad8 *const priv = counter->priv;
761 	const size_t channel_id = signal->id / 2;
762 	unsigned int cable_fault_enable;
763 
764 	mutex_lock(&priv->lock);
765 
766 	if (enable)
767 		priv->cable_fault_enable |= BIT(channel_id);
768 	else
769 		priv->cable_fault_enable &= ~BIT(channel_id);
770 
771 	/* Enable is active low in Differential Encoder Cable Status register */
772 	cable_fault_enable = ~priv->cable_fault_enable;
773 
774 	outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
775 
776 	mutex_unlock(&priv->lock);
777 
778 	return 0;
779 }
780 
quad8_signal_fck_prescaler_read(struct counter_device * counter,struct counter_signal * signal,u8 * prescaler)781 static int quad8_signal_fck_prescaler_read(struct counter_device *counter,
782 					   struct counter_signal *signal,
783 					   u8 *prescaler)
784 {
785 	const struct quad8 *const priv = counter->priv;
786 
787 	*prescaler = priv->fck_prescaler[signal->id / 2];
788 
789 	return 0;
790 }
791 
quad8_signal_fck_prescaler_write(struct counter_device * counter,struct counter_signal * signal,u8 prescaler)792 static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
793 					    struct counter_signal *signal,
794 					    u8 prescaler)
795 {
796 	struct quad8 *const priv = counter->priv;
797 	const size_t channel_id = signal->id / 2;
798 	const int base_offset = priv->base + 2 * channel_id;
799 
800 	mutex_lock(&priv->lock);
801 
802 	priv->fck_prescaler[channel_id] = prescaler;
803 
804 	/* Reset Byte Pointer */
805 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
806 
807 	/* Set filter clock factor */
808 	outb(prescaler, base_offset);
809 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
810 	     base_offset + 1);
811 
812 	mutex_unlock(&priv->lock);
813 
814 	return 0;
815 }
816 
817 static struct counter_comp quad8_signal_ext[] = {
818 	COUNTER_COMP_SIGNAL_BOOL("cable_fault", quad8_signal_cable_fault_read,
819 				 NULL),
820 	COUNTER_COMP_SIGNAL_BOOL("cable_fault_enable",
821 				 quad8_signal_cable_fault_enable_read,
822 				 quad8_signal_cable_fault_enable_write),
823 	COUNTER_COMP_SIGNAL_U8("filter_clock_prescaler",
824 			       quad8_signal_fck_prescaler_read,
825 			       quad8_signal_fck_prescaler_write)
826 };
827 
828 static DEFINE_COUNTER_ENUM(quad8_index_pol_enum, quad8_index_polarity_modes);
829 static DEFINE_COUNTER_ENUM(quad8_synch_mode_enum, quad8_synchronous_modes);
830 
831 static struct counter_comp quad8_index_ext[] = {
832 	COUNTER_COMP_SIGNAL_ENUM("index_polarity", quad8_index_polarity_get,
833 				 quad8_index_polarity_set,
834 				 quad8_index_pol_enum),
835 	COUNTER_COMP_SIGNAL_ENUM("synchronous_mode", quad8_synchronous_mode_get,
836 				 quad8_synchronous_mode_set,
837 				 quad8_synch_mode_enum),
838 };
839 
840 #define QUAD8_QUAD_SIGNAL(_id, _name) {		\
841 	.id = (_id),				\
842 	.name = (_name),			\
843 	.ext = quad8_signal_ext,		\
844 	.num_ext = ARRAY_SIZE(quad8_signal_ext)	\
845 }
846 
847 #define	QUAD8_INDEX_SIGNAL(_id, _name) {	\
848 	.id = (_id),				\
849 	.name = (_name),			\
850 	.ext = quad8_index_ext,			\
851 	.num_ext = ARRAY_SIZE(quad8_index_ext)	\
852 }
853 
854 static struct counter_signal quad8_signals[] = {
855 	QUAD8_QUAD_SIGNAL(0, "Channel 1 Quadrature A"),
856 	QUAD8_QUAD_SIGNAL(1, "Channel 1 Quadrature B"),
857 	QUAD8_QUAD_SIGNAL(2, "Channel 2 Quadrature A"),
858 	QUAD8_QUAD_SIGNAL(3, "Channel 2 Quadrature B"),
859 	QUAD8_QUAD_SIGNAL(4, "Channel 3 Quadrature A"),
860 	QUAD8_QUAD_SIGNAL(5, "Channel 3 Quadrature B"),
861 	QUAD8_QUAD_SIGNAL(6, "Channel 4 Quadrature A"),
862 	QUAD8_QUAD_SIGNAL(7, "Channel 4 Quadrature B"),
863 	QUAD8_QUAD_SIGNAL(8, "Channel 5 Quadrature A"),
864 	QUAD8_QUAD_SIGNAL(9, "Channel 5 Quadrature B"),
865 	QUAD8_QUAD_SIGNAL(10, "Channel 6 Quadrature A"),
866 	QUAD8_QUAD_SIGNAL(11, "Channel 6 Quadrature B"),
867 	QUAD8_QUAD_SIGNAL(12, "Channel 7 Quadrature A"),
868 	QUAD8_QUAD_SIGNAL(13, "Channel 7 Quadrature B"),
869 	QUAD8_QUAD_SIGNAL(14, "Channel 8 Quadrature A"),
870 	QUAD8_QUAD_SIGNAL(15, "Channel 8 Quadrature B"),
871 	QUAD8_INDEX_SIGNAL(16, "Channel 1 Index"),
872 	QUAD8_INDEX_SIGNAL(17, "Channel 2 Index"),
873 	QUAD8_INDEX_SIGNAL(18, "Channel 3 Index"),
874 	QUAD8_INDEX_SIGNAL(19, "Channel 4 Index"),
875 	QUAD8_INDEX_SIGNAL(20, "Channel 5 Index"),
876 	QUAD8_INDEX_SIGNAL(21, "Channel 6 Index"),
877 	QUAD8_INDEX_SIGNAL(22, "Channel 7 Index"),
878 	QUAD8_INDEX_SIGNAL(23, "Channel 8 Index")
879 };
880 
881 #define QUAD8_COUNT_SYNAPSES(_id) {					\
882 	{								\
883 		.actions_list = quad8_synapse_actions_list,		\
884 		.num_actions = ARRAY_SIZE(quad8_synapse_actions_list),	\
885 		.signal = quad8_signals + 2 * (_id)			\
886 	},								\
887 	{								\
888 		.actions_list = quad8_synapse_actions_list,		\
889 		.num_actions = ARRAY_SIZE(quad8_synapse_actions_list),	\
890 		.signal = quad8_signals + 2 * (_id) + 1			\
891 	},								\
892 	{								\
893 		.actions_list = quad8_index_actions_list,		\
894 		.num_actions = ARRAY_SIZE(quad8_index_actions_list),	\
895 		.signal = quad8_signals + 2 * (_id) + 16		\
896 	}								\
897 }
898 
899 static struct counter_synapse quad8_count_synapses[][3] = {
900 	QUAD8_COUNT_SYNAPSES(0), QUAD8_COUNT_SYNAPSES(1),
901 	QUAD8_COUNT_SYNAPSES(2), QUAD8_COUNT_SYNAPSES(3),
902 	QUAD8_COUNT_SYNAPSES(4), QUAD8_COUNT_SYNAPSES(5),
903 	QUAD8_COUNT_SYNAPSES(6), QUAD8_COUNT_SYNAPSES(7)
904 };
905 
906 static const enum counter_count_mode quad8_cnt_modes[] = {
907 	COUNTER_COUNT_MODE_NORMAL,
908 	COUNTER_COUNT_MODE_RANGE_LIMIT,
909 	COUNTER_COUNT_MODE_NON_RECYCLE,
910 	COUNTER_COUNT_MODE_MODULO_N,
911 };
912 
913 static DEFINE_COUNTER_AVAILABLE(quad8_count_mode_available, quad8_cnt_modes);
914 
915 static DEFINE_COUNTER_ENUM(quad8_error_noise_enum, quad8_noise_error_states);
916 
917 static struct counter_comp quad8_count_ext[] = {
918 	COUNTER_COMP_CEILING(quad8_count_ceiling_read,
919 			     quad8_count_ceiling_write),
920 	COUNTER_COMP_FLOOR(quad8_count_floor_read, NULL),
921 	COUNTER_COMP_COUNT_MODE(quad8_count_mode_read, quad8_count_mode_write,
922 				quad8_count_mode_available),
923 	COUNTER_COMP_DIRECTION(quad8_direction_read),
924 	COUNTER_COMP_ENABLE(quad8_count_enable_read, quad8_count_enable_write),
925 	COUNTER_COMP_COUNT_ENUM("error_noise", quad8_error_noise_get, NULL,
926 				quad8_error_noise_enum),
927 	COUNTER_COMP_PRESET(quad8_count_preset_read, quad8_count_preset_write),
928 	COUNTER_COMP_PRESET_ENABLE(quad8_count_preset_enable_read,
929 				   quad8_count_preset_enable_write),
930 };
931 
932 #define QUAD8_COUNT(_id, _cntname) {					\
933 	.id = (_id),							\
934 	.name = (_cntname),						\
935 	.functions_list = quad8_count_functions_list,			\
936 	.num_functions = ARRAY_SIZE(quad8_count_functions_list),	\
937 	.synapses = quad8_count_synapses[(_id)],			\
938 	.num_synapses =	2,						\
939 	.ext = quad8_count_ext,						\
940 	.num_ext = ARRAY_SIZE(quad8_count_ext)				\
941 }
942 
943 static struct counter_count quad8_counts[] = {
944 	QUAD8_COUNT(0, "Channel 1 Count"),
945 	QUAD8_COUNT(1, "Channel 2 Count"),
946 	QUAD8_COUNT(2, "Channel 3 Count"),
947 	QUAD8_COUNT(3, "Channel 4 Count"),
948 	QUAD8_COUNT(4, "Channel 5 Count"),
949 	QUAD8_COUNT(5, "Channel 6 Count"),
950 	QUAD8_COUNT(6, "Channel 7 Count"),
951 	QUAD8_COUNT(7, "Channel 8 Count")
952 };
953 
quad8_probe(struct device * dev,unsigned int id)954 static int quad8_probe(struct device *dev, unsigned int id)
955 {
956 	struct quad8 *priv;
957 	int i, j;
958 	unsigned int base_offset;
959 
960 	if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
961 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
962 			base[id], base[id] + QUAD8_EXTENT);
963 		return -EBUSY;
964 	}
965 
966 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
967 	if (!priv)
968 		return -ENOMEM;
969 
970 	/* Initialize Counter device and driver data */
971 	priv->counter.name = dev_name(dev);
972 	priv->counter.parent = dev;
973 	priv->counter.ops = &quad8_ops;
974 	priv->counter.counts = quad8_counts;
975 	priv->counter.num_counts = ARRAY_SIZE(quad8_counts);
976 	priv->counter.signals = quad8_signals;
977 	priv->counter.num_signals = ARRAY_SIZE(quad8_signals);
978 	priv->counter.priv = priv;
979 	priv->base = base[id];
980 
981 	/* Initialize mutex */
982 	mutex_init(&priv->lock);
983 
984 	/* Reset all counters and disable interrupt function */
985 	outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
986 	/* Set initial configuration for all counters */
987 	for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
988 		base_offset = base[id] + 2 * i;
989 		/* Reset Byte Pointer */
990 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
991 		/* Reset filter clock factor */
992 		outb(0, base_offset);
993 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
994 		     base_offset + 1);
995 		/* Reset Byte Pointer */
996 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
997 		/* Reset Preset Register */
998 		for (j = 0; j < 3; j++)
999 			outb(0x00, base_offset);
1000 		/* Reset Borrow, Carry, Compare, and Sign flags */
1001 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
1002 		/* Reset Error flag */
1003 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
1004 		/* Binary encoding; Normal count; non-quadrature mode */
1005 		outb(QUAD8_CTR_CMR, base_offset + 1);
1006 		/* Disable A and B inputs; preset on index; FLG1 as Carry */
1007 		outb(QUAD8_CTR_IOR, base_offset + 1);
1008 		/* Disable index function; negative index polarity */
1009 		outb(QUAD8_CTR_IDR, base_offset + 1);
1010 	}
1011 	/* Disable Differential Encoder Cable Status for all channels */
1012 	outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS);
1013 	/* Enable all counters */
1014 	outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
1015 
1016 	return devm_counter_register(dev, &priv->counter);
1017 }
1018 
1019 static struct isa_driver quad8_driver = {
1020 	.probe = quad8_probe,
1021 	.driver = {
1022 		.name = "104-quad-8"
1023 	}
1024 };
1025 
1026 module_isa_driver(quad8_driver, num_quad8);
1027 
1028 MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
1029 MODULE_DESCRIPTION("ACCES 104-QUAD-8 driver");
1030 MODULE_LICENSE("GPL v2");
1031