mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [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®