* [PATCH master 1/2] clock: make udelay(), get_time_ns() and is_timeout() weak aliases
@ 2026-09-09 11:42 Ahmad Fatoum
2026-09-09 11:42 ` [PATCH master 2/2] ARM64: fall back to reading the counter directly in the PBL udelay() Ahmad Fatoum
0 siblings, 1 reply; 2+ messages in thread
From: Ahmad Fatoum @ 2026-09-09 11:42 UTC (permalink / raw)
To: barebox; +Cc: ske, mtr, Ahmad Fatoum
The ARM64 PBL is about to override these three for the time before a
clocksource is registered. Give the clocksource based implementations
names of their own and turn the generic ones into weak aliases, so an
override can still call the clocksource based version. No functional
change.
Assisted-by: Claude:fable-5.1
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
common/clock.c | 13 ++++++++++---
include/clock.h | 5 +++++
2 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/common/clock.c b/common/clock.c
index fc770ac8530f..0f2f47ed5399 100644
--- a/common/clock.c
+++ b/common/clock.c
@@ -50,7 +50,7 @@ late_initcall(dummy_csrc_warn);
/**
* get_time_ns - get current timestamp in nanoseconds
*/
-uint64_t get_time_ns(void)
+uint64_t clocksource_current_get_time_ns(void)
{
struct clocksource *cs = current_clock;
uint64_t cycle_now, cycle_delta;
@@ -73,6 +73,8 @@ uint64_t get_time_ns(void)
time_ns += ns_offset;
return time_ns;
}
+
+uint64_t get_time_ns(void) __weak __alias(clocksource_current_get_time_ns);
EXPORT_SYMBOL(get_time_ns);
/**
@@ -165,7 +167,7 @@ int is_timeout_non_interruptible(uint64_t start_ns, uint64_t time_offset_ns)
}
EXPORT_SYMBOL(is_timeout_non_interruptible);
-int is_timeout(uint64_t start_ns, uint64_t time_offset_ns)
+int clocksource_current_is_timeout(uint64_t start_ns, uint64_t time_offset_ns)
{
int ret = is_timeout_non_interruptible(start_ns, time_offset_ns);
@@ -174,6 +176,9 @@ int is_timeout(uint64_t start_ns, uint64_t time_offset_ns)
return ret;
}
+
+int is_timeout(uint64_t start_ns, uint64_t time_offset_ns)
+ __weak __alias(clocksource_current_is_timeout);
EXPORT_SYMBOL(is_timeout);
void ndelay(unsigned long nsecs)
@@ -184,12 +189,14 @@ void ndelay(unsigned long nsecs)
}
EXPORT_SYMBOL(ndelay);
-void udelay(unsigned long usecs)
+void clocksource_current_udelay(unsigned long usecs)
{
uint64_t start = get_time_ns();
while(!is_timeout(start, usecs * USECOND));
}
+
+void udelay(unsigned long usecs) __weak __alias(clocksource_current_udelay);
EXPORT_SYMBOL(udelay);
void mdelay(unsigned long msecs)
diff --git a/include/clock.h b/include/clock.h
index ca69535e9185..ea87bfd2020a 100644
--- a/include/clock.h
+++ b/include/clock.h
@@ -58,6 +58,11 @@ void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);
void mdelay_non_interruptible(unsigned long msecs);
+/* the clocksource based implementations the weak functions above alias */
+void clocksource_current_udelay(unsigned long usecs);
+uint64_t clocksource_current_get_time_ns(void);
+int clocksource_current_is_timeout(uint64_t start_ns, uint64_t time_offset_ns);
+
#if IN_PROPER
void clocksource_srand(void);
#else
--
2.47.3
^ permalink raw reply [flat|nested] 2+ messages in thread* [PATCH master 2/2] ARM64: fall back to reading the counter directly in the PBL udelay()
2026-09-09 11:42 [PATCH master 1/2] clock: make udelay(), get_time_ns() and is_timeout() weak aliases Ahmad Fatoum
@ 2026-09-09 11:42 ` Ahmad Fatoum
0 siblings, 0 replies; 2+ messages in thread
From: Ahmad Fatoum @ 2026-09-09 11:42 UTC (permalink / raw)
To: barebox; +Cc: ske, mtr, Ahmad Fatoum
Since commit 67531b21addc ("ARM64: enable PBL_CLOCKSOURCE compatibility"),
udelay() in PBL expects a clocksource to have been registered. That
happens from a constructor in setup_c(), but i.MX93 already polls the
ELE mailbox from its unrelocated lowlevel init before that and now
panics with "No PBL clocksource has been initialized".
Let's override udelay(), get_time_ns() and is_timeout() in the ARM64
PBL: As long as no clocksource is registered, udelay() reads the counter
directly like it used to and time stands still, so timeouts never fire.
The check happens in assembly without touching the stack and
current_clock is moved to .data, so this is usable from image entry on,
before BSS is cleared.
Fixes: 67531b21addc ("ARM64: enable PBL_CLOCKSOURCE compatibility")
Assisted-by: Claude:fable-5.1
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
arch/arm/lib64/Makefile | 1 +
arch/arm/lib64/clocksource-registered.S | 16 +++++++++
arch/arm/lib64/delay.c | 47 +++++++++++++++++++++++++
common/clock.c | 4 ++-
include/clock.h | 5 +++
5 files changed, 72 insertions(+), 1 deletion(-)
create mode 100644 arch/arm/lib64/clocksource-registered.S
create mode 100644 arch/arm/lib64/delay.c
diff --git a/arch/arm/lib64/Makefile b/arch/arm/lib64/Makefile
index 218de68a5468..a3b0b00f3024 100644
--- a/arch/arm/lib64/Makefile
+++ b/arch/arm/lib64/Makefile
@@ -11,4 +11,5 @@ obj-pbl-y += setjmp.o
obj-pbl-y += reloc.o
obj-y += io.o
pbl-$(CONFIG_CLOCKSOURCE_ARM_ARCHITECTED_TIMER) += clocksource.o
+pbl-$(CONFIG_PBL_CLOCKSOURCE) += delay.o clocksource-registered.o
pbl-y += div0.o
diff --git a/arch/arm/lib64/clocksource-registered.S b/arch/arm/lib64/clocksource-registered.S
new file mode 100644
index 000000000000..242643926388
--- /dev/null
+++ b/arch/arm/lib64/clocksource-registered.S
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+/*
+ * Returns nonzero once init_clock() has set current_clock. Reads it
+ * PC-relatively and uses no stack, so it works before relocation,
+ * before BSS is cleared and before a stack is set up. Clobbers x0 only.
+ */
+.section .text.clocksource_registered, "ax"
+ENTRY(clocksource_registered)
+ ldr_l x0, current_clock
+ cmp x0, #0
+ cset w0, ne
+ ret
+ENDPROC(clocksource_registered)
diff --git a/arch/arm/lib64/delay.c b/arch/arm/lib64/delay.c
new file mode 100644
index 000000000000..1ff770ca5450
--- /dev/null
+++ b/arch/arm/lib64/delay.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <asm/system.h>
+#include <clock.h>
+
+/* Uses only architected timer system registers, so usable before relocation */
+static __always_inline void arch_timer_udelay(unsigned long us)
+{
+ unsigned long cntfrq = get_cntfrq();
+ unsigned long ticks = (us * cntfrq) / 1000000;
+ unsigned long start = get_cntpct();
+
+ while ((long)(start + ticks - get_cntpct()) > 0)
+ ;
+}
+
+/*
+ * Until a clocksource is registered, read the counter directly, which
+ * works as soon as CNTFRQ_EL0 is programmed, even before relocation.
+ */
+void __prereloc udelay(unsigned long us)
+{
+ if (clocksource_registered())
+ clocksource_current_udelay(us);
+ else
+ arch_timer_udelay(us);
+}
+
+/*
+ * Until a clocksource is registered, time stands still and timeouts
+ * never fire, so polls wait indefinitely instead of panicking.
+ */
+uint64_t __prereloc get_time_ns(void)
+{
+ if (clocksource_registered())
+ return clocksource_current_get_time_ns();
+
+ return 0;
+}
+
+int __prereloc is_timeout(uint64_t start_ns, uint64_t time_offset_ns)
+{
+ if (clocksource_registered())
+ return clocksource_current_is_timeout(start_ns, time_offset_ns);
+
+ return 0;
+}
diff --git a/common/clock.c b/common/clock.c
index 0f2f47ed5399..67c02f369a6c 100644
--- a/common/clock.c
+++ b/common/clock.c
@@ -35,7 +35,9 @@ static struct clocksource dummy_cs = {
.priority = -1,
};
-static struct clocksource *current_clock = IN_PROPER ? &dummy_cs : NULL;
+/* in .data, so clocksource_registered() reads NULL even before BSS is cleared */
+struct clocksource *current_clock __section(.data) =
+ IN_PROPER ? &dummy_cs : NULL;
static int dummy_csrc_warn(void)
{
diff --git a/include/clock.h b/include/clock.h
index ea87bfd2020a..88de5deb47b9 100644
--- a/include/clock.h
+++ b/include/clock.h
@@ -53,6 +53,11 @@ static inline int is_timeout_interruptible(uint64_t start_ns,
void arm_architected_timer_udelay(unsigned long us);
+extern struct clocksource *current_clock;
+
+/* implemented in assembly, so usable before relocation and stack setup */
+bool clocksource_registered(void);
+
void ndelay(unsigned long nsecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);
--
2.47.3
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-09-09 12:38 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-09 11:42 [PATCH master 1/2] clock: make udelay(), get_time_ns() and is_timeout() weak aliases Ahmad Fatoum
2026-09-09 11:42 ` [PATCH master 2/2] ARM64: fall back to reading the counter directly in the PBL udelay() Ahmad Fatoum
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox