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