lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <2fb4aa29e8c581f5c7e97ab7678ccb34e99e5c6e.1642369117.git.sander@svanheule.net>
Date:   Sun, 16 Jan 2022 22:39:25 +0100
From:   Sander Vanheule <sander@...nheule.net>
To:     linux-kernel@...r.kernel.org, devicetree@...r.kernel.org
Cc:     Daniel Lezcano <daniel.lezcano@...aro.org>,
        Thomas Gleixner <tglx@...utronix.de>,
        Rob Herring <robh+dt@...nel.org>,
        Sander Vanheule <sander@...nheule.net>
Subject: [PATCH 2/2] clocksource/drivers: Add Realtek Otto timer driver

Driver to provide initial support for the timer/counter blocks found on
Realtek Otto MIPS SoCs.

This implementation hard enables the derived clock with a divisor of 2,
to obtain the highest possible timer resolution. A real derived and
reconfigurable timer clock can be implemented later, should the need
arise for longer timeouts.

Since this platform has other clocksources (CSRC_R4K or MIPS GIC), the
timers are only added as clockevent devices.

Signed-off-by: Sander Vanheule <sander@...nheule.net>
---
 MAINTAINERS                              |   6 +
 drivers/clocksource/Kconfig              |   8 +
 drivers/clocksource/Makefile             |   1 +
 drivers/clocksource/timer-realtek-otto.c | 216 +++++++++++++++++++++++
 4 files changed, 231 insertions(+)
 create mode 100644 drivers/clocksource/timer-realtek-otto.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 6d9dafd09de0..ad7c9b10fcd8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16342,6 +16342,12 @@ S:	Maintained
 F:	include/sound/rt*.h
 F:	sound/soc/codecs/rt*
 
+REALTEK OTTO TIMER
+M:	Sander Vanheule <sander@...nheule.net>
+S:	Maintained
+F:	Documentation/devicetree/bindings/timer/realtek,otto-tc.yaml
+F:	drivers/clocksource/timer-realtek-otto.c
+
 REALTEK OTTO WATCHDOG
 M:	Sander Vanheule <sander@...nheule.net>
 L:	linux-watchdog@...r.kernel.org
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index cfb8ea0df3b1..03557568182d 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -84,6 +84,14 @@ config IXP4XX_TIMER
 	help
 	  Enables support for the Intel XScale IXP4xx SoC timer.
 
+config REALTEK_OTTO_TIMER
+	bool "Realtek Otto timer driver"
+	depends on MACH_REALTEK_RTL || COMPILE_TEST
+	select TIMER_OF
+	help
+	  Enables support for the timer/counter found on Realtek's networking
+	  SoC series RTL838x, RTL839x, RTL930x, RTL931x.
+
 config ROCKCHIP_TIMER
 	bool "Rockchip timer driver" if COMPILE_TEST
 	depends on ARM || ARM64
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index fa5f624eadb6..43c792e4a574 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_DW_APB_TIMER)	+= dw_apb_timer.o
 obj-$(CONFIG_DW_APB_TIMER_OF)	+= dw_apb_timer_of.o
 obj-$(CONFIG_FTTMR010_TIMER)	+= timer-fttmr010.o
 obj-$(CONFIG_IXP4XX_TIMER)	+= timer-ixp4xx.o
+obj-$(CONFIG_REALTEK_OTTO_TIMER)      += timer-realtek-otto.o
 obj-$(CONFIG_ROCKCHIP_TIMER)      += timer-rockchip.o
 obj-$(CONFIG_CLKSRC_NOMADIK_MTU)	+= nomadik-mtu.o
 obj-$(CONFIG_CLKSRC_DBX500_PRCMU)	+= clksrc-dbx500-prcmu.o
diff --git a/drivers/clocksource/timer-realtek-otto.c b/drivers/clocksource/timer-realtek-otto.c
new file mode 100644
index 000000000000..2c07be042614
--- /dev/null
+++ b/drivers/clocksource/timer-realtek-otto.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Realtek Otto platform timer/counter
+ *
+ * Copyright
+ *	Sander Vanheule - 2022
+ *
+ * Up-counting 28-bit timer that can operate in oneshot or repeat mode,
+ * providing interrupts at roll-over. The maximum value is written to the DATA
+ * register, while the current timer value can be read from the CNT register.
+ *
+ * A divided clock is used to drive the timer, derived from the bus clock. The
+ * clock divisor is configurable from 2 to 65535. Divisor values of 0 and 1
+ * disable the timer clock. The timer can also be enabled independently from
+ * the clock selection with the ENABLE flag.
+ *
+ * The SoC's interrupt controller can configure the CPU affinity of the timer
+ * interrupts to any subset of the available CPUs.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clocksource.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#include "timer-of.h"
+
+struct otto_tc_ctrl {
+	struct timer_of to;
+	spinlock_t lock;
+};
+
+#define OTTO_TC_REG_OFFSET(to, offset)	(timer_of_base(to) + offset)
+
+#define OTTO_TC_REG_DATA(to)		OTTO_TC_REG_OFFSET(to, 0x0)
+#define OTTO_TC_REG_CNT(to)		OTTO_TC_REG_OFFSET(to, 0x4)
+#define OTTO_TC_WIDTH			28
+#define OTTO_TC_MAX_PERIOD		BIT(OTTO_TC_WIDTH)
+
+#define OTTO_TC_REG_CTL(to)		OTTO_TC_REG_OFFSET(to, 0x8)
+#define OTTO_TC_CTL_ENABLE		BIT(28)
+#define OTTO_TC_CTL_MODE		BIT(24)
+#define OTTO_TC_MODE_ONESHOT		FIELD_PREP(OTTO_TC_CTL_MODE, 0)
+#define OTTO_TC_MODE_REPEAT		FIELD_PREP(OTTO_TC_CTL_MODE, 1)
+#define OTTO_TC_CTL_DIVISOR		GENMASK(15, 0)
+
+#define OTTO_TC_MIN_DIVISOR		2
+#define OTTO_TC_MAX_DIVISOR		OTTO_TC_CTL_DIVISOR
+
+#define OTTO_TC_REG_ICTL(to)		OTTO_TC_REG_OFFSET(to, 0xC)
+#define OTTO_TC_ICTL_ENABLE		BIT(20)
+#define OTTO_TC_ICTL_STATUS		BIT(16)
+
+static inline void otto_tc_irq_enable_clear(struct timer_of *to)
+{
+	writel(OTTO_TC_ICTL_ENABLE | OTTO_TC_ICTL_STATUS, OTTO_TC_REG_ICTL(to));
+}
+
+static inline struct otto_tc_ctrl *otto_tc_timer_to_ctrl(struct timer_of *to)
+{
+	return container_of(to, struct otto_tc_ctrl, to);
+}
+
+static irqreturn_t otto_tc_handler(int irq, void *dev_id)
+{
+	struct clock_event_device *ced = dev_id;
+	struct timer_of *to = to_timer_of(ced);
+
+	otto_tc_irq_enable_clear(to);
+	ced->event_handler(ced);
+
+	return IRQ_HANDLED;
+}
+
+static void otto_tc_set_divisor(struct otto_tc_ctrl *ctrl, u16 divisor)
+{
+	struct timer_of *to = &ctrl->to;
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(&ctrl->lock, flags);
+	val = readl(OTTO_TC_REG_CTL(to)) & ~OTTO_TC_CTL_DIVISOR;
+	val |= FIELD_PREP(OTTO_TC_CTL_DIVISOR, divisor);
+	writel(val, OTTO_TC_REG_CTL(to));
+	spin_unlock_irqrestore(&ctrl->lock, flags);
+}
+
+static void otto_tc_set_timeout(struct clock_event_device *ced, u32 value)
+{
+	struct timer_of *to = to_timer_of(ced);
+	struct otto_tc_ctrl *ctrl = otto_tc_timer_to_ctrl(to);
+	unsigned long flags;
+
+	spin_lock_irqsave(&ctrl->lock, flags);
+	writel(value, OTTO_TC_REG_DATA(to));
+	spin_unlock_irqrestore(&ctrl->lock, flags);
+}
+
+/*
+ * Timers can only be (re)started from the disabled state. The clkevt state
+ * machine is expected perform the necessary disables (shutdown) before moving
+ * a timer into an enabled state through .set_oneshot() or .set_periodic().
+ */
+static void otto_tc_set_mode(struct clock_event_device *ced, bool started, u32 mode)
+{
+	struct timer_of *to = to_timer_of(ced);
+	struct otto_tc_ctrl *ctrl = otto_tc_timer_to_ctrl(to);
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(&ctrl->lock, flags);
+	val = readl(OTTO_TC_REG_CTL(to));
+	val = (val & ~OTTO_TC_CTL_MODE) | mode;
+	if (started)
+		val |= OTTO_TC_CTL_ENABLE;
+	else
+		val &= ~OTTO_TC_CTL_ENABLE;
+	writel(val, OTTO_TC_REG_CTL(to));
+	spin_unlock_irqrestore(&ctrl->lock, flags);
+}
+
+static int otto_tc_set_next_event(unsigned long evt, struct clock_event_device *ced)
+{
+	otto_tc_set_timeout(ced, evt);
+
+	return 0;
+}
+
+static int otto_tc_set_periodic(struct clock_event_device *ced)
+{
+	struct timer_of *to = to_timer_of(ced);
+
+	otto_tc_set_timeout(ced, timer_of_period(to));
+	otto_tc_set_mode(ced, true, OTTO_TC_MODE_REPEAT);
+
+	return 0;
+}
+
+static int otto_tc_set_oneshot(struct clock_event_device *ced)
+{
+	otto_tc_set_mode(ced, true, OTTO_TC_MODE_ONESHOT);
+
+	return 0;
+}
+
+static int otto_tc_set_oneshot_stopped(struct clock_event_device *ced)
+{
+	otto_tc_set_mode(ced, false, OTTO_TC_MODE_ONESHOT);
+
+	return 0;
+}
+
+static int otto_tc_set_shutdown(struct clock_event_device *ced)
+{
+	otto_tc_set_mode(ced, false, 0);
+
+	return 0;
+}
+
+static void __init otto_tc_init_clkevt(struct timer_of *to)
+{
+	struct clock_event_device *ced = &to->clkevt;
+
+	ced->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC |
+		CLOCK_EVT_FEAT_DYNIRQ;
+	ced->set_next_event = otto_tc_set_next_event;
+	ced->set_state_periodic = otto_tc_set_periodic;
+	ced->set_state_oneshot = otto_tc_set_oneshot;
+	ced->set_state_oneshot_stopped = otto_tc_set_oneshot_stopped;
+	ced->set_state_shutdown = otto_tc_set_shutdown;
+	ced->cpumask = cpumask_of(0);
+	ced->rating = 300;
+
+	clockevents_config_and_register(ced, timer_of_rate(to), 1, OTTO_TC_MAX_PERIOD);
+}
+
+static int __init otto_tc_init(struct device_node *node)
+{
+	struct otto_tc_ctrl *ctrl;
+	int err;
+
+	ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl)
+		return -ENOMEM;
+
+	spin_lock_init(&ctrl->lock);
+
+	ctrl->to.flags = TIMER_OF_BASE | TIMER_OF_IRQ | TIMER_OF_CLOCK;
+	ctrl->to.of_clk.name = "bus";
+	ctrl->to.of_irq.flags = IRQF_TIMER;
+	ctrl->to.of_irq.handler = otto_tc_handler;
+
+	err = timer_of_init(node, &ctrl->to);
+	if (err)
+		goto err_out;
+
+	/* Reset timer state */
+	writel(0, OTTO_TC_REG_CTL(&ctrl->to));
+	writel(0, OTTO_TC_REG_DATA(&ctrl->to));
+
+	/* TODO Replace by a real derived clock */
+	otto_tc_set_divisor(ctrl, OTTO_TC_MIN_DIVISOR);
+	ctrl->to.of_clk.rate /= OTTO_TC_MIN_DIVISOR;
+	ctrl->to.of_clk.period /= OTTO_TC_MIN_DIVISOR;
+
+	otto_tc_irq_enable_clear(&ctrl->to);
+	otto_tc_init_clkevt(&ctrl->to);
+
+	return 0;
+
+err_out:
+	kfree(ctrl);
+
+	return err;
+}
+TIMER_OF_DECLARE(otto_tc, "realtek,otto-tc", otto_tc_init);
-- 
2.34.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ