xref: /openbmc/linux/arch/x86/kernel/time.c (revision d8ad6d39)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
247926214SThomas Gleixner /*
347926214SThomas Gleixner  *  Copyright (c) 1991,1992,1995  Linus Torvalds
447926214SThomas Gleixner  *  Copyright (c) 1994  Alan Modra
547926214SThomas Gleixner  *  Copyright (c) 1995  Markus Kuhn
647926214SThomas Gleixner  *  Copyright (c) 1996  Ingo Molnar
747926214SThomas Gleixner  *  Copyright (c) 1998  Andrea Arcangeli
847926214SThomas Gleixner  *  Copyright (c) 2002,2006  Vojtech Pavlik
947926214SThomas Gleixner  *  Copyright (c) 2003  Andi Kleen
1047926214SThomas Gleixner  *
1147926214SThomas Gleixner  */
1247926214SThomas Gleixner 
132a21ad57SThomas Gleixner #include <linux/clocksource.h>
1447926214SThomas Gleixner #include <linux/clockchips.h>
1547926214SThomas Gleixner #include <linux/interrupt.h>
16447ae316SNicolai Stange #include <linux/irq.h>
17334955efSRalf Baechle #include <linux/i8253.h>
1847926214SThomas Gleixner #include <linux/time.h>
1969c60c88SPaul Gortmaker #include <linux/export.h>
2047926214SThomas Gleixner 
2147926214SThomas Gleixner #include <asm/vsyscall.h>
2247926214SThomas Gleixner #include <asm/x86_init.h>
2347926214SThomas Gleixner #include <asm/i8259.h>
2447926214SThomas Gleixner #include <asm/timer.h>
2547926214SThomas Gleixner #include <asm/hpet.h>
2647926214SThomas Gleixner #include <asm/time.h>
2747926214SThomas Gleixner 
profile_pc(struct pt_regs * regs)2847926214SThomas Gleixner unsigned long profile_pc(struct pt_regs *regs)
2947926214SThomas Gleixner {
3047926214SThomas Gleixner 	unsigned long pc = instruction_pointer(regs);
3147926214SThomas Gleixner 
32f39b6f0eSAndy Lutomirski 	if (!user_mode(regs) && in_lock_functions(pc)) {
3347926214SThomas Gleixner #ifdef CONFIG_FRAME_POINTER
3447926214SThomas Gleixner 		return *(unsigned long *)(regs->bp + sizeof(long));
3547926214SThomas Gleixner #else
363c88c692SPeter Zijlstra 		unsigned long *sp = (unsigned long *)regs->sp;
3747926214SThomas Gleixner 		/*
3847926214SThomas Gleixner 		 * Return address is either directly at stack pointer
3947926214SThomas Gleixner 		 * or above a saved flags. Eflags has bits 22-31 zero,
4047926214SThomas Gleixner 		 * kernel addresses don't.
4147926214SThomas Gleixner 		 */
4247926214SThomas Gleixner 		if (sp[0] >> 22)
4347926214SThomas Gleixner 			return sp[0];
4447926214SThomas Gleixner 		if (sp[1] >> 22)
4547926214SThomas Gleixner 			return sp[1];
4647926214SThomas Gleixner #endif
4747926214SThomas Gleixner 	}
4847926214SThomas Gleixner 	return pc;
4947926214SThomas Gleixner }
5047926214SThomas Gleixner EXPORT_SYMBOL(profile_pc);
5147926214SThomas Gleixner 
5247926214SThomas Gleixner /*
5347926214SThomas Gleixner  * Default timer interrupt handler for PIT/HPET
5447926214SThomas Gleixner  */
timer_interrupt(int irq,void * dev_id)5547926214SThomas Gleixner static irqreturn_t timer_interrupt(int irq, void *dev_id)
5647926214SThomas Gleixner {
5747926214SThomas Gleixner 	global_clock_event->event_handler(global_clock_event);
5847926214SThomas Gleixner 	return IRQ_HANDLED;
5947926214SThomas Gleixner }
6047926214SThomas Gleixner 
setup_default_timer_irq(void)61b1b4f2feSDou Liyang static void __init setup_default_timer_irq(void)
6247926214SThomas Gleixner {
634dd2a1b9Safzal mohammed 	unsigned long flags = IRQF_NOBALANCING | IRQF_IRQPOLL | IRQF_TIMER;
644dd2a1b9Safzal mohammed 
656d671e1bSPeter Zijlstra 	/*
664dd2a1b9Safzal mohammed 	 * Unconditionally register the legacy timer interrupt; even
674dd2a1b9Safzal mohammed 	 * without legacy PIC/PIT we need this for the HPET0 in legacy
684dd2a1b9Safzal mohammed 	 * replacement mode.
696d671e1bSPeter Zijlstra 	 */
704dd2a1b9Safzal mohammed 	if (request_irq(0, timer_interrupt, flags, "timer", NULL))
716d671e1bSPeter Zijlstra 		pr_info("Failed to register legacy timer interrupt\n");
7247926214SThomas Gleixner }
7347926214SThomas Gleixner 
7447926214SThomas Gleixner /* Default timer init function */
hpet_time_init(void)7547926214SThomas Gleixner void __init hpet_time_init(void)
7647926214SThomas Gleixner {
77c8c40767SThomas Gleixner 	if (!hpet_enable()) {
78c8c40767SThomas Gleixner 		if (!pit_timer_init())
79c8c40767SThomas Gleixner 			return;
80c8c40767SThomas Gleixner 	}
81c8c40767SThomas Gleixner 
8247926214SThomas Gleixner 	setup_default_timer_irq();
8347926214SThomas Gleixner }
8447926214SThomas Gleixner 
x86_late_time_init(void)8554e2603fSThomas Gleixner static __init void x86_late_time_init(void)
8647926214SThomas Gleixner {
87935356ceSDou Liyang 	/*
8897992387SThomas Gleixner 	 * Before PIT/HPET init, select the interrupt mode. This is required
8997992387SThomas Gleixner 	 * to make the decision whether PIT should be initialized correct.
9097992387SThomas Gleixner 	 */
9197992387SThomas Gleixner 	x86_init.irqs.intr_mode_select();
9297992387SThomas Gleixner 
9397992387SThomas Gleixner 	/* Setup the legacy timers */
9497992387SThomas Gleixner 	x86_init.timers.timer_init();
9597992387SThomas Gleixner 
9697992387SThomas Gleixner 	/*
9797992387SThomas Gleixner 	 * After PIT/HPET timers init, set up the final interrupt mode for
9897992387SThomas Gleixner 	 * delivering IRQs.
99935356ceSDou Liyang 	 */
100935356ceSDou Liyang 	x86_init.irqs.intr_mode_init();
101dd0a70c8SThomas Gleixner 	tsc_init();
10247926214SThomas Gleixner 
10347926214SThomas Gleixner 	if (static_cpu_has(X86_FEATURE_WAITPKG))
10447926214SThomas Gleixner 		use_tpause_delay();
10547926214SThomas Gleixner }
10647926214SThomas Gleixner 
10747926214SThomas Gleixner /*
10847926214SThomas Gleixner  * Initialize TSC and delay the periodic timer init to
10947926214SThomas Gleixner  * late x86_late_time_init() so ioremap works.
11047926214SThomas Gleixner  */
time_init(void)11147926214SThomas Gleixner void __init time_init(void)
1122a21ad57SThomas Gleixner {
1132a21ad57SThomas Gleixner 	late_time_init = x86_late_time_init;
1142a21ad57SThomas Gleixner }
1152a21ad57SThomas Gleixner 
1162a21ad57SThomas Gleixner /*
1172a21ad57SThomas Gleixner  * Sanity check the vdso related archdata content.
118b95a8a27SThomas Gleixner  */
clocksource_arch_init(struct clocksource * cs)1192a21ad57SThomas Gleixner void clocksource_arch_init(struct clocksource *cs)
1202a21ad57SThomas Gleixner {
121a51e996dSThomas Gleixner 	if (cs->vdso_clock_mode == VDSO_CLOCKMODE_NONE)
122b95a8a27SThomas Gleixner 		return;
123a51e996dSThomas Gleixner 
124b95a8a27SThomas Gleixner 	if (cs->mask != CLOCKSOURCE_MASK(64)) {
125a51e996dSThomas Gleixner 		pr_warn("clocksource %s registered with invalid mask %016llx for VDSO. Disabling VDSO support.\n",
1262a21ad57SThomas Gleixner 			cs->name, cs->mask);
127 		cs->vdso_clock_mode = VDSO_CLOCKMODE_NONE;
128 	}
129 }
130