Linux Documentation
 help / color / mirror / Atom feed
From: tirthendu.sarkar@intel.com
To: Thomas Gleixner <tglx@linutronix.de>,
	Ingo Molnar <mingo@redhat.com>, Borislav Petkov <bp@alien8.de>,
	Dave Hansen <dave.hansen@linux.intel.com>,
	x86@kernel.org
Cc: "H . Peter Anvin" <hpa@zytor.com>,
	Jonathan Corbet <corbet@lwn.net>,
	Andy Shevchenko <andriy.shevchenko@intel.com>,
	linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH] x86/time: Enable HPET under ACPI hardware-reduced mode with hpet=force
Date: Mon,  5 Oct 2026 03:55:50 -0400	[thread overview]
Message-ID: <20261005075550.463089-1-tirthendu.sarkar@intel.com> (raw)

From: Tirthendu Sarkar <tirthendu.sarkar@intel.com>

ACPI hardware-reduced platforms unconditionally stub
x86_init.timers.timer_init() to a noop, on the assumption that such
platforms never provide legacy timer hardware. That is not universally
true: virtualized/embedded x86 platforms can declare hardware-reduced
mode while still enumerating a genuine HPET via the standard ACPI HPET
table. On those, HPET never gets a chance to initialize at all,
regardless of whether one is present.

"hpet=force" is the existing opt-in for forcing HPET on in unusual
situations, but today its effect is limited to a later PCI-quirk
address override and bypassing the PC10-damaged auto-disable; it does
not reach this earlier stubbing. Passing it on such a platform
silently does nothing.

Let "hpet=force" call hpet_enable() when .timer_init() was stubbed by
ACPI hardware-reduced mode. This has to happen in x86_late_time_init()
rather than at the stub site: that decision runs from setup_arch(),
while "hpet=" is a __setup() handler and so is not parsed until
parse_args(), further into start_kernel().

Only hpet_enable() is wanted here, not hpet_time_init(), which also
falls back to the PIT and requests the legacy timer interrupt. Neither
has hardware behind it on such a platform, and the fallback is not
merely inert: the PIT clockevent it registers as global_clock_event
never ticks, and calibrate_APIC_clock() cross-checks the APIC timer
against jiffies advancing via exactly that clockevent, so the APIC
timer is marked CLOCK_EVT_FEAT_DUMMY and disabled as well ("APIC timer
disabled due to verification failure"). Where the APIC timer was the
only usable clockevent, that is a boot hang.

This covers hardware-reduced platforms that install their own
.timer_init() rather than being stubbed, too. Intel MID points
x86_init.acpi.reduced_hw_early_init() at a noop, so
acpi_generic_reduced_hw_init() never runs there and .timer_init() keeps
pointing at intel_mid_time_init(), which sets up the local APIC timer
only and never touches the HPET - even though Tangier does have one at
0xfed00000. None of the platform .timer_init() implementations enable
the HPET themselves, so it cannot end up registered twice.

The ACPI hardware-reduced check is still required: without it,
"hpet=force" on an ordinary platform would call hpet_enable() a second
time after hpet_time_init() already did, registering the clocksource
twice. Hyper-V guests run hardware-reduced too, so the path is
reachable there as well, but only ever behind the explicit opt-in.

Nothing changes for non-hardware-reduced platforms, or for
hardware-reduced platforms booted without "hpet=force". With
"hpet=force" but no usable HPET, hpet_enable() fails and nothing else
happens, matching today's outcome.

Also document the new behaviour under "hpet=", and fix that entry's
stale "[X86-32,HPET]" tag.

Signed-off-by: Tirthendu Sarkar <tirthendu.sarkar@intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
---
Tested under QEMU on a hardware-reduced platform with no HPET table, and
with an HPET table describing an HPET that does not come up; neither falls
through to the PIT. Also forced the Intel MID boot path under QEMU
(hardware_subarch = X86_SUBARCH_INTEL_MID), which exercises that code path
but not real Merrifield/Moorefield silicon. On a hardware-reduced platform
with a genuine HPET, the HPET initializes and is confirmed to drive
timekeeping.

 .../admin-guide/kernel-parameters.txt         |  8 +++++--
 arch/x86/include/asm/hpet.h                   |  1 +
 arch/x86/kernel/time.c                        | 22 ++++++++++++++++++-
 3 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 68647ff4bdd24..2722c74eb5a57 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -2033,12 +2033,16 @@ Kernel parameters
 			not exceed the maximum allowed hostname length (usually
 			64 characters) and will be truncated otherwise.
 
-	hpet=		[X86-32,HPET] option to control HPET usage
+	hpet=		[X86,HPET] option to control HPET usage
 			Format: { enable (default) | disable | force |
 				verbose }
 			disable: disable HPET and use PIT instead
 			force: allow force enabled of undocumented chips (ICH4,
-				VIA, nVidia)
+				VIA, nVidia). Also attempts HPET initialization
+				on ACPI hardware-reduced platforms, which
+				otherwise skip legacy timer init entirely. The
+				legacy PIT is never used as a fallback there,
+				since such platforms do not provide one.
 			verbose: show contents of HPET registers during setup
 
 	hpet_mmap=	[X86, HPET_MMAP] Allow userspace to mmap HPET
diff --git a/arch/x86/include/asm/hpet.h b/arch/x86/include/asm/hpet.h
index ab0c78855ecb2..4cb8f6d2e13dc 100644
--- a/arch/x86/include/asm/hpet.h
+++ b/arch/x86/include/asm/hpet.h
@@ -97,6 +97,7 @@ static inline int hpet_enable(void) { return 0; }
 static inline int is_hpet_enabled(void) { return 0; }
 #define hpet_readl(a) 0
 #define default_setup_hpet_msi	NULL
+#define hpet_force_user		false
 
 #endif
 #endif /* _ASM_X86_HPET_H */
diff --git a/arch/x86/kernel/time.c b/arch/x86/kernel/time.c
index 4061430ac74c5..5184493689396 100644
--- a/arch/x86/kernel/time.c
+++ b/arch/x86/kernel/time.c
@@ -10,13 +10,14 @@
  *
  */
 
+#include <linux/acpi.h>
 #include <linux/clocksource.h>
 #include <linux/clockchips.h>
+#include <linux/export.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/i8253.h>
 #include <linux/time.h>
-#include <linux/export.h>
 
 #include <asm/vsyscall.h>
 #include <asm/x86_init.h>
@@ -72,6 +73,25 @@ static __init void x86_late_time_init(void)
 	 */
 	x86_init.irqs.intr_mode_select();
 
+	/*
+	 * ACPI hardware-reduced mode unconditionally stubs .timer_init() to
+	 * a noop, on the assumption that no legacy timer exists. Some such
+	 * platforms do provide a real HPET, so let "hpet=force" give it a
+	 * chance to come up. Only hpet_enable() is wanted here, not
+	 * hpet_time_init(): its PIT fallback and legacy timer interrupt have
+	 * no hardware behind them on such platforms, and registering a PIT
+	 * clockevent whose interrupt can never be delivered also fails the
+	 * local APIC timer's calibration cross-check, disabling that too.
+	 *
+	 * This also covers hardware-reduced platforms that install their own
+	 * .timer_init() rather than being stubbed (e.g. Intel MID, which
+	 * sets up the local APIC timer only). None of them enable the HPET
+	 * themselves, so it cannot end up registered twice.
+	 */
+	if (IS_ENABLED(CONFIG_HPET_TIMER) && acpi_reduced_hardware() &&
+	    hpet_force_user)
+		hpet_enable();
+
 	/* Setup the legacy timers */
 	x86_init.timers.timer_init();
 
-- 
2.52.0


                 reply	other threads:[~2026-10-05  7:55 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=20261005075550.463089-1-tirthendu.sarkar@intel.com \
    --to=tirthendu.sarkar@intel.com \
    --cc=andriy.shevchenko@intel.com \
    --cc=bp@alien8.de \
    --cc=corbet@lwn.net \
    --cc=dave.hansen@linux.intel.com \
    --cc=hpa@zytor.com \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@redhat.com \
    --cc=tglx@linutronix.de \
    --cc=x86@kernel.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