[PATCH 2/2] clocksource/drivers/arm_arch_timer_mmio: Restore support for early init

Stephan Gerhold stephan.gerhold at linaro.org
Wed Jun 10 10:53:11 PDT 2026


Some single-core Qualcomm modem platforms (e.g. MDM9625, MDM9607) have an
obscure timer setup where the global Arm MMIO timer (arm,armv7-timer-mem)
is used as the only available timer for the CPU. This setup used to work
fine until commit 0f67b56d84b4 ("clocksource/drivers/arm_arch_timer_mmio:
Switch over to standalone driver") when the early timer initialization
using TIMER_OF_DECLARE() was removed when moving to the standalone MMIO
driver.

We need some timer early to run properly, so without another timer in the
system the only choice is to make the MMIO timer available early again
using TIMER_OF_DECLARE(). Use the refactoring in the previous commit to
reuse most of the initialization code in the new standalone driver and
probe one timer early if required. ACPI-based systems and platforms with a
CPU-local CP15 timer continue to probe the timer late as before.

Reported-by: Jack Matthews <jack at jackmatthe.ws>
Closes: https://lore.kernel.org/r/46A20F89-E208-4091-8B6E-B5C38BF82B42@jackmatthe.ws/
Fixes: 0f67b56d84b4 ("clocksource/drivers/arm_arch_timer_mmio: Switch over to standalone driver")
Signed-off-by: Stephan Gerhold <stephan.gerhold at linaro.org>
---
I couldn't find any existing (fully-supported) platform upstream that
relies on this, so I omitted Cc stable. MDM9607 does have most of the
necessary drivers upstream, it's just missing the DT (I would like to
upstream that once ready).
---
 drivers/clocksource/arm_arch_timer_mmio.c | 47 +++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/drivers/clocksource/arm_arch_timer_mmio.c b/drivers/clocksource/arm_arch_timer_mmio.c
index 5cb94051c4be..d128dff7067f 100644
--- a/drivers/clocksource/arm_arch_timer_mmio.c
+++ b/drivers/clocksource/arm_arch_timer_mmio.c
@@ -15,6 +15,7 @@
 #include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/of_address.h>
 #include <linux/platform_device.h>
@@ -409,6 +410,8 @@ static struct arch_timer *arch_timer_mmio_init(struct arch_timer_mem *gt_block,
 	return_ptr(at);
 }
 
+static struct device_node *arch_timer_mmio_early_np;
+
 static int arch_timer_mmio_probe(struct platform_device *pdev)
 {
 	struct device_node *np = pdev->dev.of_node;
@@ -417,6 +420,10 @@ static int arch_timer_mmio_probe(struct platform_device *pdev)
 	int ret;
 
 	if (np) {
+		/* Check if timer was already probed early */
+		if (np == arch_timer_mmio_early_np)
+			return 0;
+
 		gt_block = devm_kzalloc(&pdev->dev, sizeof(*gt_block),
 					GFP_KERNEL);
 		if (!gt_block)
@@ -436,6 +443,46 @@ static int arch_timer_mmio_probe(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct of_device_id arch_timer_cp15_match[] __initconst = {
+	{ .compatible = "arm,armv7-timer", },
+	{ .compatible = "arm,armv8-timer", },
+	{}
+};
+
+static bool __init arch_timer_mmio_has_cp15(void)
+{
+	struct device_node *np __free(device_node) =
+		of_find_matching_node(NULL, arch_timer_cp15_match);
+
+	return np && of_device_is_available(np);
+}
+
+static int __init arch_timer_mmio_of_early_init(struct device_node *np)
+{
+	struct arch_timer *at;
+	int ret;
+
+	if (arch_timer_mmio_early_np || arch_timer_mmio_has_cp15())
+		return -EPROBE_DEFER;
+
+	struct arch_timer_mem *gt_block __free(kfree) = kzalloc_obj(*gt_block);
+	if (!gt_block)
+		return -ENOMEM;
+
+	ret = of_populate_gt_block(np, gt_block);
+	if (ret)
+		return ret;
+
+	at = arch_timer_mmio_init(gt_block, np);
+	if (IS_ERR(at))
+		return PTR_ERR(at);
+	retain_and_null_ptr(gt_block);
+
+	arch_timer_mmio_early_np = np;
+	return 0;
+}
+TIMER_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem", arch_timer_mmio_of_early_init);
+
 static const struct of_device_id arch_timer_mmio_of_table[] = {
 	{ .compatible = "arm,armv7-timer-mem", },
 	{}

-- 
2.54.0




More information about the linux-arm-kernel mailing list