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