linux-old/arch/s390x/kernel/time.c
<<
>>
Prefs
   1/*
   2 *  arch/s390/kernel/time.c
   3 *
   4 *  S390 version
   5 *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
   6 *    Author(s): Hartmut Penner (hp@de.ibm.com),
   7 *               Martin Schwidefsky (schwidefsky@de.ibm.com),
   8 *               Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
   9 *
  10 *  Derived from "arch/i386/kernel/time.c"
  11 *    Copyright (C) 1991, 1992, 1995  Linus Torvalds
  12 */
  13
  14#include <linux/errno.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/interrupt.h>
  21#include <linux/time.h>
  22#include <linux/delay.h>
  23#include <linux/init.h>
  24#include <linux/smp.h>
  25#include <linux/types.h>
  26
  27#include <asm/uaccess.h>
  28#include <asm/delay.h>
  29
  30#include <linux/timex.h>
  31#include <linux/config.h>
  32
  33#include <asm/irq.h>
  34#include <asm/s390_ext.h>
  35
  36/* change this if you have some constant time drift */
  37#define USECS_PER_JIFFY     ((unsigned long) 1000000/HZ)
  38#define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12)
  39
  40/*
  41 * Create a small time difference between the timer interrupts
  42 * on the different cpus to avoid lock contention.
  43 */
  44#define CPU_DEVIATION       (smp_processor_id() << 12)
  45
  46#define TICK_SIZE tick
  47
  48static ext_int_info_t ext_int_info_timer;
  49static u64 init_timer_cc;
  50static u64 xtime_cc;
  51
  52extern rwlock_t xtime_lock;
  53extern unsigned long wall_jiffies;
  54
  55void tod_to_timeval(__u64 todval, struct timeval *xtime)
  56{
  57        todval >>= 12;
  58        xtime->tv_sec = todval / 1000000;
  59        xtime->tv_usec = todval % 1000000;
  60}
  61
  62static inline unsigned long do_gettimeoffset(void) 
  63{
  64        __u64 now;
  65
  66        asm volatile ("STCK 0(%0)" : : "a" (&now) : "memory", "cc");
  67        now = (now - init_timer_cc) >> 12;
  68        /* We require the offset from the latest update of xtime */
  69        now -= (__u64) wall_jiffies*USECS_PER_JIFFY;
  70        return (unsigned long) now;
  71}
  72
  73/*
  74 * This version of gettimeofday has microsecond resolution.
  75 */
  76void do_gettimeofday(struct timeval *tv)
  77{
  78        unsigned long flags;
  79        unsigned long usec, sec;
  80
  81        read_lock_irqsave(&xtime_lock, flags);
  82        sec = xtime.tv_sec;
  83        usec = xtime.tv_usec + do_gettimeoffset();
  84        read_unlock_irqrestore(&xtime_lock, flags);
  85
  86        while (usec >= 1000000) {
  87                usec -= 1000000;
  88                sec++;
  89        }
  90
  91        tv->tv_sec = sec;
  92        tv->tv_usec = usec;
  93}
  94
  95void do_settimeofday(struct timeval *tv)
  96{
  97
  98        write_lock_irq(&xtime_lock);
  99        /* This is revolting. We need to set the xtime.tv_usec
 100         * correctly. However, the value in this location is
 101         * is value at the last tick.
 102         * Discover what correction gettimeofday
 103         * would have done, and then undo it!
 104         */
 105        tv->tv_usec -= do_gettimeoffset();
 106
 107        while (tv->tv_usec < 0) {
 108                tv->tv_usec += 1000000;
 109                tv->tv_sec--;
 110        }
 111
 112        xtime = *tv;
 113        time_adjust = 0;                /* stop active adjtime() */
 114        time_status |= STA_UNSYNC;
 115        time_maxerror = NTP_PHASE_LIMIT;
 116        time_esterror = NTP_PHASE_LIMIT;
 117        write_unlock_irq(&xtime_lock);
 118}
 119
 120/*
 121 * timer_interrupt() needs to keep up the real-time clock,
 122 * as well as call the "do_timer()" routine every clocktick
 123 */
 124void account_ticks(struct pt_regs *regs)
 125{
 126        int cpu = smp_processor_id();
 127        __u64 tmp;
 128        __u32 ticks;
 129
 130        /* Calculate how many ticks have passed. */
 131        tmp = S390_lowcore.int_clock - S390_lowcore.jiffy_timer;
 132        if (tmp >= 2*CLK_TICKS_PER_JIFFY) {  /* more than one tick ? */
 133                ticks = tmp / CLK_TICKS_PER_JIFFY + 1;
 134                S390_lowcore.jiffy_timer +=
 135                        CLK_TICKS_PER_JIFFY * (__u64) ticks;
 136        } else if (tmp > CLK_TICKS_PER_JIFFY) {
 137                ticks = 2;
 138                S390_lowcore.jiffy_timer += 2*CLK_TICKS_PER_JIFFY;
 139        } else {
 140                ticks = 1;
 141                S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY;
 142        }
 143
 144        /* set clock comparator for next tick */
 145        tmp = S390_lowcore.jiffy_timer + CPU_DEVIATION;
 146        asm volatile ("SCKC %0" : : "m" (tmp));
 147
 148        irq_enter(cpu, 0);
 149
 150#ifdef CONFIG_SMP
 151        /*
 152         * Do not rely on the boot cpu to do the calls to do_timer.
 153         * Spread it over all cpus instead.
 154         */
 155        write_lock(&xtime_lock);
 156        if (S390_lowcore.jiffy_timer > xtime_cc) {
 157                __u32 xticks;
 158
 159                tmp = S390_lowcore.jiffy_timer - xtime_cc;
 160                if (tmp >= 2*CLK_TICKS_PER_JIFFY) {
 161                        xticks = tmp / CLK_TICKS_PER_JIFFY;
 162                        xtime_cc += (__u64) xticks * CLK_TICKS_PER_JIFFY;
 163                } else {
 164                        xticks = 1;
 165                        xtime_cc += CLK_TICKS_PER_JIFFY;
 166                }
 167                while (xticks--)
 168                        do_timer(regs);
 169        }
 170        write_unlock(&xtime_lock);
 171        while (ticks--)
 172                update_process_times(user_mode(regs));
 173#else
 174        while (ticks--)
 175                do_timer(regs);
 176#endif
 177
 178        irq_exit(cpu, 0);
 179}
 180
 181/*
 182 * Start the clock comparator on the current CPU
 183 */
 184void init_cpu_timer(void)
 185{
 186        unsigned long cr0;
 187        __u64 timer;
 188
 189        timer = init_timer_cc + (__u64) jiffies * CLK_TICKS_PER_JIFFY;
 190        S390_lowcore.jiffy_timer = timer + CLK_TICKS_PER_JIFFY;
 191        timer += CLK_TICKS_PER_JIFFY + CPU_DEVIATION;
 192        asm volatile ("SCKC %0" : : "m" (timer));
 193        /* allow clock comparator timer interrupt */
 194        asm volatile ("STCTG 0,0,%0" : "=m" (cr0) : : "memory");
 195        cr0 |= 0x800;
 196        asm volatile ("LCTLG 0,0,%0" : : "m" (cr0) : "memory");
 197}
 198
 199/*
 200 * Initialize the TOD clock and the CPU timer of
 201 * the boot cpu.
 202 */
 203void __init time_init(void)
 204{
 205        __u64 set_time_cc;
 206        int cc;
 207
 208        /* kick the TOD clock */
 209        asm volatile ("STCK 0(%1)\n\t"
 210                      "IPM  %0\n\t"
 211                      "SRL  %0,28" : "=r" (cc) : "a" (&init_timer_cc) 
 212                                   : "memory", "cc");
 213        switch (cc) {
 214        case 0: /* clock in set state: all is fine */
 215                break;
 216        case 1: /* clock in non-set state: FIXME */
 217                printk("time_init: TOD clock in non-set state\n");
 218                break;
 219        case 2: /* clock in error state: FIXME */
 220                printk("time_init: TOD clock in error state\n");
 221                break;
 222        case 3: /* clock in stopped or not-operational state: FIXME */
 223                printk("time_init: TOD clock stopped/non-operational\n");
 224                break;
 225        }
 226
 227        /* set xtime */
 228        xtime_cc = init_timer_cc + CLK_TICKS_PER_JIFFY;
 229        set_time_cc = init_timer_cc - 0x8126d60e46000000LL +
 230                      (0x3c26700LL*1000000*4096);
 231        tod_to_timeval(set_time_cc, &xtime);
 232
 233        /* request the 0x1004 external interrupt */
 234        if (register_early_external_interrupt(0x1004, NULL,
 235                                              &ext_int_info_timer) != 0)
 236                panic("Couldn't request external interrupt 0x1004");
 237
 238        /* init CPU timer */
 239        init_cpu_timer();
 240}
 241
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.