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