* [PATCH] m68k: delay: Fix rounding of short udelay() and ndelay()
@ 2026-11-13 14:38 Graeme Harker
0 siblings, 0 replies; only message in thread
From: Graeme Harker @ 2026-11-13 14:38 UTC (permalink / raw)
To: geert; +Cc: linux-m68k, linux-kernel, Graeme Harker
__xdelay() takes the high word of xloops * loops_per_jiffy and only then
multiplies by HZ, so the loop count is truncated to a multiple of HZ.
On a 16 MHz 68030 (loops_per_jiffy 17792, HZ 100) every udelay() below
57 us becomes __delay(0): measured on such a machine, udelay(10),
udelay(20) and udelay(50) all take the same 8 us, the call overhead,
and udelay(100) takes 67 us.
Multiply by loops per second instead and keep the high word, which
truncates by less than one loop; __delay() runs one more loop than it
is given, so the delay is never short. As on x86, a factor of 4 is
moved from loops per second to xloops, so that the product
loops_per_jiffy * HZ only overflows above 2**34 loops per second
(34000 BogoMIPS).
ndelay() works out loops per microsecond from loops_per_jiffy rounded
down to a multiple of 2048, which on this CPU is 1 instead of 1.78.
With a 64-bit multiply available, use __xdelay() as udelay() does,
with 5 (2**32 / 10**9 rounded up) as on x86. The CPU_HAS_NO_MULDIV64
variants are unchanged.
Measured on the 68030 (1 s of each, timed by jiffies, interrupts on),
time per call before -> after:
udelay(10) 8.0 us -> 18.2 us
udelay(20) 8.0 us -> 29.0 us
udelay(50) 8.5 us -> 61.5 us
udelay(100) 67 us -> 114 us
udelay(1000) 1020 us -> 1070 us
ndelay(400) 1.8 us -> 7.9 us
About 8 us of each is the call and the 64-bit multiply on this CPU.
ndelay() now pays for that multiply too, so very short ndelay()s cost
more; they are no longer short when loops_per_jiffy is small.
Signed-off-by: Graeme Harker <graeme.harker@gmail.com>
Assisted-by: Claude:claude-opus-5.5
Assisted-by: Claude:claude-fable-5.2
---
The 68030 is a homebrew board whose support is not upstream. The
timings above were taken on v7.3-rc1 with the multiply written as
xloops * (loops_per_jiffy * HZ). The form sent here, with the factor
of 4 moved to xloops, gives the same loop count whenever neither
product overflows (compared on the host for loops_per_jiffy from 16896
to 170000000 and every delay the table uses); it has been booted under
QEMU (virt, 770 BogoMIPS) but the table has not been re-measured with
it.
Without the factor of 4 the product would overflow above 8590
BogoMIPS, which a fast QEMU host is within reach of; the old code had
no such limit in practice.
Build-tested on m68k master (v7.4-rc2) with multi_defconfig,
sun3_defconfig, virt_defconfig and m5208evb_defconfig (the
CPU_HAS_NO_MULDIV64 side).
arch/m68k/include/asm/delay.h | 26 ++++++++++++++++++++++++--
1 file changed, 24 insertions(+), 2 deletions(-)
diff --git a/arch/m68k/include/asm/delay.h b/arch/m68k/include/asm/delay.h
index 751712f8beea..2d6b18a23f4e 100644
--- a/arch/m68k/include/asm/delay.h
+++ b/arch/m68k/include/asm/delay.h
@@ -57,14 +57,24 @@ extern void __bad_udelay(void);
#else
+/*
+ * xloops is the delay in units of 2**-32 s. Multiply by loops per second
+ * and keep the high word, so the result is truncated by less than one loop;
+ * __delay() runs one more loop than asked, so the delay is never short.
+ * Scaling by loops_per_jiffy first and by HZ afterwards would truncate in
+ * steps of HZ loops, turning any delay below HZ loops into __delay(0).
+ *
+ * As on x86, a factor of 4 is moved from loops per second to xloops, so
+ * that loops_per_jiffy * HZ may be up to 2**34.
+ */
static inline void __xdelay(unsigned long xloops)
{
unsigned long tmp;
__asm__ ("mulul %2,%0:%1"
: "=d" (xloops), "=d" (tmp)
- : "d" (xloops), "1" (loops_per_jiffy));
- __delay(xloops * HZ);
+ : "d" (xloops * 4), "1" (loops_per_jiffy * (HZ / 4)));
+ __delay(xloops);
}
/*
@@ -113,6 +123,7 @@ static inline void __udelay(unsigned long usecs)
*/
#define HZSCALE (268435456 / (1000000 / HZ))
+#ifdef CONFIG_CPU_HAS_NO_MULDIV64
static inline void ndelay(unsigned long nsec)
{
__delay(DIV_ROUND_UP(nsec *
@@ -120,6 +131,17 @@ static inline void ndelay(unsigned long nsec)
(loops_per_jiffy >> 11)) >> 6),
1000));
}
+#else
+/*
+ * The shifts above round loops_per_jiffy down to a multiple of 2048, which
+ * on a slow CPU loses most of it. With a 64-bit multiply, use __xdelay():
+ * 5 is 2**32 / 10**9 rounded up, as on x86.
+ */
+static inline void ndelay(unsigned long nsec)
+{
+ __xdelay(nsec * 5);
+}
+#endif
#define ndelay(n) ndelay(n)
#endif /* defined(_M68K_DELAY_H) */
base-commit: 8c9ddebb300c719362a52a2d465f44e7d055778d
prerequisite-patch-id: d8f836f1e047af08e6fff01d411b44e442086214
--
2.53.0
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-11-13 14:38 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-13 14:38 [PATCH] m68k: delay: Fix rounding of short udelay() and ndelay() Graeme Harker
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®