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