Skip to content

Commit c8c4076

Browse files
committed
x86/timer: Skip PIT initialization on modern chipsets
Recent Intel chipsets including Skylake and ApolloLake have a special ITSSPRC register which allows the 8254 PIT to be gated. When gated, the 8254 registers can still be programmed as normal, but there are no IRQ0 timer interrupts. Some products such as the Connex L1430 and exone go Rugged E11 use this register to ship with the PIT gated by default. This causes Linux to fail to boot: Kernel panic - not syncing: IO-APIC + timer doesn't work! Boot with apic=debug and send a report. The panic happens before the framebuffer is initialized, so to the user, it appears as an early boot hang on a black screen. Affected products typically have a BIOS option that can be used to enable the 8254 and make Linux work (Chipset -> South Cluster Configuration -> Miscellaneous Configuration -> 8254 Clock Gating), however it would be best to make Linux support the no-8254 case. Modern sytems allow to discover the TSC and local APIC timer frequencies, so the calibration against the PIT is not required. These systems have always running timers and the local APIC timer works also in deep power states. So the setup of the PIT including the IO-APIC timer interrupt delivery checks are a pointless exercise. Skip the PIT setup and the IO-APIC timer interrupt checks on these systems, which avoids the panic caused by non ticking PITs and also speeds up the boot process. Thanks to Daniel for providing the changelog, initial analysis of the problem and testing against a variety of machines. Reported-by: Daniel Drake <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Tested-by: Daniel Drake <[email protected]> Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: [email protected] Link: https://lkml.kernel.org/r/[email protected]
1 parent dde3626 commit c8c4076

File tree

6 files changed

+63
-3
lines changed

6 files changed

+63
-3
lines changed

arch/x86/include/asm/apic.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ extern void lapic_assign_system_vectors(void);
173173
extern void lapic_assign_legacy_vector(unsigned int isairq, bool replace);
174174
extern void lapic_online(void);
175175
extern void lapic_offline(void);
176+
extern bool apic_needs_pit(void);
176177

177178
#else /* !CONFIG_X86_LOCAL_APIC */
178179
static inline void lapic_shutdown(void) { }
@@ -186,6 +187,7 @@ static inline void init_bsp_APIC(void) { }
186187
static inline void apic_intr_mode_init(void) { }
187188
static inline void lapic_assign_system_vectors(void) { }
188189
static inline void lapic_assign_legacy_vector(unsigned int i, bool r) { }
190+
static inline bool apic_needs_pit(void) { return true; }
189191
#endif /* !CONFIG_X86_LOCAL_APIC */
190192

191193
#ifdef CONFIG_X86_X2APIC

arch/x86/include/asm/time.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
extern void hpet_time_init(void);
99
extern void time_init(void);
10+
extern bool pit_timer_init(void);
1011

1112
extern struct clock_event_device *global_clock_event;
1213

arch/x86/kernel/apic/apic.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,33 @@ static int __init lapic_init_clockevent(void)
820820
return 0;
821821
}
822822

823+
bool __init apic_needs_pit(void)
824+
{
825+
/*
826+
* If the frequencies are not known, PIT is required for both TSC
827+
* and apic timer calibration.
828+
*/
829+
if (!tsc_khz || !cpu_khz)
830+
return true;
831+
832+
/* Is there an APIC at all? */
833+
if (!boot_cpu_has(X86_FEATURE_APIC))
834+
return true;
835+
836+
/* Deadline timer is based on TSC so no further PIT action required */
837+
if (boot_cpu_has(X86_FEATURE_TSC_DEADLINE_TIMER))
838+
return false;
839+
840+
/* APIC timer disabled? */
841+
if (disable_apic_timer)
842+
return true;
843+
/*
844+
* The APIC timer frequency is known already, no PIT calibration
845+
* required. If unknown, let the PIT be initialized.
846+
*/
847+
return lapic_timer_period == 0;
848+
}
849+
823850
static int __init calibrate_APIC_clock(void)
824851
{
825852
struct clock_event_device *levt = this_cpu_ptr(&lapic_events);

arch/x86/kernel/apic/io_apic.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#include <asm/acpi.h>
5959
#include <asm/dma.h>
6060
#include <asm/timer.h>
61+
#include <asm/time.h>
6162
#include <asm/i8259.h>
6263
#include <asm/setup.h>
6364
#include <asm/irq_remapping.h>
@@ -2083,6 +2084,9 @@ static inline void __init check_timer(void)
20832084
unsigned long flags;
20842085
int no_pin1 = 0;
20852086

2087+
if (!global_clock_event)
2088+
return;
2089+
20862090
local_irq_save(flags);
20872091

20882092
/*

arch/x86/kernel/i8253.c

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <linux/timex.h>
99
#include <linux/i8253.h>
1010

11+
#include <asm/apic.h>
1112
#include <asm/hpet.h>
1213
#include <asm/time.h>
1314
#include <asm/smp.h>
@@ -18,10 +19,32 @@
1819
*/
1920
struct clock_event_device *global_clock_event;
2021

21-
void __init setup_pit_timer(void)
22+
/*
23+
* Modern chipsets can disable the PIT clock which makes it unusable. It
24+
* would be possible to enable the clock but the registers are chipset
25+
* specific and not discoverable. Avoid the whack a mole game.
26+
*
27+
* These platforms have discoverable TSC/CPU frequencies but this also
28+
* requires to know the local APIC timer frequency as it normally is
29+
* calibrated against the PIT interrupt.
30+
*/
31+
static bool __init use_pit(void)
32+
{
33+
if (!IS_ENABLED(CONFIG_X86_TSC) || !boot_cpu_has(X86_FEATURE_TSC))
34+
return true;
35+
36+
/* This also returns true when APIC is disabled */
37+
return apic_needs_pit();
38+
}
39+
40+
bool __init pit_timer_init(void)
2241
{
42+
if (!use_pit())
43+
return false;
44+
2345
clockevent_i8253_init(true);
2446
global_clock_event = &i8253_clockevent;
47+
return true;
2548
}
2649

2750
#ifndef CONFIG_X86_64

arch/x86/kernel/time.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,11 @@ static void __init setup_default_timer_irq(void)
8282
/* Default timer init function */
8383
void __init hpet_time_init(void)
8484
{
85-
if (!hpet_enable())
86-
setup_pit_timer();
85+
if (!hpet_enable()) {
86+
if (!pit_timer_init())
87+
return;
88+
}
89+
8790
setup_default_timer_irq();
8891
}
8992

0 commit comments

Comments
 (0)