1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Intel 8254 Programmable Interval Timer
4 * Copyright (C) William Breathitt Gray
5 */
6#include <linux/bitfield.h>
7#include <linux/bits.h>
8#include <linux/counter.h>
9#include <linux/device.h>
10#include <linux/err.h>
11#include <linux/export.h>
12#include <linux/i8254.h>
13#include <linux/limits.h>
14#include <linux/module.h>
15#include <linux/mutex.h>
16#include <linux/regmap.h>
17
18#include <asm/unaligned.h>
19
20#define I8254_COUNTER_REG(_counter) (_counter)
21#define I8254_CONTROL_REG 0x3
22
23#define I8254_SC GENMASK(7, 6)
24#define I8254_RW GENMASK(5, 4)
25#define I8254_M GENMASK(3, 1)
26#define I8254_CONTROL(_sc, _rw, _m) \
27	(u8_encode_bits(_sc, I8254_SC) | u8_encode_bits(_rw, I8254_RW) | \
28	 u8_encode_bits(_m, I8254_M))
29
30#define I8254_RW_TWO_BYTE 0x3
31#define I8254_MODE_INTERRUPT_ON_TERMINAL_COUNT 0
32#define I8254_MODE_HARDWARE_RETRIGGERABLE_ONESHOT 1
33#define I8254_MODE_RATE_GENERATOR 2
34#define I8254_MODE_SQUARE_WAVE_MODE 3
35#define I8254_MODE_SOFTWARE_TRIGGERED_STROBE 4
36#define I8254_MODE_HARDWARE_TRIGGERED_STROBE 5
37
38#define I8254_COUNTER_LATCH(_counter) I8254_CONTROL(_counter, 0x0, 0x0)
39#define I8254_PROGRAM_COUNTER(_counter, _mode) I8254_CONTROL(_counter, I8254_RW_TWO_BYTE, _mode)
40
41#define I8254_NUM_COUNTERS 3
42
43/**
44 * struct i8254 - I8254 device private data structure
45 * @lock:	synchronization lock to prevent I/O race conditions
46 * @preset:	array of Counter Register states
47 * @out_mode:	array of mode configuration states
48 * @map:	Regmap for the device
49 */
50struct i8254 {
51	struct mutex lock;
52	u16 preset[I8254_NUM_COUNTERS];
53	u8 out_mode[I8254_NUM_COUNTERS];
54	struct regmap *map;
55};
56
57static int i8254_count_read(struct counter_device *const counter, struct counter_count *const count,
58			    u64 *const val)
59{
60	struct i8254 *const priv = counter_priv(counter);
61	int ret;
62	u8 value[2];
63
64	mutex_lock(&priv->lock);
65
66	ret = regmap_write(priv->map, I8254_CONTROL_REG, I8254_COUNTER_LATCH(count->id));
67	if (ret) {
68		mutex_unlock(&priv->lock);
69		return ret;
70	}
71	ret = regmap_noinc_read(priv->map, I8254_COUNTER_REG(count->id), value, sizeof(value));
72	if (ret) {
73		mutex_unlock(&priv->lock);
74		return ret;
75	}
76
77	mutex_unlock(&priv->lock);
78
79	*val = get_unaligned_le16(value);
80
81	return ret;
82}
83
84static int i8254_function_read(struct counter_device *const counter,
85			       struct counter_count *const count,
86			       enum counter_function *const function)
87{
88	*function = COUNTER_FUNCTION_DECREASE;
89	return 0;
90}
91
92#define I8254_SYNAPSES_PER_COUNT 2
93#define I8254_SIGNAL_ID_CLK 0
94#define I8254_SIGNAL_ID_GATE 1
95
96static int i8254_action_read(struct counter_device *const counter,
97			     struct counter_count *const count,
98			     struct counter_synapse *const synapse,
99			     enum counter_synapse_action *const action)
100{
101	struct i8254 *const priv = counter_priv(counter);
102
103	switch (synapse->signal->id % I8254_SYNAPSES_PER_COUNT) {
104	case I8254_SIGNAL_ID_CLK:
105		*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
106		return 0;
107	case I8254_SIGNAL_ID_GATE:
108		switch (priv->out_mode[count->id]) {
109		case I8254_MODE_HARDWARE_RETRIGGERABLE_ONESHOT:
110		case I8254_MODE_RATE_GENERATOR:
111		case I8254_MODE_SQUARE_WAVE_MODE:
112		case I8254_MODE_HARDWARE_TRIGGERED_STROBE:
113			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
114			return 0;
115		default:
116			*action = COUNTER_SYNAPSE_ACTION_NONE;
117			return 0;
118		}
119	default:
120		/* should never reach this path */
121		return -EINVAL;
122	}
123}
124
125static int i8254_count_ceiling_read(struct counter_device *const counter,
126				    struct counter_count *const count, u64 *const ceiling)
127{
128	struct i8254 *const priv = counter_priv(counter);
129
130	mutex_lock(&priv->lock);
131
132	switch (priv->out_mode[count->id]) {
133	case I8254_MODE_RATE_GENERATOR:
134		/* Rate Generator decrements 0 by one and the counter "wraps around" */
135		*ceiling = (priv->preset[count->id] == 0) ? U16_MAX : priv->preset[count->id];
136		break;
137	case I8254_MODE_SQUARE_WAVE_MODE:
138		if (priv->preset[count->id] % 2)
139			*ceiling = priv->preset[count->id] - 1;
140		else if (priv->preset[count->id] == 0)
141			/* Square Wave Mode decrements 0 by two and the counter "wraps around" */
142			*ceiling = U16_MAX - 1;
143		else
144			*ceiling = priv->preset[count->id];
145		break;
146	default:
147		*ceiling = U16_MAX;
148		break;
149	}
150
151	mutex_unlock(&priv->lock);
152
153	return 0;
154}
155
156static int i8254_count_mode_read(struct counter_device *const counter,
157				 struct counter_count *const count,
158				 enum counter_count_mode *const count_mode)
159{
160	const struct i8254 *const priv = counter_priv(counter);
161
162	switch (priv->out_mode[count->id]) {
163	case I8254_MODE_INTERRUPT_ON_TERMINAL_COUNT:
164		*count_mode = COUNTER_COUNT_MODE_INTERRUPT_ON_TERMINAL_COUNT;
165		return 0;
166	case I8254_MODE_HARDWARE_RETRIGGERABLE_ONESHOT:
167		*count_mode = COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT;
168		return 0;
169	case I8254_MODE_RATE_GENERATOR:
170		*count_mode = COUNTER_COUNT_MODE_RATE_GENERATOR;
171		return 0;
172	case I8254_MODE_SQUARE_WAVE_MODE:
173		*count_mode = COUNTER_COUNT_MODE_SQUARE_WAVE_MODE;
174		return 0;
175	case I8254_MODE_SOFTWARE_TRIGGERED_STROBE:
176		*count_mode = COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE;
177		return 0;
178	case I8254_MODE_HARDWARE_TRIGGERED_STROBE:
179		*count_mode = COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE;
180		return 0;
181	default:
182		/* should never reach this path */
183		return -EINVAL;
184	}
185}
186
187static int i8254_count_mode_write(struct counter_device *const counter,
188				  struct counter_count *const count,
189				  const enum counter_count_mode count_mode)
190{
191	struct i8254 *const priv = counter_priv(counter);
192	u8 out_mode;
193	int ret;
194
195	switch (count_mode) {
196	case COUNTER_COUNT_MODE_INTERRUPT_ON_TERMINAL_COUNT:
197		out_mode = I8254_MODE_INTERRUPT_ON_TERMINAL_COUNT;
198		break;
199	case COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT:
200		out_mode = I8254_MODE_HARDWARE_RETRIGGERABLE_ONESHOT;
201		break;
202	case COUNTER_COUNT_MODE_RATE_GENERATOR:
203		out_mode = I8254_MODE_RATE_GENERATOR;
204		break;
205	case COUNTER_COUNT_MODE_SQUARE_WAVE_MODE:
206		out_mode = I8254_MODE_SQUARE_WAVE_MODE;
207		break;
208	case COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE:
209		out_mode = I8254_MODE_SOFTWARE_TRIGGERED_STROBE;
210		break;
211	case COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE:
212		out_mode = I8254_MODE_HARDWARE_TRIGGERED_STROBE;
213		break;
214	default:
215		/* should never reach this path */
216		return -EINVAL;
217	}
218
219	mutex_lock(&priv->lock);
220
221	/* Counter Register is cleared when the counter is programmed */
222	priv->preset[count->id] = 0;
223	priv->out_mode[count->id] = out_mode;
224	ret = regmap_write(priv->map, I8254_CONTROL_REG,
225			   I8254_PROGRAM_COUNTER(count->id, out_mode));
226
227	mutex_unlock(&priv->lock);
228
229	return ret;
230}
231
232static int i8254_count_floor_read(struct counter_device *const counter,
233				  struct counter_count *const count, u64 *const floor)
234{
235	struct i8254 *const priv = counter_priv(counter);
236
237	mutex_lock(&priv->lock);
238
239	switch (priv->out_mode[count->id]) {
240	case I8254_MODE_RATE_GENERATOR:
241		/* counter is always reloaded after 1, but 0 is a possible reload value */
242		*floor = (priv->preset[count->id] == 0) ? 0 : 1;
243		break;
244	case I8254_MODE_SQUARE_WAVE_MODE:
245		/* counter is always reloaded after 2 for even preset values */
246		*floor = (priv->preset[count->id] % 2 || priv->preset[count->id] == 0) ? 0 : 2;
247		break;
248	default:
249		*floor = 0;
250		break;
251	}
252
253	mutex_unlock(&priv->lock);
254
255	return 0;
256}
257
258static int i8254_count_preset_read(struct counter_device *const counter,
259				   struct counter_count *const count, u64 *const preset)
260{
261	const struct i8254 *const priv = counter_priv(counter);
262
263	*preset = priv->preset[count->id];
264
265	return 0;
266}
267
268static int i8254_count_preset_write(struct counter_device *const counter,
269				    struct counter_count *const count, const u64 preset)
270{
271	struct i8254 *const priv = counter_priv(counter);
272	int ret;
273	u8 value[2];
274
275	if (preset > U16_MAX)
276		return -ERANGE;
277
278	mutex_lock(&priv->lock);
279
280	if (priv->out_mode[count->id] == I8254_MODE_RATE_GENERATOR ||
281	    priv->out_mode[count->id] == I8254_MODE_SQUARE_WAVE_MODE) {
282		if (preset == 1) {
283			mutex_unlock(&priv->lock);
284			return -EINVAL;
285		}
286	}
287
288	priv->preset[count->id] = preset;
289
290	put_unaligned_le16(preset, value);
291	ret = regmap_noinc_write(priv->map, I8254_COUNTER_REG(count->id), value, 2);
292
293	mutex_unlock(&priv->lock);
294
295	return ret;
296}
297
298static int i8254_init_hw(struct regmap *const map)
299{
300	unsigned long i;
301	int ret;
302
303	for (i = 0; i < I8254_NUM_COUNTERS; i++) {
304		/* Initialize each counter to Mode 0 */
305		ret = regmap_write(map, I8254_CONTROL_REG,
306				   I8254_PROGRAM_COUNTER(i, I8254_MODE_INTERRUPT_ON_TERMINAL_COUNT));
307		if (ret)
308			return ret;
309	}
310
311	return 0;
312}
313
314static const struct counter_ops i8254_ops = {
315	.count_read = i8254_count_read,
316	.function_read = i8254_function_read,
317	.action_read = i8254_action_read,
318};
319
320#define I8254_SIGNAL(_id, _name) {		\
321	.id = (_id),				\
322	.name = (_name),			\
323}
324
325static struct counter_signal i8254_signals[] = {
326	I8254_SIGNAL(0, "CLK 0"), I8254_SIGNAL(1, "GATE 0"),
327	I8254_SIGNAL(2, "CLK 1"), I8254_SIGNAL(3, "GATE 1"),
328	I8254_SIGNAL(4, "CLK 2"), I8254_SIGNAL(5, "GATE 2"),
329};
330
331static const enum counter_synapse_action i8254_clk_actions[] = {
332	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
333};
334static const enum counter_synapse_action i8254_gate_actions[] = {
335	COUNTER_SYNAPSE_ACTION_NONE,
336	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
337};
338
339#define I8254_SYNAPSES_BASE(_id) ((_id) * I8254_SYNAPSES_PER_COUNT)
340#define I8254_SYNAPSE_CLK(_id) {					\
341	.actions_list	= i8254_clk_actions,				\
342	.num_actions	= ARRAY_SIZE(i8254_clk_actions),		\
343	.signal		= &i8254_signals[I8254_SYNAPSES_BASE(_id) + 0],	\
344}
345#define I8254_SYNAPSE_GATE(_id) {					\
346	.actions_list	= i8254_gate_actions,				\
347	.num_actions	= ARRAY_SIZE(i8254_gate_actions),		\
348	.signal		= &i8254_signals[I8254_SYNAPSES_BASE(_id) + 1],	\
349}
350
351static struct counter_synapse i8254_synapses[] = {
352	I8254_SYNAPSE_CLK(0), I8254_SYNAPSE_GATE(0),
353	I8254_SYNAPSE_CLK(1), I8254_SYNAPSE_GATE(1),
354	I8254_SYNAPSE_CLK(2), I8254_SYNAPSE_GATE(2),
355};
356
357static const enum counter_function i8254_functions_list[] = {
358	COUNTER_FUNCTION_DECREASE,
359};
360
361static const enum counter_count_mode i8254_count_modes[] = {
362	COUNTER_COUNT_MODE_INTERRUPT_ON_TERMINAL_COUNT,
363	COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT,
364	COUNTER_COUNT_MODE_RATE_GENERATOR,
365	COUNTER_COUNT_MODE_SQUARE_WAVE_MODE,
366	COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE,
367	COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE,
368};
369
370static DEFINE_COUNTER_AVAILABLE(i8254_count_modes_available, i8254_count_modes);
371
372static struct counter_comp i8254_count_ext[] = {
373	COUNTER_COMP_CEILING(i8254_count_ceiling_read, NULL),
374	COUNTER_COMP_COUNT_MODE(i8254_count_mode_read, i8254_count_mode_write,
375				i8254_count_modes_available),
376	COUNTER_COMP_FLOOR(i8254_count_floor_read, NULL),
377	COUNTER_COMP_PRESET(i8254_count_preset_read, i8254_count_preset_write),
378};
379
380#define I8254_COUNT(_id, _name) {				\
381	.id = (_id),						\
382	.name = (_name),					\
383	.functions_list = i8254_functions_list,			\
384	.num_functions = ARRAY_SIZE(i8254_functions_list),	\
385	.synapses = &i8254_synapses[I8254_SYNAPSES_BASE(_id)],	\
386	.num_synapses =	I8254_SYNAPSES_PER_COUNT,		\
387	.ext = i8254_count_ext,					\
388	.num_ext = ARRAY_SIZE(i8254_count_ext)			\
389}
390
391static struct counter_count i8254_counts[I8254_NUM_COUNTERS] = {
392	I8254_COUNT(0, "Counter 0"), I8254_COUNT(1, "Counter 1"), I8254_COUNT(2, "Counter 2"),
393};
394
395/**
396 * devm_i8254_regmap_register - Register an i8254 Counter device
397 * @dev: device that is registering this i8254 Counter device
398 * @config: configuration for i8254_regmap_config
399 *
400 * Registers an Intel 8254 Programmable Interval Timer Counter device. Returns 0 on success and
401 * negative error number on failure.
402 */
403int devm_i8254_regmap_register(struct device *const dev,
404			       const struct i8254_regmap_config *const config)
405{
406	struct counter_device *counter;
407	struct i8254 *priv;
408	int err;
409
410	if (!config->parent)
411		return -EINVAL;
412
413	if (!config->map)
414		return -EINVAL;
415
416	counter = devm_counter_alloc(dev, sizeof(*priv));
417	if (!counter)
418		return -ENOMEM;
419	priv = counter_priv(counter);
420	priv->map = config->map;
421
422	counter->name = dev_name(config->parent);
423	counter->parent = config->parent;
424	counter->ops = &i8254_ops;
425	counter->counts = i8254_counts;
426	counter->num_counts = ARRAY_SIZE(i8254_counts);
427	counter->signals = i8254_signals;
428	counter->num_signals = ARRAY_SIZE(i8254_signals);
429
430	mutex_init(&priv->lock);
431
432	err = i8254_init_hw(priv->map);
433	if (err)
434		return err;
435
436	err = devm_counter_add(dev, counter);
437	if (err < 0)
438		return dev_err_probe(dev, err, "Failed to add counter\n");
439
440	return 0;
441}
442EXPORT_SYMBOL_NS_GPL(devm_i8254_regmap_register, I8254);
443
444MODULE_AUTHOR("William Breathitt Gray");
445MODULE_DESCRIPTION("Intel 8254 Programmable Interval Timer");
446MODULE_LICENSE("GPL");
447MODULE_IMPORT_NS(COUNTER);
448