xref: /kernel/linux/linux-6.6/drivers/counter/i8254.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Intel 8254 Programmable Interval Timer
462306a36Sopenharmony_ci * Copyright (C) William Breathitt Gray
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/bitfield.h>
762306a36Sopenharmony_ci#include <linux/bits.h>
862306a36Sopenharmony_ci#include <linux/counter.h>
962306a36Sopenharmony_ci#include <linux/device.h>
1062306a36Sopenharmony_ci#include <linux/err.h>
1162306a36Sopenharmony_ci#include <linux/export.h>
1262306a36Sopenharmony_ci#include <linux/i8254.h>
1362306a36Sopenharmony_ci#include <linux/limits.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/mutex.h>
1662306a36Sopenharmony_ci#include <linux/regmap.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <asm/unaligned.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define I8254_COUNTER_REG(_counter) (_counter)
2162306a36Sopenharmony_ci#define I8254_CONTROL_REG 0x3
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define I8254_SC GENMASK(7, 6)
2462306a36Sopenharmony_ci#define I8254_RW GENMASK(5, 4)
2562306a36Sopenharmony_ci#define I8254_M GENMASK(3, 1)
2662306a36Sopenharmony_ci#define I8254_CONTROL(_sc, _rw, _m) \
2762306a36Sopenharmony_ci	(u8_encode_bits(_sc, I8254_SC) | u8_encode_bits(_rw, I8254_RW) | \
2862306a36Sopenharmony_ci	 u8_encode_bits(_m, I8254_M))
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define I8254_RW_TWO_BYTE 0x3
3162306a36Sopenharmony_ci#define I8254_MODE_INTERRUPT_ON_TERMINAL_COUNT 0
3262306a36Sopenharmony_ci#define I8254_MODE_HARDWARE_RETRIGGERABLE_ONESHOT 1
3362306a36Sopenharmony_ci#define I8254_MODE_RATE_GENERATOR 2
3462306a36Sopenharmony_ci#define I8254_MODE_SQUARE_WAVE_MODE 3
3562306a36Sopenharmony_ci#define I8254_MODE_SOFTWARE_TRIGGERED_STROBE 4
3662306a36Sopenharmony_ci#define I8254_MODE_HARDWARE_TRIGGERED_STROBE 5
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define I8254_COUNTER_LATCH(_counter) I8254_CONTROL(_counter, 0x0, 0x0)
3962306a36Sopenharmony_ci#define I8254_PROGRAM_COUNTER(_counter, _mode) I8254_CONTROL(_counter, I8254_RW_TWO_BYTE, _mode)
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define I8254_NUM_COUNTERS 3
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/**
4462306a36Sopenharmony_ci * struct i8254 - I8254 device private data structure
4562306a36Sopenharmony_ci * @lock:	synchronization lock to prevent I/O race conditions
4662306a36Sopenharmony_ci * @preset:	array of Counter Register states
4762306a36Sopenharmony_ci * @out_mode:	array of mode configuration states
4862306a36Sopenharmony_ci * @map:	Regmap for the device
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_cistruct i8254 {
5162306a36Sopenharmony_ci	struct mutex lock;
5262306a36Sopenharmony_ci	u16 preset[I8254_NUM_COUNTERS];
5362306a36Sopenharmony_ci	u8 out_mode[I8254_NUM_COUNTERS];
5462306a36Sopenharmony_ci	struct regmap *map;
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic int i8254_count_read(struct counter_device *const counter, struct counter_count *const count,
5862306a36Sopenharmony_ci			    u64 *const val)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct i8254 *const priv = counter_priv(counter);
6162306a36Sopenharmony_ci	int ret;
6262306a36Sopenharmony_ci	u8 value[2];
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	mutex_lock(&priv->lock);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	ret = regmap_write(priv->map, I8254_CONTROL_REG, I8254_COUNTER_LATCH(count->id));
6762306a36Sopenharmony_ci	if (ret) {
6862306a36Sopenharmony_ci		mutex_unlock(&priv->lock);
6962306a36Sopenharmony_ci		return ret;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci	ret = regmap_noinc_read(priv->map, I8254_COUNTER_REG(count->id), value, sizeof(value));
7262306a36Sopenharmony_ci	if (ret) {
7362306a36Sopenharmony_ci		mutex_unlock(&priv->lock);
7462306a36Sopenharmony_ci		return ret;
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	*val = get_unaligned_le16(value);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	return ret;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic int i8254_function_read(struct counter_device *const counter,
8562306a36Sopenharmony_ci			       struct counter_count *const count,
8662306a36Sopenharmony_ci			       enum counter_function *const function)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	*function = COUNTER_FUNCTION_DECREASE;
8962306a36Sopenharmony_ci	return 0;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci#define I8254_SYNAPSES_PER_COUNT 2
9362306a36Sopenharmony_ci#define I8254_SIGNAL_ID_CLK 0
9462306a36Sopenharmony_ci#define I8254_SIGNAL_ID_GATE 1
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic int i8254_action_read(struct counter_device *const counter,
9762306a36Sopenharmony_ci			     struct counter_count *const count,
9862306a36Sopenharmony_ci			     struct counter_synapse *const synapse,
9962306a36Sopenharmony_ci			     enum counter_synapse_action *const action)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	struct i8254 *const priv = counter_priv(counter);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	switch (synapse->signal->id % I8254_SYNAPSES_PER_COUNT) {
10462306a36Sopenharmony_ci	case I8254_SIGNAL_ID_CLK:
10562306a36Sopenharmony_ci		*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
10662306a36Sopenharmony_ci		return 0;
10762306a36Sopenharmony_ci	case I8254_SIGNAL_ID_GATE:
10862306a36Sopenharmony_ci		switch (priv->out_mode[count->id]) {
10962306a36Sopenharmony_ci		case I8254_MODE_HARDWARE_RETRIGGERABLE_ONESHOT:
11062306a36Sopenharmony_ci		case I8254_MODE_RATE_GENERATOR:
11162306a36Sopenharmony_ci		case I8254_MODE_SQUARE_WAVE_MODE:
11262306a36Sopenharmony_ci		case I8254_MODE_HARDWARE_TRIGGERED_STROBE:
11362306a36Sopenharmony_ci			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
11462306a36Sopenharmony_ci			return 0;
11562306a36Sopenharmony_ci		default:
11662306a36Sopenharmony_ci			*action = COUNTER_SYNAPSE_ACTION_NONE;
11762306a36Sopenharmony_ci			return 0;
11862306a36Sopenharmony_ci		}
11962306a36Sopenharmony_ci	default:
12062306a36Sopenharmony_ci		/* should never reach this path */
12162306a36Sopenharmony_ci		return -EINVAL;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic int i8254_count_ceiling_read(struct counter_device *const counter,
12662306a36Sopenharmony_ci				    struct counter_count *const count, u64 *const ceiling)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct i8254 *const priv = counter_priv(counter);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	mutex_lock(&priv->lock);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	switch (priv->out_mode[count->id]) {
13362306a36Sopenharmony_ci	case I8254_MODE_RATE_GENERATOR:
13462306a36Sopenharmony_ci		/* Rate Generator decrements 0 by one and the counter "wraps around" */
13562306a36Sopenharmony_ci		*ceiling = (priv->preset[count->id] == 0) ? U16_MAX : priv->preset[count->id];
13662306a36Sopenharmony_ci		break;
13762306a36Sopenharmony_ci	case I8254_MODE_SQUARE_WAVE_MODE:
13862306a36Sopenharmony_ci		if (priv->preset[count->id] % 2)
13962306a36Sopenharmony_ci			*ceiling = priv->preset[count->id] - 1;
14062306a36Sopenharmony_ci		else if (priv->preset[count->id] == 0)
14162306a36Sopenharmony_ci			/* Square Wave Mode decrements 0 by two and the counter "wraps around" */
14262306a36Sopenharmony_ci			*ceiling = U16_MAX - 1;
14362306a36Sopenharmony_ci		else
14462306a36Sopenharmony_ci			*ceiling = priv->preset[count->id];
14562306a36Sopenharmony_ci		break;
14662306a36Sopenharmony_ci	default:
14762306a36Sopenharmony_ci		*ceiling = U16_MAX;
14862306a36Sopenharmony_ci		break;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return 0;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic int i8254_count_mode_read(struct counter_device *const counter,
15762306a36Sopenharmony_ci				 struct counter_count *const count,
15862306a36Sopenharmony_ci				 enum counter_count_mode *const count_mode)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	const struct i8254 *const priv = counter_priv(counter);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	switch (priv->out_mode[count->id]) {
16362306a36Sopenharmony_ci	case I8254_MODE_INTERRUPT_ON_TERMINAL_COUNT:
16462306a36Sopenharmony_ci		*count_mode = COUNTER_COUNT_MODE_INTERRUPT_ON_TERMINAL_COUNT;
16562306a36Sopenharmony_ci		return 0;
16662306a36Sopenharmony_ci	case I8254_MODE_HARDWARE_RETRIGGERABLE_ONESHOT:
16762306a36Sopenharmony_ci		*count_mode = COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT;
16862306a36Sopenharmony_ci		return 0;
16962306a36Sopenharmony_ci	case I8254_MODE_RATE_GENERATOR:
17062306a36Sopenharmony_ci		*count_mode = COUNTER_COUNT_MODE_RATE_GENERATOR;
17162306a36Sopenharmony_ci		return 0;
17262306a36Sopenharmony_ci	case I8254_MODE_SQUARE_WAVE_MODE:
17362306a36Sopenharmony_ci		*count_mode = COUNTER_COUNT_MODE_SQUARE_WAVE_MODE;
17462306a36Sopenharmony_ci		return 0;
17562306a36Sopenharmony_ci	case I8254_MODE_SOFTWARE_TRIGGERED_STROBE:
17662306a36Sopenharmony_ci		*count_mode = COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE;
17762306a36Sopenharmony_ci		return 0;
17862306a36Sopenharmony_ci	case I8254_MODE_HARDWARE_TRIGGERED_STROBE:
17962306a36Sopenharmony_ci		*count_mode = COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE;
18062306a36Sopenharmony_ci		return 0;
18162306a36Sopenharmony_ci	default:
18262306a36Sopenharmony_ci		/* should never reach this path */
18362306a36Sopenharmony_ci		return -EINVAL;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic int i8254_count_mode_write(struct counter_device *const counter,
18862306a36Sopenharmony_ci				  struct counter_count *const count,
18962306a36Sopenharmony_ci				  const enum counter_count_mode count_mode)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct i8254 *const priv = counter_priv(counter);
19262306a36Sopenharmony_ci	u8 out_mode;
19362306a36Sopenharmony_ci	int ret;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	switch (count_mode) {
19662306a36Sopenharmony_ci	case COUNTER_COUNT_MODE_INTERRUPT_ON_TERMINAL_COUNT:
19762306a36Sopenharmony_ci		out_mode = I8254_MODE_INTERRUPT_ON_TERMINAL_COUNT;
19862306a36Sopenharmony_ci		break;
19962306a36Sopenharmony_ci	case COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT:
20062306a36Sopenharmony_ci		out_mode = I8254_MODE_HARDWARE_RETRIGGERABLE_ONESHOT;
20162306a36Sopenharmony_ci		break;
20262306a36Sopenharmony_ci	case COUNTER_COUNT_MODE_RATE_GENERATOR:
20362306a36Sopenharmony_ci		out_mode = I8254_MODE_RATE_GENERATOR;
20462306a36Sopenharmony_ci		break;
20562306a36Sopenharmony_ci	case COUNTER_COUNT_MODE_SQUARE_WAVE_MODE:
20662306a36Sopenharmony_ci		out_mode = I8254_MODE_SQUARE_WAVE_MODE;
20762306a36Sopenharmony_ci		break;
20862306a36Sopenharmony_ci	case COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE:
20962306a36Sopenharmony_ci		out_mode = I8254_MODE_SOFTWARE_TRIGGERED_STROBE;
21062306a36Sopenharmony_ci		break;
21162306a36Sopenharmony_ci	case COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE:
21262306a36Sopenharmony_ci		out_mode = I8254_MODE_HARDWARE_TRIGGERED_STROBE;
21362306a36Sopenharmony_ci		break;
21462306a36Sopenharmony_ci	default:
21562306a36Sopenharmony_ci		/* should never reach this path */
21662306a36Sopenharmony_ci		return -EINVAL;
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	mutex_lock(&priv->lock);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	/* Counter Register is cleared when the counter is programmed */
22262306a36Sopenharmony_ci	priv->preset[count->id] = 0;
22362306a36Sopenharmony_ci	priv->out_mode[count->id] = out_mode;
22462306a36Sopenharmony_ci	ret = regmap_write(priv->map, I8254_CONTROL_REG,
22562306a36Sopenharmony_ci			   I8254_PROGRAM_COUNTER(count->id, out_mode));
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	return ret;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic int i8254_count_floor_read(struct counter_device *const counter,
23362306a36Sopenharmony_ci				  struct counter_count *const count, u64 *const floor)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct i8254 *const priv = counter_priv(counter);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	mutex_lock(&priv->lock);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	switch (priv->out_mode[count->id]) {
24062306a36Sopenharmony_ci	case I8254_MODE_RATE_GENERATOR:
24162306a36Sopenharmony_ci		/* counter is always reloaded after 1, but 0 is a possible reload value */
24262306a36Sopenharmony_ci		*floor = (priv->preset[count->id] == 0) ? 0 : 1;
24362306a36Sopenharmony_ci		break;
24462306a36Sopenharmony_ci	case I8254_MODE_SQUARE_WAVE_MODE:
24562306a36Sopenharmony_ci		/* counter is always reloaded after 2 for even preset values */
24662306a36Sopenharmony_ci		*floor = (priv->preset[count->id] % 2 || priv->preset[count->id] == 0) ? 0 : 2;
24762306a36Sopenharmony_ci		break;
24862306a36Sopenharmony_ci	default:
24962306a36Sopenharmony_ci		*floor = 0;
25062306a36Sopenharmony_ci		break;
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	return 0;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic int i8254_count_preset_read(struct counter_device *const counter,
25962306a36Sopenharmony_ci				   struct counter_count *const count, u64 *const preset)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	const struct i8254 *const priv = counter_priv(counter);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	*preset = priv->preset[count->id];
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	return 0;
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic int i8254_count_preset_write(struct counter_device *const counter,
26962306a36Sopenharmony_ci				    struct counter_count *const count, const u64 preset)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	struct i8254 *const priv = counter_priv(counter);
27262306a36Sopenharmony_ci	int ret;
27362306a36Sopenharmony_ci	u8 value[2];
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (preset > U16_MAX)
27662306a36Sopenharmony_ci		return -ERANGE;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	mutex_lock(&priv->lock);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (priv->out_mode[count->id] == I8254_MODE_RATE_GENERATOR ||
28162306a36Sopenharmony_ci	    priv->out_mode[count->id] == I8254_MODE_SQUARE_WAVE_MODE) {
28262306a36Sopenharmony_ci		if (preset == 1) {
28362306a36Sopenharmony_ci			mutex_unlock(&priv->lock);
28462306a36Sopenharmony_ci			return -EINVAL;
28562306a36Sopenharmony_ci		}
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	priv->preset[count->id] = preset;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	put_unaligned_le16(preset, value);
29162306a36Sopenharmony_ci	ret = regmap_noinc_write(priv->map, I8254_COUNTER_REG(count->id), value, 2);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return ret;
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic int i8254_init_hw(struct regmap *const map)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	unsigned long i;
30162306a36Sopenharmony_ci	int ret;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	for (i = 0; i < I8254_NUM_COUNTERS; i++) {
30462306a36Sopenharmony_ci		/* Initialize each counter to Mode 0 */
30562306a36Sopenharmony_ci		ret = regmap_write(map, I8254_CONTROL_REG,
30662306a36Sopenharmony_ci				   I8254_PROGRAM_COUNTER(i, I8254_MODE_INTERRUPT_ON_TERMINAL_COUNT));
30762306a36Sopenharmony_ci		if (ret)
30862306a36Sopenharmony_ci			return ret;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	return 0;
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic const struct counter_ops i8254_ops = {
31562306a36Sopenharmony_ci	.count_read = i8254_count_read,
31662306a36Sopenharmony_ci	.function_read = i8254_function_read,
31762306a36Sopenharmony_ci	.action_read = i8254_action_read,
31862306a36Sopenharmony_ci};
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci#define I8254_SIGNAL(_id, _name) {		\
32162306a36Sopenharmony_ci	.id = (_id),				\
32262306a36Sopenharmony_ci	.name = (_name),			\
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic struct counter_signal i8254_signals[] = {
32662306a36Sopenharmony_ci	I8254_SIGNAL(0, "CLK 0"), I8254_SIGNAL(1, "GATE 0"),
32762306a36Sopenharmony_ci	I8254_SIGNAL(2, "CLK 1"), I8254_SIGNAL(3, "GATE 1"),
32862306a36Sopenharmony_ci	I8254_SIGNAL(4, "CLK 2"), I8254_SIGNAL(5, "GATE 2"),
32962306a36Sopenharmony_ci};
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic const enum counter_synapse_action i8254_clk_actions[] = {
33262306a36Sopenharmony_ci	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
33362306a36Sopenharmony_ci};
33462306a36Sopenharmony_cistatic const enum counter_synapse_action i8254_gate_actions[] = {
33562306a36Sopenharmony_ci	COUNTER_SYNAPSE_ACTION_NONE,
33662306a36Sopenharmony_ci	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
33762306a36Sopenharmony_ci};
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci#define I8254_SYNAPSES_BASE(_id) ((_id) * I8254_SYNAPSES_PER_COUNT)
34062306a36Sopenharmony_ci#define I8254_SYNAPSE_CLK(_id) {					\
34162306a36Sopenharmony_ci	.actions_list	= i8254_clk_actions,				\
34262306a36Sopenharmony_ci	.num_actions	= ARRAY_SIZE(i8254_clk_actions),		\
34362306a36Sopenharmony_ci	.signal		= &i8254_signals[I8254_SYNAPSES_BASE(_id) + 0],	\
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci#define I8254_SYNAPSE_GATE(_id) {					\
34662306a36Sopenharmony_ci	.actions_list	= i8254_gate_actions,				\
34762306a36Sopenharmony_ci	.num_actions	= ARRAY_SIZE(i8254_gate_actions),		\
34862306a36Sopenharmony_ci	.signal		= &i8254_signals[I8254_SYNAPSES_BASE(_id) + 1],	\
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic struct counter_synapse i8254_synapses[] = {
35262306a36Sopenharmony_ci	I8254_SYNAPSE_CLK(0), I8254_SYNAPSE_GATE(0),
35362306a36Sopenharmony_ci	I8254_SYNAPSE_CLK(1), I8254_SYNAPSE_GATE(1),
35462306a36Sopenharmony_ci	I8254_SYNAPSE_CLK(2), I8254_SYNAPSE_GATE(2),
35562306a36Sopenharmony_ci};
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic const enum counter_function i8254_functions_list[] = {
35862306a36Sopenharmony_ci	COUNTER_FUNCTION_DECREASE,
35962306a36Sopenharmony_ci};
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic const enum counter_count_mode i8254_count_modes[] = {
36262306a36Sopenharmony_ci	COUNTER_COUNT_MODE_INTERRUPT_ON_TERMINAL_COUNT,
36362306a36Sopenharmony_ci	COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT,
36462306a36Sopenharmony_ci	COUNTER_COUNT_MODE_RATE_GENERATOR,
36562306a36Sopenharmony_ci	COUNTER_COUNT_MODE_SQUARE_WAVE_MODE,
36662306a36Sopenharmony_ci	COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE,
36762306a36Sopenharmony_ci	COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE,
36862306a36Sopenharmony_ci};
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic DEFINE_COUNTER_AVAILABLE(i8254_count_modes_available, i8254_count_modes);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic struct counter_comp i8254_count_ext[] = {
37362306a36Sopenharmony_ci	COUNTER_COMP_CEILING(i8254_count_ceiling_read, NULL),
37462306a36Sopenharmony_ci	COUNTER_COMP_COUNT_MODE(i8254_count_mode_read, i8254_count_mode_write,
37562306a36Sopenharmony_ci				i8254_count_modes_available),
37662306a36Sopenharmony_ci	COUNTER_COMP_FLOOR(i8254_count_floor_read, NULL),
37762306a36Sopenharmony_ci	COUNTER_COMP_PRESET(i8254_count_preset_read, i8254_count_preset_write),
37862306a36Sopenharmony_ci};
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci#define I8254_COUNT(_id, _name) {				\
38162306a36Sopenharmony_ci	.id = (_id),						\
38262306a36Sopenharmony_ci	.name = (_name),					\
38362306a36Sopenharmony_ci	.functions_list = i8254_functions_list,			\
38462306a36Sopenharmony_ci	.num_functions = ARRAY_SIZE(i8254_functions_list),	\
38562306a36Sopenharmony_ci	.synapses = &i8254_synapses[I8254_SYNAPSES_BASE(_id)],	\
38662306a36Sopenharmony_ci	.num_synapses =	I8254_SYNAPSES_PER_COUNT,		\
38762306a36Sopenharmony_ci	.ext = i8254_count_ext,					\
38862306a36Sopenharmony_ci	.num_ext = ARRAY_SIZE(i8254_count_ext)			\
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic struct counter_count i8254_counts[I8254_NUM_COUNTERS] = {
39262306a36Sopenharmony_ci	I8254_COUNT(0, "Counter 0"), I8254_COUNT(1, "Counter 1"), I8254_COUNT(2, "Counter 2"),
39362306a36Sopenharmony_ci};
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci/**
39662306a36Sopenharmony_ci * devm_i8254_regmap_register - Register an i8254 Counter device
39762306a36Sopenharmony_ci * @dev: device that is registering this i8254 Counter device
39862306a36Sopenharmony_ci * @config: configuration for i8254_regmap_config
39962306a36Sopenharmony_ci *
40062306a36Sopenharmony_ci * Registers an Intel 8254 Programmable Interval Timer Counter device. Returns 0 on success and
40162306a36Sopenharmony_ci * negative error number on failure.
40262306a36Sopenharmony_ci */
40362306a36Sopenharmony_ciint devm_i8254_regmap_register(struct device *const dev,
40462306a36Sopenharmony_ci			       const struct i8254_regmap_config *const config)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	struct counter_device *counter;
40762306a36Sopenharmony_ci	struct i8254 *priv;
40862306a36Sopenharmony_ci	int err;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	if (!config->parent)
41162306a36Sopenharmony_ci		return -EINVAL;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	if (!config->map)
41462306a36Sopenharmony_ci		return -EINVAL;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	counter = devm_counter_alloc(dev, sizeof(*priv));
41762306a36Sopenharmony_ci	if (!counter)
41862306a36Sopenharmony_ci		return -ENOMEM;
41962306a36Sopenharmony_ci	priv = counter_priv(counter);
42062306a36Sopenharmony_ci	priv->map = config->map;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	counter->name = dev_name(config->parent);
42362306a36Sopenharmony_ci	counter->parent = config->parent;
42462306a36Sopenharmony_ci	counter->ops = &i8254_ops;
42562306a36Sopenharmony_ci	counter->counts = i8254_counts;
42662306a36Sopenharmony_ci	counter->num_counts = ARRAY_SIZE(i8254_counts);
42762306a36Sopenharmony_ci	counter->signals = i8254_signals;
42862306a36Sopenharmony_ci	counter->num_signals = ARRAY_SIZE(i8254_signals);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	mutex_init(&priv->lock);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	err = i8254_init_hw(priv->map);
43362306a36Sopenharmony_ci	if (err)
43462306a36Sopenharmony_ci		return err;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	err = devm_counter_add(dev, counter);
43762306a36Sopenharmony_ci	if (err < 0)
43862306a36Sopenharmony_ci		return dev_err_probe(dev, err, "Failed to add counter\n");
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	return 0;
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(devm_i8254_regmap_register, I8254);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ciMODULE_AUTHOR("William Breathitt Gray");
44562306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel 8254 Programmable Interval Timer");
44662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
44762306a36Sopenharmony_ciMODULE_IMPORT_NS(COUNTER);
448