162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Counter driver for the ACCES 104-QUAD-8
462306a36Sopenharmony_ci * Copyright (C) 2016 William Breathitt Gray
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This driver supports the ACCES 104-QUAD-8 and ACCES 104-QUAD-4.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/bitfield.h>
962306a36Sopenharmony_ci#include <linux/bits.h>
1062306a36Sopenharmony_ci#include <linux/counter.h>
1162306a36Sopenharmony_ci#include <linux/device.h>
1262306a36Sopenharmony_ci#include <linux/err.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <linux/ioport.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/isa.h>
1762306a36Sopenharmony_ci#include <linux/kernel.h>
1862306a36Sopenharmony_ci#include <linux/list.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/moduleparam.h>
2162306a36Sopenharmony_ci#include <linux/regmap.h>
2262306a36Sopenharmony_ci#include <linux/spinlock.h>
2362306a36Sopenharmony_ci#include <linux/types.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <asm/unaligned.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define QUAD8_EXTENT 32
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic unsigned int base[max_num_isa_dev(QUAD8_EXTENT)];
3062306a36Sopenharmony_cistatic unsigned int num_quad8;
3162306a36Sopenharmony_cimodule_param_hw_array(base, uint, ioport, &num_quad8, 0);
3262306a36Sopenharmony_ciMODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic unsigned int irq[max_num_isa_dev(QUAD8_EXTENT)];
3562306a36Sopenharmony_cistatic unsigned int num_irq;
3662306a36Sopenharmony_cimodule_param_hw_array(irq, uint, irq, &num_irq, 0);
3762306a36Sopenharmony_ciMODULE_PARM_DESC(irq, "ACCES 104-QUAD-8 interrupt line numbers");
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define QUAD8_NUM_COUNTERS 8
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define QUAD8_DATA(_channel) ((_channel) * 2)
4262306a36Sopenharmony_ci#define QUAD8_CONTROL(_channel) (QUAD8_DATA(_channel) + 1)
4362306a36Sopenharmony_ci#define QUAD8_INTERRUPT_STATUS 0x10
4462306a36Sopenharmony_ci#define QUAD8_CHANNEL_OPERATION 0x11
4562306a36Sopenharmony_ci#define QUAD8_INDEX_INTERRUPT 0x12
4662306a36Sopenharmony_ci#define QUAD8_INDEX_INPUT_LEVELS 0x16
4762306a36Sopenharmony_ci#define QUAD8_CABLE_STATUS 0x17
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/**
5062306a36Sopenharmony_ci * struct quad8 - device private data structure
5162306a36Sopenharmony_ci * @lock:		lock to prevent clobbering device states during R/W ops
5262306a36Sopenharmony_ci * @cmr:		array of Counter Mode Register states
5362306a36Sopenharmony_ci * @ior:		array of Input / Output Control Register states
5462306a36Sopenharmony_ci * @idr:		array of Index Control Register states
5562306a36Sopenharmony_ci * @fck_prescaler:	array of filter clock prescaler configurations
5662306a36Sopenharmony_ci * @preset:		array of preset values
5762306a36Sopenharmony_ci * @cable_fault_enable:	differential encoder cable status enable configurations
5862306a36Sopenharmony_ci * @map:		regmap for the device
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_cistruct quad8 {
6162306a36Sopenharmony_ci	spinlock_t lock;
6262306a36Sopenharmony_ci	u8 cmr[QUAD8_NUM_COUNTERS];
6362306a36Sopenharmony_ci	u8 ior[QUAD8_NUM_COUNTERS];
6462306a36Sopenharmony_ci	u8 idr[QUAD8_NUM_COUNTERS];
6562306a36Sopenharmony_ci	unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
6662306a36Sopenharmony_ci	unsigned int preset[QUAD8_NUM_COUNTERS];
6762306a36Sopenharmony_ci	unsigned int cable_fault_enable;
6862306a36Sopenharmony_ci	struct regmap *map;
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic const struct regmap_range quad8_wr_ranges[] = {
7262306a36Sopenharmony_ci	regmap_reg_range(0x0, 0xF), regmap_reg_range(0x11, 0x12), regmap_reg_range(0x17, 0x17),
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_cistatic const struct regmap_range quad8_rd_ranges[] = {
7562306a36Sopenharmony_ci	regmap_reg_range(0x0, 0x12), regmap_reg_range(0x16, 0x18),
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_cistatic const struct regmap_access_table quad8_wr_table = {
7862306a36Sopenharmony_ci	.yes_ranges = quad8_wr_ranges,
7962306a36Sopenharmony_ci	.n_yes_ranges = ARRAY_SIZE(quad8_wr_ranges),
8062306a36Sopenharmony_ci};
8162306a36Sopenharmony_cistatic const struct regmap_access_table quad8_rd_table = {
8262306a36Sopenharmony_ci	.yes_ranges = quad8_rd_ranges,
8362306a36Sopenharmony_ci	.n_yes_ranges = ARRAY_SIZE(quad8_rd_ranges),
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_cistatic const struct regmap_config quad8_regmap_config = {
8662306a36Sopenharmony_ci	.reg_bits = 8,
8762306a36Sopenharmony_ci	.reg_stride = 1,
8862306a36Sopenharmony_ci	.val_bits = 8,
8962306a36Sopenharmony_ci	.io_port = true,
9062306a36Sopenharmony_ci	.wr_table = &quad8_wr_table,
9162306a36Sopenharmony_ci	.rd_table = &quad8_rd_table,
9262306a36Sopenharmony_ci};
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/* Error flag */
9562306a36Sopenharmony_ci#define FLAG_E BIT(4)
9662306a36Sopenharmony_ci/* Up/Down flag */
9762306a36Sopenharmony_ci#define FLAG_UD BIT(5)
9862306a36Sopenharmony_ci/* Counting up */
9962306a36Sopenharmony_ci#define UP 0x1
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci#define REGISTER_SELECTION GENMASK(6, 5)
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/* Reset and Load Signal Decoders */
10462306a36Sopenharmony_ci#define SELECT_RLD u8_encode_bits(0x0, REGISTER_SELECTION)
10562306a36Sopenharmony_ci/* Counter Mode Register */
10662306a36Sopenharmony_ci#define SELECT_CMR u8_encode_bits(0x1, REGISTER_SELECTION)
10762306a36Sopenharmony_ci/* Input / Output Control Register */
10862306a36Sopenharmony_ci#define SELECT_IOR u8_encode_bits(0x2, REGISTER_SELECTION)
10962306a36Sopenharmony_ci/* Index Control Register */
11062306a36Sopenharmony_ci#define SELECT_IDR u8_encode_bits(0x3, REGISTER_SELECTION)
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/*
11362306a36Sopenharmony_ci * Reset and Load Signal Decoders
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_ci#define RESETS GENMASK(2, 1)
11662306a36Sopenharmony_ci#define LOADS GENMASK(4, 3)
11762306a36Sopenharmony_ci/* Reset Byte Pointer (three byte data pointer) */
11862306a36Sopenharmony_ci#define RESET_BP BIT(0)
11962306a36Sopenharmony_ci/* Reset Borrow Toggle, Carry toggle, Compare toggle, Sign, and Index flags */
12062306a36Sopenharmony_ci#define RESET_BT_CT_CPT_S_IDX u8_encode_bits(0x2, RESETS)
12162306a36Sopenharmony_ci/* Reset Error flag */
12262306a36Sopenharmony_ci#define RESET_E u8_encode_bits(0x3, RESETS)
12362306a36Sopenharmony_ci/* Preset Register to Counter */
12462306a36Sopenharmony_ci#define TRANSFER_PR_TO_CNTR u8_encode_bits(0x1, LOADS)
12562306a36Sopenharmony_ci/* Transfer Counter to Output Latch */
12662306a36Sopenharmony_ci#define TRANSFER_CNTR_TO_OL u8_encode_bits(0x2, LOADS)
12762306a36Sopenharmony_ci/* Transfer Preset Register LSB to FCK Prescaler */
12862306a36Sopenharmony_ci#define TRANSFER_PR0_TO_PSC u8_encode_bits(0x3, LOADS)
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci/*
13162306a36Sopenharmony_ci * Counter Mode Registers
13262306a36Sopenharmony_ci */
13362306a36Sopenharmony_ci#define COUNT_ENCODING BIT(0)
13462306a36Sopenharmony_ci#define COUNT_MODE GENMASK(2, 1)
13562306a36Sopenharmony_ci#define QUADRATURE_MODE GENMASK(4, 3)
13662306a36Sopenharmony_ci/* Binary count */
13762306a36Sopenharmony_ci#define BINARY u8_encode_bits(0x0, COUNT_ENCODING)
13862306a36Sopenharmony_ci/* Normal count */
13962306a36Sopenharmony_ci#define NORMAL_COUNT 0x0
14062306a36Sopenharmony_ci/* Range Limit */
14162306a36Sopenharmony_ci#define RANGE_LIMIT 0x1
14262306a36Sopenharmony_ci/* Non-recycle count */
14362306a36Sopenharmony_ci#define NON_RECYCLE_COUNT 0x2
14462306a36Sopenharmony_ci/* Modulo-N */
14562306a36Sopenharmony_ci#define MODULO_N 0x3
14662306a36Sopenharmony_ci/* Non-quadrature */
14762306a36Sopenharmony_ci#define NON_QUADRATURE 0x0
14862306a36Sopenharmony_ci/* Quadrature X1 */
14962306a36Sopenharmony_ci#define QUADRATURE_X1 0x1
15062306a36Sopenharmony_ci/* Quadrature X2 */
15162306a36Sopenharmony_ci#define QUADRATURE_X2 0x2
15262306a36Sopenharmony_ci/* Quadrature X4 */
15362306a36Sopenharmony_ci#define QUADRATURE_X4 0x3
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/*
15662306a36Sopenharmony_ci * Input/Output Control Register
15762306a36Sopenharmony_ci */
15862306a36Sopenharmony_ci#define AB_GATE BIT(0)
15962306a36Sopenharmony_ci#define LOAD_PIN BIT(1)
16062306a36Sopenharmony_ci#define FLG_PINS GENMASK(4, 3)
16162306a36Sopenharmony_ci/* Disable inputs A and B */
16262306a36Sopenharmony_ci#define DISABLE_AB u8_encode_bits(0x0, AB_GATE)
16362306a36Sopenharmony_ci/* Load Counter input */
16462306a36Sopenharmony_ci#define LOAD_CNTR 0x0
16562306a36Sopenharmony_ci/* FLG1 = CARRY(active low); FLG2 = BORROW(active low) */
16662306a36Sopenharmony_ci#define FLG1_CARRY_FLG2_BORROW 0x0
16762306a36Sopenharmony_ci/* FLG1 = COMPARE(active low); FLG2 = BORROW(active low) */
16862306a36Sopenharmony_ci#define FLG1_COMPARE_FLG2_BORROW 0x1
16962306a36Sopenharmony_ci/* FLG1 = Carry(active low)/Borrow(active low); FLG2 = U/D(active low) flag */
17062306a36Sopenharmony_ci#define FLG1_CARRYBORROW_FLG2_UD 0x2
17162306a36Sopenharmony_ci/* FLG1 = INDX (low pulse at INDEX pin active level); FLG2 = E flag */
17262306a36Sopenharmony_ci#define FLG1_INDX_FLG2_E 0x3
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci/*
17562306a36Sopenharmony_ci * INDEX CONTROL REGISTERS
17662306a36Sopenharmony_ci */
17762306a36Sopenharmony_ci#define INDEX_MODE BIT(0)
17862306a36Sopenharmony_ci#define INDEX_POLARITY BIT(1)
17962306a36Sopenharmony_ci/* Disable Index mode */
18062306a36Sopenharmony_ci#define DISABLE_INDEX_MODE 0x0
18162306a36Sopenharmony_ci/* Enable Index mode */
18262306a36Sopenharmony_ci#define ENABLE_INDEX_MODE 0x1
18362306a36Sopenharmony_ci/* Negative Index Polarity */
18462306a36Sopenharmony_ci#define NEGATIVE_INDEX_POLARITY 0x0
18562306a36Sopenharmony_ci/* Positive Index Polarity */
18662306a36Sopenharmony_ci#define POSITIVE_INDEX_POLARITY 0x1
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/*
18962306a36Sopenharmony_ci * Channel Operation Register
19062306a36Sopenharmony_ci */
19162306a36Sopenharmony_ci#define COUNTERS_OPERATION BIT(0)
19262306a36Sopenharmony_ci#define INTERRUPT_FUNCTION BIT(2)
19362306a36Sopenharmony_ci/* Enable all Counters */
19462306a36Sopenharmony_ci#define ENABLE_COUNTERS u8_encode_bits(0x0, COUNTERS_OPERATION)
19562306a36Sopenharmony_ci/* Reset all Counters */
19662306a36Sopenharmony_ci#define RESET_COUNTERS u8_encode_bits(0x1, COUNTERS_OPERATION)
19762306a36Sopenharmony_ci/* Disable the interrupt function */
19862306a36Sopenharmony_ci#define DISABLE_INTERRUPT_FUNCTION u8_encode_bits(0x0, INTERRUPT_FUNCTION)
19962306a36Sopenharmony_ci/* Enable the interrupt function */
20062306a36Sopenharmony_ci#define ENABLE_INTERRUPT_FUNCTION u8_encode_bits(0x1, INTERRUPT_FUNCTION)
20162306a36Sopenharmony_ci/* Any write to the Channel Operation register clears any pending interrupts */
20262306a36Sopenharmony_ci#define CLEAR_PENDING_INTERRUPTS (ENABLE_COUNTERS | ENABLE_INTERRUPT_FUNCTION)
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci/* Each Counter is 24 bits wide */
20562306a36Sopenharmony_ci#define LS7267_CNTR_MAX GENMASK(23, 0)
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic __always_inline int quad8_control_register_update(struct regmap *const map, u8 *const buf,
20862306a36Sopenharmony_ci							 const size_t channel, const u8 val,
20962306a36Sopenharmony_ci							 const u8 field)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	u8p_replace_bits(&buf[channel], val, field);
21262306a36Sopenharmony_ci	return regmap_write(map, QUAD8_CONTROL(channel), buf[channel]);
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic int quad8_signal_read(struct counter_device *counter,
21662306a36Sopenharmony_ci			     struct counter_signal *signal,
21762306a36Sopenharmony_ci			     enum counter_signal_level *level)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	const struct quad8 *const priv = counter_priv(counter);
22062306a36Sopenharmony_ci	int ret;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/* Only Index signal levels can be read */
22362306a36Sopenharmony_ci	if (signal->id < 16)
22462306a36Sopenharmony_ci		return -EINVAL;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	ret = regmap_test_bits(priv->map, QUAD8_INDEX_INPUT_LEVELS, BIT(signal->id - 16));
22762306a36Sopenharmony_ci	if (ret < 0)
22862306a36Sopenharmony_ci		return ret;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	*level = (ret) ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	return 0;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic int quad8_count_read(struct counter_device *counter,
23662306a36Sopenharmony_ci			    struct counter_count *count, u64 *val)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct quad8 *const priv = counter_priv(counter);
23962306a36Sopenharmony_ci	unsigned long irqflags;
24062306a36Sopenharmony_ci	u8 value[3];
24162306a36Sopenharmony_ci	int ret;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, irqflags);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	ret = regmap_write(priv->map, QUAD8_CONTROL(count->id),
24662306a36Sopenharmony_ci			   SELECT_RLD | RESET_BP | TRANSFER_CNTR_TO_OL);
24762306a36Sopenharmony_ci	if (ret)
24862306a36Sopenharmony_ci		goto exit_unlock;
24962306a36Sopenharmony_ci	ret = regmap_noinc_read(priv->map, QUAD8_DATA(count->id), value, sizeof(value));
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ciexit_unlock:
25262306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, irqflags);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	*val = get_unaligned_le24(value);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return ret;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic int quad8_preset_register_set(struct quad8 *const priv, const size_t id,
26062306a36Sopenharmony_ci				     const unsigned long preset)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	u8 value[3];
26362306a36Sopenharmony_ci	int ret;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	put_unaligned_le24(preset, value);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	ret = regmap_write(priv->map, QUAD8_CONTROL(id), SELECT_RLD | RESET_BP);
26862306a36Sopenharmony_ci	if (ret)
26962306a36Sopenharmony_ci		return ret;
27062306a36Sopenharmony_ci	return regmap_noinc_write(priv->map, QUAD8_DATA(id), value, sizeof(value));
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic int quad8_flag_register_reset(struct quad8 *const priv, const size_t id)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	int ret;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	ret = regmap_write(priv->map, QUAD8_CONTROL(id), SELECT_RLD | RESET_BT_CT_CPT_S_IDX);
27862306a36Sopenharmony_ci	if (ret)
27962306a36Sopenharmony_ci		return ret;
28062306a36Sopenharmony_ci	return regmap_write(priv->map, QUAD8_CONTROL(id), SELECT_RLD | RESET_E);
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic int quad8_count_write(struct counter_device *counter,
28462306a36Sopenharmony_ci			     struct counter_count *count, u64 val)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	struct quad8 *const priv = counter_priv(counter);
28762306a36Sopenharmony_ci	unsigned long irqflags;
28862306a36Sopenharmony_ci	int ret;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	if (val > LS7267_CNTR_MAX)
29162306a36Sopenharmony_ci		return -ERANGE;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, irqflags);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/* Counter can only be set via Preset Register */
29662306a36Sopenharmony_ci	ret = quad8_preset_register_set(priv, count->id, val);
29762306a36Sopenharmony_ci	if (ret)
29862306a36Sopenharmony_ci		goto exit_unlock;
29962306a36Sopenharmony_ci	ret = regmap_write(priv->map, QUAD8_CONTROL(count->id), SELECT_RLD | TRANSFER_PR_TO_CNTR);
30062306a36Sopenharmony_ci	if (ret)
30162306a36Sopenharmony_ci		goto exit_unlock;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	ret = quad8_flag_register_reset(priv, count->id);
30462306a36Sopenharmony_ci	if (ret)
30562306a36Sopenharmony_ci		goto exit_unlock;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/* Set Preset Register back to original value */
30862306a36Sopenharmony_ci	ret = quad8_preset_register_set(priv, count->id, priv->preset[count->id]);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ciexit_unlock:
31162306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, irqflags);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	return ret;
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic const enum counter_function quad8_count_functions_list[] = {
31762306a36Sopenharmony_ci	COUNTER_FUNCTION_PULSE_DIRECTION,
31862306a36Sopenharmony_ci	COUNTER_FUNCTION_QUADRATURE_X1_A,
31962306a36Sopenharmony_ci	COUNTER_FUNCTION_QUADRATURE_X2_A,
32062306a36Sopenharmony_ci	COUNTER_FUNCTION_QUADRATURE_X4,
32162306a36Sopenharmony_ci};
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic int quad8_function_get(const struct quad8 *const priv, const size_t id,
32462306a36Sopenharmony_ci			      enum counter_function *const function)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	switch (u8_get_bits(priv->cmr[id], QUADRATURE_MODE)) {
32762306a36Sopenharmony_ci	case NON_QUADRATURE:
32862306a36Sopenharmony_ci		*function = COUNTER_FUNCTION_PULSE_DIRECTION;
32962306a36Sopenharmony_ci		return 0;
33062306a36Sopenharmony_ci	case QUADRATURE_X1:
33162306a36Sopenharmony_ci		*function = COUNTER_FUNCTION_QUADRATURE_X1_A;
33262306a36Sopenharmony_ci		return 0;
33362306a36Sopenharmony_ci	case QUADRATURE_X2:
33462306a36Sopenharmony_ci		*function = COUNTER_FUNCTION_QUADRATURE_X2_A;
33562306a36Sopenharmony_ci		return 0;
33662306a36Sopenharmony_ci	case QUADRATURE_X4:
33762306a36Sopenharmony_ci		*function = COUNTER_FUNCTION_QUADRATURE_X4;
33862306a36Sopenharmony_ci		return 0;
33962306a36Sopenharmony_ci	default:
34062306a36Sopenharmony_ci		/* should never reach this path */
34162306a36Sopenharmony_ci		return -EINVAL;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic int quad8_function_read(struct counter_device *counter,
34662306a36Sopenharmony_ci			       struct counter_count *count,
34762306a36Sopenharmony_ci			       enum counter_function *function)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	struct quad8 *const priv = counter_priv(counter);
35062306a36Sopenharmony_ci	unsigned long irqflags;
35162306a36Sopenharmony_ci	int retval;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, irqflags);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	retval = quad8_function_get(priv, count->id, function);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, irqflags);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	return retval;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic int quad8_function_write(struct counter_device *counter,
36362306a36Sopenharmony_ci				struct counter_count *count,
36462306a36Sopenharmony_ci				enum counter_function function)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct quad8 *const priv = counter_priv(counter);
36762306a36Sopenharmony_ci	const int id = count->id;
36862306a36Sopenharmony_ci	unsigned long irqflags;
36962306a36Sopenharmony_ci	unsigned int mode_cfg;
37062306a36Sopenharmony_ci	bool synchronous_mode;
37162306a36Sopenharmony_ci	int ret;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	switch (function) {
37462306a36Sopenharmony_ci	case COUNTER_FUNCTION_PULSE_DIRECTION:
37562306a36Sopenharmony_ci		mode_cfg = NON_QUADRATURE;
37662306a36Sopenharmony_ci		break;
37762306a36Sopenharmony_ci	case COUNTER_FUNCTION_QUADRATURE_X1_A:
37862306a36Sopenharmony_ci		mode_cfg = QUADRATURE_X1;
37962306a36Sopenharmony_ci		break;
38062306a36Sopenharmony_ci	case COUNTER_FUNCTION_QUADRATURE_X2_A:
38162306a36Sopenharmony_ci		mode_cfg = QUADRATURE_X2;
38262306a36Sopenharmony_ci		break;
38362306a36Sopenharmony_ci	case COUNTER_FUNCTION_QUADRATURE_X4:
38462306a36Sopenharmony_ci		mode_cfg = QUADRATURE_X4;
38562306a36Sopenharmony_ci		break;
38662306a36Sopenharmony_ci	default:
38762306a36Sopenharmony_ci		/* should never reach this path */
38862306a36Sopenharmony_ci		return -EINVAL;
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, irqflags);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	/* Synchronous function not supported in non-quadrature mode */
39462306a36Sopenharmony_ci	synchronous_mode = u8_get_bits(priv->idr[id], INDEX_MODE) == ENABLE_INDEX_MODE;
39562306a36Sopenharmony_ci	if (synchronous_mode && mode_cfg == NON_QUADRATURE) {
39662306a36Sopenharmony_ci		ret = quad8_control_register_update(priv->map, priv->idr, id, DISABLE_INDEX_MODE,
39762306a36Sopenharmony_ci						    INDEX_MODE);
39862306a36Sopenharmony_ci		if (ret)
39962306a36Sopenharmony_ci			goto exit_unlock;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	ret = quad8_control_register_update(priv->map, priv->cmr, id, mode_cfg, QUADRATURE_MODE);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ciexit_unlock:
40562306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, irqflags);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	return ret;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic int quad8_direction_read(struct counter_device *counter,
41162306a36Sopenharmony_ci				struct counter_count *count,
41262306a36Sopenharmony_ci				enum counter_count_direction *direction)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	const struct quad8 *const priv = counter_priv(counter);
41562306a36Sopenharmony_ci	unsigned int flag;
41662306a36Sopenharmony_ci	int ret;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	ret = regmap_read(priv->map, QUAD8_CONTROL(count->id), &flag);
41962306a36Sopenharmony_ci	if (ret)
42062306a36Sopenharmony_ci		return ret;
42162306a36Sopenharmony_ci	*direction = (u8_get_bits(flag, FLAG_UD) == UP) ? COUNTER_COUNT_DIRECTION_FORWARD :
42262306a36Sopenharmony_ci		COUNTER_COUNT_DIRECTION_BACKWARD;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	return 0;
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic const enum counter_synapse_action quad8_index_actions_list[] = {
42862306a36Sopenharmony_ci	COUNTER_SYNAPSE_ACTION_NONE,
42962306a36Sopenharmony_ci	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
43062306a36Sopenharmony_ci};
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic const enum counter_synapse_action quad8_synapse_actions_list[] = {
43362306a36Sopenharmony_ci	COUNTER_SYNAPSE_ACTION_NONE,
43462306a36Sopenharmony_ci	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
43562306a36Sopenharmony_ci	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
43662306a36Sopenharmony_ci	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
43762306a36Sopenharmony_ci};
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic int quad8_action_read(struct counter_device *counter,
44062306a36Sopenharmony_ci			     struct counter_count *count,
44162306a36Sopenharmony_ci			     struct counter_synapse *synapse,
44262306a36Sopenharmony_ci			     enum counter_synapse_action *action)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	struct quad8 *const priv = counter_priv(counter);
44562306a36Sopenharmony_ci	unsigned long irqflags;
44662306a36Sopenharmony_ci	int err;
44762306a36Sopenharmony_ci	enum counter_function function;
44862306a36Sopenharmony_ci	const size_t signal_a_id = count->synapses[0].signal->id;
44962306a36Sopenharmony_ci	enum counter_count_direction direction;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	/* Default action mode */
45262306a36Sopenharmony_ci	*action = COUNTER_SYNAPSE_ACTION_NONE;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	/* Handle Index signals */
45562306a36Sopenharmony_ci	if (synapse->signal->id >= 16) {
45662306a36Sopenharmony_ci		if (u8_get_bits(priv->ior[count->id], LOAD_PIN) == LOAD_CNTR)
45762306a36Sopenharmony_ci			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
45862306a36Sopenharmony_ci		return 0;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, irqflags);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	/* Get Count function and direction atomically */
46462306a36Sopenharmony_ci	err = quad8_function_get(priv, count->id, &function);
46562306a36Sopenharmony_ci	if (err) {
46662306a36Sopenharmony_ci		spin_unlock_irqrestore(&priv->lock, irqflags);
46762306a36Sopenharmony_ci		return err;
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci	err = quad8_direction_read(counter, count, &direction);
47062306a36Sopenharmony_ci	if (err) {
47162306a36Sopenharmony_ci		spin_unlock_irqrestore(&priv->lock, irqflags);
47262306a36Sopenharmony_ci		return err;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, irqflags);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	/* Determine action mode based on current count function mode */
47862306a36Sopenharmony_ci	switch (function) {
47962306a36Sopenharmony_ci	case COUNTER_FUNCTION_PULSE_DIRECTION:
48062306a36Sopenharmony_ci		if (synapse->signal->id == signal_a_id)
48162306a36Sopenharmony_ci			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
48262306a36Sopenharmony_ci		return 0;
48362306a36Sopenharmony_ci	case COUNTER_FUNCTION_QUADRATURE_X1_A:
48462306a36Sopenharmony_ci		if (synapse->signal->id == signal_a_id) {
48562306a36Sopenharmony_ci			if (direction == COUNTER_COUNT_DIRECTION_FORWARD)
48662306a36Sopenharmony_ci				*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
48762306a36Sopenharmony_ci			else
48862306a36Sopenharmony_ci				*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
48962306a36Sopenharmony_ci		}
49062306a36Sopenharmony_ci		return 0;
49162306a36Sopenharmony_ci	case COUNTER_FUNCTION_QUADRATURE_X2_A:
49262306a36Sopenharmony_ci		if (synapse->signal->id == signal_a_id)
49362306a36Sopenharmony_ci			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
49462306a36Sopenharmony_ci		return 0;
49562306a36Sopenharmony_ci	case COUNTER_FUNCTION_QUADRATURE_X4:
49662306a36Sopenharmony_ci		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
49762306a36Sopenharmony_ci		return 0;
49862306a36Sopenharmony_ci	default:
49962306a36Sopenharmony_ci		/* should never reach this path */
50062306a36Sopenharmony_ci		return -EINVAL;
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic int quad8_events_configure(struct counter_device *counter)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	struct quad8 *const priv = counter_priv(counter);
50762306a36Sopenharmony_ci	unsigned long irq_enabled = 0;
50862306a36Sopenharmony_ci	unsigned long irqflags;
50962306a36Sopenharmony_ci	struct counter_event_node *event_node;
51062306a36Sopenharmony_ci	u8 flg_pins;
51162306a36Sopenharmony_ci	int ret;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, irqflags);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	list_for_each_entry(event_node, &counter->events_list, l) {
51662306a36Sopenharmony_ci		switch (event_node->event) {
51762306a36Sopenharmony_ci		case COUNTER_EVENT_OVERFLOW:
51862306a36Sopenharmony_ci			flg_pins = FLG1_CARRY_FLG2_BORROW;
51962306a36Sopenharmony_ci			break;
52062306a36Sopenharmony_ci		case COUNTER_EVENT_THRESHOLD:
52162306a36Sopenharmony_ci			flg_pins = FLG1_COMPARE_FLG2_BORROW;
52262306a36Sopenharmony_ci			break;
52362306a36Sopenharmony_ci		case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
52462306a36Sopenharmony_ci			flg_pins = FLG1_CARRYBORROW_FLG2_UD;
52562306a36Sopenharmony_ci			break;
52662306a36Sopenharmony_ci		case COUNTER_EVENT_INDEX:
52762306a36Sopenharmony_ci			flg_pins = FLG1_INDX_FLG2_E;
52862306a36Sopenharmony_ci			break;
52962306a36Sopenharmony_ci		default:
53062306a36Sopenharmony_ci			/* should never reach this path */
53162306a36Sopenharmony_ci			ret = -EINVAL;
53262306a36Sopenharmony_ci			goto exit_unlock;
53362306a36Sopenharmony_ci		}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci		/* Enable IRQ line */
53662306a36Sopenharmony_ci		irq_enabled |= BIT(event_node->channel);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci		/* Skip configuration if it is the same as previously set */
53962306a36Sopenharmony_ci		if (flg_pins == u8_get_bits(priv->ior[event_node->channel], FLG_PINS))
54062306a36Sopenharmony_ci			continue;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci		/* Save new IRQ function configuration */
54362306a36Sopenharmony_ci		ret = quad8_control_register_update(priv->map, priv->ior, event_node->channel,
54462306a36Sopenharmony_ci						    flg_pins, FLG_PINS);
54562306a36Sopenharmony_ci		if (ret)
54662306a36Sopenharmony_ci			goto exit_unlock;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	ret = regmap_write(priv->map, QUAD8_INDEX_INTERRUPT, irq_enabled);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ciexit_unlock:
55262306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, irqflags);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	return ret;
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_cistatic int quad8_watch_validate(struct counter_device *counter,
55862306a36Sopenharmony_ci				const struct counter_watch *watch)
55962306a36Sopenharmony_ci{
56062306a36Sopenharmony_ci	struct counter_event_node *event_node;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if (watch->channel > QUAD8_NUM_COUNTERS - 1)
56362306a36Sopenharmony_ci		return -EINVAL;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	switch (watch->event) {
56662306a36Sopenharmony_ci	case COUNTER_EVENT_OVERFLOW:
56762306a36Sopenharmony_ci	case COUNTER_EVENT_THRESHOLD:
56862306a36Sopenharmony_ci	case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
56962306a36Sopenharmony_ci	case COUNTER_EVENT_INDEX:
57062306a36Sopenharmony_ci		list_for_each_entry(event_node, &counter->next_events_list, l)
57162306a36Sopenharmony_ci			if (watch->channel == event_node->channel &&
57262306a36Sopenharmony_ci				watch->event != event_node->event)
57362306a36Sopenharmony_ci				return -EINVAL;
57462306a36Sopenharmony_ci		return 0;
57562306a36Sopenharmony_ci	default:
57662306a36Sopenharmony_ci		return -EINVAL;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic const struct counter_ops quad8_ops = {
58162306a36Sopenharmony_ci	.signal_read = quad8_signal_read,
58262306a36Sopenharmony_ci	.count_read = quad8_count_read,
58362306a36Sopenharmony_ci	.count_write = quad8_count_write,
58462306a36Sopenharmony_ci	.function_read = quad8_function_read,
58562306a36Sopenharmony_ci	.function_write = quad8_function_write,
58662306a36Sopenharmony_ci	.action_read = quad8_action_read,
58762306a36Sopenharmony_ci	.events_configure = quad8_events_configure,
58862306a36Sopenharmony_ci	.watch_validate = quad8_watch_validate,
58962306a36Sopenharmony_ci};
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_cistatic const char *const quad8_index_polarity_modes[] = {
59262306a36Sopenharmony_ci	"negative",
59362306a36Sopenharmony_ci	"positive"
59462306a36Sopenharmony_ci};
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic int quad8_index_polarity_get(struct counter_device *counter,
59762306a36Sopenharmony_ci				    struct counter_signal *signal,
59862306a36Sopenharmony_ci				    u32 *index_polarity)
59962306a36Sopenharmony_ci{
60062306a36Sopenharmony_ci	const struct quad8 *const priv = counter_priv(counter);
60162306a36Sopenharmony_ci	const size_t channel_id = signal->id - 16;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	*index_polarity = u8_get_bits(priv->idr[channel_id], INDEX_POLARITY);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	return 0;
60662306a36Sopenharmony_ci}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_cistatic int quad8_index_polarity_set(struct counter_device *counter,
60962306a36Sopenharmony_ci				    struct counter_signal *signal,
61062306a36Sopenharmony_ci				    u32 index_polarity)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct quad8 *const priv = counter_priv(counter);
61362306a36Sopenharmony_ci	const size_t channel_id = signal->id - 16;
61462306a36Sopenharmony_ci	unsigned long irqflags;
61562306a36Sopenharmony_ci	int ret;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, irqflags);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	ret = quad8_control_register_update(priv->map, priv->idr, channel_id, index_polarity,
62062306a36Sopenharmony_ci					    INDEX_POLARITY);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, irqflags);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	return ret;
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_cistatic int quad8_polarity_read(struct counter_device *counter,
62862306a36Sopenharmony_ci			       struct counter_signal *signal,
62962306a36Sopenharmony_ci			       enum counter_signal_polarity *polarity)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	int err;
63262306a36Sopenharmony_ci	u32 index_polarity;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	err = quad8_index_polarity_get(counter, signal, &index_polarity);
63562306a36Sopenharmony_ci	if (err)
63662306a36Sopenharmony_ci		return err;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	*polarity = (index_polarity == POSITIVE_INDEX_POLARITY) ? COUNTER_SIGNAL_POLARITY_POSITIVE :
63962306a36Sopenharmony_ci		COUNTER_SIGNAL_POLARITY_NEGATIVE;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	return 0;
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_cistatic int quad8_polarity_write(struct counter_device *counter,
64562306a36Sopenharmony_ci				struct counter_signal *signal,
64662306a36Sopenharmony_ci				enum counter_signal_polarity polarity)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	const u32 pol = (polarity == COUNTER_SIGNAL_POLARITY_POSITIVE) ? POSITIVE_INDEX_POLARITY :
64962306a36Sopenharmony_ci									 NEGATIVE_INDEX_POLARITY;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	return quad8_index_polarity_set(counter, signal, pol);
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_cistatic const char *const quad8_synchronous_modes[] = {
65562306a36Sopenharmony_ci	"non-synchronous",
65662306a36Sopenharmony_ci	"synchronous"
65762306a36Sopenharmony_ci};
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cistatic int quad8_synchronous_mode_get(struct counter_device *counter,
66062306a36Sopenharmony_ci				      struct counter_signal *signal,
66162306a36Sopenharmony_ci				      u32 *synchronous_mode)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	const struct quad8 *const priv = counter_priv(counter);
66462306a36Sopenharmony_ci	const size_t channel_id = signal->id - 16;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	*synchronous_mode = u8_get_bits(priv->idr[channel_id], INDEX_MODE);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	return 0;
66962306a36Sopenharmony_ci}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_cistatic int quad8_synchronous_mode_set(struct counter_device *counter,
67262306a36Sopenharmony_ci				      struct counter_signal *signal,
67362306a36Sopenharmony_ci				      u32 synchronous_mode)
67462306a36Sopenharmony_ci{
67562306a36Sopenharmony_ci	struct quad8 *const priv = counter_priv(counter);
67662306a36Sopenharmony_ci	const size_t channel_id = signal->id - 16;
67762306a36Sopenharmony_ci	u8 quadrature_mode;
67862306a36Sopenharmony_ci	unsigned long irqflags;
67962306a36Sopenharmony_ci	int ret;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, irqflags);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	/* Index function must be non-synchronous in non-quadrature mode */
68462306a36Sopenharmony_ci	quadrature_mode = u8_get_bits(priv->idr[channel_id], QUADRATURE_MODE);
68562306a36Sopenharmony_ci	if (synchronous_mode && quadrature_mode == NON_QUADRATURE) {
68662306a36Sopenharmony_ci		ret = -EINVAL;
68762306a36Sopenharmony_ci		goto exit_unlock;
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	ret = quad8_control_register_update(priv->map, priv->idr, channel_id, synchronous_mode,
69162306a36Sopenharmony_ci					    INDEX_MODE);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ciexit_unlock:
69462306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, irqflags);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	return ret;
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic int quad8_count_floor_read(struct counter_device *counter,
70062306a36Sopenharmony_ci				  struct counter_count *count, u64 *floor)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	/* Only a floor of 0 is supported */
70362306a36Sopenharmony_ci	*floor = 0;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	return 0;
70662306a36Sopenharmony_ci}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_cistatic int quad8_count_mode_read(struct counter_device *counter,
70962306a36Sopenharmony_ci				 struct counter_count *count,
71062306a36Sopenharmony_ci				 enum counter_count_mode *cnt_mode)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	const struct quad8 *const priv = counter_priv(counter);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	switch (u8_get_bits(priv->cmr[count->id], COUNT_MODE)) {
71562306a36Sopenharmony_ci	case NORMAL_COUNT:
71662306a36Sopenharmony_ci		*cnt_mode = COUNTER_COUNT_MODE_NORMAL;
71762306a36Sopenharmony_ci		break;
71862306a36Sopenharmony_ci	case RANGE_LIMIT:
71962306a36Sopenharmony_ci		*cnt_mode = COUNTER_COUNT_MODE_RANGE_LIMIT;
72062306a36Sopenharmony_ci		break;
72162306a36Sopenharmony_ci	case NON_RECYCLE_COUNT:
72262306a36Sopenharmony_ci		*cnt_mode = COUNTER_COUNT_MODE_NON_RECYCLE;
72362306a36Sopenharmony_ci		break;
72462306a36Sopenharmony_ci	case MODULO_N:
72562306a36Sopenharmony_ci		*cnt_mode = COUNTER_COUNT_MODE_MODULO_N;
72662306a36Sopenharmony_ci		break;
72762306a36Sopenharmony_ci	}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	return 0;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_cistatic int quad8_count_mode_write(struct counter_device *counter,
73362306a36Sopenharmony_ci				  struct counter_count *count,
73462306a36Sopenharmony_ci				  enum counter_count_mode cnt_mode)
73562306a36Sopenharmony_ci{
73662306a36Sopenharmony_ci	struct quad8 *const priv = counter_priv(counter);
73762306a36Sopenharmony_ci	unsigned int count_mode;
73862306a36Sopenharmony_ci	unsigned long irqflags;
73962306a36Sopenharmony_ci	int ret;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	switch (cnt_mode) {
74262306a36Sopenharmony_ci	case COUNTER_COUNT_MODE_NORMAL:
74362306a36Sopenharmony_ci		count_mode = NORMAL_COUNT;
74462306a36Sopenharmony_ci		break;
74562306a36Sopenharmony_ci	case COUNTER_COUNT_MODE_RANGE_LIMIT:
74662306a36Sopenharmony_ci		count_mode = RANGE_LIMIT;
74762306a36Sopenharmony_ci		break;
74862306a36Sopenharmony_ci	case COUNTER_COUNT_MODE_NON_RECYCLE:
74962306a36Sopenharmony_ci		count_mode = NON_RECYCLE_COUNT;
75062306a36Sopenharmony_ci		break;
75162306a36Sopenharmony_ci	case COUNTER_COUNT_MODE_MODULO_N:
75262306a36Sopenharmony_ci		count_mode = MODULO_N;
75362306a36Sopenharmony_ci		break;
75462306a36Sopenharmony_ci	default:
75562306a36Sopenharmony_ci		/* should never reach this path */
75662306a36Sopenharmony_ci		return -EINVAL;
75762306a36Sopenharmony_ci	}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, irqflags);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	ret = quad8_control_register_update(priv->map, priv->cmr, count->id, count_mode,
76262306a36Sopenharmony_ci					    COUNT_MODE);
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, irqflags);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	return ret;
76762306a36Sopenharmony_ci}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_cistatic int quad8_count_enable_read(struct counter_device *counter,
77062306a36Sopenharmony_ci				   struct counter_count *count, u8 *enable)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	const struct quad8 *const priv = counter_priv(counter);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	*enable = u8_get_bits(priv->ior[count->id], AB_GATE);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	return 0;
77762306a36Sopenharmony_ci}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_cistatic int quad8_count_enable_write(struct counter_device *counter,
78062306a36Sopenharmony_ci				    struct counter_count *count, u8 enable)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	struct quad8 *const priv = counter_priv(counter);
78362306a36Sopenharmony_ci	unsigned long irqflags;
78462306a36Sopenharmony_ci	int ret;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, irqflags);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	ret = quad8_control_register_update(priv->map, priv->ior, count->id, enable, AB_GATE);
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, irqflags);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	return ret;
79362306a36Sopenharmony_ci}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_cistatic const char *const quad8_noise_error_states[] = {
79662306a36Sopenharmony_ci	"No excessive noise is present at the count inputs",
79762306a36Sopenharmony_ci	"Excessive noise is present at the count inputs"
79862306a36Sopenharmony_ci};
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic int quad8_error_noise_get(struct counter_device *counter,
80162306a36Sopenharmony_ci				 struct counter_count *count, u32 *noise_error)
80262306a36Sopenharmony_ci{
80362306a36Sopenharmony_ci	const struct quad8 *const priv = counter_priv(counter);
80462306a36Sopenharmony_ci	unsigned int flag;
80562306a36Sopenharmony_ci	int ret;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	ret = regmap_read(priv->map, QUAD8_CONTROL(count->id), &flag);
80862306a36Sopenharmony_ci	if (ret)
80962306a36Sopenharmony_ci		return ret;
81062306a36Sopenharmony_ci	*noise_error = u8_get_bits(flag, FLAG_E);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	return 0;
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_cistatic int quad8_count_preset_read(struct counter_device *counter,
81662306a36Sopenharmony_ci				   struct counter_count *count, u64 *preset)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	const struct quad8 *const priv = counter_priv(counter);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	*preset = priv->preset[count->id];
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	return 0;
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_cistatic int quad8_count_preset_write(struct counter_device *counter,
82662306a36Sopenharmony_ci				    struct counter_count *count, u64 preset)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	struct quad8 *const priv = counter_priv(counter);
82962306a36Sopenharmony_ci	unsigned long irqflags;
83062306a36Sopenharmony_ci	int ret;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	if (preset > LS7267_CNTR_MAX)
83362306a36Sopenharmony_ci		return -ERANGE;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, irqflags);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	priv->preset[count->id] = preset;
83862306a36Sopenharmony_ci	ret = quad8_preset_register_set(priv, count->id, preset);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, irqflags);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	return ret;
84362306a36Sopenharmony_ci}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_cistatic int quad8_count_ceiling_read(struct counter_device *counter,
84662306a36Sopenharmony_ci				    struct counter_count *count, u64 *ceiling)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	struct quad8 *const priv = counter_priv(counter);
84962306a36Sopenharmony_ci	unsigned long irqflags;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, irqflags);
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	/* Range Limit and Modulo-N count modes use preset value as ceiling */
85462306a36Sopenharmony_ci	switch (u8_get_bits(priv->cmr[count->id], COUNT_MODE)) {
85562306a36Sopenharmony_ci	case RANGE_LIMIT:
85662306a36Sopenharmony_ci	case MODULO_N:
85762306a36Sopenharmony_ci		*ceiling = priv->preset[count->id];
85862306a36Sopenharmony_ci		break;
85962306a36Sopenharmony_ci	default:
86062306a36Sopenharmony_ci		*ceiling = LS7267_CNTR_MAX;
86162306a36Sopenharmony_ci		break;
86262306a36Sopenharmony_ci	}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, irqflags);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	return 0;
86762306a36Sopenharmony_ci}
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_cistatic int quad8_count_ceiling_write(struct counter_device *counter,
87062306a36Sopenharmony_ci				     struct counter_count *count, u64 ceiling)
87162306a36Sopenharmony_ci{
87262306a36Sopenharmony_ci	struct quad8 *const priv = counter_priv(counter);
87362306a36Sopenharmony_ci	unsigned long irqflags;
87462306a36Sopenharmony_ci	int ret;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	if (ceiling > LS7267_CNTR_MAX)
87762306a36Sopenharmony_ci		return -ERANGE;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, irqflags);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	/* Range Limit and Modulo-N count modes use preset value as ceiling */
88262306a36Sopenharmony_ci	switch (u8_get_bits(priv->cmr[count->id], COUNT_MODE)) {
88362306a36Sopenharmony_ci	case RANGE_LIMIT:
88462306a36Sopenharmony_ci	case MODULO_N:
88562306a36Sopenharmony_ci		priv->preset[count->id] = ceiling;
88662306a36Sopenharmony_ci		ret = quad8_preset_register_set(priv, count->id, ceiling);
88762306a36Sopenharmony_ci		break;
88862306a36Sopenharmony_ci	default:
88962306a36Sopenharmony_ci		ret = -EINVAL;
89062306a36Sopenharmony_ci		break;
89162306a36Sopenharmony_ci	}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, irqflags);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	return ret;
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_cistatic int quad8_count_preset_enable_read(struct counter_device *counter,
89962306a36Sopenharmony_ci					  struct counter_count *count,
90062306a36Sopenharmony_ci					  u8 *preset_enable)
90162306a36Sopenharmony_ci{
90262306a36Sopenharmony_ci	const struct quad8 *const priv = counter_priv(counter);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	/* Preset enable is active low in Input/Output Control register */
90562306a36Sopenharmony_ci	*preset_enable = !u8_get_bits(priv->ior[count->id], LOAD_PIN);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	return 0;
90862306a36Sopenharmony_ci}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_cistatic int quad8_count_preset_enable_write(struct counter_device *counter,
91162306a36Sopenharmony_ci					   struct counter_count *count,
91262306a36Sopenharmony_ci					   u8 preset_enable)
91362306a36Sopenharmony_ci{
91462306a36Sopenharmony_ci	struct quad8 *const priv = counter_priv(counter);
91562306a36Sopenharmony_ci	unsigned long irqflags;
91662306a36Sopenharmony_ci	int ret;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, irqflags);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	/* Preset enable is active low in Input/Output Control register */
92162306a36Sopenharmony_ci	ret = quad8_control_register_update(priv->map, priv->ior, count->id, !preset_enable,
92262306a36Sopenharmony_ci					    LOAD_PIN);
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, irqflags);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	return ret;
92762306a36Sopenharmony_ci}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_cistatic int quad8_signal_cable_fault_read(struct counter_device *counter,
93062306a36Sopenharmony_ci					 struct counter_signal *signal,
93162306a36Sopenharmony_ci					 u8 *cable_fault)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	struct quad8 *const priv = counter_priv(counter);
93462306a36Sopenharmony_ci	const size_t channel_id = signal->id / 2;
93562306a36Sopenharmony_ci	unsigned long irqflags;
93662306a36Sopenharmony_ci	bool disabled;
93762306a36Sopenharmony_ci	int ret;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, irqflags);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	disabled = !(priv->cable_fault_enable & BIT(channel_id));
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	if (disabled) {
94462306a36Sopenharmony_ci		spin_unlock_irqrestore(&priv->lock, irqflags);
94562306a36Sopenharmony_ci		return -EINVAL;
94662306a36Sopenharmony_ci	}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	ret = regmap_test_bits(priv->map, QUAD8_CABLE_STATUS, BIT(channel_id));
94962306a36Sopenharmony_ci	if (ret < 0) {
95062306a36Sopenharmony_ci		spin_unlock_irqrestore(&priv->lock, irqflags);
95162306a36Sopenharmony_ci		return ret;
95262306a36Sopenharmony_ci	}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, irqflags);
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	/* Logic 0 = cable fault */
95762306a36Sopenharmony_ci	*cable_fault = !ret;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	return 0;
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_cistatic int quad8_signal_cable_fault_enable_read(struct counter_device *counter,
96362306a36Sopenharmony_ci						struct counter_signal *signal,
96462306a36Sopenharmony_ci						u8 *enable)
96562306a36Sopenharmony_ci{
96662306a36Sopenharmony_ci	const struct quad8 *const priv = counter_priv(counter);
96762306a36Sopenharmony_ci	const size_t channel_id = signal->id / 2;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	*enable = !!(priv->cable_fault_enable & BIT(channel_id));
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	return 0;
97262306a36Sopenharmony_ci}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_cistatic int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
97562306a36Sopenharmony_ci						 struct counter_signal *signal,
97662306a36Sopenharmony_ci						 u8 enable)
97762306a36Sopenharmony_ci{
97862306a36Sopenharmony_ci	struct quad8 *const priv = counter_priv(counter);
97962306a36Sopenharmony_ci	const size_t channel_id = signal->id / 2;
98062306a36Sopenharmony_ci	unsigned long irqflags;
98162306a36Sopenharmony_ci	unsigned int cable_fault_enable;
98262306a36Sopenharmony_ci	int ret;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, irqflags);
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	if (enable)
98762306a36Sopenharmony_ci		priv->cable_fault_enable |= BIT(channel_id);
98862306a36Sopenharmony_ci	else
98962306a36Sopenharmony_ci		priv->cable_fault_enable &= ~BIT(channel_id);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	/* Enable is active low in Differential Encoder Cable Status register */
99262306a36Sopenharmony_ci	cable_fault_enable = ~priv->cable_fault_enable;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	ret = regmap_write(priv->map, QUAD8_CABLE_STATUS, cable_fault_enable);
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, irqflags);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	return ret;
99962306a36Sopenharmony_ci}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_cistatic int quad8_signal_fck_prescaler_read(struct counter_device *counter,
100262306a36Sopenharmony_ci					   struct counter_signal *signal,
100362306a36Sopenharmony_ci					   u8 *prescaler)
100462306a36Sopenharmony_ci{
100562306a36Sopenharmony_ci	const struct quad8 *const priv = counter_priv(counter);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	*prescaler = priv->fck_prescaler[signal->id / 2];
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	return 0;
101062306a36Sopenharmony_ci}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_cistatic int quad8_filter_clock_prescaler_set(struct quad8 *const priv, const size_t id,
101362306a36Sopenharmony_ci					    const u8 prescaler)
101462306a36Sopenharmony_ci{
101562306a36Sopenharmony_ci	int ret;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	ret = regmap_write(priv->map, QUAD8_CONTROL(id), SELECT_RLD | RESET_BP);
101862306a36Sopenharmony_ci	if (ret)
101962306a36Sopenharmony_ci		return ret;
102062306a36Sopenharmony_ci	ret = regmap_write(priv->map, QUAD8_DATA(id), prescaler);
102162306a36Sopenharmony_ci	if (ret)
102262306a36Sopenharmony_ci		return ret;
102362306a36Sopenharmony_ci	return regmap_write(priv->map, QUAD8_CONTROL(id), SELECT_RLD | TRANSFER_PR0_TO_PSC);
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_cistatic int quad8_signal_fck_prescaler_write(struct counter_device *counter,
102762306a36Sopenharmony_ci					    struct counter_signal *signal,
102862306a36Sopenharmony_ci					    u8 prescaler)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	struct quad8 *const priv = counter_priv(counter);
103162306a36Sopenharmony_ci	const size_t channel_id = signal->id / 2;
103262306a36Sopenharmony_ci	unsigned long irqflags;
103362306a36Sopenharmony_ci	int ret;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, irqflags);
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	priv->fck_prescaler[channel_id] = prescaler;
103862306a36Sopenharmony_ci	ret = quad8_filter_clock_prescaler_set(priv, channel_id, prescaler);
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, irqflags);
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	return ret;
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_cistatic struct counter_comp quad8_signal_ext[] = {
104662306a36Sopenharmony_ci	COUNTER_COMP_SIGNAL_BOOL("cable_fault", quad8_signal_cable_fault_read,
104762306a36Sopenharmony_ci				 NULL),
104862306a36Sopenharmony_ci	COUNTER_COMP_SIGNAL_BOOL("cable_fault_enable",
104962306a36Sopenharmony_ci				 quad8_signal_cable_fault_enable_read,
105062306a36Sopenharmony_ci				 quad8_signal_cable_fault_enable_write),
105162306a36Sopenharmony_ci	COUNTER_COMP_SIGNAL_U8("filter_clock_prescaler",
105262306a36Sopenharmony_ci			       quad8_signal_fck_prescaler_read,
105362306a36Sopenharmony_ci			       quad8_signal_fck_prescaler_write)
105462306a36Sopenharmony_ci};
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_cistatic const enum counter_signal_polarity quad8_polarities[] = {
105762306a36Sopenharmony_ci	COUNTER_SIGNAL_POLARITY_POSITIVE,
105862306a36Sopenharmony_ci	COUNTER_SIGNAL_POLARITY_NEGATIVE,
105962306a36Sopenharmony_ci};
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_cistatic DEFINE_COUNTER_AVAILABLE(quad8_polarity_available, quad8_polarities);
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_cistatic DEFINE_COUNTER_ENUM(quad8_index_pol_enum, quad8_index_polarity_modes);
106462306a36Sopenharmony_cistatic DEFINE_COUNTER_ENUM(quad8_synch_mode_enum, quad8_synchronous_modes);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_cistatic struct counter_comp quad8_index_ext[] = {
106762306a36Sopenharmony_ci	COUNTER_COMP_SIGNAL_ENUM("index_polarity", quad8_index_polarity_get,
106862306a36Sopenharmony_ci				 quad8_index_polarity_set,
106962306a36Sopenharmony_ci				 quad8_index_pol_enum),
107062306a36Sopenharmony_ci	COUNTER_COMP_POLARITY(quad8_polarity_read, quad8_polarity_write,
107162306a36Sopenharmony_ci			      quad8_polarity_available),
107262306a36Sopenharmony_ci	COUNTER_COMP_SIGNAL_ENUM("synchronous_mode", quad8_synchronous_mode_get,
107362306a36Sopenharmony_ci				 quad8_synchronous_mode_set,
107462306a36Sopenharmony_ci				 quad8_synch_mode_enum),
107562306a36Sopenharmony_ci};
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci#define QUAD8_QUAD_SIGNAL(_id, _name) {		\
107862306a36Sopenharmony_ci	.id = (_id),				\
107962306a36Sopenharmony_ci	.name = (_name),			\
108062306a36Sopenharmony_ci	.ext = quad8_signal_ext,		\
108162306a36Sopenharmony_ci	.num_ext = ARRAY_SIZE(quad8_signal_ext)	\
108262306a36Sopenharmony_ci}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci#define	QUAD8_INDEX_SIGNAL(_id, _name) {	\
108562306a36Sopenharmony_ci	.id = (_id),				\
108662306a36Sopenharmony_ci	.name = (_name),			\
108762306a36Sopenharmony_ci	.ext = quad8_index_ext,			\
108862306a36Sopenharmony_ci	.num_ext = ARRAY_SIZE(quad8_index_ext)	\
108962306a36Sopenharmony_ci}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_cistatic struct counter_signal quad8_signals[] = {
109262306a36Sopenharmony_ci	QUAD8_QUAD_SIGNAL(0, "Channel 1 Quadrature A"),
109362306a36Sopenharmony_ci	QUAD8_QUAD_SIGNAL(1, "Channel 1 Quadrature B"),
109462306a36Sopenharmony_ci	QUAD8_QUAD_SIGNAL(2, "Channel 2 Quadrature A"),
109562306a36Sopenharmony_ci	QUAD8_QUAD_SIGNAL(3, "Channel 2 Quadrature B"),
109662306a36Sopenharmony_ci	QUAD8_QUAD_SIGNAL(4, "Channel 3 Quadrature A"),
109762306a36Sopenharmony_ci	QUAD8_QUAD_SIGNAL(5, "Channel 3 Quadrature B"),
109862306a36Sopenharmony_ci	QUAD8_QUAD_SIGNAL(6, "Channel 4 Quadrature A"),
109962306a36Sopenharmony_ci	QUAD8_QUAD_SIGNAL(7, "Channel 4 Quadrature B"),
110062306a36Sopenharmony_ci	QUAD8_QUAD_SIGNAL(8, "Channel 5 Quadrature A"),
110162306a36Sopenharmony_ci	QUAD8_QUAD_SIGNAL(9, "Channel 5 Quadrature B"),
110262306a36Sopenharmony_ci	QUAD8_QUAD_SIGNAL(10, "Channel 6 Quadrature A"),
110362306a36Sopenharmony_ci	QUAD8_QUAD_SIGNAL(11, "Channel 6 Quadrature B"),
110462306a36Sopenharmony_ci	QUAD8_QUAD_SIGNAL(12, "Channel 7 Quadrature A"),
110562306a36Sopenharmony_ci	QUAD8_QUAD_SIGNAL(13, "Channel 7 Quadrature B"),
110662306a36Sopenharmony_ci	QUAD8_QUAD_SIGNAL(14, "Channel 8 Quadrature A"),
110762306a36Sopenharmony_ci	QUAD8_QUAD_SIGNAL(15, "Channel 8 Quadrature B"),
110862306a36Sopenharmony_ci	QUAD8_INDEX_SIGNAL(16, "Channel 1 Index"),
110962306a36Sopenharmony_ci	QUAD8_INDEX_SIGNAL(17, "Channel 2 Index"),
111062306a36Sopenharmony_ci	QUAD8_INDEX_SIGNAL(18, "Channel 3 Index"),
111162306a36Sopenharmony_ci	QUAD8_INDEX_SIGNAL(19, "Channel 4 Index"),
111262306a36Sopenharmony_ci	QUAD8_INDEX_SIGNAL(20, "Channel 5 Index"),
111362306a36Sopenharmony_ci	QUAD8_INDEX_SIGNAL(21, "Channel 6 Index"),
111462306a36Sopenharmony_ci	QUAD8_INDEX_SIGNAL(22, "Channel 7 Index"),
111562306a36Sopenharmony_ci	QUAD8_INDEX_SIGNAL(23, "Channel 8 Index")
111662306a36Sopenharmony_ci};
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci#define QUAD8_COUNT_SYNAPSES(_id) {					\
111962306a36Sopenharmony_ci	{								\
112062306a36Sopenharmony_ci		.actions_list = quad8_synapse_actions_list,		\
112162306a36Sopenharmony_ci		.num_actions = ARRAY_SIZE(quad8_synapse_actions_list),	\
112262306a36Sopenharmony_ci		.signal = quad8_signals + 2 * (_id)			\
112362306a36Sopenharmony_ci	},								\
112462306a36Sopenharmony_ci	{								\
112562306a36Sopenharmony_ci		.actions_list = quad8_synapse_actions_list,		\
112662306a36Sopenharmony_ci		.num_actions = ARRAY_SIZE(quad8_synapse_actions_list),	\
112762306a36Sopenharmony_ci		.signal = quad8_signals + 2 * (_id) + 1			\
112862306a36Sopenharmony_ci	},								\
112962306a36Sopenharmony_ci	{								\
113062306a36Sopenharmony_ci		.actions_list = quad8_index_actions_list,		\
113162306a36Sopenharmony_ci		.num_actions = ARRAY_SIZE(quad8_index_actions_list),	\
113262306a36Sopenharmony_ci		.signal = quad8_signals + 2 * (_id) + 16		\
113362306a36Sopenharmony_ci	}								\
113462306a36Sopenharmony_ci}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_cistatic struct counter_synapse quad8_count_synapses[][3] = {
113762306a36Sopenharmony_ci	QUAD8_COUNT_SYNAPSES(0), QUAD8_COUNT_SYNAPSES(1),
113862306a36Sopenharmony_ci	QUAD8_COUNT_SYNAPSES(2), QUAD8_COUNT_SYNAPSES(3),
113962306a36Sopenharmony_ci	QUAD8_COUNT_SYNAPSES(4), QUAD8_COUNT_SYNAPSES(5),
114062306a36Sopenharmony_ci	QUAD8_COUNT_SYNAPSES(6), QUAD8_COUNT_SYNAPSES(7)
114162306a36Sopenharmony_ci};
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_cistatic const enum counter_count_mode quad8_cnt_modes[] = {
114462306a36Sopenharmony_ci	COUNTER_COUNT_MODE_NORMAL,
114562306a36Sopenharmony_ci	COUNTER_COUNT_MODE_RANGE_LIMIT,
114662306a36Sopenharmony_ci	COUNTER_COUNT_MODE_NON_RECYCLE,
114762306a36Sopenharmony_ci	COUNTER_COUNT_MODE_MODULO_N,
114862306a36Sopenharmony_ci};
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_cistatic DEFINE_COUNTER_AVAILABLE(quad8_count_mode_available, quad8_cnt_modes);
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_cistatic DEFINE_COUNTER_ENUM(quad8_error_noise_enum, quad8_noise_error_states);
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_cistatic struct counter_comp quad8_count_ext[] = {
115562306a36Sopenharmony_ci	COUNTER_COMP_CEILING(quad8_count_ceiling_read,
115662306a36Sopenharmony_ci			     quad8_count_ceiling_write),
115762306a36Sopenharmony_ci	COUNTER_COMP_FLOOR(quad8_count_floor_read, NULL),
115862306a36Sopenharmony_ci	COUNTER_COMP_COUNT_MODE(quad8_count_mode_read, quad8_count_mode_write,
115962306a36Sopenharmony_ci				quad8_count_mode_available),
116062306a36Sopenharmony_ci	COUNTER_COMP_DIRECTION(quad8_direction_read),
116162306a36Sopenharmony_ci	COUNTER_COMP_ENABLE(quad8_count_enable_read, quad8_count_enable_write),
116262306a36Sopenharmony_ci	COUNTER_COMP_COUNT_ENUM("error_noise", quad8_error_noise_get, NULL,
116362306a36Sopenharmony_ci				quad8_error_noise_enum),
116462306a36Sopenharmony_ci	COUNTER_COMP_PRESET(quad8_count_preset_read, quad8_count_preset_write),
116562306a36Sopenharmony_ci	COUNTER_COMP_PRESET_ENABLE(quad8_count_preset_enable_read,
116662306a36Sopenharmony_ci				   quad8_count_preset_enable_write),
116762306a36Sopenharmony_ci};
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci#define QUAD8_COUNT(_id, _cntname) {					\
117062306a36Sopenharmony_ci	.id = (_id),							\
117162306a36Sopenharmony_ci	.name = (_cntname),						\
117262306a36Sopenharmony_ci	.functions_list = quad8_count_functions_list,			\
117362306a36Sopenharmony_ci	.num_functions = ARRAY_SIZE(quad8_count_functions_list),	\
117462306a36Sopenharmony_ci	.synapses = quad8_count_synapses[(_id)],			\
117562306a36Sopenharmony_ci	.num_synapses =	2,						\
117662306a36Sopenharmony_ci	.ext = quad8_count_ext,						\
117762306a36Sopenharmony_ci	.num_ext = ARRAY_SIZE(quad8_count_ext)				\
117862306a36Sopenharmony_ci}
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_cistatic struct counter_count quad8_counts[] = {
118162306a36Sopenharmony_ci	QUAD8_COUNT(0, "Channel 1 Count"),
118262306a36Sopenharmony_ci	QUAD8_COUNT(1, "Channel 2 Count"),
118362306a36Sopenharmony_ci	QUAD8_COUNT(2, "Channel 3 Count"),
118462306a36Sopenharmony_ci	QUAD8_COUNT(3, "Channel 4 Count"),
118562306a36Sopenharmony_ci	QUAD8_COUNT(4, "Channel 5 Count"),
118662306a36Sopenharmony_ci	QUAD8_COUNT(5, "Channel 6 Count"),
118762306a36Sopenharmony_ci	QUAD8_COUNT(6, "Channel 7 Count"),
118862306a36Sopenharmony_ci	QUAD8_COUNT(7, "Channel 8 Count")
118962306a36Sopenharmony_ci};
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_cistatic irqreturn_t quad8_irq_handler(int irq, void *private)
119262306a36Sopenharmony_ci{
119362306a36Sopenharmony_ci	struct counter_device *counter = private;
119462306a36Sopenharmony_ci	struct quad8 *const priv = counter_priv(counter);
119562306a36Sopenharmony_ci	unsigned int status;
119662306a36Sopenharmony_ci	unsigned long irq_status;
119762306a36Sopenharmony_ci	unsigned long channel;
119862306a36Sopenharmony_ci	unsigned int flg_pins;
119962306a36Sopenharmony_ci	u8 event;
120062306a36Sopenharmony_ci	int ret;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	ret = regmap_read(priv->map, QUAD8_INTERRUPT_STATUS, &status);
120362306a36Sopenharmony_ci	if (ret)
120462306a36Sopenharmony_ci		return ret;
120562306a36Sopenharmony_ci	if (!status)
120662306a36Sopenharmony_ci		return IRQ_NONE;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	irq_status = status;
120962306a36Sopenharmony_ci	for_each_set_bit(channel, &irq_status, QUAD8_NUM_COUNTERS) {
121062306a36Sopenharmony_ci		flg_pins = u8_get_bits(priv->ior[channel], FLG_PINS);
121162306a36Sopenharmony_ci		switch (flg_pins) {
121262306a36Sopenharmony_ci		case FLG1_CARRY_FLG2_BORROW:
121362306a36Sopenharmony_ci			event = COUNTER_EVENT_OVERFLOW;
121462306a36Sopenharmony_ci				break;
121562306a36Sopenharmony_ci		case FLG1_COMPARE_FLG2_BORROW:
121662306a36Sopenharmony_ci			event = COUNTER_EVENT_THRESHOLD;
121762306a36Sopenharmony_ci				break;
121862306a36Sopenharmony_ci		case FLG1_CARRYBORROW_FLG2_UD:
121962306a36Sopenharmony_ci			event = COUNTER_EVENT_OVERFLOW_UNDERFLOW;
122062306a36Sopenharmony_ci				break;
122162306a36Sopenharmony_ci		case FLG1_INDX_FLG2_E:
122262306a36Sopenharmony_ci			event = COUNTER_EVENT_INDEX;
122362306a36Sopenharmony_ci				break;
122462306a36Sopenharmony_ci		default:
122562306a36Sopenharmony_ci			/* should never reach this path */
122662306a36Sopenharmony_ci			WARN_ONCE(true, "invalid interrupt trigger function %u configured for channel %lu\n",
122762306a36Sopenharmony_ci				  flg_pins, channel);
122862306a36Sopenharmony_ci			continue;
122962306a36Sopenharmony_ci		}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci		counter_push_event(counter, event, channel);
123262306a36Sopenharmony_ci	}
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	ret = regmap_write(priv->map, QUAD8_CHANNEL_OPERATION, CLEAR_PENDING_INTERRUPTS);
123562306a36Sopenharmony_ci	if (ret)
123662306a36Sopenharmony_ci		return ret;
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	return IRQ_HANDLED;
123962306a36Sopenharmony_ci}
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_cistatic int quad8_init_counter(struct quad8 *const priv, const size_t channel)
124262306a36Sopenharmony_ci{
124362306a36Sopenharmony_ci	int ret;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	ret = quad8_filter_clock_prescaler_set(priv, channel, 0);
124662306a36Sopenharmony_ci	if (ret)
124762306a36Sopenharmony_ci		return ret;
124862306a36Sopenharmony_ci	ret = quad8_preset_register_set(priv, channel, 0);
124962306a36Sopenharmony_ci	if (ret)
125062306a36Sopenharmony_ci		return ret;
125162306a36Sopenharmony_ci	ret = quad8_flag_register_reset(priv, channel);
125262306a36Sopenharmony_ci	if (ret)
125362306a36Sopenharmony_ci		return ret;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	/* Binary encoding; Normal count; non-quadrature mode */
125662306a36Sopenharmony_ci	priv->cmr[channel] = SELECT_CMR | BINARY | u8_encode_bits(NORMAL_COUNT, COUNT_MODE) |
125762306a36Sopenharmony_ci			     u8_encode_bits(NON_QUADRATURE, QUADRATURE_MODE);
125862306a36Sopenharmony_ci	ret = regmap_write(priv->map, QUAD8_CONTROL(channel), priv->cmr[channel]);
125962306a36Sopenharmony_ci	if (ret)
126062306a36Sopenharmony_ci		return ret;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	/* Disable A and B inputs; preset on index; FLG1 as Carry */
126362306a36Sopenharmony_ci	priv->ior[channel] = SELECT_IOR | DISABLE_AB | u8_encode_bits(LOAD_CNTR, LOAD_PIN) |
126462306a36Sopenharmony_ci			     u8_encode_bits(FLG1_CARRY_FLG2_BORROW, FLG_PINS);
126562306a36Sopenharmony_ci	ret = regmap_write(priv->map, QUAD8_CONTROL(channel), priv->ior[channel]);
126662306a36Sopenharmony_ci	if (ret)
126762306a36Sopenharmony_ci		return ret;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	/* Disable index function; negative index polarity */
127062306a36Sopenharmony_ci	priv->idr[channel] = SELECT_IDR | u8_encode_bits(DISABLE_INDEX_MODE, INDEX_MODE) |
127162306a36Sopenharmony_ci			     u8_encode_bits(NEGATIVE_INDEX_POLARITY, INDEX_POLARITY);
127262306a36Sopenharmony_ci	return regmap_write(priv->map, QUAD8_CONTROL(channel), priv->idr[channel]);
127362306a36Sopenharmony_ci}
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_cistatic int quad8_probe(struct device *dev, unsigned int id)
127662306a36Sopenharmony_ci{
127762306a36Sopenharmony_ci	struct counter_device *counter;
127862306a36Sopenharmony_ci	struct quad8 *priv;
127962306a36Sopenharmony_ci	void __iomem *regs;
128062306a36Sopenharmony_ci	unsigned long i;
128162306a36Sopenharmony_ci	int ret;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
128462306a36Sopenharmony_ci		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
128562306a36Sopenharmony_ci			base[id], base[id] + QUAD8_EXTENT);
128662306a36Sopenharmony_ci		return -EBUSY;
128762306a36Sopenharmony_ci	}
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	counter = devm_counter_alloc(dev, sizeof(*priv));
129062306a36Sopenharmony_ci	if (!counter)
129162306a36Sopenharmony_ci		return -ENOMEM;
129262306a36Sopenharmony_ci	priv = counter_priv(counter);
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	regs = devm_ioport_map(dev, base[id], QUAD8_EXTENT);
129562306a36Sopenharmony_ci	if (!regs)
129662306a36Sopenharmony_ci		return -ENOMEM;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	priv->map = devm_regmap_init_mmio(dev, regs, &quad8_regmap_config);
129962306a36Sopenharmony_ci	if (IS_ERR(priv->map))
130062306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(priv->map),
130162306a36Sopenharmony_ci				     "Unable to initialize register map\n");
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	/* Initialize Counter device and driver data */
130462306a36Sopenharmony_ci	counter->name = dev_name(dev);
130562306a36Sopenharmony_ci	counter->parent = dev;
130662306a36Sopenharmony_ci	counter->ops = &quad8_ops;
130762306a36Sopenharmony_ci	counter->counts = quad8_counts;
130862306a36Sopenharmony_ci	counter->num_counts = ARRAY_SIZE(quad8_counts);
130962306a36Sopenharmony_ci	counter->signals = quad8_signals;
131062306a36Sopenharmony_ci	counter->num_signals = ARRAY_SIZE(quad8_signals);
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	spin_lock_init(&priv->lock);
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	/* Reset Index/Interrupt Register */
131562306a36Sopenharmony_ci	ret = regmap_write(priv->map, QUAD8_INDEX_INTERRUPT, 0x00);
131662306a36Sopenharmony_ci	if (ret)
131762306a36Sopenharmony_ci		return ret;
131862306a36Sopenharmony_ci	/* Reset all counters and disable interrupt function */
131962306a36Sopenharmony_ci	ret = regmap_write(priv->map, QUAD8_CHANNEL_OPERATION,
132062306a36Sopenharmony_ci			   RESET_COUNTERS | DISABLE_INTERRUPT_FUNCTION);
132162306a36Sopenharmony_ci	if (ret)
132262306a36Sopenharmony_ci		return ret;
132362306a36Sopenharmony_ci	/* Set initial configuration for all counters */
132462306a36Sopenharmony_ci	for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
132562306a36Sopenharmony_ci		ret = quad8_init_counter(priv, i);
132662306a36Sopenharmony_ci		if (ret)
132762306a36Sopenharmony_ci			return ret;
132862306a36Sopenharmony_ci	}
132962306a36Sopenharmony_ci	/* Disable Differential Encoder Cable Status for all channels */
133062306a36Sopenharmony_ci	ret = regmap_write(priv->map, QUAD8_CABLE_STATUS, GENMASK(7, 0));
133162306a36Sopenharmony_ci	if (ret)
133262306a36Sopenharmony_ci		return ret;
133362306a36Sopenharmony_ci	/* Enable all counters and enable interrupt function */
133462306a36Sopenharmony_ci	ret = regmap_write(priv->map, QUAD8_CHANNEL_OPERATION,
133562306a36Sopenharmony_ci			   ENABLE_COUNTERS | ENABLE_INTERRUPT_FUNCTION);
133662306a36Sopenharmony_ci	if (ret)
133762306a36Sopenharmony_ci		return ret;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	ret = devm_request_irq(&counter->dev, irq[id], quad8_irq_handler,
134062306a36Sopenharmony_ci			       IRQF_SHARED, counter->name, counter);
134162306a36Sopenharmony_ci	if (ret)
134262306a36Sopenharmony_ci		return ret;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	ret = devm_counter_add(dev, counter);
134562306a36Sopenharmony_ci	if (ret < 0)
134662306a36Sopenharmony_ci		return dev_err_probe(dev, ret, "Failed to add counter\n");
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	return 0;
134962306a36Sopenharmony_ci}
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_cistatic struct isa_driver quad8_driver = {
135262306a36Sopenharmony_ci	.probe = quad8_probe,
135362306a36Sopenharmony_ci	.driver = {
135462306a36Sopenharmony_ci		.name = "104-quad-8"
135562306a36Sopenharmony_ci	}
135662306a36Sopenharmony_ci};
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_cimodule_isa_driver_with_irq(quad8_driver, num_quad8, num_irq);
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ciMODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
136162306a36Sopenharmony_ciMODULE_DESCRIPTION("ACCES 104-QUAD-8 driver");
136262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
136362306a36Sopenharmony_ciMODULE_IMPORT_NS(COUNTER);
1364