linux/arch/m68k/kernel/time.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/m68k/kernel/time.c
   3 *
   4 *  Copyright (C) 1991, 1992, 1995  Linus Torvalds
   5 *
   6 * This file contains the m68k-specific time handling details.
   7 * Most of the stuff is located in the machine specific files.
   8 *
   9 * 1997-09-10   Updated NTP code according to technical memorandum Jan '96
  10 *              "A Kernel Model for Precision Timekeeping" by Dave Mills
  11 */
  12
  13#include <linux/errno.h>
  14#include <linux/module.h>
  15#include <linux/sched.h>
  16#include <linux/kernel.h>
  17#include <linux/param.h>
  18#include <linux/string.h>
  19#include <linux/mm.h>
  20#include <linux/rtc.h>
  21
  22#include <asm/machdep.h>
  23#include <asm/io.h>
  24#include <asm/irq_regs.h>
  25
  26#include <linux/time.h>
  27#include <linux/timex.h>
  28#include <linux/profile.h>
  29
  30static inline int set_rtc_mmss(unsigned long nowtime)
  31{
  32  if (mach_set_clock_mmss)
  33    return mach_set_clock_mmss (nowtime);
  34  return -1;
  35}
  36
  37/*
  38 * timer_interrupt() needs to keep up the real-time clock,
  39 * as well as call the "do_timer()" routine every clocktick
  40 */
  41static irqreturn_t timer_interrupt(int irq, void *dummy)
  42{
  43        do_timer(1);
  44#ifndef CONFIG_SMP
  45        update_process_times(user_mode(get_irq_regs()));
  46#endif
  47        profile_tick(CPU_PROFILING);
  48
  49#ifdef CONFIG_HEARTBEAT
  50        /* use power LED as a heartbeat instead -- much more useful
  51           for debugging -- based on the version for PReP by Cort */
  52        /* acts like an actual heart beat -- ie thump-thump-pause... */
  53        if (mach_heartbeat) {
  54            static unsigned cnt = 0, period = 0, dist = 0;
  55
  56            if (cnt == 0 || cnt == dist)
  57                mach_heartbeat( 1 );
  58            else if (cnt == 7 || cnt == dist+7)
  59                mach_heartbeat( 0 );
  60
  61            if (++cnt > period) {
  62                cnt = 0;
  63                /* The hyperbolic function below modifies the heartbeat period
  64                 * length in dependency of the current (5min) load. It goes
  65                 * through the points f(0)=126, f(1)=86, f(5)=51,
  66                 * f(inf)->30. */
  67                period = ((672<<FSHIFT)/(5*avenrun[0]+(7<<FSHIFT))) + 30;
  68                dist = period / 4;
  69            }
  70        }
  71#endif /* CONFIG_HEARTBEAT */
  72        return IRQ_HANDLED;
  73}
  74
  75void __init time_init(void)
  76{
  77        struct rtc_time time;
  78
  79        if (mach_hwclk) {
  80                mach_hwclk(0, &time);
  81
  82                if ((time.tm_year += 1900) < 1970)
  83                        time.tm_year += 100;
  84                xtime.tv_sec = mktime(time.tm_year, time.tm_mon, time.tm_mday,
  85                                      time.tm_hour, time.tm_min, time.tm_sec);
  86                xtime.tv_nsec = 0;
  87        }
  88        wall_to_monotonic.tv_sec = -xtime.tv_sec;
  89
  90        mach_sched_init(timer_interrupt);
  91}
  92
  93/*
  94 * This version of gettimeofday has near microsecond resolution.
  95 */
  96void do_gettimeofday(struct timeval *tv)
  97{
  98        unsigned long flags;
  99        unsigned long seq;
 100        unsigned long usec, sec;
 101        unsigned long max_ntp_tick = tick_usec - tickadj;
 102
 103        do {
 104                seq = read_seqbegin_irqsave(&xtime_lock, flags);
 105
 106                usec = mach_gettimeoffset();
 107
 108                /*
 109                 * If time_adjust is negative then NTP is slowing the clock
 110                 * so make sure not to go into next possible interval.
 111                 * Better to lose some accuracy than have time go backwards..
 112                 */
 113                if (unlikely(time_adjust < 0))
 114                        usec = min(usec, max_ntp_tick);
 115
 116                sec = xtime.tv_sec;
 117                usec += xtime.tv_nsec/1000;
 118        } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
 119
 120
 121        while (usec >= 1000000) {
 122                usec -= 1000000;
 123                sec++;
 124        }
 125
 126        tv->tv_sec = sec;
 127        tv->tv_usec = usec;
 128}
 129
 130EXPORT_SYMBOL(do_gettimeofday);
 131
 132int do_settimeofday(struct timespec *tv)
 133{
 134        time_t wtm_sec, sec = tv->tv_sec;
 135        long wtm_nsec, nsec = tv->tv_nsec;
 136
 137        if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
 138                return -EINVAL;
 139
 140        write_seqlock_irq(&xtime_lock);
 141        /* This is revolting. We need to set the xtime.tv_nsec
 142         * correctly. However, the value in this location is
 143         * is value at the last tick.
 144         * Discover what correction gettimeofday
 145         * would have done, and then undo it!
 146         */
 147        nsec -= 1000 * mach_gettimeoffset();
 148
 149        wtm_sec  = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
 150        wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
 151
 152        set_normalized_timespec(&xtime, sec, nsec);
 153        set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
 154
 155        ntp_clear();
 156        write_sequnlock_irq(&xtime_lock);
 157        clock_was_set();
 158        return 0;
 159}
 160
 161EXPORT_SYMBOL(do_settimeofday);
 162