All of lore.kernel.org
 help / color / mirror / Atom feed
From: Sander Vanheule <sander@svanheule.net>
To: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>,
	Thomas Gleixner <tglx@linutronix.de>,
	Rob Herring <robh+dt@kernel.org>,
	Sander Vanheule <sander@svanheule.net>
Subject: [PATCH 2/2] clocksource/drivers: Add Realtek Otto timer driver
Date: Sun, 16 Jan 2022 22:39:25 +0100	[thread overview]
Message-ID: <2fb4aa29e8c581f5c7e97ab7678ccb34e99e5c6e.1642369117.git.sander@svanheule.net> (raw)
In-Reply-To: <cover.1642369117.git.sander@svanheule.net>

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@svanheule.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@svanheule.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@svanheule.net>
 L:	linux-watchdog@vger.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


  parent reply	other threads:[~2022-01-16 21:39 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-01-16 21:39 [PATCH 0/2] Realtek Otto timer/counter support Sander Vanheule
2022-01-16 21:39 ` [PATCH 1/2] dt-bindings: timer: Add realtek,otto-tc binding Sander Vanheule
2022-02-09  2:49   ` Rob Herring
2022-02-09 10:58     ` Sander Vanheule
2022-01-16 21:39 ` Sander Vanheule [this message]
2022-01-17  1:28   ` [PATCH 2/2] clocksource/drivers: Add Realtek Otto timer driver kernel test robot
2022-01-17  1:28     ` kernel test robot
2022-01-17  8:03     ` Sander Vanheule
2022-01-17  8:03       ` Sander Vanheule
2022-01-17  7:38   ` kernel test robot
2022-01-17  7:38     ` kernel test robot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=2fb4aa29e8c581f5c7e97ab7678ccb34e99e5c6e.1642369117.git.sander@svanheule.net \
    --to=sander@svanheule.net \
    --cc=daniel.lezcano@linaro.org \
    --cc=devicetree@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=robh+dt@kernel.org \
    --cc=tglx@linutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.