linux/arch/arm/kernel/time.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/arm/kernel/time.c
   3 *
   4 *  Copyright (C) 1991, 1992, 1995  Linus Torvalds
   5 *  Modifications for ARM (C) 1994-2001 Russell King
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 *
  11 *  This file contains the ARM-specific time handling details:
  12 *  reading the RTC at bootup, etc...
  13 *
  14 *  1994-07-02  Alan Modra
  15 *              fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime
  16 *  1998-12-20  Updated NTP code according to technical memorandum Jan '96
  17 *              "A Kernel Model for Precision Timekeeping" by Dave Mills
  18 */
  19#include <linux/module.h>
  20#include <linux/kernel.h>
  21#include <linux/interrupt.h>
  22#include <linux/time.h>
  23#include <linux/init.h>
  24#include <linux/sched.h>
  25#include <linux/smp.h>
  26#include <linux/timex.h>
  27#include <linux/errno.h>
  28#include <linux/profile.h>
  29#include <linux/sysdev.h>
  30#include <linux/timer.h>
  31#include <linux/irq.h>
  32
  33#include <linux/mc146818rtc.h>
  34
  35#include <asm/leds.h>
  36#include <asm/thread_info.h>
  37#include <asm/stacktrace.h>
  38#include <asm/mach/time.h>
  39
  40/*
  41 * Our system timer.
  42 */
  43struct sys_timer *system_timer;
  44
  45#if defined(CONFIG_RTC_DRV_CMOS) || defined(CONFIG_RTC_DRV_CMOS_MODULE)
  46/* this needs a better home */
  47DEFINE_SPINLOCK(rtc_lock);
  48
  49#ifdef CONFIG_RTC_DRV_CMOS_MODULE
  50EXPORT_SYMBOL(rtc_lock);
  51#endif
  52#endif  /* pc-style 'CMOS' RTC support */
  53
  54/* change this if you have some constant time drift */
  55#define USECS_PER_JIFFY (1000000/HZ)
  56
  57#ifdef CONFIG_SMP
  58unsigned long profile_pc(struct pt_regs *regs)
  59{
  60        struct stackframe frame;
  61
  62        if (!in_lock_functions(regs->ARM_pc))
  63                return regs->ARM_pc;
  64
  65        frame.fp = regs->ARM_fp;
  66        frame.sp = regs->ARM_sp;
  67        frame.lr = regs->ARM_lr;
  68        frame.pc = regs->ARM_pc;
  69        do {
  70                int ret = unwind_frame(&frame);
  71                if (ret < 0)
  72                        return 0;
  73        } while (in_lock_functions(frame.pc));
  74
  75        return frame.pc;
  76}
  77EXPORT_SYMBOL(profile_pc);
  78#endif
  79
  80/*
  81 * hook for setting the RTC's idea of the current time.
  82 */
  83int (*set_rtc)(void);
  84
  85#ifndef CONFIG_GENERIC_TIME
  86static unsigned long dummy_gettimeoffset(void)
  87{
  88        return 0;
  89}
  90#endif
  91
  92static unsigned long next_rtc_update;
  93
  94/*
  95 * If we have an externally synchronized linux clock, then update
  96 * CMOS clock accordingly every ~11 minutes.  set_rtc() has to be
  97 * called as close as possible to 500 ms before the new second
  98 * starts.
  99 */
 100static inline void do_set_rtc(void)
 101{
 102        if (!ntp_synced() || set_rtc == NULL)
 103                return;
 104
 105        if (next_rtc_update &&
 106            time_before((unsigned long)xtime.tv_sec, next_rtc_update))
 107                return;
 108
 109        if (xtime.tv_nsec < 500000000 - ((unsigned) tick_nsec >> 1) &&
 110            xtime.tv_nsec >= 500000000 + ((unsigned) tick_nsec >> 1))
 111                return;
 112
 113        if (set_rtc())
 114                /*
 115                 * rtc update failed.  Try again in 60s
 116                 */
 117                next_rtc_update = xtime.tv_sec + 60;
 118        else
 119                next_rtc_update = xtime.tv_sec + 660;
 120}
 121
 122#ifdef CONFIG_LEDS
 123
 124static void dummy_leds_event(led_event_t evt)
 125{
 126}
 127
 128void (*leds_event)(led_event_t) = dummy_leds_event;
 129
 130struct leds_evt_name {
 131        const char      name[8];
 132        int             on;
 133        int             off;
 134};
 135
 136static const struct leds_evt_name evt_names[] = {
 137        { "amber", led_amber_on, led_amber_off },
 138        { "blue",  led_blue_on,  led_blue_off  },
 139        { "green", led_green_on, led_green_off },
 140        { "red",   led_red_on,   led_red_off   },
 141};
 142
 143static ssize_t leds_store(struct sys_device *dev,
 144                        struct sysdev_attribute *attr,
 145                        const char *buf, size_t size)
 146{
 147        int ret = -EINVAL, len = strcspn(buf, " ");
 148
 149        if (len > 0 && buf[len] == '\0')
 150                len--;
 151
 152        if (strncmp(buf, "claim", len) == 0) {
 153                leds_event(led_claim);
 154                ret = size;
 155        } else if (strncmp(buf, "release", len) == 0) {
 156                leds_event(led_release);
 157                ret = size;
 158        } else {
 159                int i;
 160
 161                for (i = 0; i < ARRAY_SIZE(evt_names); i++) {
 162                        if (strlen(evt_names[i].name) != len ||
 163                            strncmp(buf, evt_names[i].name, len) != 0)
 164                                continue;
 165                        if (strncmp(buf+len, " on", 3) == 0) {
 166                                leds_event(evt_names[i].on);
 167                                ret = size;
 168                        } else if (strncmp(buf+len, " off", 4) == 0) {
 169                                leds_event(evt_names[i].off);
 170                                ret = size;
 171                        }
 172                        break;
 173                }
 174        }
 175        return ret;
 176}
 177
 178static SYSDEV_ATTR(event, 0200, NULL, leds_store);
 179
 180static int leds_suspend(struct sys_device *dev, pm_message_t state)
 181{
 182        leds_event(led_stop);
 183        return 0;
 184}
 185
 186static int leds_resume(struct sys_device *dev)
 187{
 188        leds_event(led_start);
 189        return 0;
 190}
 191
 192static int leds_shutdown(struct sys_device *dev)
 193{
 194        leds_event(led_halted);
 195        return 0;
 196}
 197
 198static struct sysdev_class leds_sysclass = {
 199        .name           = "leds",
 200        .shutdown       = leds_shutdown,
 201        .suspend        = leds_suspend,
 202        .resume         = leds_resume,
 203};
 204
 205static struct sys_device leds_device = {
 206        .id             = 0,
 207        .cls            = &leds_sysclass,
 208};
 209
 210static int __init leds_init(void)
 211{
 212        int ret;
 213        ret = sysdev_class_register(&leds_sysclass);
 214        if (ret == 0)
 215                ret = sysdev_register(&leds_device);
 216        if (ret == 0)
 217                ret = sysdev_create_file(&leds_device, &attr_event);
 218        return ret;
 219}
 220
 221device_initcall(leds_init);
 222
 223EXPORT_SYMBOL(leds_event);
 224#endif
 225
 226#ifdef CONFIG_LEDS_TIMER
 227static inline void do_leds(void)
 228{
 229        static unsigned int count = HZ/2;
 230
 231        if (--count == 0) {
 232                count = HZ/2;
 233                leds_event(led_timer);
 234        }
 235}
 236#else
 237#define do_leds()
 238#endif
 239
 240#ifndef CONFIG_GENERIC_TIME
 241void do_gettimeofday(struct timeval *tv)
 242{
 243        unsigned long flags;
 244        unsigned long seq;
 245        unsigned long usec, sec;
 246
 247        do {
 248                seq = read_seqbegin_irqsave(&xtime_lock, flags);
 249                usec = system_timer->offset();
 250                sec = xtime.tv_sec;
 251                usec += xtime.tv_nsec / 1000;
 252        } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
 253
 254        /* usec may have gone up a lot: be safe */
 255        while (usec >= 1000000) {
 256                usec -= 1000000;
 257                sec++;
 258        }
 259
 260        tv->tv_sec = sec;
 261        tv->tv_usec = usec;
 262}
 263
 264EXPORT_SYMBOL(do_gettimeofday);
 265
 266int do_settimeofday(struct timespec *tv)
 267{
 268        time_t wtm_sec, sec = tv->tv_sec;
 269        long wtm_nsec, nsec = tv->tv_nsec;
 270
 271        if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
 272                return -EINVAL;
 273
 274        write_seqlock_irq(&xtime_lock);
 275        /*
 276         * This is revolting. We need to set "xtime" correctly. However, the
 277         * value in this location is the value at the most recent update of
 278         * wall time.  Discover what correction gettimeofday() would have
 279         * done, and then undo it!
 280         */
 281        nsec -= system_timer->offset() * NSEC_PER_USEC;
 282
 283        wtm_sec  = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
 284        wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
 285
 286        set_normalized_timespec(&xtime, sec, nsec);
 287        set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
 288
 289        ntp_clear();
 290        write_sequnlock_irq(&xtime_lock);
 291        clock_was_set();
 292        return 0;
 293}
 294
 295EXPORT_SYMBOL(do_settimeofday);
 296#endif /* !CONFIG_GENERIC_TIME */
 297
 298/**
 299 * save_time_delta - Save the offset between system time and RTC time
 300 * @delta: pointer to timespec to store delta
 301 * @rtc: pointer to timespec for current RTC time
 302 *
 303 * Return a delta between the system time and the RTC time, such
 304 * that system time can be restored later with restore_time_delta()
 305 */
 306void save_time_delta(struct timespec *delta, struct timespec *rtc)
 307{
 308        set_normalized_timespec(delta,
 309                                xtime.tv_sec - rtc->tv_sec,
 310                                xtime.tv_nsec - rtc->tv_nsec);
 311}
 312EXPORT_SYMBOL(save_time_delta);
 313
 314/**
 315 * restore_time_delta - Restore the current system time
 316 * @delta: delta returned by save_time_delta()
 317 * @rtc: pointer to timespec for current RTC time
 318 */
 319void restore_time_delta(struct timespec *delta, struct timespec *rtc)
 320{
 321        struct timespec ts;
 322
 323        set_normalized_timespec(&ts,
 324                                delta->tv_sec + rtc->tv_sec,
 325                                delta->tv_nsec + rtc->tv_nsec);
 326
 327        do_settimeofday(&ts);
 328}
 329EXPORT_SYMBOL(restore_time_delta);
 330
 331#ifndef CONFIG_GENERIC_CLOCKEVENTS
 332/*
 333 * Kernel system timer support.
 334 */
 335void timer_tick(void)
 336{
 337        profile_tick(CPU_PROFILING);
 338        do_leds();
 339        do_set_rtc();
 340        write_seqlock(&xtime_lock);
 341        do_timer(1);
 342        write_sequnlock(&xtime_lock);
 343#ifndef CONFIG_SMP
 344        update_process_times(user_mode(get_irq_regs()));
 345#endif
 346}
 347#endif
 348
 349#if defined(CONFIG_PM) && !defined(CONFIG_GENERIC_CLOCKEVENTS)
 350static int timer_suspend(struct sys_device *dev, pm_message_t state)
 351{
 352        struct sys_timer *timer = container_of(dev, struct sys_timer, dev);
 353
 354        if (timer->suspend != NULL)
 355                timer->suspend();
 356
 357        return 0;
 358}
 359
 360static int timer_resume(struct sys_device *dev)
 361{
 362        struct sys_timer *timer = container_of(dev, struct sys_timer, dev);
 363
 364        if (timer->resume != NULL)
 365                timer->resume();
 366
 367        return 0;
 368}
 369#else
 370#define timer_suspend NULL
 371#define timer_resume NULL
 372#endif
 373
 374static struct sysdev_class timer_sysclass = {
 375        .name           = "timer",
 376        .suspend        = timer_suspend,
 377        .resume         = timer_resume,
 378};
 379
 380static int __init timer_init_sysfs(void)
 381{
 382        int ret = sysdev_class_register(&timer_sysclass);
 383        if (ret == 0) {
 384                system_timer->dev.cls = &timer_sysclass;
 385                ret = sysdev_register(&system_timer->dev);
 386        }
 387
 388        return ret;
 389}
 390
 391device_initcall(timer_init_sysfs);
 392
 393void __init time_init(void)
 394{
 395#ifndef CONFIG_GENERIC_TIME
 396        if (system_timer->offset == NULL)
 397                system_timer->offset = dummy_gettimeoffset;
 398#endif
 399        system_timer->init();
 400}
 401
 402