Commit dc31ee3e authored by David S. Miller's avatar David S. Miller

[SPARC]: Update for tv_nsec in xtime.

parent b9e36474
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/jiffies.h>
#include <asm/ebus.h> #include <asm/ebus.h>
#include <asm/sbus.h> /* for sanity check... */ #include <asm/sbus.h> /* for sanity check... */
...@@ -792,48 +793,54 @@ static __inline__ unsigned long do_gettimeoffset(void) ...@@ -792,48 +793,54 @@ static __inline__ unsigned long do_gettimeoffset(void)
return offset + count; return offset + count;
} }
extern volatile unsigned long wall_jiffies; extern unsigned long wall_jiffies;
extern rwlock_t xtime_lock;
static void pci_do_gettimeofday(struct timeval *tv) static void pci_do_gettimeofday(struct timeval *tv)
{ {
unsigned long flags; unsigned long flags;
unsigned long usec, sec;
local_irq_save(flags); read_lock_irqsave(&xtime_lock, flags);
*tv = xtime; usec = do_gettimeoffset();
tv->tv_usec += do_gettimeoffset(); {
unsigned long lost = jiffies - wall_jiffies;
/* if (lost)
* xtime is atomically updated in timer_bh. The difference usec += lost * (1000000 / HZ);
* between jiffies and wall_jiffies is nonzero if the timer }
* bottom half hasnt executed yet. sec = xtime.tv_sec;
*/ usec += (xtime.tv_nsec / 1000);
if ((jiffies - wall_jiffies) != 0) read_unlock_irqrestore(&xtime_lock, flags);
tv->tv_usec += USECS_PER_JIFFY;
local_irq_restore(flags);
if (tv->tv_usec >= 1000000) { while (usec >= 1000000) {
tv->tv_usec -= 1000000; usec -= 1000000;
tv->tv_sec++; sec++;
} }
tv->tv_sec = sec;
tv->tv_usec = usec;
} }
static void pci_do_settimeofday(struct timeval *tv) static void pci_do_settimeofday(struct timeval *tv)
{ {
unsigned long flags; /*
* This is revolting. We need to set "xtime" correctly. However, the
local_irq_save(flags); * value in this location is the value at the most recent update of
* wall time. Discover what correction gettimeofday() would have
* made, and then undo it!
*/
tv->tv_usec -= do_gettimeoffset(); tv->tv_usec -= do_gettimeoffset();
if(tv->tv_usec < 0) { tv->tv_usec -= (jiffies - wall_jiffies) * (1000000 / HZ);
while (tv->tv_usec < 0) {
tv->tv_usec += 1000000; tv->tv_usec += 1000000;
tv->tv_sec--; tv->tv_sec--;
} }
xtime = *tv; xtime.tv_sec = tv->tv_sec;
xtime.tv_nsec = (tv->tv_usec * 1000);
time_adjust = 0; /* stop active adjtime() */ time_adjust = 0; /* stop active adjtime() */
time_status |= STA_UNSYNC; time_status |= STA_UNSYNC;
time_maxerror = NTP_PHASE_LIMIT; time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT;
local_irq_restore(flags);
} }
#if 0 #if 0
......
...@@ -43,6 +43,8 @@ ...@@ -43,6 +43,8 @@
extern rwlock_t xtime_lock; extern rwlock_t xtime_lock;
extern unsigned long wall_jiffies;
u64 jiffies_64; u64 jiffies_64;
enum sparc_clock_type sp_clock_typ; enum sparc_clock_type sp_clock_typ;
...@@ -114,6 +116,9 @@ __volatile__ unsigned int *master_l10_limit; ...@@ -114,6 +116,9 @@ __volatile__ unsigned int *master_l10_limit;
* timer_interrupt() needs to keep up the real-time clock, * timer_interrupt() needs to keep up the real-time clock,
* as well as call the "do_timer()" routine every clocktick * as well as call the "do_timer()" routine every clocktick
*/ */
#define TICK_SIZE (tick_nsec / 1000)
void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{ {
/* last time the cmos clock got updated */ /* last time the cmos clock got updated */
...@@ -142,8 +147,8 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) ...@@ -142,8 +147,8 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs)
/* Determine when to update the Mostek clock. */ /* Determine when to update the Mostek clock. */
if ((time_status & STA_UNSYNC) == 0 && if ((time_status & STA_UNSYNC) == 0 &&
xtime.tv_sec > last_rtc_update + 660 && xtime.tv_sec > last_rtc_update + 660 &&
xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 && (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 &&
xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) { (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) {
if (set_rtc_mmss(xtime.tv_sec) == 0) if (set_rtc_mmss(xtime.tv_sec) == 0)
last_rtc_update = xtime.tv_sec; last_rtc_update = xtime.tv_sec;
else else
...@@ -400,7 +405,7 @@ void __init sbus_time_init(void) ...@@ -400,7 +405,7 @@ void __init sbus_time_init(void)
mon = MSTK_REG_MONTH(mregs); mon = MSTK_REG_MONTH(mregs);
year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) ); year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) );
xtime.tv_sec = mktime(year, mon, day, hour, min, sec); xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
xtime.tv_usec = 0; xtime.tv_nsec = 0;
mregs->creg &= ~MSTK_CREG_READ; mregs->creg &= ~MSTK_CREG_READ;
spin_unlock_irq(&mostek_lock); spin_unlock_irq(&mostek_lock);
#ifdef CONFIG_SUN4 #ifdef CONFIG_SUN4
...@@ -431,7 +436,7 @@ void __init sbus_time_init(void) ...@@ -431,7 +436,7 @@ void __init sbus_time_init(void)
intersil_start(iregs); intersil_start(iregs);
xtime.tv_sec = mktime(year, mon, day, hour, min, sec); xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
xtime.tv_usec = 0; xtime.tv_nsec = 0;
printk("%u/%u/%u %u:%u:%u\n",day,mon,year,hour,min,sec); printk("%u/%u/%u %u:%u:%u\n",day,mon,year,hour,min,sec);
} }
#endif #endif
...@@ -467,48 +472,33 @@ extern __inline__ unsigned long do_gettimeoffset(void) ...@@ -467,48 +472,33 @@ extern __inline__ unsigned long do_gettimeoffset(void)
return offset + count; return offset + count;
} }
/* This need not obtain the xtime_lock as it is coded in /* Ok, my cute asm atomicity trick doesn't work anymore.
* an implicitly SMP safe way already. * There are just too many variables that need to be protected
* now (both members of xtime, wall_jiffies, et al.)
*/ */
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
/* Load doubles must be used on xtime so that what we get unsigned long flags;
* is guarenteed to be atomic, this is why we can run this unsigned long usec, sec;
* with interrupts on full blast. Don't touch this... -DaveM
*/ read_lock_irqsave(&xtime_lock, flags);
__asm__ __volatile__( usec = do_gettimeoffset();
"sethi %hi(master_l10_counter), %o1\n\t" {
"ld [%o1 + %lo(master_l10_counter)], %g3\n\t" unsigned long lost = jiffies - wall_jiffies;
"sethi %hi(xtime), %g2\n" if (lost)
"1:\n\t" usec += lost * (1000000 / HZ);
"ldd [%g2 + %lo(xtime)], %o4\n\t" }
"ld [%g3], %o1\n\t" sec = xtime.tv_sec;
"ldd [%g2 + %lo(xtime)], %o2\n\t" usec += (xtime.tv_nsec / 1000);
"xor %o4, %o2, %o2\n\t" read_unlock_irqrestore(&xtime_lock, flags);
"xor %o5, %o3, %o3\n\t"
"orcc %o2, %o3, %g0\n\t" while (usec >= 1000000) {
"bne 1b\n\t" usec -= 1000000;
" cmp %o1, 0\n\t" sec++;
"bge 1f\n\t" }
" srl %o1, 0xa, %o1\n\t"
"sethi %hi(tick), %o3\n\t" tv->tv_sec = sec;
"ld [%o3 + %lo(tick)], %o3\n\t" tv->tv_usec = usec;
"sethi %hi(0x1fffff), %o2\n\t"
"or %o2, %lo(0x1fffff), %o2\n\t"
"add %o5, %o3, %o5\n\t"
"and %o1, %o2, %o1\n"
"1:\n\t"
"add %o5, %o1, %o5\n\t"
"sethi %hi(1000000), %o2\n\t"
"or %o2, %lo(1000000), %o2\n\t"
"cmp %o5, %o2\n\t"
"bl,a 1f\n\t"
" st %o4, [%o0 + 0x0]\n\t"
"add %o4, 0x1, %o4\n\t"
"sub %o5, %o2, %o5\n\t"
"st %o4, [%o0 + 0x0]\n"
"1:\n\t"
"st %o5, [%o0 + 0x4]\n");
} }
void do_settimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv)
...@@ -520,12 +510,20 @@ void do_settimeofday(struct timeval *tv) ...@@ -520,12 +510,20 @@ void do_settimeofday(struct timeval *tv)
static void sbus_do_settimeofday(struct timeval *tv) static void sbus_do_settimeofday(struct timeval *tv)
{ {
/*
* This is revolting. We need to set "xtime" correctly. However, the
* value in this location is the value at the most recent update of
* wall time. Discover what correction gettimeofday() would have
* made, and then undo it!
*/
tv->tv_usec -= do_gettimeoffset(); tv->tv_usec -= do_gettimeoffset();
if(tv->tv_usec < 0) { tv->tv_usec -= (jiffies - wall_jiffies) * (1000000 / HZ);
while (tv->tv_usec < 0) {
tv->tv_usec += 1000000; tv->tv_usec += 1000000;
tv->tv_sec--; tv->tv_sec--;
} }
xtime = *tv; xtime.tv_sec = tv->tv_sec;
xtime.tv_nsec = (tv->tv_usec * 1000);
time_adjust = 0; /* stop active adjtime() */ time_adjust = 0; /* stop active adjtime() */
time_status |= STA_UNSYNC; time_status |= STA_UNSYNC;
time_maxerror = NTP_PHASE_LIMIT; time_maxerror = NTP_PHASE_LIMIT;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment