From: Graeme Harker <graeme.harker@gmail.com>
To: geert@linux-m68k.org
Cc: linux-m68k@lists.linux-m68k.org, linux-kernel@vger.kernel.org,
Graeme Harker <graeme.harker@gmail.com>
Subject: [PATCH] m68k: delay: Fix rounding of short udelay() and ndelay()
Date: Fri, 13 Nov 2026 14:38:32 +0000 [thread overview]
Message-ID: <20261113143832.168233-1-graeme.harker@gmail.com> (raw)
__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
reply other threads:[~2026-11-13 14:38 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261113143832.168233-1-graeme.harker@gmail.com \
--to=graeme.harker@gmail.com \
--cc=geert@linux-m68k.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-m68k@lists.linux-m68k.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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®