xref: /openbmc/linux/arch/arm/kernel/time.c (revision 9dd34948aaf631610355891a70cc55408eae840e)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  linux/arch/arm/kernel/time.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *  Copyright (C) 1991, 1992, 1995  Linus Torvalds
51da177e4SLinus Torvalds  *  Modifications for ARM (C) 1994-2001 Russell King
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or modify
81da177e4SLinus Torvalds  * it under the terms of the GNU General Public License version 2 as
91da177e4SLinus Torvalds  * published by the Free Software Foundation.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  *  This file contains the ARM-specific time handling details:
121da177e4SLinus Torvalds  *  reading the RTC at bootup, etc...
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *  1994-07-02  Alan Modra
151da177e4SLinus Torvalds  *              fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime
161da177e4SLinus Torvalds  *  1998-12-20  Updated NTP code according to technical memorandum Jan '96
171da177e4SLinus Torvalds  *              "A Kernel Model for Precision Timekeeping" by Dave Mills
181da177e4SLinus Torvalds  */
191da177e4SLinus Torvalds #include <linux/module.h>
201da177e4SLinus Torvalds #include <linux/kernel.h>
211da177e4SLinus Torvalds #include <linux/interrupt.h>
221da177e4SLinus Torvalds #include <linux/time.h>
231da177e4SLinus Torvalds #include <linux/init.h>
241da177e4SLinus Torvalds #include <linux/smp.h>
251da177e4SLinus Torvalds #include <linux/timex.h>
261da177e4SLinus Torvalds #include <linux/errno.h>
271da177e4SLinus Torvalds #include <linux/profile.h>
281da177e4SLinus Torvalds #include <linux/sysdev.h>
291da177e4SLinus Torvalds #include <linux/timer.h>
30e317c8ccSFrederik Deweerdt #include <linux/irq.h>
311da177e4SLinus Torvalds 
329ca3f07bSBen Dooks #include <linux/mc146818rtc.h>
339ca3f07bSBen Dooks 
341da177e4SLinus Torvalds #include <asm/leds.h>
351da177e4SLinus Torvalds #include <asm/thread_info.h>
361da177e4SLinus Torvalds #include <asm/mach/time.h>
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds /*
391da177e4SLinus Torvalds  * Our system timer.
401da177e4SLinus Torvalds  */
411da177e4SLinus Torvalds struct sys_timer *system_timer;
421da177e4SLinus Torvalds 
43*9dd34948SDavid Brownell #if defined(CONFIG_RTC_DRV_CMOS) || defined(CONFIG_RTC_DRV_CMOS_MODULE)
441da177e4SLinus Torvalds /* this needs a better home */
451da177e4SLinus Torvalds DEFINE_SPINLOCK(rtc_lock);
461da177e4SLinus Torvalds 
47*9dd34948SDavid Brownell #ifdef CONFIG_RTC_DRV_CMOS_MODULE
481da177e4SLinus Torvalds EXPORT_SYMBOL(rtc_lock);
491da177e4SLinus Torvalds #endif
50*9dd34948SDavid Brownell #endif	/* pc-style 'CMOS' RTC support */
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds /* change this if you have some constant time drift */
531da177e4SLinus Torvalds #define USECS_PER_JIFFY	(1000000/HZ)
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds #ifdef CONFIG_SMP
561da177e4SLinus Torvalds unsigned long profile_pc(struct pt_regs *regs)
571da177e4SLinus Torvalds {
581da177e4SLinus Torvalds 	unsigned long fp, pc = instruction_pointer(regs);
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds 	if (in_lock_functions(pc)) {
611da177e4SLinus Torvalds 		fp = regs->ARM_fp;
621da177e4SLinus Torvalds 		pc = pc_pointer(((unsigned long *)fp)[-1]);
631da177e4SLinus Torvalds 	}
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds 	return pc;
661da177e4SLinus Torvalds }
671da177e4SLinus Torvalds EXPORT_SYMBOL(profile_pc);
681da177e4SLinus Torvalds #endif
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds /*
711da177e4SLinus Torvalds  * hook for setting the RTC's idea of the current time.
721da177e4SLinus Torvalds  */
731da177e4SLinus Torvalds int (*set_rtc)(void);
741da177e4SLinus Torvalds 
75746140c7SKevin Hilman #ifndef CONFIG_GENERIC_TIME
761da177e4SLinus Torvalds static unsigned long dummy_gettimeoffset(void)
771da177e4SLinus Torvalds {
781da177e4SLinus Torvalds 	return 0;
791da177e4SLinus Torvalds }
80746140c7SKevin Hilman #endif
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds /*
831da177e4SLinus Torvalds  * Scheduler clock - returns current time in nanosec units.
841da177e4SLinus Torvalds  * This is the default implementation.  Sub-architecture
851da177e4SLinus Torvalds  * implementations can override this.
861da177e4SLinus Torvalds  */
871da177e4SLinus Torvalds unsigned long long __attribute__((weak)) sched_clock(void)
881da177e4SLinus Torvalds {
891da177e4SLinus Torvalds 	return (unsigned long long)jiffies * (1000000000 / HZ);
901da177e4SLinus Torvalds }
911da177e4SLinus Torvalds 
92e97126cdSRussell King /*
93e97126cdSRussell King  * An implementation of printk_clock() independent from
94e97126cdSRussell King  * sched_clock().  This avoids non-bootable kernels when
95e97126cdSRussell King  * printk_clock is enabled.
96e97126cdSRussell King  */
97e97126cdSRussell King unsigned long long printk_clock(void)
98e97126cdSRussell King {
99e97126cdSRussell King 	return (unsigned long long)(jiffies - INITIAL_JIFFIES) *
100e97126cdSRussell King 			(1000000000 / HZ);
101e97126cdSRussell King }
102e97126cdSRussell King 
1031da177e4SLinus Torvalds static unsigned long next_rtc_update;
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds /*
1061da177e4SLinus Torvalds  * If we have an externally synchronized linux clock, then update
1071da177e4SLinus Torvalds  * CMOS clock accordingly every ~11 minutes.  set_rtc() has to be
1081da177e4SLinus Torvalds  * called as close as possible to 500 ms before the new second
1091da177e4SLinus Torvalds  * starts.
1101da177e4SLinus Torvalds  */
1111da177e4SLinus Torvalds static inline void do_set_rtc(void)
1121da177e4SLinus Torvalds {
113b149ee22Sjohn stultz 	if (!ntp_synced() || set_rtc == NULL)
1141da177e4SLinus Torvalds 		return;
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds 	if (next_rtc_update &&
1171da177e4SLinus Torvalds 	    time_before((unsigned long)xtime.tv_sec, next_rtc_update))
1181da177e4SLinus Torvalds 		return;
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds 	if (xtime.tv_nsec < 500000000 - ((unsigned) tick_nsec >> 1) &&
1211da177e4SLinus Torvalds 	    xtime.tv_nsec >= 500000000 + ((unsigned) tick_nsec >> 1))
1221da177e4SLinus Torvalds 		return;
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds 	if (set_rtc())
1251da177e4SLinus Torvalds 		/*
1261da177e4SLinus Torvalds 		 * rtc update failed.  Try again in 60s
1271da177e4SLinus Torvalds 		 */
1281da177e4SLinus Torvalds 		next_rtc_update = xtime.tv_sec + 60;
1291da177e4SLinus Torvalds 	else
1301da177e4SLinus Torvalds 		next_rtc_update = xtime.tv_sec + 660;
1311da177e4SLinus Torvalds }
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds #ifdef CONFIG_LEDS
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds static void dummy_leds_event(led_event_t evt)
1361da177e4SLinus Torvalds {
1371da177e4SLinus Torvalds }
1381da177e4SLinus Torvalds 
1391da177e4SLinus Torvalds void (*leds_event)(led_event_t) = dummy_leds_event;
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds struct leds_evt_name {
1421da177e4SLinus Torvalds 	const char	name[8];
1431da177e4SLinus Torvalds 	int		on;
1441da177e4SLinus Torvalds 	int		off;
1451da177e4SLinus Torvalds };
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds static const struct leds_evt_name evt_names[] = {
1481da177e4SLinus Torvalds 	{ "amber", led_amber_on, led_amber_off },
1491da177e4SLinus Torvalds 	{ "blue",  led_blue_on,  led_blue_off  },
1501da177e4SLinus Torvalds 	{ "green", led_green_on, led_green_off },
1511da177e4SLinus Torvalds 	{ "red",   led_red_on,   led_red_off   },
1521da177e4SLinus Torvalds };
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds static ssize_t leds_store(struct sys_device *dev, const char *buf, size_t size)
1551da177e4SLinus Torvalds {
1561da177e4SLinus Torvalds 	int ret = -EINVAL, len = strcspn(buf, " ");
1571da177e4SLinus Torvalds 
1581da177e4SLinus Torvalds 	if (len > 0 && buf[len] == '\0')
1591da177e4SLinus Torvalds 		len--;
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds 	if (strncmp(buf, "claim", len) == 0) {
1621da177e4SLinus Torvalds 		leds_event(led_claim);
1631da177e4SLinus Torvalds 		ret = size;
1641da177e4SLinus Torvalds 	} else if (strncmp(buf, "release", len) == 0) {
1651da177e4SLinus Torvalds 		leds_event(led_release);
1661da177e4SLinus Torvalds 		ret = size;
1671da177e4SLinus Torvalds 	} else {
1681da177e4SLinus Torvalds 		int i;
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds 		for (i = 0; i < ARRAY_SIZE(evt_names); i++) {
1711da177e4SLinus Torvalds 			if (strlen(evt_names[i].name) != len ||
1721da177e4SLinus Torvalds 			    strncmp(buf, evt_names[i].name, len) != 0)
1731da177e4SLinus Torvalds 				continue;
1741da177e4SLinus Torvalds 			if (strncmp(buf+len, " on", 3) == 0) {
1751da177e4SLinus Torvalds 				leds_event(evt_names[i].on);
1761da177e4SLinus Torvalds 				ret = size;
1771da177e4SLinus Torvalds 			} else if (strncmp(buf+len, " off", 4) == 0) {
1781da177e4SLinus Torvalds 				leds_event(evt_names[i].off);
1791da177e4SLinus Torvalds 				ret = size;
1801da177e4SLinus Torvalds 			}
1811da177e4SLinus Torvalds 			break;
1821da177e4SLinus Torvalds 		}
1831da177e4SLinus Torvalds 	}
1841da177e4SLinus Torvalds 	return ret;
1851da177e4SLinus Torvalds }
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds static SYSDEV_ATTR(event, 0200, NULL, leds_store);
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds static int leds_suspend(struct sys_device *dev, pm_message_t state)
1901da177e4SLinus Torvalds {
1911da177e4SLinus Torvalds 	leds_event(led_stop);
1921da177e4SLinus Torvalds 	return 0;
1931da177e4SLinus Torvalds }
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds static int leds_resume(struct sys_device *dev)
1961da177e4SLinus Torvalds {
1971da177e4SLinus Torvalds 	leds_event(led_start);
1981da177e4SLinus Torvalds 	return 0;
1991da177e4SLinus Torvalds }
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds static int leds_shutdown(struct sys_device *dev)
2021da177e4SLinus Torvalds {
2031da177e4SLinus Torvalds 	leds_event(led_halted);
2041da177e4SLinus Torvalds 	return 0;
2051da177e4SLinus Torvalds }
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds static struct sysdev_class leds_sysclass = {
2081da177e4SLinus Torvalds 	set_kset_name("leds"),
2091da177e4SLinus Torvalds 	.shutdown	= leds_shutdown,
2101da177e4SLinus Torvalds 	.suspend	= leds_suspend,
2111da177e4SLinus Torvalds 	.resume		= leds_resume,
2121da177e4SLinus Torvalds };
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds static struct sys_device leds_device = {
2151da177e4SLinus Torvalds 	.id		= 0,
2161da177e4SLinus Torvalds 	.cls		= &leds_sysclass,
2171da177e4SLinus Torvalds };
2181da177e4SLinus Torvalds 
2191da177e4SLinus Torvalds static int __init leds_init(void)
2201da177e4SLinus Torvalds {
2211da177e4SLinus Torvalds 	int ret;
2221da177e4SLinus Torvalds 	ret = sysdev_class_register(&leds_sysclass);
2231da177e4SLinus Torvalds 	if (ret == 0)
2241da177e4SLinus Torvalds 		ret = sysdev_register(&leds_device);
2251da177e4SLinus Torvalds 	if (ret == 0)
2261da177e4SLinus Torvalds 		ret = sysdev_create_file(&leds_device, &attr_event);
2271da177e4SLinus Torvalds 	return ret;
2281da177e4SLinus Torvalds }
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds device_initcall(leds_init);
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds EXPORT_SYMBOL(leds_event);
2331da177e4SLinus Torvalds #endif
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds #ifdef CONFIG_LEDS_TIMER
2361da177e4SLinus Torvalds static inline void do_leds(void)
2371da177e4SLinus Torvalds {
2386d15cb42SDavid Brownell 	static unsigned int count = HZ/2;
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds 	if (--count == 0) {
2416d15cb42SDavid Brownell 		count = HZ/2;
2421da177e4SLinus Torvalds 		leds_event(led_timer);
2431da177e4SLinus Torvalds 	}
2441da177e4SLinus Torvalds }
2451da177e4SLinus Torvalds #else
2461da177e4SLinus Torvalds #define	do_leds()
2471da177e4SLinus Torvalds #endif
2481da177e4SLinus Torvalds 
249746140c7SKevin Hilman #ifndef CONFIG_GENERIC_TIME
2501da177e4SLinus Torvalds void do_gettimeofday(struct timeval *tv)
2511da177e4SLinus Torvalds {
2521da177e4SLinus Torvalds 	unsigned long flags;
2531da177e4SLinus Torvalds 	unsigned long seq;
2548ef38609SAtsushi Nemoto 	unsigned long usec, sec;
2551da177e4SLinus Torvalds 
2561da177e4SLinus Torvalds 	do {
2571da177e4SLinus Torvalds 		seq = read_seqbegin_irqsave(&xtime_lock, flags);
2581da177e4SLinus Torvalds 		usec = system_timer->offset();
2591da177e4SLinus Torvalds 		sec = xtime.tv_sec;
2601da177e4SLinus Torvalds 		usec += xtime.tv_nsec / 1000;
2611da177e4SLinus Torvalds 	} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
2621da177e4SLinus Torvalds 
2631da177e4SLinus Torvalds 	/* usec may have gone up a lot: be safe */
2641da177e4SLinus Torvalds 	while (usec >= 1000000) {
2651da177e4SLinus Torvalds 		usec -= 1000000;
2661da177e4SLinus Torvalds 		sec++;
2671da177e4SLinus Torvalds 	}
2681da177e4SLinus Torvalds 
2691da177e4SLinus Torvalds 	tv->tv_sec = sec;
2701da177e4SLinus Torvalds 	tv->tv_usec = usec;
2711da177e4SLinus Torvalds }
2721da177e4SLinus Torvalds 
2731da177e4SLinus Torvalds EXPORT_SYMBOL(do_gettimeofday);
2741da177e4SLinus Torvalds 
2751da177e4SLinus Torvalds int do_settimeofday(struct timespec *tv)
2761da177e4SLinus Torvalds {
2771da177e4SLinus Torvalds 	time_t wtm_sec, sec = tv->tv_sec;
2781da177e4SLinus Torvalds 	long wtm_nsec, nsec = tv->tv_nsec;
2791da177e4SLinus Torvalds 
2801da177e4SLinus Torvalds 	if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
2811da177e4SLinus Torvalds 		return -EINVAL;
2821da177e4SLinus Torvalds 
2831da177e4SLinus Torvalds 	write_seqlock_irq(&xtime_lock);
2841da177e4SLinus Torvalds 	/*
2851da177e4SLinus Torvalds 	 * This is revolting. We need to set "xtime" correctly. However, the
2861da177e4SLinus Torvalds 	 * value in this location is the value at the most recent update of
2871da177e4SLinus Torvalds 	 * wall time.  Discover what correction gettimeofday() would have
2881da177e4SLinus Torvalds 	 * done, and then undo it!
2891da177e4SLinus Torvalds 	 */
2901da177e4SLinus Torvalds 	nsec -= system_timer->offset() * NSEC_PER_USEC;
2911da177e4SLinus Torvalds 
2921da177e4SLinus Torvalds 	wtm_sec  = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
2931da177e4SLinus Torvalds 	wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
2941da177e4SLinus Torvalds 
2951da177e4SLinus Torvalds 	set_normalized_timespec(&xtime, sec, nsec);
2961da177e4SLinus Torvalds 	set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
2971da177e4SLinus Torvalds 
298b149ee22Sjohn stultz 	ntp_clear();
2991da177e4SLinus Torvalds 	write_sequnlock_irq(&xtime_lock);
3001da177e4SLinus Torvalds 	clock_was_set();
3011da177e4SLinus Torvalds 	return 0;
3021da177e4SLinus Torvalds }
3031da177e4SLinus Torvalds 
3041da177e4SLinus Torvalds EXPORT_SYMBOL(do_settimeofday);
305746140c7SKevin Hilman #endif /* !CONFIG_GENERIC_TIME */
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds /**
3081da177e4SLinus Torvalds  * save_time_delta - Save the offset between system time and RTC time
3091da177e4SLinus Torvalds  * @delta: pointer to timespec to store delta
3101da177e4SLinus Torvalds  * @rtc: pointer to timespec for current RTC time
3111da177e4SLinus Torvalds  *
3121da177e4SLinus Torvalds  * Return a delta between the system time and the RTC time, such
3131da177e4SLinus Torvalds  * that system time can be restored later with restore_time_delta()
3141da177e4SLinus Torvalds  */
3151da177e4SLinus Torvalds void save_time_delta(struct timespec *delta, struct timespec *rtc)
3161da177e4SLinus Torvalds {
3171da177e4SLinus Torvalds 	set_normalized_timespec(delta,
3181da177e4SLinus Torvalds 				xtime.tv_sec - rtc->tv_sec,
3191da177e4SLinus Torvalds 				xtime.tv_nsec - rtc->tv_nsec);
3201da177e4SLinus Torvalds }
3211da177e4SLinus Torvalds EXPORT_SYMBOL(save_time_delta);
3221da177e4SLinus Torvalds 
3231da177e4SLinus Torvalds /**
3241da177e4SLinus Torvalds  * restore_time_delta - Restore the current system time
3251da177e4SLinus Torvalds  * @delta: delta returned by save_time_delta()
3261da177e4SLinus Torvalds  * @rtc: pointer to timespec for current RTC time
3271da177e4SLinus Torvalds  */
3281da177e4SLinus Torvalds void restore_time_delta(struct timespec *delta, struct timespec *rtc)
3291da177e4SLinus Torvalds {
3301da177e4SLinus Torvalds 	struct timespec ts;
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds 	set_normalized_timespec(&ts,
3331da177e4SLinus Torvalds 				delta->tv_sec + rtc->tv_sec,
3341da177e4SLinus Torvalds 				delta->tv_nsec + rtc->tv_nsec);
3351da177e4SLinus Torvalds 
3361da177e4SLinus Torvalds 	do_settimeofday(&ts);
3371da177e4SLinus Torvalds }
3381da177e4SLinus Torvalds EXPORT_SYMBOL(restore_time_delta);
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds /*
3411da177e4SLinus Torvalds  * Kernel system timer support.
3421da177e4SLinus Torvalds  */
3430cd61b68SLinus Torvalds void timer_tick(void)
3441da177e4SLinus Torvalds {
345e317c8ccSFrederik Deweerdt 	profile_tick(CPU_PROFILING);
3461da177e4SLinus Torvalds 	do_leds();
3471da177e4SLinus Torvalds 	do_set_rtc();
3483171a030SAtsushi Nemoto 	do_timer(1);
3491da177e4SLinus Torvalds #ifndef CONFIG_SMP
350c97d4869SRussell King 	update_process_times(user_mode(get_irq_regs()));
3511da177e4SLinus Torvalds #endif
3521da177e4SLinus Torvalds }
3531da177e4SLinus Torvalds 
3541da177e4SLinus Torvalds #ifdef CONFIG_PM
3551da177e4SLinus Torvalds static int timer_suspend(struct sys_device *dev, pm_message_t state)
3561da177e4SLinus Torvalds {
3571da177e4SLinus Torvalds 	struct sys_timer *timer = container_of(dev, struct sys_timer, dev);
3581da177e4SLinus Torvalds 
3591da177e4SLinus Torvalds 	if (timer->suspend != NULL)
3601da177e4SLinus Torvalds 		timer->suspend();
3611da177e4SLinus Torvalds 
3621da177e4SLinus Torvalds 	return 0;
3631da177e4SLinus Torvalds }
3641da177e4SLinus Torvalds 
3651da177e4SLinus Torvalds static int timer_resume(struct sys_device *dev)
3661da177e4SLinus Torvalds {
3671da177e4SLinus Torvalds 	struct sys_timer *timer = container_of(dev, struct sys_timer, dev);
3681da177e4SLinus Torvalds 
3691da177e4SLinus Torvalds 	if (timer->resume != NULL)
3701da177e4SLinus Torvalds 		timer->resume();
3711da177e4SLinus Torvalds 
3721da177e4SLinus Torvalds 	return 0;
3731da177e4SLinus Torvalds }
3741da177e4SLinus Torvalds #else
3751da177e4SLinus Torvalds #define timer_suspend NULL
3761da177e4SLinus Torvalds #define timer_resume NULL
3771da177e4SLinus Torvalds #endif
3781da177e4SLinus Torvalds 
3791da177e4SLinus Torvalds static struct sysdev_class timer_sysclass = {
3801da177e4SLinus Torvalds 	set_kset_name("timer"),
3811da177e4SLinus Torvalds 	.suspend	= timer_suspend,
3821da177e4SLinus Torvalds 	.resume		= timer_resume,
3831da177e4SLinus Torvalds };
3841da177e4SLinus Torvalds 
3858749af68SRussell King #ifdef CONFIG_NO_IDLE_HZ
3868749af68SRussell King static int timer_dyn_tick_enable(void)
3878749af68SRussell King {
3888749af68SRussell King 	struct dyn_tick_timer *dyn_tick = system_timer->dyn_tick;
3898749af68SRussell King 	unsigned long flags;
3908749af68SRussell King 	int ret = -ENODEV;
3918749af68SRussell King 
3928749af68SRussell King 	if (dyn_tick) {
393ebc67da6STony Lindgren 		spin_lock_irqsave(&dyn_tick->lock, flags);
3948749af68SRussell King 		ret = 0;
3958749af68SRussell King 		if (!(dyn_tick->state & DYN_TICK_ENABLED)) {
3968749af68SRussell King 			ret = dyn_tick->enable();
3978749af68SRussell King 
3988749af68SRussell King 			if (ret == 0)
3998749af68SRussell King 				dyn_tick->state |= DYN_TICK_ENABLED;
4008749af68SRussell King 		}
401ebc67da6STony Lindgren 		spin_unlock_irqrestore(&dyn_tick->lock, flags);
4028749af68SRussell King 	}
4038749af68SRussell King 
4048749af68SRussell King 	return ret;
4058749af68SRussell King }
4068749af68SRussell King 
4078749af68SRussell King static int timer_dyn_tick_disable(void)
4088749af68SRussell King {
4098749af68SRussell King 	struct dyn_tick_timer *dyn_tick = system_timer->dyn_tick;
4108749af68SRussell King 	unsigned long flags;
4118749af68SRussell King 	int ret = -ENODEV;
4128749af68SRussell King 
4138749af68SRussell King 	if (dyn_tick) {
414ebc67da6STony Lindgren 		spin_lock_irqsave(&dyn_tick->lock, flags);
4158749af68SRussell King 		ret = 0;
4168749af68SRussell King 		if (dyn_tick->state & DYN_TICK_ENABLED) {
4178749af68SRussell King 			ret = dyn_tick->disable();
4188749af68SRussell King 
4198749af68SRussell King 			if (ret == 0)
4208749af68SRussell King 				dyn_tick->state &= ~DYN_TICK_ENABLED;
4218749af68SRussell King 		}
422ebc67da6STony Lindgren 		spin_unlock_irqrestore(&dyn_tick->lock, flags);
4238749af68SRussell King 	}
4248749af68SRussell King 
4258749af68SRussell King 	return ret;
4268749af68SRussell King }
4278749af68SRussell King 
4282ea83398SRussell King /*
4292ea83398SRussell King  * Reprogram the system timer for at least the calculated time interval.
4302ea83398SRussell King  * This function should be called from the idle thread with IRQs disabled,
4312ea83398SRussell King  * immediately before sleeping.
4322ea83398SRussell King  */
4338749af68SRussell King void timer_dyn_reprogram(void)
4348749af68SRussell King {
4358749af68SRussell King 	struct dyn_tick_timer *dyn_tick = system_timer->dyn_tick;
436ebc67da6STony Lindgren 	unsigned long next, seq, flags;
4378749af68SRussell King 
438ebc67da6STony Lindgren 	if (!dyn_tick)
439ebc67da6STony Lindgren 		return;
440ebc67da6STony Lindgren 
441ebc67da6STony Lindgren 	spin_lock_irqsave(&dyn_tick->lock, flags);
442ebc67da6STony Lindgren 	if (dyn_tick->state & DYN_TICK_ENABLED) {
44369239749STony Lindgren 		next = next_timer_interrupt();
44469239749STony Lindgren 		do {
44569239749STony Lindgren 			seq = read_seqbegin(&xtime_lock);
446ebc67da6STony Lindgren 			dyn_tick->reprogram(next - jiffies);
44769239749STony Lindgren 		} while (read_seqretry(&xtime_lock, seq));
4488749af68SRussell King 	}
449ebc67da6STony Lindgren 	spin_unlock_irqrestore(&dyn_tick->lock, flags);
4503618886fSBen Dooks }
4518749af68SRussell King 
4528749af68SRussell King static ssize_t timer_show_dyn_tick(struct sys_device *dev, char *buf)
4538749af68SRussell King {
4548749af68SRussell King 	return sprintf(buf, "%i\n",
4558749af68SRussell King 		       (system_timer->dyn_tick->state & DYN_TICK_ENABLED) >> 1);
4568749af68SRussell King }
4578749af68SRussell King 
4588749af68SRussell King static ssize_t timer_set_dyn_tick(struct sys_device *dev, const char *buf,
4598749af68SRussell King 				  size_t count)
4608749af68SRussell King {
4618749af68SRussell King 	unsigned int enable = simple_strtoul(buf, NULL, 2);
4628749af68SRussell King 
4638749af68SRussell King 	if (enable)
4648749af68SRussell King 		timer_dyn_tick_enable();
4658749af68SRussell King 	else
4668749af68SRussell King 		timer_dyn_tick_disable();
4678749af68SRussell King 
4688749af68SRussell King 	return count;
4698749af68SRussell King }
4708749af68SRussell King static SYSDEV_ATTR(dyn_tick, 0644, timer_show_dyn_tick, timer_set_dyn_tick);
4718749af68SRussell King 
4728749af68SRussell King /*
4738749af68SRussell King  * dyntick=enable|disable
4748749af68SRussell King  */
4758749af68SRussell King static char dyntick_str[4] __initdata = "";
4768749af68SRussell King 
4778749af68SRussell King static int __init dyntick_setup(char *str)
4788749af68SRussell King {
4798749af68SRussell King 	if (str)
4808749af68SRussell King 		strlcpy(dyntick_str, str, sizeof(dyntick_str));
4818749af68SRussell King 	return 1;
4828749af68SRussell King }
4838749af68SRussell King 
4848749af68SRussell King __setup("dyntick=", dyntick_setup);
4858749af68SRussell King #endif
4868749af68SRussell King 
4871da177e4SLinus Torvalds static int __init timer_init_sysfs(void)
4881da177e4SLinus Torvalds {
4891da177e4SLinus Torvalds 	int ret = sysdev_class_register(&timer_sysclass);
4901da177e4SLinus Torvalds 	if (ret == 0) {
4911da177e4SLinus Torvalds 		system_timer->dev.cls = &timer_sysclass;
4921da177e4SLinus Torvalds 		ret = sysdev_register(&system_timer->dev);
4931da177e4SLinus Torvalds 	}
4948749af68SRussell King 
4958749af68SRussell King #ifdef CONFIG_NO_IDLE_HZ
4968749af68SRussell King 	if (ret == 0 && system_timer->dyn_tick) {
4978749af68SRussell King 		ret = sysdev_create_file(&system_timer->dev, &attr_dyn_tick);
4988749af68SRussell King 
4998749af68SRussell King 		/*
5008749af68SRussell King 		 * Turn on dynamic tick after calibrate delay
5018749af68SRussell King 		 * for correct bogomips
5028749af68SRussell King 		 */
5038749af68SRussell King 		if (ret == 0 && dyntick_str[0] == 'e')
5048749af68SRussell King 			ret = timer_dyn_tick_enable();
5058749af68SRussell King 	}
5068749af68SRussell King #endif
5078749af68SRussell King 
5081da177e4SLinus Torvalds 	return ret;
5091da177e4SLinus Torvalds }
5101da177e4SLinus Torvalds 
5111da177e4SLinus Torvalds device_initcall(timer_init_sysfs);
5121da177e4SLinus Torvalds 
5131da177e4SLinus Torvalds void __init time_init(void)
5141da177e4SLinus Torvalds {
515746140c7SKevin Hilman #ifndef CONFIG_GENERIC_TIME
5161da177e4SLinus Torvalds 	if (system_timer->offset == NULL)
5171da177e4SLinus Torvalds 		system_timer->offset = dummy_gettimeoffset;
518746140c7SKevin Hilman #endif
5191da177e4SLinus Torvalds 	system_timer->init();
520ebc67da6STony Lindgren 
521ebc67da6STony Lindgren #ifdef CONFIG_NO_IDLE_HZ
522ebc67da6STony Lindgren 	if (system_timer->dyn_tick)
523ebc67da6STony Lindgren 		system_timer->dyn_tick->lock = SPIN_LOCK_UNLOCKED;
524ebc67da6STony Lindgren #endif
5251da177e4SLinus Torvalds }
5261da177e4SLinus Torvalds 
527