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