1 // SPDX-License-Identifier: GPL-2.0
3 * Intel 8254 Programmable Interval Timer
4 * Copyright (C) William Breathitt Gray
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>
18 #include <linux/unaligned.h>
20 #define I8254_COUNTER_REG(_counter) (_counter)
21 #define I8254_CONTROL_REG 0x3
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))
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
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)
41 #define I8254_NUM_COUNTERS 3
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
52 u16 preset
[I8254_NUM_COUNTERS
];
53 u8 out_mode
[I8254_NUM_COUNTERS
];
57 static int i8254_count_read(struct counter_device
*const counter
, struct counter_count
*const count
,
60 struct i8254
*const priv
= counter_priv(counter
);
64 mutex_lock(&priv
->lock
);
66 ret
= regmap_write(priv
->map
, I8254_CONTROL_REG
, I8254_COUNTER_LATCH(count
->id
));
68 mutex_unlock(&priv
->lock
);
71 ret
= regmap_noinc_read(priv
->map
, I8254_COUNTER_REG(count
->id
), value
, sizeof(value
));
73 mutex_unlock(&priv
->lock
);
77 mutex_unlock(&priv
->lock
);
79 *val
= get_unaligned_le16(value
);
84 static int i8254_function_read(struct counter_device
*const counter
,
85 struct counter_count
*const count
,
86 enum counter_function
*const function
)
88 *function
= COUNTER_FUNCTION_DECREASE
;
92 #define I8254_SYNAPSES_PER_COUNT 2
93 #define I8254_SIGNAL_ID_CLK 0
94 #define I8254_SIGNAL_ID_GATE 1
96 static 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
)
101 struct i8254
*const priv
= counter_priv(counter
);
103 switch (synapse
->signal
->id
% I8254_SYNAPSES_PER_COUNT
) {
104 case I8254_SIGNAL_ID_CLK
:
105 *action
= COUNTER_SYNAPSE_ACTION_FALLING_EDGE
;
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
;
116 *action
= COUNTER_SYNAPSE_ACTION_NONE
;
120 /* should never reach this path */
125 static int i8254_count_ceiling_read(struct counter_device
*const counter
,
126 struct counter_count
*const count
, u64
*const ceiling
)
128 struct i8254
*const priv
= counter_priv(counter
);
130 mutex_lock(&priv
->lock
);
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
];
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;
144 *ceiling
= priv
->preset
[count
->id
];
151 mutex_unlock(&priv
->lock
);
156 static int i8254_count_mode_read(struct counter_device
*const counter
,
157 struct counter_count
*const count
,
158 enum counter_count_mode
*const count_mode
)
160 const struct i8254
*const priv
= counter_priv(counter
);
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
;
166 case I8254_MODE_HARDWARE_RETRIGGERABLE_ONESHOT
:
167 *count_mode
= COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT
;
169 case I8254_MODE_RATE_GENERATOR
:
170 *count_mode
= COUNTER_COUNT_MODE_RATE_GENERATOR
;
172 case I8254_MODE_SQUARE_WAVE_MODE
:
173 *count_mode
= COUNTER_COUNT_MODE_SQUARE_WAVE_MODE
;
175 case I8254_MODE_SOFTWARE_TRIGGERED_STROBE
:
176 *count_mode
= COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE
;
178 case I8254_MODE_HARDWARE_TRIGGERED_STROBE
:
179 *count_mode
= COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE
;
182 /* should never reach this path */
187 static int i8254_count_mode_write(struct counter_device
*const counter
,
188 struct counter_count
*const count
,
189 const enum counter_count_mode count_mode
)
191 struct i8254
*const priv
= counter_priv(counter
);
195 switch (count_mode
) {
196 case COUNTER_COUNT_MODE_INTERRUPT_ON_TERMINAL_COUNT
:
197 out_mode
= I8254_MODE_INTERRUPT_ON_TERMINAL_COUNT
;
199 case COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT
:
200 out_mode
= I8254_MODE_HARDWARE_RETRIGGERABLE_ONESHOT
;
202 case COUNTER_COUNT_MODE_RATE_GENERATOR
:
203 out_mode
= I8254_MODE_RATE_GENERATOR
;
205 case COUNTER_COUNT_MODE_SQUARE_WAVE_MODE
:
206 out_mode
= I8254_MODE_SQUARE_WAVE_MODE
;
208 case COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE
:
209 out_mode
= I8254_MODE_SOFTWARE_TRIGGERED_STROBE
;
211 case COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE
:
212 out_mode
= I8254_MODE_HARDWARE_TRIGGERED_STROBE
;
215 /* should never reach this path */
219 mutex_lock(&priv
->lock
);
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
));
227 mutex_unlock(&priv
->lock
);
232 static int i8254_count_floor_read(struct counter_device
*const counter
,
233 struct counter_count
*const count
, u64
*const floor
)
235 struct i8254
*const priv
= counter_priv(counter
);
237 mutex_lock(&priv
->lock
);
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;
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;
253 mutex_unlock(&priv
->lock
);
258 static int i8254_count_preset_read(struct counter_device
*const counter
,
259 struct counter_count
*const count
, u64
*const preset
)
261 const struct i8254
*const priv
= counter_priv(counter
);
263 *preset
= priv
->preset
[count
->id
];
268 static int i8254_count_preset_write(struct counter_device
*const counter
,
269 struct counter_count
*const count
, const u64 preset
)
271 struct i8254
*const priv
= counter_priv(counter
);
275 if (preset
> U16_MAX
)
278 mutex_lock(&priv
->lock
);
280 if (priv
->out_mode
[count
->id
] == I8254_MODE_RATE_GENERATOR
||
281 priv
->out_mode
[count
->id
] == I8254_MODE_SQUARE_WAVE_MODE
) {
283 mutex_unlock(&priv
->lock
);
288 priv
->preset
[count
->id
] = preset
;
290 put_unaligned_le16(preset
, value
);
291 ret
= regmap_noinc_write(priv
->map
, I8254_COUNTER_REG(count
->id
), value
, 2);
293 mutex_unlock(&priv
->lock
);
298 static int i8254_init_hw(struct regmap
*const map
)
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
));
314 static const struct counter_ops i8254_ops
= {
315 .count_read
= i8254_count_read
,
316 .function_read
= i8254_function_read
,
317 .action_read
= i8254_action_read
,
320 #define I8254_SIGNAL(_id, _name) { \
325 static 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"),
331 static const enum counter_synapse_action i8254_clk_actions
[] = {
332 COUNTER_SYNAPSE_ACTION_FALLING_EDGE
,
334 static const enum counter_synapse_action i8254_gate_actions
[] = {
335 COUNTER_SYNAPSE_ACTION_NONE
,
336 COUNTER_SYNAPSE_ACTION_RISING_EDGE
,
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], \
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], \
351 static 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),
357 static const enum counter_function i8254_functions_list
[] = {
358 COUNTER_FUNCTION_DECREASE
,
361 static 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
,
370 static DEFINE_COUNTER_AVAILABLE(i8254_count_modes_available
, i8254_count_modes
);
372 static 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
),
380 #define I8254_COUNT(_id, _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) \
391 static struct counter_count i8254_counts
[I8254_NUM_COUNTERS
] = {
392 I8254_COUNT(0, "Counter 0"), I8254_COUNT(1, "Counter 1"), I8254_COUNT(2, "Counter 2"),
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
400 * Registers an Intel 8254 Programmable Interval Timer Counter device. Returns 0 on success and
401 * negative error number on failure.
403 int devm_i8254_regmap_register(struct device
*const dev
,
404 const struct i8254_regmap_config
*const config
)
406 struct counter_device
*counter
;
416 counter
= devm_counter_alloc(dev
, sizeof(*priv
));
419 priv
= counter_priv(counter
);
420 priv
->map
= config
->map
;
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
);
430 mutex_init(&priv
->lock
);
432 err
= i8254_init_hw(priv
->map
);
436 err
= devm_counter_add(dev
, counter
);
438 return dev_err_probe(dev
, err
, "Failed to add counter\n");
442 EXPORT_SYMBOL_NS_GPL(devm_i8254_regmap_register
, I8254
);
444 MODULE_AUTHOR("William Breathitt Gray");
445 MODULE_DESCRIPTION("Intel 8254 Programmable Interval Timer");
446 MODULE_LICENSE("GPL");
447 MODULE_IMPORT_NS(COUNTER
);