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: <20251114090448.285685-3-haowen.ting@realtek.com>
Date: Fri, 14 Nov 2025 17:04:48 +0800
From: Hao-Wen Ting <haowen.ting@...ltek.com>
To: <daniel.lezcano@...aro.org>, <tglx@...utronix.de>
CC: <jinn.cheng@...ltek.com>, <edwardwu@...ltek.com>, <phelic@...ltek.com>,
        <shawn.huang724@...ltek.com>, <haowen.ting@...ltek.com>,
        <cy.huang@...ltek.com>, <james.tai@...ltek.com>, <cylee12@...ltek.com>,
        <phinex@...ltek.com>, <conor+dt@...nel.org>, <krzk+dt@...nel.org>,
        <robh@...nel.org>, <devicetree@...r.kernel.org>,
        <linux-kernel@...r.kernel.org>, <stanley_chang@...ltek.com>
Subject: [PATCH v2 2/2] clocksource: Add Realtek systimer as tick broadcast driver

Add a tick broadcast timer driver for Realtek SoCs.

On Realtek platforms, CPUs can enter deep idle states (C-states) where
the local timer is stopped and powered off. Without a global tick
broadcast timer, one CPU must remain awake to wake up the others,
preventing all CPUs from entering deep idle simultaneously.

This driver provides a tick broadcast timer which remains active
during deep idle states. This allows all CPUs to enter power-cut
idle states simultaneously, significantly reducing overall power
consumption.

The timer operates at 1MHz and supports oneshot mode.

Signed-off-by: Hao-Wen Ting <haowen.ting@...ltek.com>
---
 MAINTAINERS                         |   5 +
 drivers/clocksource/Kconfig         |  10 ++
 drivers/clocksource/Makefile        |   1 +
 drivers/clocksource/timer-realtek.c | 172 ++++++++++++++++++++++++++++
 4 files changed, 188 insertions(+)
 create mode 100644 drivers/clocksource/timer-realtek.c

diff --git a/MAINTAINERS b/MAINTAINERS
index c7a116b795d5..90f511bb4982 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21670,6 +21670,11 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/spi/realtek,rtl9301-snand.yaml
 F:	drivers/spi/spi-realtek-rtl-snand.c
 
+REALTEK SYSTIMER DRIVER
+M:	Hao-Wen Ting <haowen.ting@...ltek.com>
+S:	Maintained
+F:	drivers/clocksource/timer-realtek.c
+
 REALTEK WIRELESS DRIVER (rtlwifi family)
 M:	Ping-Ke Shih <pkshih@...ltek.com>
 L:	linux-wireless@...r.kernel.org
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index ffcd23668763..0c1835b48a18 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -782,4 +782,14 @@ config NXP_STM_TIMER
           Enables the support for NXP System Timer Module found in the
           s32g NXP platform series.
 
+config RTK_SYSTIMER
+	bool "Realtek SYSTIMER support"
+	depends on ARM || ARM64
+	select TIMER_OF
+	help
+	  This enables the global tick-broadcast timer on Realtek platforms.
+	  If your Realtek platform supports power-cut level CPU idle states,
+	  enabling this timer allows all CPUs to enter power-cut simultaneously
+	  to achieve lower power consumption.
+
 endmenu
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index ec4452ee958f..b46376af6b49 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -95,3 +95,4 @@ obj-$(CONFIG_CLKSRC_LOONGSON1_PWM)	+= timer-loongson1-pwm.o
 obj-$(CONFIG_EP93XX_TIMER)		+= timer-ep93xx.o
 obj-$(CONFIG_RALINK_TIMER)		+= timer-ralink.o
 obj-$(CONFIG_NXP_STM_TIMER)		+= timer-nxp-stm.o
+obj-$(CONFIG_RTK_SYSTIMER)		+= timer-realtek.o
diff --git a/drivers/clocksource/timer-realtek.c b/drivers/clocksource/timer-realtek.c
new file mode 100644
index 000000000000..f484fd97e964
--- /dev/null
+++ b/drivers/clocksource/timer-realtek.c
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 Realtek Semiconductor Corp.
+ */
+
+#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
+
+#include <linux/irqflags.h>
+#include <linux/interrupt.h>
+#include "timer-of.h"
+
+#define ENBL 1
+#define DSBL 0
+
+#define SYSTIMER_RATE 1000000
+#define SYSTIMER_MIN_DELTA 0x64
+#define SYSTIMER_MAX_DELTA ULONG_MAX
+
+/* SYSTIMER Register Offset (RTK Internal Use) */
+#define TS_LW_OFST 0x0
+#define TS_HW_OFST 0x4
+#define TS_CMP_VAL_LW_OFST 0x8
+#define TS_CMP_VAL_HW_OFST 0xC
+#define TS_CMP_CTRL_OFST 0x10
+#define TS_CMP_STAT_OFST 0x14
+
+/* SYSTIMER CMP CTRL REG Mask */
+#define TS_CMP_EN_MASK 0x1
+#define TS_WR_EN0_MASK 0x2
+
+static void __iomem *systimer_base;
+
+static u64 rtk_ts64_read(void)
+{
+	u64 ts;
+	u32 low, high;
+
+	/* Caution: Read LSB word (TS_LW_OFST) first then MSB (TS_HW_OFST) */
+	low = readl(systimer_base + TS_LW_OFST);
+	high = readl(systimer_base + TS_HW_OFST);
+
+	pr_debug("64bit-TS:HW=0x%08x,LW=0x%08x\n", high, low);
+	ts = ((u64)high << 32) | low;
+
+	return ts;
+}
+
+static void rtk_cmp_value_write(u64 value)
+{
+	u32 high, low;
+
+	low = value & 0xFFFFFFFF;
+	high = value >> 32;
+	pr_debug("Write 64bit-CMP:HW=0x%08x,LW=0x%08x\n", high, low);
+
+	writel(high, systimer_base + TS_CMP_VAL_HW_OFST);
+	writel(low, systimer_base + TS_CMP_VAL_LW_OFST);
+}
+
+static inline void rtk_cmp_en_write(bool cmp_en)
+{
+	u32 val;
+
+	val = TS_WR_EN0_MASK;
+	if (cmp_en == ENBL)
+		val |= TS_CMP_EN_MASK;
+
+	writel(val, systimer_base + TS_CMP_CTRL_OFST);
+	pr_debug("Write TS CMP CTRL = 0x%08x\n", val);
+}
+
+static int rtk_syst_clkevt_next_ktime(ktime_t expires,
+				      struct clock_event_device *clkevt)
+{
+	u64 cmp_val;
+	unsigned long flags;
+	ktime_t now = ktime_get();
+	s64 delta_ns = ktime_to_ns(ktime_sub(expires, now));
+	u64 delta_us = delta_ns / 1000;
+
+	pr_debug("delta_ns = %lld, clkevt.min_delta_ns = %llu\n",
+		 delta_ns, clkevt->min_delta_ns);
+
+	if (delta_ns <= (s64)clkevt->min_delta_ns) {
+		delta_ns = clkevt->min_delta_ns;
+		delta_us = delta_ns / 1000;
+		pr_debug("Clamping delta_ns to min_delta_ns\n");
+	}
+
+	rtk_cmp_en_write(DSBL);
+	local_irq_save(flags);
+	cmp_val = rtk_ts64_read();
+
+	/* Set CMP value to current timestamp plus delta_us */
+	rtk_cmp_value_write(cmp_val + delta_us);
+	rtk_cmp_en_write(ENBL);
+	local_irq_restore(flags);
+	return 0;
+}
+
+static irqreturn_t rtk_ts_match_intr_handler(int irq, void *dev_id)
+{
+	u32 val;
+	void __iomem *reg_base;
+	struct clock_event_device *clkevt = dev_id;
+
+	/* Disable TS CMP Match */
+	rtk_cmp_en_write(DSBL);
+
+	/* Clear TS CMP INTR */
+	reg_base = systimer_base + TS_CMP_STAT_OFST;
+	val = readl(reg_base) & TS_CMP_EN_MASK;
+	writel(val | TS_CMP_EN_MASK, reg_base);
+
+	clkevt->event_handler(clkevt);
+
+	return IRQ_HANDLED;
+}
+
+static int rtk_syst_shutdown(struct clock_event_device *clkevt)
+{
+	void __iomem *reg_base;
+	u64 cmp_val = 0;
+
+	/* Disable TS CMP Match */
+	rtk_cmp_en_write(DSBL);
+	/* Set compare value to 0 */
+	rtk_cmp_value_write(cmp_val);
+
+	/* Clear TS CMP INTR */
+	reg_base = systimer_base + TS_CMP_STAT_OFST;
+	writel(TS_CMP_EN_MASK, reg_base);
+	return 0;
+}
+
+static struct timer_of _to = {
+	.flags = TIMER_OF_IRQ | TIMER_OF_BASE,
+
+	.clkevt = {
+		.name = "rtk-clkevt",
+		.rating = 300,
+		.cpumask = cpu_possible_mask,
+		.features = CLOCK_EVT_FEAT_DYNIRQ |
+			    CLOCK_EVT_FEAT_ONESHOT |
+			    CLOCK_EVT_FEAT_KTIME,
+		.set_next_ktime = rtk_syst_clkevt_next_ktime,
+		.set_state_oneshot = rtk_syst_shutdown,
+		.set_state_shutdown = rtk_syst_shutdown
+	},
+
+	.of_irq = {
+		.flags = IRQF_TIMER | IRQF_IRQPOLL,
+		.handler = rtk_ts_match_intr_handler
+	},
+};
+
+static int __init rtk_systimer_init(struct device_node *node)
+{
+	int ret;
+
+	ret = timer_of_init(node, &_to);
+	if (ret)
+		return ret;
+
+	systimer_base = timer_of_base(&_to);
+	clockevents_config_and_register(&_to.clkevt, SYSTIMER_RATE,
+					SYSTIMER_MIN_DELTA, SYSTIMER_MAX_DELTA);
+
+	return 0;
+}
+
+TIMER_OF_DECLARE(rtk_systimer, "realtek,rtd1625-systimer", rtk_systimer_init);
-- 
2.34.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ