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(map: priv->map, I8254_CONTROL_REG, I8254_COUNTER_LATCH(count->id));
67 if (ret) {
68 mutex_unlock(lock: &priv->lock);
69 return ret;
70 }
71 ret = regmap_noinc_read(map: priv->map, I8254_COUNTER_REG(count->id), val: value, val_len: sizeof(value));
72 if (ret) {
73 mutex_unlock(lock: &priv->lock);
74 return ret;
75 }
76
77 mutex_unlock(lock: &priv->lock);
78
79 *val = get_unaligned_le16(p: 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(lock: &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(map: priv->map, I8254_CONTROL_REG,
225 I8254_PROGRAM_COUNTER(count->id, out_mode));
226
227 mutex_unlock(lock: &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(lock: &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(lock: &priv->lock);
284 return -EINVAL;
285 }
286 }
287
288 priv->preset[count->id] = preset;
289
290 put_unaligned_le16(val: preset, p: value);
291 ret = regmap_noinc_write(map: priv->map, I8254_COUNTER_REG(count->id), val: value, val_len: 2);
292
293 mutex_unlock(lock: &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: sizeof(*priv));
417 if (!counter)
418 return -ENOMEM;
419 priv = counter_priv(counter);
420 priv->map = config->map;
421
422 counter->name = dev_name(dev: 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(map: 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, fmt: "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

source code of linux/drivers/counter/i8254.c