The Intel 8254 PIT first appeared in the early 1980s and was used
initially in IBM PC compatibles. The popularity of the original Intel
825x family of chips led to many subsequent variants and clones of the
interface in various chips and integrated circuits. Although still
popular, interfaces compatible with the Intel 8254 PIT are nowdays
typically found embedded in larger VLSI processing chips and FPGA
components rather than as discrete ICs.
This patch series introduces a library to provide support for interfaces
compatible with the venerable Intel 8254 Programmable Interval Timer
(PIT). Modules wanting access to the i8254 library should select the
newly introduced CONFIG_I8254 Kconfig option, and import the I8254
symbol namespace.
Support for the i8254 is added in respective follow-up patches for the
104-dio-48e driver and stx104 driver whose devices feature i8254
compatible interfaces. Several additional dependencies are necessary for
the 104-dio-48e [0][1][2] and stx104 [3][4].
Due to the dependency requirements, I can take the i8254 introduction
patch through the Counter tree and provide an immutable branch that can
be merged to the GPIO and IIO trees; the 104-dio-48e patch and stx104
patch could then be picked up separately by the respective subsystem
maintainers.
[0] https://lore.kernel.org/all/05a878d340251b781387db4b6490f288e41a651c.1680543810.git.william.gray@linaro.org/
[1] https://lore.kernel.org/all/[email protected]/
[2] https://lore.kernel.org/all/[email protected]/
[3] https://lore.kernel.org/all/[email protected]/
[4] https://lore.kernel.org/all/[email protected]/
William Breathitt Gray (3):
counter: i8254: Introduce the Intel 8254 interface library module
gpio: 104-dio-48e: Add Counter/Timer support
iio: addac: stx104: Add 8254 Counter/Timer support
Documentation/ABI/testing/sysfs-bus-counter | 54 +++
MAINTAINERS | 7 +
drivers/counter/Kconfig | 15 +
drivers/counter/Makefile | 1 +
drivers/counter/counter-sysfs.c | 8 +-
drivers/counter/i8254.c | 447 ++++++++++++++++++++
drivers/gpio/Kconfig | 1 +
drivers/gpio/gpio-104-dio-48e.c | 127 +++++-
drivers/iio/addac/Kconfig | 1 +
drivers/iio/addac/stx104.c | 61 ++-
include/linux/i8254.h | 21 +
include/uapi/linux/counter.h | 6 +
12 files changed, 730 insertions(+), 19 deletions(-)
create mode 100644 drivers/counter/i8254.c
create mode 100644 include/linux/i8254.h
base-commit: 09a9639e56c01c7a00d6c0ca63f4c7c41abe075d
prerequisite-patch-id: 934c63dd47cb47e19739af076b95d3d55f5604f1
prerequisite-patch-id: 02aafdd535091da6a4ed6abbb20fb661f74af9fb
prerequisite-patch-id: cd19046150b7cff1be4ac7152198777aa960a3df
prerequisite-patch-id: bd3e3830d9ce4f3876a77483364d7190b7fdffa7
prerequisite-patch-id: 1e091c1f8f945a56cac59070221c4284306ba087
prerequisite-patch-id: c6f681fcbf7495c5ed6a596872dc4f762f22d977
prerequisite-patch-id: 239b016817624d56a4a2bddea1fda95282cb3d81
prerequisite-patch-id: 5fbfe7df44dcf5a629cd82ba8383480cb05b52d1
prerequisite-patch-id: 25a89f7312f225aaca11ef192e8d1f903a8b20e8
prerequisite-patch-id: 899b556161f417e20db8e957c5099b92c3dcb673
prerequisite-patch-id: eb09641cfb9e7caf7641ae6cb8e84e33cbb665a6
--
2.39.2
Exposes consumer library functions providing support for interfaces
compatible with the venerable Intel 8254 Programmable Interval Timer
(PIT).
The Intel 8254 PIT first appeared in the early 1980s and was used
initially in IBM PC compatibles. The popularity of the original Intel
825x family of chips led to many subsequent variants and clones of the
interface in various chips and integrated circuits. Although still
popular, interfaces compatible with the Intel 8254 PIT are nowdays
typically found embedded in larger VLSI processing chips and FPGA
components rather than as discrete ICs.
A CONFIG_I8254 Kconfig option is introduced by this patch. Modules
wanting access to these i8254 library functions should select this
Kconfig option, and import the I8254 symbol namespace.
Signed-off-by: William Breathitt Gray <[email protected]>
---
Documentation/ABI/testing/sysfs-bus-counter | 54 +++
MAINTAINERS | 7 +
drivers/counter/Kconfig | 15 +
drivers/counter/Makefile | 1 +
drivers/counter/counter-sysfs.c | 8 +-
drivers/counter/i8254.c | 447 ++++++++++++++++++++
include/linux/i8254.h | 21 +
include/uapi/linux/counter.h | 6 +
8 files changed, 558 insertions(+), 1 deletion(-)
create mode 100644 drivers/counter/i8254.c
create mode 100644 include/linux/i8254.h
diff --git a/Documentation/ABI/testing/sysfs-bus-counter b/Documentation/ABI/testing/sysfs-bus-counter
index ff83320b4255..6e0946e826d8 100644
--- a/Documentation/ABI/testing/sysfs-bus-counter
+++ b/Documentation/ABI/testing/sysfs-bus-counter
@@ -60,6 +60,60 @@ Description:
counter does not freeze at the boundary points, but
counts continuously throughout.
+ interrupt on terminal count:
+ The output signal is initially low, and will remain low
+ until the counter reaches zero. The output signal then
+ goes high and remains high until a new preset value is
+ set.
+
+ hardware retriggerable one-shot:
+ The output signal is initially high. The output signal
+ will go low by a trigger input signal, and will remain
+ low until the counter reaches zero. The output will then
+ go high and remain high until the next trigger. A
+ trigger results in loading the counter to the preset
+ value and setting the output signal low, thus starting
+ the one-shot pulse.
+
+ rate generator:
+ The output signal is initially high. When the counter
+ has decremented to 1, the output signal goes low for one
+ clock pulse. The output signal then goes high again, the
+ counter is reloaded to the preset value, and the process
+ repeats in a periodic manner as such.
+
+ square wave mode:
+ The output signal is initially high.
+
+ If the initial count is even, the counter is decremented
+ by two on succeeding clock pulses. When the count
+ expires, the output signal changes value and the
+ counter is reloaded to the preset value. The process
+ repeats in periodic manner as such.
+
+ If the initial count is odd, the initial count minus one
+ (an even number) is loaded and then is decremented by
+ two on succeeding clock pulses. One clock pulse after
+ the count expires, the output signal goes low and the
+ counter is reloaded to the preset value minus one.
+ Succeeding clock pulses decrement the count by two. When
+ the count expires, the output goes high again and the
+ counter is reloaded to the preset value minus one. The
+ process repeats in a periodic manner as such.
+
+ software triggered strobe:
+ The output signal is initially high. When the count
+ expires, the output will go low for one clock pulse and
+ then go high again. The counting sequence is "triggered"
+ by setting the preset value.
+
+ hardware triggered strobe:
+ The output signal is initially high. Counting is started
+ by a trigger input signal. When the count expires, the
+ output signal will go low for one clock pulse and then
+ go high again. A trigger results in loading the counter
+ to the preset value.
+
What: /sys/bus/counter/devices/counterX/countY/count_mode_available
What: /sys/bus/counter/devices/counterX/countY/error_noise_available
What: /sys/bus/counter/devices/counterX/countY/function_available
diff --git a/MAINTAINERS b/MAINTAINERS
index 90abe83c02f3..ac170417e1c9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10170,6 +10170,13 @@ L: [email protected]
S: Maintained
F: drivers/video/fbdev/i810/
+INTEL 8254 COUNTER DRIVER
+M: William Breathitt Gray <[email protected]>
+L: [email protected]
+S: Maintained
+F: drivers/counter/i8254.c
+F: include/linux/i8254.h
+
INTEL 8255 GPIO DRIVER
M: William Breathitt Gray <[email protected]>
L: [email protected]
diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig
index b5ba8fb02cf7..5a711811819b 100644
--- a/drivers/counter/Kconfig
+++ b/drivers/counter/Kconfig
@@ -10,6 +10,21 @@ menuconfig COUNTER
interface. You only need to enable this, if you also want to enable
one or more of the counter device drivers below.
+config I8254
+ tristate
+ select COUNTER
+ select REGMAP
+ help
+ Enables support for the i8254 interface library functions. The i8254
+ interface library provides functions to facilitate communication with
+ interfaces compatible with the venerable Intel 8254 Programmable
+ Interval Timer (PIT). The Intel 825x family of chips was first
+ released in the early 1980s but compatible interfaces are nowadays
+ typically found embedded in larger VLSI processing chips and FPGA
+ components.
+
+ If built as a module its name will be i8254.
+
if COUNTER
config 104_QUAD_8
diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
index b9a369e0d4fc..395c17ddc37a 100644
--- a/drivers/counter/Makefile
+++ b/drivers/counter/Makefile
@@ -6,6 +6,7 @@
obj-$(CONFIG_COUNTER) += counter.o
counter-y := counter-core.o counter-sysfs.o counter-chrdev.o
+obj-$(CONFIG_I8254) += i8254.o
obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
obj-$(CONFIG_INTERRUPT_CNT) += interrupt-cnt.o
obj-$(CONFIG_STM32_TIMER_CNT) += stm32-timer-cnt.o
diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
index b9efe66f9f8d..42c523343d32 100644
--- a/drivers/counter/counter-sysfs.c
+++ b/drivers/counter/counter-sysfs.c
@@ -88,7 +88,13 @@ static const char *const counter_count_mode_str[] = {
[COUNTER_COUNT_MODE_NORMAL] = "normal",
[COUNTER_COUNT_MODE_RANGE_LIMIT] = "range limit",
[COUNTER_COUNT_MODE_NON_RECYCLE] = "non-recycle",
- [COUNTER_COUNT_MODE_MODULO_N] = "modulo-n"
+ [COUNTER_COUNT_MODE_MODULO_N] = "modulo-n",
+ [COUNTER_COUNT_MODE_INTERRUPT_ON_TERMINAL_COUNT] = "interrupt on terminal count",
+ [COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT] = "hardware retriggerable one-shot",
+ [COUNTER_COUNT_MODE_RATE_GENERATOR] = "rate generator",
+ [COUNTER_COUNT_MODE_SQUARE_WAVE_MODE] = "square wave mode",
+ [COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE] = "software triggered strobe",
+ [COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE] = "hardware triggered strobe",
};
static const char *const counter_signal_polarity_str[] = {
diff --git a/drivers/counter/i8254.c b/drivers/counter/i8254.c
new file mode 100644
index 000000000000..c41e4fdc9601
--- /dev/null
+++ b/drivers/counter/i8254.c
@@ -0,0 +1,447 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel 8254 Programmable Interval Timer
+ * Copyright (C) William Breathitt Gray
+ */
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/counter.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/i8254.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+
+#include <asm/unaligned.h>
+
+#define I8254_COUNTER_REG(_counter) (_counter)
+#define I8254_CONTROL_REG 0x3
+
+#define I8254_SC GENMASK(7, 6)
+#define I8254_RW GENMASK(5, 4)
+#define I8254_M GENMASK(3, 1)
+#define I8254_CONTROL(_sc, _rw, _m) \
+ (u8_encode_bits(_sc, I8254_SC) | u8_encode_bits(_rw, I8254_RW) | \
+ u8_encode_bits(_m, I8254_M))
+
+#define I8254_RW_TWO_BYTE 0x3
+#define I8254_MODE_INTERRUPT_ON_TERMINAL_COUNT 0
+#define I8254_MODE_HARDWARE_RETRIGGERABLE_ONESHOT 1
+#define I8254_MODE_RATE_GENERATOR 2
+#define I8254_MODE_SQUARE_WAVE_MODE 3
+#define I8254_MODE_SOFTWARE_TRIGGERED_STROBE 4
+#define I8254_MODE_HARDWARE_TRIGGERED_STROBE 5
+
+#define I8254_COUNTER_LATCH(_counter) I8254_CONTROL(_counter, 0x0, 0x0)
+#define I8254_PROGRAM_COUNTER(_counter, _mode) I8254_CONTROL(_counter, I8254_RW_TWO_BYTE, _mode)
+
+#define I8254_NUM_COUNTERS 3
+
+/**
+ * struct i8254 - I8254 device private data structure
+ * @lock: synchronization lock to prevent I/O race conditions
+ * @preset: array of Counter Register states
+ * @out_mode: array of mode configuration states
+ * @map: Regmap for the device
+ */
+struct i8254 {
+ struct mutex lock;
+ u16 preset[I8254_NUM_COUNTERS];
+ u8 out_mode[I8254_NUM_COUNTERS];
+ struct regmap *map;
+};
+
+static int i8254_count_read(struct counter_device *const counter, struct counter_count *const count,
+ u64 *const val)
+{
+ struct i8254 *const priv = counter_priv(counter);
+ int ret;
+ u8 value[2];
+
+ mutex_lock(&priv->lock);
+
+ ret = regmap_write(priv->map, I8254_CONTROL_REG, I8254_COUNTER_LATCH(count->id));
+ if (ret) {
+ mutex_unlock(&priv->lock);
+ return ret;
+ }
+ ret = regmap_noinc_read(priv->map, I8254_COUNTER_REG(count->id), value, sizeof(value));
+ if (ret) {
+ mutex_unlock(&priv->lock);
+ return ret;
+ }
+
+ mutex_unlock(&priv->lock);
+
+ *val = get_unaligned_le16(value);
+
+ return ret;
+}
+
+static int i8254_function_read(struct counter_device *const counter,
+ struct counter_count *const count,
+ enum counter_function *const function)
+{
+ *function = COUNTER_FUNCTION_DECREASE;
+ return 0;
+}
+
+#define I8254_SYNAPSES_PER_COUNT 2
+#define I8254_SIGNAL_ID_CLK 0
+#define I8254_SIGNAL_ID_GATE 1
+
+static int i8254_action_read(struct counter_device *const counter,
+ struct counter_count *const count,
+ struct counter_synapse *const synapse,
+ enum counter_synapse_action *const action)
+{
+ struct i8254 *const priv = counter_priv(counter);
+
+ switch (synapse->signal->id % I8254_SYNAPSES_PER_COUNT) {
+ case I8254_SIGNAL_ID_CLK:
+ *action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
+ return 0;
+ case I8254_SIGNAL_ID_GATE:
+ switch (priv->out_mode[count->id]) {
+ case I8254_MODE_HARDWARE_RETRIGGERABLE_ONESHOT:
+ case I8254_MODE_RATE_GENERATOR:
+ case I8254_MODE_SQUARE_WAVE_MODE:
+ case I8254_MODE_HARDWARE_TRIGGERED_STROBE:
+ *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
+ return 0;
+ default:
+ *action = COUNTER_SYNAPSE_ACTION_NONE;
+ return 0;
+ }
+ default:
+ /* should never reach this path */
+ return -EINVAL;
+ }
+}
+
+static int i8254_count_ceiling_read(struct counter_device *const counter,
+ struct counter_count *const count, u64 *const ceiling)
+{
+ struct i8254 *const priv = counter_priv(counter);
+
+ mutex_lock(&priv->lock);
+
+ switch (priv->out_mode[count->id]) {
+ case I8254_MODE_RATE_GENERATOR:
+ /* Rate Generator decrements 0 by one and the counter "wraps around" */
+ *ceiling = (priv->preset[count->id] == 0) ? U16_MAX : priv->preset[count->id];
+ break;
+ case I8254_MODE_SQUARE_WAVE_MODE:
+ if (priv->preset[count->id] % 2)
+ *ceiling = priv->preset[count->id] - 1;
+ else if (priv->preset[count->id] == 0)
+ /* Square Wave Mode decrements 0 by two and the counter "wraps around" */
+ *ceiling = U16_MAX - 1;
+ else
+ *ceiling = priv->preset[count->id];
+ break;
+ default:
+ *ceiling = U16_MAX;
+ break;
+ }
+
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static int i8254_count_mode_read(struct counter_device *const counter,
+ struct counter_count *const count,
+ enum counter_count_mode *const count_mode)
+{
+ const struct i8254 *const priv = counter_priv(counter);
+
+ switch (priv->out_mode[count->id]) {
+ case I8254_MODE_INTERRUPT_ON_TERMINAL_COUNT:
+ *count_mode = COUNTER_COUNT_MODE_INTERRUPT_ON_TERMINAL_COUNT;
+ return 0;
+ case I8254_MODE_HARDWARE_RETRIGGERABLE_ONESHOT:
+ *count_mode = COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT;
+ return 0;
+ case I8254_MODE_RATE_GENERATOR:
+ *count_mode = COUNTER_COUNT_MODE_RATE_GENERATOR;
+ return 0;
+ case I8254_MODE_SQUARE_WAVE_MODE:
+ *count_mode = COUNTER_COUNT_MODE_SQUARE_WAVE_MODE;
+ return 0;
+ case I8254_MODE_SOFTWARE_TRIGGERED_STROBE:
+ *count_mode = COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE;
+ return 0;
+ case I8254_MODE_HARDWARE_TRIGGERED_STROBE:
+ *count_mode = COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE;
+ return 0;
+ default:
+ /* should never reach this path */
+ return -EINVAL;
+ }
+}
+
+static int i8254_count_mode_write(struct counter_device *const counter,
+ struct counter_count *const count,
+ const enum counter_count_mode count_mode)
+{
+ struct i8254 *const priv = counter_priv(counter);
+ u8 out_mode;
+ int ret;
+
+ switch (count_mode) {
+ case COUNTER_COUNT_MODE_INTERRUPT_ON_TERMINAL_COUNT:
+ out_mode = I8254_MODE_INTERRUPT_ON_TERMINAL_COUNT;
+ break;
+ case COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT:
+ out_mode = I8254_MODE_HARDWARE_RETRIGGERABLE_ONESHOT;
+ break;
+ case COUNTER_COUNT_MODE_RATE_GENERATOR:
+ out_mode = I8254_MODE_RATE_GENERATOR;
+ break;
+ case COUNTER_COUNT_MODE_SQUARE_WAVE_MODE:
+ out_mode = I8254_MODE_SQUARE_WAVE_MODE;
+ break;
+ case COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE:
+ out_mode = I8254_MODE_SOFTWARE_TRIGGERED_STROBE;
+ break;
+ case COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE:
+ out_mode = I8254_MODE_HARDWARE_TRIGGERED_STROBE;
+ break;
+ default:
+ /* should never reach this path */
+ return -EINVAL;
+ }
+
+ mutex_lock(&priv->lock);
+
+ /* Counter Register is cleared when the counter is programmed */
+ priv->preset[count->id] = 0;
+ priv->out_mode[count->id] = out_mode;
+ ret = regmap_write(priv->map, I8254_CONTROL_REG,
+ I8254_PROGRAM_COUNTER(count->id, out_mode));
+
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static int i8254_count_floor_read(struct counter_device *const counter,
+ struct counter_count *const count, u64 *const floor)
+{
+ struct i8254 *const priv = counter_priv(counter);
+
+ mutex_lock(&priv->lock);
+
+ switch (priv->out_mode[count->id]) {
+ case I8254_MODE_RATE_GENERATOR:
+ /* counter is always reloaded after 1, but 0 is a possible reload value */
+ *floor = (priv->preset[count->id] == 0) ? 0 : 1;
+ break;
+ case I8254_MODE_SQUARE_WAVE_MODE:
+ /* counter is always reloaded after 2 for even preset values */
+ *floor = (priv->preset[count->id] % 2 || priv->preset[count->id] == 0) ? 0 : 2;
+ break;
+ default:
+ *floor = 0;
+ break;
+ }
+
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static int i8254_count_preset_read(struct counter_device *const counter,
+ struct counter_count *const count, u64 *const preset)
+{
+ const struct i8254 *const priv = counter_priv(counter);
+
+ *preset = priv->preset[count->id];
+
+ return 0;
+}
+
+static int i8254_count_preset_write(struct counter_device *const counter,
+ struct counter_count *const count, const u64 preset)
+{
+ struct i8254 *const priv = counter_priv(counter);
+ int ret;
+ u8 value[2];
+
+ if (preset > U16_MAX)
+ return -ERANGE;
+
+ mutex_lock(&priv->lock);
+
+ if (priv->out_mode[count->id] == I8254_MODE_RATE_GENERATOR ||
+ priv->out_mode[count->id] == I8254_MODE_SQUARE_WAVE_MODE) {
+ if (preset == 1) {
+ mutex_unlock(&priv->lock);
+ return -EINVAL;
+ }
+ }
+
+ priv->preset[count->id] = preset;
+
+ put_unaligned_le16(preset, value);
+ ret = regmap_noinc_write(priv->map, I8254_COUNTER_REG(count->id), value, 2);
+
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static int i8254_init_hw(struct regmap *const map)
+{
+ unsigned long i;
+ int ret;
+
+ for (i = 0; i < I8254_NUM_COUNTERS; i++) {
+ /* Initialize each counter to Mode 0 */
+ ret = regmap_write(map, I8254_CONTROL_REG,
+ I8254_PROGRAM_COUNTER(i, I8254_MODE_INTERRUPT_ON_TERMINAL_COUNT));
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct counter_ops i8254_ops = {
+ .count_read = i8254_count_read,
+ .function_read = i8254_function_read,
+ .action_read = i8254_action_read,
+};
+
+#define I8254_SIGNAL(_id, _name) { \
+ .id = (_id), \
+ .name = (_name), \
+}
+
+static struct counter_signal i8254_signals[] = {
+ I8254_SIGNAL(0, "CLK 0"), I8254_SIGNAL(1, "GATE 0"),
+ I8254_SIGNAL(2, "CLK 1"), I8254_SIGNAL(3, "GATE 1"),
+ I8254_SIGNAL(4, "CLK 2"), I8254_SIGNAL(5, "GATE 2"),
+};
+
+static const enum counter_synapse_action i8254_clk_actions[] = {
+ COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
+};
+static const enum counter_synapse_action i8254_gate_actions[] = {
+ COUNTER_SYNAPSE_ACTION_NONE,
+ COUNTER_SYNAPSE_ACTION_RISING_EDGE,
+};
+
+#define I8254_SYNAPSES_BASE(_id) ((_id) * I8254_SYNAPSES_PER_COUNT)
+#define I8254_SYNAPSE_CLK(_id) { \
+ .actions_list = i8254_clk_actions, \
+ .num_actions = ARRAY_SIZE(i8254_clk_actions), \
+ .signal = &i8254_signals[I8254_SYNAPSES_BASE(_id) + 0], \
+}
+#define I8254_SYNAPSE_GATE(_id) { \
+ .actions_list = i8254_gate_actions, \
+ .num_actions = ARRAY_SIZE(i8254_gate_actions), \
+ .signal = &i8254_signals[I8254_SYNAPSES_BASE(_id) + 1], \
+}
+
+static struct counter_synapse i8254_synapses[] = {
+ I8254_SYNAPSE_CLK(0), I8254_SYNAPSE_GATE(0),
+ I8254_SYNAPSE_CLK(1), I8254_SYNAPSE_GATE(1),
+ I8254_SYNAPSE_CLK(2), I8254_SYNAPSE_GATE(2),
+};
+
+static const enum counter_function i8254_functions_list[] = {
+ COUNTER_FUNCTION_DECREASE,
+};
+
+static const enum counter_count_mode i8254_count_modes[] = {
+ COUNTER_COUNT_MODE_INTERRUPT_ON_TERMINAL_COUNT,
+ COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT,
+ COUNTER_COUNT_MODE_RATE_GENERATOR,
+ COUNTER_COUNT_MODE_SQUARE_WAVE_MODE,
+ COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE,
+ COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE,
+};
+
+static DEFINE_COUNTER_AVAILABLE(i8254_count_modes_available, i8254_count_modes);
+
+static struct counter_comp i8254_count_ext[] = {
+ COUNTER_COMP_CEILING(i8254_count_ceiling_read, NULL),
+ COUNTER_COMP_COUNT_MODE(i8254_count_mode_read, i8254_count_mode_write,
+ i8254_count_modes_available),
+ COUNTER_COMP_FLOOR(i8254_count_floor_read, NULL),
+ COUNTER_COMP_PRESET(i8254_count_preset_read, i8254_count_preset_write),
+};
+
+#define I8254_COUNT(_id, _name) { \
+ .id = (_id), \
+ .name = (_name), \
+ .functions_list = i8254_functions_list, \
+ .num_functions = ARRAY_SIZE(i8254_functions_list), \
+ .synapses = &i8254_synapses[I8254_SYNAPSES_BASE(_id)], \
+ .num_synapses = I8254_SYNAPSES_PER_COUNT, \
+ .ext = i8254_count_ext, \
+ .num_ext = ARRAY_SIZE(i8254_count_ext) \
+}
+
+static struct counter_count i8254_counts[I8254_NUM_COUNTERS] = {
+ I8254_COUNT(0, "Counter 0"), I8254_COUNT(1, "Counter 1"), I8254_COUNT(2, "Counter 2"),
+};
+
+/**
+ * devm_i8254_regmap_register - Register an i8254 Counter device
+ * @dev: device that is registering this i8254 Counter device
+ * @config: configuration for i8254_regmap_config
+ *
+ * Registers an Intel 8254 Programmable Interval Timer Counter device. Returns 0 on success and
+ * negative error number on failure.
+ */
+int devm_i8254_regmap_register(struct device *const dev,
+ const struct i8254_regmap_config *const config)
+{
+ struct counter_device *counter;
+ struct i8254 *priv;
+ int err;
+
+ if (!config->parent)
+ return -EINVAL;
+
+ if (!config->map)
+ return -EINVAL;
+
+ counter = devm_counter_alloc(dev, sizeof(*priv));
+ if (!counter)
+ return -ENOMEM;
+ priv = counter_priv(counter);
+ priv->map = config->map;
+
+ counter->name = dev_name(config->parent);
+ counter->parent = config->parent;
+ counter->ops = &i8254_ops;
+ counter->counts = i8254_counts;
+ counter->num_counts = ARRAY_SIZE(i8254_counts);
+ counter->signals = i8254_signals;
+ counter->num_signals = ARRAY_SIZE(i8254_signals);
+
+ mutex_init(&priv->lock);
+
+ err = i8254_init_hw(priv->map);
+ if (err)
+ return err;
+
+ err = devm_counter_add(dev, counter);
+ if (err < 0)
+ return dev_err_probe(dev, err, "Failed to add counter\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(devm_i8254_regmap_register, I8254);
+
+MODULE_AUTHOR("William Breathitt Gray");
+MODULE_DESCRIPTION("Intel 8254 Programmable Interval Timer");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(COUNTER);
diff --git a/include/linux/i8254.h b/include/linux/i8254.h
new file mode 100644
index 000000000000..a675c309232b
--- /dev/null
+++ b/include/linux/i8254.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) William Breathitt Gray */
+#ifndef _I8254_H_
+#define _I8254_H_
+
+struct device;
+struct regmap;
+
+/**
+ * struct i8254_regmap_config - Configuration for the register map of an i8254
+ * @parent: parent device
+ * @map: regmap for the i8254
+ */
+struct i8254_regmap_config {
+ struct device *parent;
+ struct regmap *map;
+};
+
+int devm_i8254_regmap_register(struct device *dev, const struct i8254_regmap_config *config);
+
+#endif /* _I8254_H_ */
diff --git a/include/uapi/linux/counter.h b/include/uapi/linux/counter.h
index 8ab12d731e3b..fc248ef00e86 100644
--- a/include/uapi/linux/counter.h
+++ b/include/uapi/linux/counter.h
@@ -127,6 +127,12 @@ enum counter_count_mode {
COUNTER_COUNT_MODE_RANGE_LIMIT,
COUNTER_COUNT_MODE_NON_RECYCLE,
COUNTER_COUNT_MODE_MODULO_N,
+ COUNTER_COUNT_MODE_INTERRUPT_ON_TERMINAL_COUNT,
+ COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT,
+ COUNTER_COUNT_MODE_RATE_GENERATOR,
+ COUNTER_COUNT_MODE_SQUARE_WAVE_MODE,
+ COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE,
+ COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE,
};
/* Count function values */
--
2.39.2
The 104-DIO-48E features an 8254 Counter/Timer chip providing three
counter/timers which can be used for frequency measurement, frequency
output, pulse width modulation, pulse width measurement, event count,
etc. The counter/timers use the same addresses as PPI 0 (addresses 0x0
to 0x3), so a raw_spinlock_t is used to synchronize operations between
the two regmap mappings to prevent clobbering.
Signed-off-by: William Breathitt Gray <[email protected]>
---
drivers/gpio/Kconfig | 1 +
drivers/gpio/gpio-104-dio-48e.c | 127 ++++++++++++++++++++++++++++----
2 files changed, 112 insertions(+), 16 deletions(-)
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index badbe0582318..1fcc1245f954 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -840,6 +840,7 @@ config GPIO_104_DIO_48E
select REGMAP_IRQ
select GPIOLIB_IRQCHIP
select GPIO_I8255
+ select I8254
help
Enables GPIO support for the ACCES 104-DIO-48E series (104-DIO-48E,
104-DIO-24E). The base port addresses for the devices may be
diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c
index 000a56d5f1d7..2df33650cbf4 100644
--- a/drivers/gpio/gpio-104-dio-48e.c
+++ b/drivers/gpio/gpio-104-dio-48e.c
@@ -9,6 +9,7 @@
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/i8254.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/isa.h>
@@ -16,6 +17,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/regmap.h>
+#include <linux/spinlock.h>
#include <linux/types.h>
#include "gpio-i8255.h"
@@ -37,6 +39,8 @@ MODULE_PARM_DESC(irq, "ACCES 104-DIO-48E interrupt line numbers");
#define DIO48E_ENABLE_INTERRUPT 0xB
#define DIO48E_DISABLE_INTERRUPT DIO48E_ENABLE_INTERRUPT
+#define DIO48E_ENABLE_COUNTER_TIMER_ADDRESSING 0xD
+#define DIO48E_DISABLE_COUNTER_TIMER_ADDRESSING DIO48E_ENABLE_COUNTER_TIMER_ADDRESSING
#define DIO48E_CLEAR_INTERRUPT 0xF
#define DIO48E_NUM_PPI 2
@@ -75,18 +79,20 @@ static const struct regmap_access_table dio48e_precious_table = {
.yes_ranges = dio48e_precious_ranges,
.n_yes_ranges = ARRAY_SIZE(dio48e_precious_ranges),
};
-static const struct regmap_config dio48e_regmap_config = {
- .reg_bits = 8,
- .reg_stride = 1,
- .val_bits = 8,
- .io_port = true,
- .max_register = 0xF,
- .wr_table = &dio48e_wr_table,
- .rd_table = &dio48e_rd_table,
- .volatile_table = &dio48e_volatile_table,
- .precious_table = &dio48e_precious_table,
- .cache_type = REGCACHE_FLAT,
- .use_raw_spinlock = true,
+
+static const struct regmap_range pit_wr_ranges[] = {
+ regmap_reg_range(0x0, 0x3),
+};
+static const struct regmap_range pit_rd_ranges[] = {
+ regmap_reg_range(0x0, 0x2),
+};
+static const struct regmap_access_table pit_wr_table = {
+ .yes_ranges = pit_wr_ranges,
+ .n_yes_ranges = ARRAY_SIZE(pit_wr_ranges),
+};
+static const struct regmap_access_table pit_rd_table = {
+ .yes_ranges = pit_rd_ranges,
+ .n_yes_ranges = ARRAY_SIZE(pit_rd_ranges),
};
/* only bit 3 on each respective Port C supports interrupts */
@@ -102,14 +108,56 @@ static const struct regmap_irq dio48e_regmap_irqs[] = {
/**
* struct dio48e_gpio - GPIO device private data structure
+ * @lock: synchronization lock to prevent I/O race conditions
* @map: Regmap for the device
+ * @regs: virtual mapping for device registers
+ * @flags: IRQ flags saved during locking
* @irq_mask: Current IRQ mask state on the device
*/
struct dio48e_gpio {
+ raw_spinlock_t lock;
struct regmap *map;
+ void __iomem *regs;
+ unsigned long flags;
unsigned int irq_mask;
};
+static void dio48e_regmap_lock(void *lock_arg) __acquires(&dio48egpio->lock)
+{
+ struct dio48e_gpio *const dio48egpio = lock_arg;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&dio48egpio->lock, flags);
+ dio48egpio->flags = flags;
+}
+
+static void dio48e_regmap_unlock(void *lock_arg) __releases(&dio48egpio->lock)
+{
+ struct dio48e_gpio *const dio48egpio = lock_arg;
+
+ raw_spin_unlock_irqrestore(&dio48egpio->lock, dio48egpio->flags);
+}
+
+static void pit_regmap_lock(void *lock_arg) __acquires(&dio48egpio->lock)
+{
+ struct dio48e_gpio *const dio48egpio = lock_arg;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&dio48egpio->lock, flags);
+ dio48egpio->flags = flags;
+
+ iowrite8(0x00, dio48egpio->regs + DIO48E_ENABLE_COUNTER_TIMER_ADDRESSING);
+}
+
+static void pit_regmap_unlock(void *lock_arg) __releases(&dio48egpio->lock)
+{
+ struct dio48e_gpio *const dio48egpio = lock_arg;
+
+ ioread8(dio48egpio->regs + DIO48E_DISABLE_COUNTER_TIMER_ADDRESSING);
+
+ raw_spin_unlock_irqrestore(&dio48egpio->lock, dio48egpio->flags);
+}
+
static int dio48e_handle_mask_sync(const int index,
const unsigned int mask_buf_def,
const unsigned int mask_buf,
@@ -176,6 +224,9 @@ static int dio48e_probe(struct device *dev, unsigned int id)
struct i8255_regmap_config config = {};
void __iomem *regs;
struct regmap *map;
+ struct regmap_config dio48e_regmap_config;
+ struct regmap_config pit_regmap_config;
+ struct i8254_regmap_config pit_config;
int err;
struct regmap_irq_chip *chip;
struct dio48e_gpio *dio48egpio;
@@ -187,21 +238,58 @@ static int dio48e_probe(struct device *dev, unsigned int id)
return -EBUSY;
}
+ dio48egpio = devm_kzalloc(dev, sizeof(*dio48egpio), GFP_KERNEL);
+ if (!dio48egpio)
+ return -ENOMEM;
+
regs = devm_ioport_map(dev, base[id], DIO48E_EXTENT);
if (!regs)
return -ENOMEM;
+ dio48egpio->regs = regs;
+
+ raw_spin_lock_init(&dio48egpio->lock);
+
+ dio48e_regmap_config = (struct regmap_config) {
+ .reg_bits = 8,
+ .reg_stride = 1,
+ .val_bits = 8,
+ .lock = dio48e_regmap_lock,
+ .unlock = dio48e_regmap_unlock,
+ .lock_arg = dio48egpio,
+ .io_port = true,
+ .wr_table = &dio48e_wr_table,
+ .rd_table = &dio48e_rd_table,
+ .volatile_table = &dio48e_volatile_table,
+ .precious_table = &dio48e_precious_table,
+ .cache_type = REGCACHE_FLAT,
+ };
+
map = devm_regmap_init_mmio(dev, regs, &dio48e_regmap_config);
if (IS_ERR(map))
return dev_err_probe(dev, PTR_ERR(map),
"Unable to initialize register map\n");
- dio48egpio = devm_kzalloc(dev, sizeof(*dio48egpio), GFP_KERNEL);
- if (!dio48egpio)
- return -ENOMEM;
-
dio48egpio->map = map;
+ pit_regmap_config = (struct regmap_config) {
+ .name = "i8254",
+ .reg_bits = 8,
+ .reg_stride = 1,
+ .val_bits = 8,
+ .lock = pit_regmap_lock,
+ .unlock = pit_regmap_unlock,
+ .lock_arg = dio48egpio,
+ .io_port = true,
+ .wr_table = &pit_wr_table,
+ .rd_table = &pit_rd_table,
+ };
+
+ pit_config.map = devm_regmap_init_mmio(dev, regs, &pit_regmap_config);
+ if (IS_ERR(pit_config.map))
+ return dev_err_probe(dev, PTR_ERR(pit_config.map),
+ "Unable to initialize i8254 register map\n");
+
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
@@ -229,6 +317,12 @@ static int dio48e_probe(struct device *dev, unsigned int id)
if (err)
return dev_err_probe(dev, err, "IRQ registration failed\n");
+ pit_config.parent = dev;
+
+ err = devm_i8254_regmap_register(dev, &pit_config);
+ if (err)
+ return err;
+
config.parent = dev;
config.map = map;
config.num_ppi = DIO48E_NUM_PPI;
@@ -249,3 +343,4 @@ module_isa_driver_with_irq(dio48e_driver, num_dio48e, num_irq);
MODULE_AUTHOR("William Breathitt Gray <[email protected]>");
MODULE_DESCRIPTION("ACCES 104-DIO-48E GPIO driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(I8254);
--
2.39.2
The STX104 features an 8254 Counter/Timer chip providing three
counter/timers which can be used for frequency measurement, frequency
output, pulse width modulation, pulse width measurement, event count,
etc. The STX104 provides a register bank selection to bank select
between the 8254 Bank and the Indexed Register Array Bank; the Indexed
Register Array is not utilized by this driver, so the 8254 Bank is
selected unconditionally.
Signed-off-by: William Breathitt Gray <[email protected]>
---
drivers/iio/addac/Kconfig | 1 +
drivers/iio/addac/stx104.c | 61 ++++++++++++++++++++++++++++++++++++--
2 files changed, 60 insertions(+), 2 deletions(-)
diff --git a/drivers/iio/addac/Kconfig b/drivers/iio/addac/Kconfig
index 877f9124803c..b2623881f0ec 100644
--- a/drivers/iio/addac/Kconfig
+++ b/drivers/iio/addac/Kconfig
@@ -38,6 +38,7 @@ config STX104
select REGMAP_MMIO
select GPIOLIB
select GPIO_REGMAP
+ select I8254
help
Say yes here to build support for the Apex Embedded Systems STX104
integrated analog PC/104 card.
diff --git a/drivers/iio/addac/stx104.c b/drivers/iio/addac/stx104.c
index d1f7ce033b46..6946a65512ca 100644
--- a/drivers/iio/addac/stx104.c
+++ b/drivers/iio/addac/stx104.c
@@ -8,6 +8,7 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/regmap.h>
+#include <linux/i8254.h>
#include <linux/iio/iio.h>
#include <linux/iio/types.h>
#include <linux/isa.h>
@@ -55,6 +56,7 @@ MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses");
#define STX104_ADC_STATUS (STX104_AIO_BASE + 0x8)
#define STX104_ADC_CONTROL (STX104_AIO_BASE + 0x9)
#define STX104_ADC_CONFIGURATION (STX104_AIO_BASE + 0x11)
+#define STX104_I8254_BASE (STX104_AIO_BASE + 0x12)
#define STX104_AIO_DATA_STRIDE 2
#define STX104_DAC_OFFSET(_channel) (STX104_DAC_BASE + STX104_AIO_DATA_STRIDE * (_channel))
@@ -77,6 +79,7 @@ MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses");
/* ADC Configuration */
#define STX104_GAIN GENMASK(1, 0)
#define STX104_ADBU BIT(2)
+#define STX104_RBK GENMASK(7, 4)
#define STX104_BIPOLAR 0
#define STX104_GAIN_X1 0
#define STX104_GAIN_X2 1
@@ -168,6 +171,32 @@ static const struct regmap_config dio_regmap_config = {
.io_port = true,
};
+static const struct regmap_range pit_wr_ranges[] = {
+ regmap_reg_range(0x0, 0x3),
+};
+static const struct regmap_range pit_rd_ranges[] = {
+ regmap_reg_range(0x0, 0x2),
+};
+static const struct regmap_access_table pit_wr_table = {
+ .yes_ranges = pit_wr_ranges,
+ .n_yes_ranges = ARRAY_SIZE(pit_wr_ranges),
+};
+static const struct regmap_access_table pit_rd_table = {
+ .yes_ranges = pit_rd_ranges,
+ .n_yes_ranges = ARRAY_SIZE(pit_rd_ranges),
+};
+
+static const struct regmap_config pit_regmap_config = {
+ .name = "i8254",
+ .reg_bits = 8,
+ .reg_stride = 1,
+ .reg_base = STX104_I8254_BASE,
+ .val_bits = 8,
+ .io_port = true,
+ .wr_table = &pit_wr_table,
+ .rd_table = &pit_rd_table,
+};
+
static int stx104_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, int *val2, long mask)
{
@@ -339,6 +368,21 @@ static const char *stx104_names[STX104_NGPIO] = {
"DIN0", "DIN1", "DIN2", "DIN3", "DOUT0", "DOUT1", "DOUT2", "DOUT3"
};
+static int bank_select_i8254(struct regmap *map)
+{
+ const u8 select_i8254[] = { 0x3, 0xB, 0xA };
+ size_t i;
+ int err;
+
+ for (i = 0; i < ARRAY_SIZE(select_i8254); i++) {
+ err = regmap_write_bits(map, STX104_ADC_CONFIGURATION, STX104_RBK, select_i8254[i]);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
static int stx104_init_hw(struct stx104_iio *const priv)
{
int err;
@@ -361,7 +405,7 @@ static int stx104_init_hw(struct stx104_iio *const priv)
if (err)
return err;
- return 0;
+ return bank_select_i8254(priv->aio_ctl_map);
}
static int stx104_probe(struct device *dev, unsigned int id)
@@ -369,6 +413,7 @@ static int stx104_probe(struct device *dev, unsigned int id)
struct iio_dev *indio_dev;
struct stx104_iio *priv;
struct gpio_regmap_config gpio_config;
+ struct i8254_regmap_config pit_config;
void __iomem *stx104_base;
struct regmap *aio_ctl_map;
struct regmap *aio_data_map;
@@ -406,6 +451,11 @@ static int stx104_probe(struct device *dev, unsigned int id)
return dev_err_probe(dev, PTR_ERR(dio_map),
"Unable to initialize dio register map\n");
+ pit_config.map = devm_regmap_init_mmio(dev, stx104_base, &pit_regmap_config);
+ if (IS_ERR(pit_config.map))
+ return dev_err_probe(dev, PTR_ERR(pit_config.map),
+ "Unable to initialize i8254 register map\n");
+
priv = iio_priv(indio_dev);
priv->aio_ctl_map = aio_ctl_map;
priv->aio_data_map = aio_data_map;
@@ -449,7 +499,13 @@ static int stx104_probe(struct device *dev, unsigned int id)
.drvdata = dio_map,
};
- return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config));
+ err = PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config));
+ if (err)
+ return err;
+
+ pit_config.parent = dev;
+
+ return devm_i8254_regmap_register(dev, &pit_config);
}
static struct isa_driver stx104_driver = {
@@ -464,3 +520,4 @@ module_isa_driver(stx104_driver, num_stx104);
MODULE_AUTHOR("William Breathitt Gray <[email protected]>");
MODULE_DESCRIPTION("Apex Embedded Systems STX104 IIO driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(I8254);
--
2.39.2
On Sun, Apr 16, 2023 at 7:37 PM William Breathitt Gray
<[email protected]> wrote:
>
> The 104-DIO-48E features an 8254 Counter/Timer chip providing three
> counter/timers which can be used for frequency measurement, frequency
> output, pulse width modulation, pulse width measurement, event count,
> etc. The counter/timers use the same addresses as PPI 0 (addresses 0x0
> to 0x3), so a raw_spinlock_t is used to synchronize operations between
> the two regmap mappings to prevent clobbering.
>
> Signed-off-by: William Breathitt Gray <[email protected]>
Looks good to me. Let me know when you have an immutable tag to pull.
Bart
Sun, Apr 16, 2023 at 01:36:52PM -0400, William Breathitt Gray kirjoitti:
> The Intel 8254 PIT first appeared in the early 1980s and was used
> initially in IBM PC compatibles. The popularity of the original Intel
> 825x family of chips led to many subsequent variants and clones of the
> interface in various chips and integrated circuits. Although still
> popular, interfaces compatible with the Intel 8254 PIT are nowdays
> typically found embedded in larger VLSI processing chips and FPGA
> components rather than as discrete ICs.
>
> This patch series introduces a library to provide support for interfaces
> compatible with the venerable Intel 8254 Programmable Interval Timer
> (PIT). Modules wanting access to the i8254 library should select the
> newly introduced CONFIG_I8254 Kconfig option, and import the I8254
> symbol namespace.
>
> Support for the i8254 is added in respective follow-up patches for the
> 104-dio-48e driver and stx104 driver whose devices feature i8254
> compatible interfaces. Several additional dependencies are necessary for
> the 104-dio-48e [0][1][2] and stx104 [3][4].
>
> Due to the dependency requirements, I can take the i8254 introduction
> patch through the Counter tree and provide an immutable branch that can
> be merged to the GPIO and IIO trees; the 104-dio-48e patch and stx104
> patch could then be picked up separately by the respective subsystem
> maintainers.
Good job!
What I'm wondering is that. Can x86 core and others which are using that chip
utilize (some of) the functions from the library?
> [0] https://lore.kernel.org/all/05a878d340251b781387db4b6490f288e41a651c.1680543810.git.william.gray@linaro.org/
> [1] https://lore.kernel.org/all/[email protected]/
> [2] https://lore.kernel.org/all/[email protected]/
> [3] https://lore.kernel.org/all/[email protected]/
> [4] https://lore.kernel.org/all/[email protected]/
--
With Best Regards,
Andy Shevchenko
On Sun, Apr 16, 2023 at 7:37 PM William Breathitt Gray
<[email protected]> wrote:
> The 104-DIO-48E features an 8254 Counter/Timer chip providing three
> counter/timers which can be used for frequency measurement, frequency
> output, pulse width modulation, pulse width measurement, event count,
> etc. The counter/timers use the same addresses as PPI 0 (addresses 0x0
> to 0x3), so a raw_spinlock_t is used to synchronize operations between
> the two regmap mappings to prevent clobbering.
>
> Signed-off-by: William Breathitt Gray <[email protected]>
Very interesting development here.
Reviewed-by: Linus Walleij <[email protected]>
Yours,
Linus Walleij
On Sat, May 20, 2023 at 12:53:51PM +0300, [email protected] wrote:
> Sun, Apr 16, 2023 at 01:36:52PM -0400, William Breathitt Gray kirjoitti:
> > The Intel 8254 PIT first appeared in the early 1980s and was used
> > initially in IBM PC compatibles. The popularity of the original Intel
> > 825x family of chips led to many subsequent variants and clones of the
> > interface in various chips and integrated circuits. Although still
> > popular, interfaces compatible with the Intel 8254 PIT are nowdays
> > typically found embedded in larger VLSI processing chips and FPGA
> > components rather than as discrete ICs.
> >
> > This patch series introduces a library to provide support for interfaces
> > compatible with the venerable Intel 8254 Programmable Interval Timer
> > (PIT). Modules wanting access to the i8254 library should select the
> > newly introduced CONFIG_I8254 Kconfig option, and import the I8254
> > symbol namespace.
> >
> > Support for the i8254 is added in respective follow-up patches for the
> > 104-dio-48e driver and stx104 driver whose devices feature i8254
> > compatible interfaces. Several additional dependencies are necessary for
> > the 104-dio-48e [0][1][2] and stx104 [3][4].
> >
> > Due to the dependency requirements, I can take the i8254 introduction
> > patch through the Counter tree and provide an immutable branch that can
> > be merged to the GPIO and IIO trees; the 104-dio-48e patch and stx104
> > patch could then be picked up separately by the respective subsystem
> > maintainers.
>
> Good job!
>
> What I'm wondering is that. Can x86 core and others which are using that chip
> utilize (some of) the functions from the library?
Essentially we just need a regmap to register the device to the system
via devm_i8254_regmap_register(), so theoretically it would be possible
to load this driver for the integrated 8254 interface used by x86 core.
The big caveat however is that the Counter subsystem currently lacks an
in-kernel API, so registering that device would just expose the
userspace Counter sysfs and chrdev interfaces.
I suppose the interest is whether we could use the configuration
functionality of the Counter subsystem to abstract some of the hardcoded
routines and magic numbers in places like drivers/clocksource/i8253.c
and similar. Right now we wouldn't be able to do so, but perhaps in the
future if an in-kernel API is developed for the Counter subsystem then
it would be possible.
An interesting side-note about compatibility: the Intel 8253 counting
behavior differs subtly from the Intel 8254 in certain situations. For
example, suppose odd counts in Mode 3: the Intel 8253 will load the
initial count directly [0] whereas the Intel 8254 loads the initial
count minus one (an even number) [1]. This results in different maximums
and minimums: for example if the initial count is 5, the Intel 8253 will
report a maximum count of 5 and a minimum count of 2 (counting 5->4->2),
whereas the Intel 8254 will report a maximum count of 4 and a minimum
count of 0 (counting 4->2->0); same square wave is produced, but
different count values are reported.
William Breathitt Gray
[0] https://www.alldatasheet.com/datasheet-pdf/pdf/66098/INTEL/8253.html
[1] https://www.alldatasheet.com/datasheet-pdf/pdf/66099/INTEL/8254.html
On Sat, May 20, 2023 at 09:28:15PM +0200, Linus Walleij wrote:
> On Sun, Apr 16, 2023 at 7:37 PM William Breathitt Gray
> <[email protected]> wrote:
>
> > The 104-DIO-48E features an 8254 Counter/Timer chip providing three
> > counter/timers which can be used for frequency measurement, frequency
> > output, pulse width modulation, pulse width measurement, event count,
> > etc. The counter/timers use the same addresses as PPI 0 (addresses 0x0
> > to 0x3), so a raw_spinlock_t is used to synchronize operations between
> > the two regmap mappings to prevent clobbering.
> >
> > Signed-off-by: William Breathitt Gray <[email protected]>
>
> Very interesting development here.
> Reviewed-by: Linus Walleij <[email protected]>
>
> Yours,
> Linus Walleij
With this patch, we should now have complete support for every feature
available on this device. A nice milestone as well after first
introducing basic GPIO support for the ACCES 104-DIO-48E in 2016.
Given that there is also Intel 8255 support, it would be fun to route
back one of the device's GPIO outputs into the Intel 8254 timer gate and
hook up a simple speaker; we could get some nice beep generation going
and party like it's 1989! B-)
William Breathitt Gray
On Sun, Apr 16, 2023 at 01:36:53PM -0400, William Breathitt Gray wrote:
> Exposes consumer library functions providing support for interfaces
> compatible with the venerable Intel 8254 Programmable Interval Timer
> (PIT).
>
> The Intel 8254 PIT first appeared in the early 1980s and was used
> initially in IBM PC compatibles. The popularity of the original Intel
> 825x family of chips led to many subsequent variants and clones of the
> interface in various chips and integrated circuits. Although still
> popular, interfaces compatible with the Intel 8254 PIT are nowdays
> typically found embedded in larger VLSI processing chips and FPGA
> components rather than as discrete ICs.
>
> A CONFIG_I8254 Kconfig option is introduced by this patch. Modules
> wanting access to these i8254 library functions should select this
> Kconfig option, and import the I8254 symbol namespace.
>
> Signed-off-by: William Breathitt Gray <[email protected]>
I've queued this patch to the counter-next branch of my Counter tree.
Jonathan, Bart, I've created an immutable branch with just this patch
for you to pull which should allow you each to merge the other patch in
this patchset for your respective tree.
The following changes since commit ac9a78681b921877518763ba0e89202254349d1b:
Linux 6.4-rc1 (2023-05-07 13:34:35 -0700)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/wbg/counter.git tags/i8254
for you to fetch changes up to b923ba2c0829a1a22151139309b4ae5d47a99d34:
counter: i8254: Introduce the Intel 8254 interface library module (2023-06-08 10:14:10 -0400)
----------------------------------------------------------------
counter: i8254: Introduce the Intel 8254 interface library module
This exposes consumer library functions providing support for interfaces
compatible with the venerable Intel 8254 Programmable Interval Timer.
----------------------------------------------------------------
William Breathitt Gray (1):
counter: i8254: Introduce the Intel 8254 interface library module
Documentation/ABI/testing/sysfs-bus-counter | 54 ++++
MAINTAINERS | 7 +
drivers/counter/Kconfig | 15 +
drivers/counter/Makefile | 1 +
drivers/counter/counter-sysfs.c | 8 +-
drivers/counter/i8254.c | 447 ++++++++++++++++++++++++++++
include/linux/i8254.h | 21 ++
include/uapi/linux/counter.h | 6 +
8 files changed, 558 insertions(+), 1 deletion(-)
create mode 100644 drivers/counter/i8254.c
create mode 100644 include/linux/i8254.h
Sincerely,
William Breathitt Gray
Hi
On 6/8/23 17:43, William Breathitt Gray wrote:
> On Sun, Apr 16, 2023 at 01:36:53PM -0400, William Breathitt Gray wrote:
>> Exposes consumer library functions providing support for interfaces
>> compatible with the venerable Intel 8254 Programmable Interval Timer
>> (PIT).
>>
>> The Intel 8254 PIT first appeared in the early 1980s and was used
>> initially in IBM PC compatibles. The popularity of the original Intel
>> 825x family of chips led to many subsequent variants and clones of the
>> interface in various chips and integrated circuits. Although still
>> popular, interfaces compatible with the Intel 8254 PIT are nowdays
>> typically found embedded in larger VLSI processing chips and FPGA
>> components rather than as discrete ICs.
>>
>> A CONFIG_I8254 Kconfig option is introduced by this patch. Modules
>> wanting access to these i8254 library functions should select this
>> Kconfig option, and import the I8254 symbol namespace.
>>
>> Signed-off-by: William Breathitt Gray <[email protected]>
>
> I've queued this patch to the counter-next branch of my Counter tree.
>
> Jonathan, Bart, I've created an immutable branch with just this patch
> for you to pull which should allow you each to merge the other patch in
> this patchset for your respective tree.
>
I noticed this patch cause in linux-next "Counter support" submenu to
disappear and its menu entries are listed directly in "Device Drivers" menu.
Then I wonder why the CONFIG_I8254 has the help text defined since
drivers should select it.
Or was the idea something like below?
diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig
index bca21df51168..80631b5b0fc6 100644
--- a/drivers/counter/Kconfig
+++ b/drivers/counter/Kconfig
@@ -10,9 +10,10 @@ menuconfig COUNTER
interface. You only need to enable this, if you also want to
enable
one or more of the counter device drivers below.
+if COUNTER
+
config I8254
- tristate
- select COUNTER
+ tristate "i8254 interface library"
select REGMAP
help
Enables support for the i8254 interface library functions.
The i8254
@@ -25,8 +26,6 @@ config I8254
If built as a module its name will be i8254.
-if COUNTER
-
config 104_QUAD_8
tristate "ACCES 104-QUAD-8 driver"
depends on (PC104 && X86) || COMPILE_TEST
On Tue, Jun 20, 2023 at 05:03:53PM +0300, Jarkko Nikula wrote:
> Hi
>
> On 6/8/23 17:43, William Breathitt Gray wrote:
> > On Sun, Apr 16, 2023 at 01:36:53PM -0400, William Breathitt Gray wrote:
> > > Exposes consumer library functions providing support for interfaces
> > > compatible with the venerable Intel 8254 Programmable Interval Timer
> > > (PIT).
> > >
> > > The Intel 8254 PIT first appeared in the early 1980s and was used
> > > initially in IBM PC compatibles. The popularity of the original Intel
> > > 825x family of chips led to many subsequent variants and clones of the
> > > interface in various chips and integrated circuits. Although still
> > > popular, interfaces compatible with the Intel 8254 PIT are nowdays
> > > typically found embedded in larger VLSI processing chips and FPGA
> > > components rather than as discrete ICs.
> > >
> > > A CONFIG_I8254 Kconfig option is introduced by this patch. Modules
> > > wanting access to these i8254 library functions should select this
> > > Kconfig option, and import the I8254 symbol namespace.
> > >
> > > Signed-off-by: William Breathitt Gray <[email protected]>
> >
> > I've queued this patch to the counter-next branch of my Counter tree.
> >
> > Jonathan, Bart, I've created an immutable branch with just this patch
> > for you to pull which should allow you each to merge the other patch in
> > this patchset for your respective tree.
> >
> I noticed this patch cause in linux-next "Counter support" submenu to
> disappear and its menu entries are listed directly in "Device Drivers" menu.
>
> Then I wonder why the CONFIG_I8254 has the help text defined since drivers
> should select it.
>
> Or was the idea something like below?
>
> diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig
> index bca21df51168..80631b5b0fc6 100644
> --- a/drivers/counter/Kconfig
> +++ b/drivers/counter/Kconfig
> @@ -10,9 +10,10 @@ menuconfig COUNTER
> interface. You only need to enable this, if you also want to
> enable
> one or more of the counter device drivers below.
>
> +if COUNTER
> +
> config I8254
> - tristate
> - select COUNTER
> + tristate "i8254 interface library"
> select REGMAP
> help
> Enables support for the i8254 interface library functions. The
> i8254
> @@ -25,8 +26,6 @@ config I8254
>
> If built as a module its name will be i8254.
>
> -if COUNTER
> -
> config 104_QUAD_8
> tristate "ACCES 104-QUAD-8 driver"
> depends on (PC104 && X86) || COMPILE_TEST
Hi Jarkko,
Thank you for pointing that out, the config I8254 entry should have been
added above the menuconfig COUNTER entry instead of below it; if you
move it you should notice the "Counter support" submenu items go back to
normal. The intention is for consumer drivers to select I8254 when they
use the library. The I8254 module doesn't do anything on its own so
that's why it's hidden in the menu (the help description is there for
the sake of reviewers). I'll submit a patch soon fixing this.
Thanks,
William Breathitt Gray