From: Jun S. <ju...@us...> - 2001-10-01 19:33:05
|
Update of /cvsroot/linux-mips/linux/arch/mips/vr4181/common In directory usw-pr-cvs1:/tmp/cvs-serv19857/arch/mips/vr4181/common Modified Files: time.c Log Message: Move Osprey/vr4181 to use the new time.c file. Now the code is really getting pretty ... Index: time.c =================================================================== RCS file: /cvsroot/linux-mips/linux/arch/mips/vr4181/common/time.c,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- time.c 2001/09/29 00:42:25 1.2 +++ time.c 2001/10/01 19:32:59 1.3 @@ -1,228 +1,148 @@ /* - * linux/arch/mips/vr41xx/time.c - * - * VR4181 timer interrupt using real-time clock. + * Copyright 2001 MontaVista Software Inc. + * Author: js...@mv... or js...@ju... * - * Copyright (C) 1991, 1992, 1995 Linus Torvalds - * Copyright (C) 1999 Bradley D. LaRonde - * Copyright (C) 2000 Michael Klar + * rtc and time ops for vr4181. Part of code is drived from + * linux-vr, originally written by Bradley D. LaRonde & Michael Klar. * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. * */ -#include <linux/config.h> -#include <linux/init.h> -#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/param.h> /* for HZ */ +#include <linux/time.h> #include <linux/interrupt.h> -#include <linux/timex.h> -#include <linux/pm.h> -#include <asm/vr4181/vr4181.h> -extern int setup_irq(unsigned int irq, struct irqaction *irqaction); -extern volatile unsigned long wall_jiffies; -extern rwlock_t xtime_lock; +#include <asm/system.h> +#include <asm/time.h> -#define USECS_PER_JIFFY (1000000/HZ) +#include <asm/vr4181/vr4181.h> + #define COUNTS_PER_JIFFY ((32768 + HZ/2) / HZ) -spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; +/* + * RTC ops + */ -#ifdef CONFIG_RTC -unsigned long epoch_adj; -#else -#define epoch_adj 0 -#endif +spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; -// per VR41xx docs, bad data can be read if between 2 counts -static inline unsigned short read_time_reg(volatile unsigned short *reg) +/* per VR41xx docs, bad data can be read if between 2 counts */ +static inline unsigned short +read_time_reg(volatile unsigned short *reg) { - unsigned short value; - do { - value = *reg; - barrier(); - } while (value != *reg); - return value; + unsigned short value; + do { + value = *reg; + barrier(); + } while (value != *reg); + return value; } -unsigned long get_rtc_time(volatile unsigned short *reg) +static unsigned long +vr4181_rtc_get_time(void) { - unsigned short regh, regm, regl; + unsigned short regh, regm, regl; - // why this crazy order, you ask? to guarantee that neither m - // nor l wrap before all 3 read - do { - regm = read_time_reg(reg + 1); - barrier(); - regh = read_time_reg(reg + 2); - barrier(); - regl = read_time_reg(reg); - } while (regm != read_time_reg(reg + 1)); - return ((regh << 17) | (regm << 1) | (regl >> 15)) + epoch_adj; + // why this crazy order, you ask? to guarantee that neither m + // nor l wrap before all 3 read + do { + regm = read_time_reg(VR4181_ETIMEMREG); + barrier(); + regh = read_time_reg(VR4181_ETIMEHREG); + barrier(); + regl = read_time_reg(VR4181_ETIMELREG); + } while (regm != read_time_reg(VR4181_ETIMEMREG)); + return ((regh << 17) | (regm << 1) | (regl >> 15)); } -void set_rtc_time(unsigned long settime, volatile unsigned short *reg) +static int +vr4181_rtc_set_time(unsigned long timeval) { - unsigned short intreg; - unsigned long flags, timeval = settime - epoch_adj; - - spin_lock_irqsave(&rtc_lock, flags); - intreg = *VR4181_RTCINTREG & 0x05; - barrier(); - *reg = timeval << 15; - *(reg + 1) = timeval >> 1; - *(reg + 2) = timeval >> 17; - barrier(); - // assume that any ints that just triggered are invalid, since the - // time value is written non-atomically in 3 separate regs - *VR4181_RTCINTREG = 0x05 ^ intreg; - spin_unlock_irqrestore(&rtc_lock, flags); -} + unsigned short intreg; + unsigned long flags; -// must be called with ints disabled: -// -static unsigned long do_gettimeoffset(void) -{ - unsigned short count; - unsigned long offset; + spin_lock_irqsave(&rtc_lock, flags); + intreg = *VR4181_RTCINTREG & 0x05; + barrier(); + *VR4181_ETIMELREG = timeval << 15; + *VR4181_ETIMEMREG = timeval >> 1; + *VR4181_ETIMEHREG = timeval >> 17; + barrier(); + // assume that any ints that just triggered are invalid, since the + // time value is written non-atomically in 3 separate regs + *VR4181_RTCINTREG = 0x05 ^ intreg; + spin_unlock_irqrestore(&rtc_lock, flags); - count = read_time_reg(VR4181_RTCL1CNTLREG); - if (count == 1) - offset = 0; - else - offset = (COUNTS_PER_JIFFY - count + 1) * 1000000 / 32768; - // detect if the counter wrapped, but int not serviced yet, being - // careful not to adjust if int happen after count was read just now - if (*VR4181_RTCINTREG & 0x0002 && (jiffies == wall_jiffies) && count != 2) - offset += USECS_PER_JIFFY; - return offset; + return 0; } -// This version of gettimeofday has about 30us resolution -void do_gettimeofday(struct timeval *tv) -{ - unsigned long flags, lost; - - read_lock_irqsave(&xtime_lock, flags); - *tv = xtime; - tv->tv_usec += do_gettimeoffset(); - - // xtime is atomically updated in timer_bh, wall_jiffies is - // updated in timer_bh, jiffies is updated in timer int, so this - // will be nonzero if the timer bottom half hasn't executed yet: - lost = jiffies - wall_jiffies; - - read_unlock_irqrestore(&xtime_lock, flags); - - while (lost--) - tv->tv_usec += USECS_PER_JIFFY; - - while (tv->tv_usec >= 1000000) { - tv->tv_usec -= 1000000; - tv->tv_sec++; - } -} -void do_settimeofday(struct timeval *tv) +/* + * timer interrupt routine (wrapper) + * + * we need our own interrupt routine because we need to clear + * RTC1 interrupt. + */ +static void +vr4181_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - write_lock_irq(&xtime_lock); - // undo whatever correction gettimeofday would have done - tv->tv_usec -= do_gettimeoffset(); - tv->tv_usec -= (jiffies - wall_jiffies) * USECS_PER_JIFFY; - - if (tv->tv_usec < 0) { - tv->tv_usec += 1000000; - tv->tv_sec--; - } + /* Clear the interrupt. */ + *VR4181_RTCINTREG = 0x2; - xtime = *tv; - time_adjust = 0; // stop active adjtime() - time_status |= STA_UNSYNC; - time_maxerror = NTP_PHASE_LIMIT; - time_esterror = NTP_PHASE_LIMIT; - write_unlock_irq(&xtime_lock); + /* call the generic one */ + timer_interrupt(irq, dev_id, regs); } -static unsigned long last_rtc_update; /* - * timer_interrupt() needs to keep up the real-time clock, - * as well as call the "do_timer()" routine every clocktick + * vr4181_time_init: + * + * We pick the following choices: + * . we use elapsed timer as the RTC. We set some reasonable init data since + * it does not persist across reset + * . we use RTC1 as the system timer interrupt source. + * . we use CPU counter for fast_gettimeoffset and we calivrate the cpu + * frequency. In other words, we use calibrate_div64_gettimeoffset(). + * . we use our own timer interrupt routine which clears the interrupt + * and then calls the generic high-level timer interrupt routine. + * */ -static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - // Clear the interrupt. - *VR4181_RTCINTREG = 0x2; - do_timer(regs); +extern int setup_irq(unsigned int irq, struct irqaction *irqaction); - // If we have an externally synchronized Linux clock, then update - // CMOS clock accordingly every ~11 minutes. - if ((time_status & STA_UNSYNC) == 0 && - xtime.tv_sec > last_rtc_update + 660) { - set_rtc_time(xtime.tv_sec, VR4181_ETIMELREG); - last_rtc_update = xtime.tv_sec; - } -} +static void +vr4181_timer_setup(struct irqaction *irq) +{ + /* over-write the handler to be our own one */ + irq->handler = vr4181_timer_interrupt; -struct irqaction timer_action = { - timer_interrupt, SA_INTERRUPT, 0, - "timer", NULL, NULL -}; + /* sets up the frequency */ + *VR4181_RTCL1LREG = COUNTS_PER_JIFFY; + *VR4181_RTCL1HREG = 0; -#ifdef CONFIG_PM -static int pm_time_request(struct pm_dev *dev, pm_request_t rqst, void *data) -{ - unsigned long flags; + /* and ack any pending ints */ + *VR4181_RTCINTREG = 0x2; - switch (rqst) { - case PM_SUSPEND: - disable_irq(VR4181_IRQ_INT1); - break; - case PM_RESUME: - write_lock_irqsave(&xtime_lock, flags); - enable_irq(VR4181_IRQ_INT1); - xtime.tv_sec = get_rtc_time(VR4181_ETIMELREG); - xtime.tv_usec = 0; - // this shouldn't be necessary, but just to be safe... - *VR4181_RTCL1LREG = COUNTS_PER_JIFFY; - *VR4181_RTCL1HREG = 0; - write_unlock_irqrestore(&xtime_lock, flags); - break; - } - return 0; -} + /* setup irqaction */ + setup_irq(VR4181_IRQ_INT1, irq); -static int __init pm_time_init(void) -{ - pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, pm_time_request); - return 0; } -__initcall(pm_time_init); -#endif - -void __init time_init(void) +void +vr4181_init_time(void) { - // Set default time near beginning of Linux VR epoch. - xtime.tv_sec = get_rtc_time(VR4181_ETIMELREG); - xtime.tv_usec = 0; - - // Use RTCLong1 for the system timer. - // It has it's own cpu interrupt, is not T-Clock dependent, - // and has sufficient resolution. - - // Set the RTCLong1 counter (32.768kHz) to expire in 1 / HZ second - *VR4181_RTCL1LREG = COUNTS_PER_JIFFY; - *VR4181_RTCL1HREG = 0; + /* setup hookup functions */ + rtc_get_time = vr4181_rtc_get_time; + rtc_set_time = vr4181_rtc_set_time; - // and ack any pending ints - *VR4181_RTCINTREG = 0x2; + board_timer_setup = vr4181_timer_setup; - // Grab the IRQ for the cpu interrupt, not the ICU interrupt - // The RTCLong1 ICU interrupt is always left unmasked - // This can't use request_irq() because it's too early for kmalloc - setup_irq(VR4181_IRQ_INT1, &timer_action); + /* let us setup some reasonable start up time */ + vr4181_rtc_set_time(mktime(2001, 10, 1, 12, 0, 0)); } + |