powerpc: Merge time.c and asm/time.h.
We now use the merged time.c for both 32-bit and 64-bit compilation with ARCH=powerpc, and for ARCH=ppc64, but not for ARCH=ppc32. This removes setup_default_decr (folds its function into time_init) and moves wakeup_decrementer into time.c. This also makes an asm-powerpc/rtc.h. Signed-off-by: Paul Mackerras <paulus@samba.org>
This commit is contained in:
Родитель
03f88e9f71
Коммит
f2783c1500
|
@ -29,7 +29,7 @@ extra-$(CONFIG_PPC64) += entry_64.o
|
||||||
extra-$(CONFIG_PPC_FPU) += fpu.o
|
extra-$(CONFIG_PPC_FPU) += fpu.o
|
||||||
extra-y += vmlinux.lds
|
extra-y += vmlinux.lds
|
||||||
|
|
||||||
obj-y += process.o init_task.o \
|
obj-y += process.o init_task.o time.o \
|
||||||
prom.o systbl.o traps.o
|
prom.o systbl.o traps.o
|
||||||
obj-$(CONFIG_PPC32) += entry_32.o idle_6xx.o setup_32.o misc_32.o
|
obj-$(CONFIG_PPC32) += entry_32.o idle_6xx.o setup_32.o misc_32.o
|
||||||
obj-$(CONFIG_PPC64) += setup_64.o misc_64.o
|
obj-$(CONFIG_PPC64) += setup_64.o misc_64.o
|
||||||
|
@ -44,7 +44,7 @@ endif
|
||||||
|
|
||||||
else
|
else
|
||||||
# stuff used from here for ARCH=ppc or ARCH=ppc64
|
# stuff used from here for ARCH=ppc or ARCH=ppc64
|
||||||
obj-$(CONFIG_PPC64) += traps.o process.o init_task.o
|
obj-$(CONFIG_PPC64) += traps.o process.o init_task.o time.o
|
||||||
|
|
||||||
fpux-$(CONFIG_PPC32) += fpu.o
|
fpux-$(CONFIG_PPC32) += fpu.o
|
||||||
extra-$(CONFIG_PPC_FPU) += $(fpux-y)
|
extra-$(CONFIG_PPC_FPU) += $(fpux-y)
|
||||||
|
|
|
@ -35,6 +35,33 @@ _GLOBAL(__delay)
|
||||||
1: bdnz 1b
|
1: bdnz 1b
|
||||||
blr
|
blr
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This returns the high 64 bits of the product of two 64-bit numbers.
|
||||||
|
*/
|
||||||
|
_GLOBAL(mulhdu)
|
||||||
|
cmpwi r6,0
|
||||||
|
cmpwi cr1,r3,0
|
||||||
|
mr r10,r4
|
||||||
|
mulhwu r4,r4,r5
|
||||||
|
beq 1f
|
||||||
|
mulhwu r0,r10,r6
|
||||||
|
mullw r7,r10,r5
|
||||||
|
addc r7,r0,r7
|
||||||
|
addze r4,r4
|
||||||
|
1: beqlr cr1 /* all done if high part of A is 0 */
|
||||||
|
mr r10,r3
|
||||||
|
mullw r9,r3,r5
|
||||||
|
mulhwu r3,r3,r5
|
||||||
|
beq 2f
|
||||||
|
mullw r0,r10,r6
|
||||||
|
mulhwu r8,r10,r6
|
||||||
|
addc r7,r0,r7
|
||||||
|
adde r4,r4,r8
|
||||||
|
addze r3,r3
|
||||||
|
2: addc r4,r4,r9
|
||||||
|
addze r3,r3
|
||||||
|
blr
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns (address we're running at) - (address we were linked at)
|
* Returns (address we're running at) - (address we were linked at)
|
||||||
* for use before the text and data are mapped to KERNELBASE.
|
* for use before the text and data are mapped to KERNELBASE.
|
||||||
|
|
|
@ -260,7 +260,6 @@ EXPORT_SYMBOL(__res);
|
||||||
#ifdef CONFIG_PPC32
|
#ifdef CONFIG_PPC32
|
||||||
EXPORT_SYMBOL(next_mmu_context);
|
EXPORT_SYMBOL(next_mmu_context);
|
||||||
EXPORT_SYMBOL(set_context);
|
EXPORT_SYMBOL(set_context);
|
||||||
EXPORT_SYMBOL(disarm_decr);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PPC_STD_MMU_32
|
#ifdef CONFIG_PPC_STD_MMU_32
|
||||||
|
|
|
@ -1083,15 +1083,6 @@ void ppc64_terminate_msg(unsigned int src, const char *msg)
|
||||||
printk("[terminate]%04x %s\n", src, msg);
|
printk("[terminate]%04x %s\n", src, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This should only be called on processor 0 during calibrate decr */
|
|
||||||
void __init setup_default_decr(void)
|
|
||||||
{
|
|
||||||
struct paca_struct *lpaca = get_paca();
|
|
||||||
|
|
||||||
lpaca->default_decr = tb_ticks_per_jiffy;
|
|
||||||
lpaca->next_jiffy_update_tb = get_tb() + tb_ticks_per_jiffy;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef CONFIG_PPC_ISERIES
|
#ifndef CONFIG_PPC_ISERIES
|
||||||
/*
|
/*
|
||||||
* This function can be used by platforms to "find" legacy serial ports.
|
* This function can be used by platforms to "find" legacy serial ports.
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
/*
|
/*
|
||||||
*
|
|
||||||
* Common time routines among all ppc machines.
|
* Common time routines among all ppc machines.
|
||||||
*
|
*
|
||||||
* Written by Cort Dougan (cort@cs.nmt.edu) to merge
|
* Written by Cort Dougan (cort@cs.nmt.edu) to merge
|
||||||
|
@ -44,29 +43,32 @@
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/timex.h>
|
#include <linux/timex.h>
|
||||||
#include <linux/kernel_stat.h>
|
#include <linux/kernel_stat.h>
|
||||||
#include <linux/mc146818rtc.h>
|
|
||||||
#include <linux/time.h>
|
#include <linux/time.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/profile.h>
|
#include <linux/profile.h>
|
||||||
#include <linux/cpu.h>
|
#include <linux/cpu.h>
|
||||||
#include <linux/security.h>
|
#include <linux/security.h>
|
||||||
|
#include <linux/percpu.h>
|
||||||
|
#include <linux/rtc.h>
|
||||||
|
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <asm/processor.h>
|
#include <asm/processor.h>
|
||||||
#include <asm/nvram.h>
|
#include <asm/nvram.h>
|
||||||
#include <asm/cache.h>
|
#include <asm/cache.h>
|
||||||
#include <asm/machdep.h>
|
#include <asm/machdep.h>
|
||||||
|
#include <asm/uaccess.h>
|
||||||
|
#include <asm/time.h>
|
||||||
|
#include <asm/prom.h>
|
||||||
|
#include <asm/irq.h>
|
||||||
|
#include <asm/div64.h>
|
||||||
|
#ifdef CONFIG_PPC64
|
||||||
|
#include <asm/systemcfg.h>
|
||||||
|
#include <asm/firmware.h>
|
||||||
|
#endif
|
||||||
#ifdef CONFIG_PPC_ISERIES
|
#ifdef CONFIG_PPC_ISERIES
|
||||||
#include <asm/iSeries/ItLpQueue.h>
|
#include <asm/iSeries/ItLpQueue.h>
|
||||||
#include <asm/iSeries/HvCallXm.h>
|
#include <asm/iSeries/HvCallXm.h>
|
||||||
#endif
|
#endif
|
||||||
#include <asm/uaccess.h>
|
|
||||||
#include <asm/time.h>
|
|
||||||
#include <asm/ppcdebug.h>
|
|
||||||
#include <asm/prom.h>
|
|
||||||
#include <asm/sections.h>
|
|
||||||
#include <asm/systemcfg.h>
|
|
||||||
#include <asm/firmware.h>
|
|
||||||
|
|
||||||
u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES;
|
u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES;
|
||||||
|
|
||||||
|
@ -81,27 +83,37 @@ unsigned long iSeries_recal_tb = 0;
|
||||||
static unsigned long first_settimeofday = 1;
|
static unsigned long first_settimeofday = 1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* The decrementer counts down by 128 every 128ns on a 601. */
|
||||||
|
#define DECREMENTER_COUNT_601 (1000000000 / HZ)
|
||||||
|
|
||||||
#define XSEC_PER_SEC (1024*1024)
|
#define XSEC_PER_SEC (1024*1024)
|
||||||
|
|
||||||
|
#ifdef CONFIG_PPC64
|
||||||
|
#define SCALE_XSEC(xsec, max) (((xsec) * max) / XSEC_PER_SEC)
|
||||||
|
#else
|
||||||
|
/* compute ((xsec << 12) * max) >> 32 */
|
||||||
|
#define SCALE_XSEC(xsec, max) mulhwu((xsec) << 12, max)
|
||||||
|
#endif
|
||||||
|
|
||||||
unsigned long tb_ticks_per_jiffy;
|
unsigned long tb_ticks_per_jiffy;
|
||||||
unsigned long tb_ticks_per_usec = 100; /* sane default */
|
unsigned long tb_ticks_per_usec = 100; /* sane default */
|
||||||
EXPORT_SYMBOL(tb_ticks_per_usec);
|
EXPORT_SYMBOL(tb_ticks_per_usec);
|
||||||
unsigned long tb_ticks_per_sec;
|
unsigned long tb_ticks_per_sec;
|
||||||
unsigned long tb_to_xs;
|
u64 tb_to_xs;
|
||||||
unsigned tb_to_us;
|
unsigned tb_to_us;
|
||||||
unsigned long processor_freq;
|
unsigned long processor_freq;
|
||||||
DEFINE_SPINLOCK(rtc_lock);
|
DEFINE_SPINLOCK(rtc_lock);
|
||||||
EXPORT_SYMBOL_GPL(rtc_lock);
|
EXPORT_SYMBOL_GPL(rtc_lock);
|
||||||
|
|
||||||
unsigned long tb_to_ns_scale;
|
u64 tb_to_ns_scale;
|
||||||
unsigned long tb_to_ns_shift;
|
unsigned tb_to_ns_shift;
|
||||||
|
|
||||||
struct gettimeofday_struct do_gtod;
|
struct gettimeofday_struct do_gtod;
|
||||||
|
|
||||||
extern unsigned long wall_jiffies;
|
extern unsigned long wall_jiffies;
|
||||||
extern int smp_tb_synchronized;
|
|
||||||
|
|
||||||
extern struct timezone sys_tz;
|
extern struct timezone sys_tz;
|
||||||
|
static long timezone_offset;
|
||||||
|
|
||||||
void ppc_adjtimex(void);
|
void ppc_adjtimex(void);
|
||||||
|
|
||||||
|
@ -110,6 +122,10 @@ static unsigned adjusting_time = 0;
|
||||||
unsigned long ppc_proc_freq;
|
unsigned long ppc_proc_freq;
|
||||||
unsigned long ppc_tb_freq;
|
unsigned long ppc_tb_freq;
|
||||||
|
|
||||||
|
#ifdef CONFIG_PPC32 /* XXX for now */
|
||||||
|
#define boot_cpuid 0
|
||||||
|
#endif
|
||||||
|
|
||||||
static __inline__ void timer_check_rtc(void)
|
static __inline__ void timer_check_rtc(void)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -129,30 +145,30 @@ static __inline__ void timer_check_rtc(void)
|
||||||
* seconds like on Intel to avoid problems with non UTC clocks.
|
* seconds like on Intel to avoid problems with non UTC clocks.
|
||||||
*/
|
*/
|
||||||
if (ntp_synced() &&
|
if (ntp_synced() &&
|
||||||
xtime.tv_sec - last_rtc_update >= 659 &&
|
xtime.tv_sec - last_rtc_update >= 659 &&
|
||||||
abs((xtime.tv_nsec/1000) - (1000000-1000000/HZ)) < 500000/HZ &&
|
abs((xtime.tv_nsec/1000) - (1000000-1000000/HZ)) < 500000/HZ &&
|
||||||
jiffies - wall_jiffies == 1) {
|
jiffies - wall_jiffies == 1) {
|
||||||
struct rtc_time tm;
|
struct rtc_time tm;
|
||||||
to_tm(xtime.tv_sec+1, &tm);
|
to_tm(xtime.tv_sec + 1 + timezone_offset, &tm);
|
||||||
tm.tm_year -= 1900;
|
tm.tm_year -= 1900;
|
||||||
tm.tm_mon -= 1;
|
tm.tm_mon -= 1;
|
||||||
if (ppc_md.set_rtc_time(&tm) == 0)
|
if (ppc_md.set_rtc_time(&tm) == 0)
|
||||||
last_rtc_update = xtime.tv_sec+1;
|
last_rtc_update = xtime.tv_sec + 1;
|
||||||
else
|
else
|
||||||
/* Try again one minute later */
|
/* Try again one minute later */
|
||||||
last_rtc_update += 60;
|
last_rtc_update += 60;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This version of gettimeofday has microsecond resolution.
|
* This version of gettimeofday has microsecond resolution.
|
||||||
*/
|
*/
|
||||||
static inline void __do_gettimeofday(struct timeval *tv, unsigned long tb_val)
|
static inline void __do_gettimeofday(struct timeval *tv, u64 tb_val)
|
||||||
{
|
{
|
||||||
unsigned long sec, usec, tb_ticks;
|
unsigned long sec, usec;
|
||||||
unsigned long xsec, tb_xsec;
|
u64 tb_ticks, xsec;
|
||||||
struct gettimeofday_vars * temp_varp;
|
struct gettimeofday_vars *temp_varp;
|
||||||
unsigned long temp_tb_to_xs, temp_stamp_xsec;
|
u64 temp_tb_to_xs, temp_stamp_xsec;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These calculations are faster (gets rid of divides)
|
* These calculations are faster (gets rid of divides)
|
||||||
|
@ -164,11 +180,10 @@ static inline void __do_gettimeofday(struct timeval *tv, unsigned long tb_val)
|
||||||
tb_ticks = tb_val - temp_varp->tb_orig_stamp;
|
tb_ticks = tb_val - temp_varp->tb_orig_stamp;
|
||||||
temp_tb_to_xs = temp_varp->tb_to_xs;
|
temp_tb_to_xs = temp_varp->tb_to_xs;
|
||||||
temp_stamp_xsec = temp_varp->stamp_xsec;
|
temp_stamp_xsec = temp_varp->stamp_xsec;
|
||||||
tb_xsec = mulhdu( tb_ticks, temp_tb_to_xs );
|
xsec = temp_stamp_xsec + mulhdu(tb_ticks, temp_tb_to_xs);
|
||||||
xsec = temp_stamp_xsec + tb_xsec;
|
|
||||||
sec = xsec / XSEC_PER_SEC;
|
sec = xsec / XSEC_PER_SEC;
|
||||||
xsec -= sec * XSEC_PER_SEC;
|
usec = (unsigned long)xsec & (XSEC_PER_SEC - 1);
|
||||||
usec = (xsec * USEC_PER_SEC)/XSEC_PER_SEC;
|
usec = SCALE_XSEC(usec, 1000000);
|
||||||
|
|
||||||
tv->tv_sec = sec;
|
tv->tv_sec = sec;
|
||||||
tv->tv_usec = usec;
|
tv->tv_usec = usec;
|
||||||
|
@ -185,6 +200,8 @@ EXPORT_SYMBOL(do_gettimeofday);
|
||||||
|
|
||||||
static inline void timer_sync_xtime(unsigned long cur_tb)
|
static inline void timer_sync_xtime(unsigned long cur_tb)
|
||||||
{
|
{
|
||||||
|
#ifdef CONFIG_PPC64
|
||||||
|
/* why do we do this? */
|
||||||
struct timeval my_tv;
|
struct timeval my_tv;
|
||||||
|
|
||||||
__do_gettimeofday(&my_tv, cur_tb);
|
__do_gettimeofday(&my_tv, cur_tb);
|
||||||
|
@ -193,6 +210,51 @@ static inline void timer_sync_xtime(unsigned long cur_tb)
|
||||||
xtime.tv_sec = my_tv.tv_sec;
|
xtime.tv_sec = my_tv.tv_sec;
|
||||||
xtime.tv_nsec = my_tv.tv_usec * 1000;
|
xtime.tv_nsec = my_tv.tv_usec * 1000;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There are two copies of tb_to_xs and stamp_xsec so that no
|
||||||
|
* lock is needed to access and use these values in
|
||||||
|
* do_gettimeofday. We alternate the copies and as long as a
|
||||||
|
* reasonable time elapses between changes, there will never
|
||||||
|
* be inconsistent values. ntpd has a minimum of one minute
|
||||||
|
* between updates.
|
||||||
|
*/
|
||||||
|
static inline void update_gtod(u64 new_tb_stamp, u64 new_stamp_xsec,
|
||||||
|
unsigned int new_tb_to_xs)
|
||||||
|
{
|
||||||
|
unsigned temp_idx;
|
||||||
|
struct gettimeofday_vars *temp_varp;
|
||||||
|
|
||||||
|
temp_idx = (do_gtod.var_idx == 0);
|
||||||
|
temp_varp = &do_gtod.vars[temp_idx];
|
||||||
|
|
||||||
|
temp_varp->tb_to_xs = new_tb_to_xs;
|
||||||
|
temp_varp->tb_orig_stamp = new_tb_stamp;
|
||||||
|
temp_varp->stamp_xsec = new_stamp_xsec;
|
||||||
|
smp_mb();
|
||||||
|
do_gtod.varp = temp_varp;
|
||||||
|
do_gtod.var_idx = temp_idx;
|
||||||
|
|
||||||
|
#ifdef CONFIG_PPC64
|
||||||
|
/*
|
||||||
|
* tb_update_count is used to allow the userspace gettimeofday code
|
||||||
|
* to assure itself that it sees a consistent view of the tb_to_xs and
|
||||||
|
* stamp_xsec variables. It reads the tb_update_count, then reads
|
||||||
|
* tb_to_xs and stamp_xsec and then reads tb_update_count again. If
|
||||||
|
* the two values of tb_update_count match and are even then the
|
||||||
|
* tb_to_xs and stamp_xsec values are consistent. If not, then it
|
||||||
|
* loops back and reads them again until this criteria is met.
|
||||||
|
*/
|
||||||
|
++(systemcfg->tb_update_count);
|
||||||
|
smp_wmb();
|
||||||
|
systemcfg->tb_orig_stamp = new_tb_stamp;
|
||||||
|
systemcfg->stamp_xsec = new_stamp_xsec;
|
||||||
|
systemcfg->tb_to_xs = new_tb_to_xs;
|
||||||
|
smp_wmb();
|
||||||
|
++(systemcfg->tb_update_count);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -205,35 +267,17 @@ static inline void timer_sync_xtime(unsigned long cur_tb)
|
||||||
* with a too big difference, then the vdso will fallback to calling
|
* with a too big difference, then the vdso will fallback to calling
|
||||||
* the syscall
|
* the syscall
|
||||||
*/
|
*/
|
||||||
static __inline__ void timer_recalc_offset(unsigned long cur_tb)
|
static __inline__ void timer_recalc_offset(u64 cur_tb)
|
||||||
{
|
{
|
||||||
struct gettimeofday_vars * temp_varp;
|
unsigned long offset;
|
||||||
unsigned temp_idx;
|
u64 new_stamp_xsec;
|
||||||
unsigned long offset, new_stamp_xsec, new_tb_orig_stamp;
|
|
||||||
|
|
||||||
if (((cur_tb - do_gtod.varp->tb_orig_stamp) & 0x80000000u) == 0)
|
offset = cur_tb - do_gtod.varp->tb_orig_stamp;
|
||||||
|
if ((offset & 0x80000000u) == 0)
|
||||||
return;
|
return;
|
||||||
|
new_stamp_xsec = do_gtod.varp->stamp_xsec
|
||||||
temp_idx = (do_gtod.var_idx == 0);
|
+ mulhdu(offset, do_gtod.varp->tb_to_xs);
|
||||||
temp_varp = &do_gtod.vars[temp_idx];
|
update_gtod(cur_tb, new_stamp_xsec, do_gtod.varp->tb_to_xs);
|
||||||
|
|
||||||
new_tb_orig_stamp = cur_tb;
|
|
||||||
offset = new_tb_orig_stamp - do_gtod.varp->tb_orig_stamp;
|
|
||||||
new_stamp_xsec = do_gtod.varp->stamp_xsec + mulhdu(offset, do_gtod.varp->tb_to_xs);
|
|
||||||
|
|
||||||
temp_varp->tb_to_xs = do_gtod.varp->tb_to_xs;
|
|
||||||
temp_varp->tb_orig_stamp = new_tb_orig_stamp;
|
|
||||||
temp_varp->stamp_xsec = new_stamp_xsec;
|
|
||||||
smp_mb();
|
|
||||||
do_gtod.varp = temp_varp;
|
|
||||||
do_gtod.var_idx = temp_idx;
|
|
||||||
|
|
||||||
++(systemcfg->tb_update_count);
|
|
||||||
smp_wmb();
|
|
||||||
systemcfg->tb_orig_stamp = new_tb_orig_stamp;
|
|
||||||
systemcfg->stamp_xsec = new_stamp_xsec;
|
|
||||||
smp_wmb();
|
|
||||||
++(systemcfg->tb_update_count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_SMP
|
#ifdef CONFIG_SMP
|
||||||
|
@ -313,7 +357,14 @@ static void iSeries_tb_recal(void)
|
||||||
* call will not be needed)
|
* call will not be needed)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
unsigned long tb_last_stamp __cacheline_aligned_in_smp;
|
u64 tb_last_stamp __cacheline_aligned_in_smp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that on ppc32 this only stores the bottom 32 bits of
|
||||||
|
* the timebase value, but that's enough to tell when a jiffy
|
||||||
|
* has passed.
|
||||||
|
*/
|
||||||
|
DEFINE_PER_CPU(unsigned long, last_jiffy);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* timer_interrupt - gets called when the decrementer overflows,
|
* timer_interrupt - gets called when the decrementer overflows,
|
||||||
|
@ -322,17 +373,30 @@ unsigned long tb_last_stamp __cacheline_aligned_in_smp;
|
||||||
void timer_interrupt(struct pt_regs * regs)
|
void timer_interrupt(struct pt_regs * regs)
|
||||||
{
|
{
|
||||||
int next_dec;
|
int next_dec;
|
||||||
unsigned long cur_tb;
|
int cpu = smp_processor_id();
|
||||||
struct paca_struct *lpaca = get_paca();
|
unsigned long ticks;
|
||||||
unsigned long cpu = smp_processor_id();
|
|
||||||
|
#ifdef CONFIG_PPC32
|
||||||
|
if (atomic_read(&ppc_n_lost_interrupts) != 0)
|
||||||
|
do_IRQ(regs);
|
||||||
|
#endif
|
||||||
|
|
||||||
irq_enter();
|
irq_enter();
|
||||||
|
|
||||||
profile_tick(CPU_PROFILING, regs);
|
profile_tick(CPU_PROFILING, regs);
|
||||||
|
|
||||||
lpaca->lppaca.int_dword.fields.decr_int = 0;
|
#ifdef CONFIG_PPC_ISERIES
|
||||||
|
get_paca()->lppaca.int_dword.fields.decr_int = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while ((ticks = tb_ticks_since(per_cpu(last_jiffy, cpu)))
|
||||||
|
>= tb_ticks_per_jiffy) {
|
||||||
|
/* Update last_jiffy */
|
||||||
|
per_cpu(last_jiffy, cpu) += tb_ticks_per_jiffy;
|
||||||
|
/* Handle RTCL overflow on 601 */
|
||||||
|
if (__USE_RTC() && per_cpu(last_jiffy, cpu) >= 1000000000)
|
||||||
|
per_cpu(last_jiffy, cpu) -= 1000000000;
|
||||||
|
|
||||||
while (lpaca->next_jiffy_update_tb <= (cur_tb = get_tb())) {
|
|
||||||
/*
|
/*
|
||||||
* We cannot disable the decrementer, so in the period
|
* We cannot disable the decrementer, so in the period
|
||||||
* between this cpu's being marked offline in cpu_online_map
|
* between this cpu's being marked offline in cpu_online_map
|
||||||
|
@ -342,27 +406,26 @@ void timer_interrupt(struct pt_regs * regs)
|
||||||
*/
|
*/
|
||||||
if (!cpu_is_offline(cpu))
|
if (!cpu_is_offline(cpu))
|
||||||
update_process_times(user_mode(regs));
|
update_process_times(user_mode(regs));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* No need to check whether cpu is offline here; boot_cpuid
|
* No need to check whether cpu is offline here; boot_cpuid
|
||||||
* should have been fixed up by now.
|
* should have been fixed up by now.
|
||||||
*/
|
*/
|
||||||
if (cpu == boot_cpuid) {
|
if (cpu != boot_cpuid)
|
||||||
write_seqlock(&xtime_lock);
|
continue;
|
||||||
tb_last_stamp = lpaca->next_jiffy_update_tb;
|
|
||||||
timer_recalc_offset(lpaca->next_jiffy_update_tb);
|
write_seqlock(&xtime_lock);
|
||||||
do_timer(regs);
|
tb_last_stamp += tb_ticks_per_jiffy;
|
||||||
timer_sync_xtime(lpaca->next_jiffy_update_tb);
|
timer_recalc_offset(tb_last_stamp);
|
||||||
timer_check_rtc();
|
do_timer(regs);
|
||||||
write_sequnlock(&xtime_lock);
|
timer_sync_xtime(tb_last_stamp);
|
||||||
if ( adjusting_time && (time_adjust == 0) )
|
timer_check_rtc();
|
||||||
ppc_adjtimex();
|
write_sequnlock(&xtime_lock);
|
||||||
}
|
if (adjusting_time && (time_adjust == 0))
|
||||||
lpaca->next_jiffy_update_tb += tb_ticks_per_jiffy;
|
ppc_adjtimex();
|
||||||
}
|
}
|
||||||
|
|
||||||
next_dec = lpaca->next_jiffy_update_tb - cur_tb;
|
next_dec = tb_ticks_per_jiffy - ticks;
|
||||||
if (next_dec > lpaca->default_decr)
|
|
||||||
next_dec = lpaca->default_decr;
|
|
||||||
set_dec(next_dec);
|
set_dec(next_dec);
|
||||||
|
|
||||||
#ifdef CONFIG_PPC_ISERIES
|
#ifdef CONFIG_PPC_ISERIES
|
||||||
|
@ -370,15 +433,47 @@ void timer_interrupt(struct pt_regs * regs)
|
||||||
process_hvlpevents(regs);
|
process_hvlpevents(regs);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_PPC64
|
||||||
/* collect purr register values often, for accurate calculations */
|
/* collect purr register values often, for accurate calculations */
|
||||||
if (firmware_has_feature(FW_FEATURE_SPLPAR)) {
|
if (firmware_has_feature(FW_FEATURE_SPLPAR)) {
|
||||||
struct cpu_usage *cu = &__get_cpu_var(cpu_usage_array);
|
struct cpu_usage *cu = &__get_cpu_var(cpu_usage_array);
|
||||||
cu->current_tb = mfspr(SPRN_PURR);
|
cu->current_tb = mfspr(SPRN_PURR);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
irq_exit();
|
irq_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wakeup_decrementer(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
set_dec(tb_ticks_per_jiffy);
|
||||||
|
/*
|
||||||
|
* We don't expect this to be called on a machine with a 601,
|
||||||
|
* so using get_tbl is fine.
|
||||||
|
*/
|
||||||
|
tb_last_stamp = get_tb();
|
||||||
|
for_each_cpu(i)
|
||||||
|
per_cpu(last_jiffy, i) = tb_last_stamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_SMPxxx
|
||||||
|
void __init smp_space_timers(unsigned int max_cpus)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned long offset = tb_ticks_per_jiffy / max_cpus;
|
||||||
|
unsigned long previous_tb = per_cpu(last_jiffy, boot_cpuid);
|
||||||
|
|
||||||
|
for_each_cpu(i) {
|
||||||
|
if (i != boot_cpuid) {
|
||||||
|
previous_tb += offset;
|
||||||
|
per_cpu(last_jiffy, i) = previous_tb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Scheduler clock - returns current time in nanosec units.
|
* Scheduler clock - returns current time in nanosec units.
|
||||||
*
|
*
|
||||||
|
@ -396,23 +491,24 @@ int do_settimeofday(struct timespec *tv)
|
||||||
time_t wtm_sec, new_sec = tv->tv_sec;
|
time_t wtm_sec, new_sec = tv->tv_sec;
|
||||||
long wtm_nsec, new_nsec = tv->tv_nsec;
|
long wtm_nsec, new_nsec = tv->tv_nsec;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned long delta_xsec;
|
|
||||||
long int tb_delta;
|
long int tb_delta;
|
||||||
unsigned long new_xsec;
|
u64 new_xsec;
|
||||||
|
|
||||||
if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
|
if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
write_seqlock_irqsave(&xtime_lock, flags);
|
write_seqlock_irqsave(&xtime_lock, flags);
|
||||||
/* Updating the RTC is not the job of this code. If the time is
|
|
||||||
* stepped under NTP, the RTC will be update after STA_UNSYNC
|
/*
|
||||||
* is cleared. Tool like clock/hwclock either copy the RTC
|
* Updating the RTC is not the job of this code. If the time is
|
||||||
|
* stepped under NTP, the RTC will be updated after STA_UNSYNC
|
||||||
|
* is cleared. Tools like clock/hwclock either copy the RTC
|
||||||
* to the system time, in which case there is no point in writing
|
* to the system time, in which case there is no point in writing
|
||||||
* to the RTC again, or write to the RTC but then they don't call
|
* to the RTC again, or write to the RTC but then they don't call
|
||||||
* settimeofday to perform this operation.
|
* settimeofday to perform this operation.
|
||||||
*/
|
*/
|
||||||
#ifdef CONFIG_PPC_ISERIES
|
#ifdef CONFIG_PPC_ISERIES
|
||||||
if ( first_settimeofday ) {
|
if (first_settimeofday) {
|
||||||
iSeries_tb_recal();
|
iSeries_tb_recal();
|
||||||
first_settimeofday = 0;
|
first_settimeofday = 0;
|
||||||
}
|
}
|
||||||
|
@ -420,7 +516,7 @@ int do_settimeofday(struct timespec *tv)
|
||||||
tb_delta = tb_ticks_since(tb_last_stamp);
|
tb_delta = tb_ticks_since(tb_last_stamp);
|
||||||
tb_delta += (jiffies - wall_jiffies) * tb_ticks_per_jiffy;
|
tb_delta += (jiffies - wall_jiffies) * tb_ticks_per_jiffy;
|
||||||
|
|
||||||
new_nsec -= tb_delta / tb_ticks_per_usec / 1000;
|
new_nsec -= 1000 * mulhwu(tb_to_us, tb_delta);
|
||||||
|
|
||||||
wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - new_sec);
|
wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - new_sec);
|
||||||
wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - new_nsec);
|
wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - new_nsec);
|
||||||
|
@ -435,28 +531,15 @@ int do_settimeofday(struct timespec *tv)
|
||||||
|
|
||||||
ntp_clear();
|
ntp_clear();
|
||||||
|
|
||||||
delta_xsec = mulhdu( (tb_last_stamp-do_gtod.varp->tb_orig_stamp),
|
new_xsec = (u64)new_nsec * XSEC_PER_SEC;
|
||||||
do_gtod.varp->tb_to_xs );
|
do_div(new_xsec, NSEC_PER_SEC);
|
||||||
|
new_xsec += (u64)new_sec * XSEC_PER_SEC;
|
||||||
new_xsec = (new_nsec * XSEC_PER_SEC) / NSEC_PER_SEC;
|
update_gtod(tb_last_stamp, new_xsec, do_gtod.varp->tb_to_xs);
|
||||||
new_xsec += new_sec * XSEC_PER_SEC;
|
|
||||||
if ( new_xsec > delta_xsec ) {
|
|
||||||
do_gtod.varp->stamp_xsec = new_xsec - delta_xsec;
|
|
||||||
systemcfg->stamp_xsec = new_xsec - delta_xsec;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* This is only for the case where the user is setting the time
|
|
||||||
* way back to a time such that the boot time would have been
|
|
||||||
* before 1970 ... eg. we booted ten days ago, and we are setting
|
|
||||||
* the time to Jan 5, 1970 */
|
|
||||||
do_gtod.varp->stamp_xsec = new_xsec;
|
|
||||||
do_gtod.varp->tb_orig_stamp = tb_last_stamp;
|
|
||||||
systemcfg->stamp_xsec = new_xsec;
|
|
||||||
systemcfg->tb_orig_stamp = tb_last_stamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_PPC64
|
||||||
systemcfg->tz_minuteswest = sys_tz.tz_minuteswest;
|
systemcfg->tz_minuteswest = sys_tz.tz_minuteswest;
|
||||||
systemcfg->tz_dsttime = sys_tz.tz_dsttime;
|
systemcfg->tz_dsttime = sys_tz.tz_dsttime;
|
||||||
|
#endif
|
||||||
|
|
||||||
write_sequnlock_irqrestore(&xtime_lock, flags);
|
write_sequnlock_irqrestore(&xtime_lock, flags);
|
||||||
clock_was_set();
|
clock_was_set();
|
||||||
|
@ -520,21 +603,40 @@ void __init generic_calibrate_decr(void)
|
||||||
tb_to_us = mulhwu_scale_factor(ppc_tb_freq, 1000000);
|
tb_to_us = mulhwu_scale_factor(ppc_tb_freq, 1000000);
|
||||||
div128_by_32(1024*1024, 0, tb_ticks_per_sec, &divres);
|
div128_by_32(1024*1024, 0, tb_ticks_per_sec, &divres);
|
||||||
tb_to_xs = divres.result_low;
|
tb_to_xs = divres.result_low;
|
||||||
|
|
||||||
setup_default_decr();
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
unsigned long get_boot_time(void)
|
||||||
|
{
|
||||||
|
struct rtc_time tm;
|
||||||
|
|
||||||
|
if (ppc_md.get_boot_time)
|
||||||
|
return ppc_md.get_boot_time();
|
||||||
|
if (!ppc_md.get_rtc_time)
|
||||||
|
return 0;
|
||||||
|
ppc_md.get_rtc_time(&tm);
|
||||||
|
return mktime(tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
|
||||||
|
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function is only called on the boot processor */
|
||||||
void __init time_init(void)
|
void __init time_init(void)
|
||||||
{
|
{
|
||||||
/* This function is only called on the boot processor */
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct rtc_time tm;
|
unsigned long tm = 0;
|
||||||
struct div_result res;
|
struct div_result res;
|
||||||
unsigned long scale, shift;
|
u64 scale;
|
||||||
|
unsigned shift;
|
||||||
|
|
||||||
|
if (ppc_md.time_init != NULL)
|
||||||
|
timezone_offset = ppc_md.time_init();
|
||||||
|
|
||||||
ppc_md.calibrate_decr();
|
ppc_md.calibrate_decr();
|
||||||
|
|
||||||
|
#ifdef CONFIG_PPC64
|
||||||
|
get_paca()->default_decr = tb_ticks_per_jiffy;
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compute scale factor for sched_clock.
|
* Compute scale factor for sched_clock.
|
||||||
* The calibrate_decr() function has set tb_ticks_per_sec,
|
* The calibrate_decr() function has set tb_ticks_per_sec,
|
||||||
|
@ -557,29 +659,37 @@ void __init time_init(void)
|
||||||
#ifdef CONFIG_PPC_ISERIES
|
#ifdef CONFIG_PPC_ISERIES
|
||||||
if (!piranha_simulator)
|
if (!piranha_simulator)
|
||||||
#endif
|
#endif
|
||||||
ppc_md.get_boot_time(&tm);
|
tm = get_boot_time();
|
||||||
|
|
||||||
write_seqlock_irqsave(&xtime_lock, flags);
|
write_seqlock_irqsave(&xtime_lock, flags);
|
||||||
xtime.tv_sec = mktime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
xtime.tv_sec = tm;
|
||||||
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
xtime.tv_nsec = 0;
|
||||||
tb_last_stamp = get_tb();
|
tb_last_stamp = get_tb();
|
||||||
do_gtod.varp = &do_gtod.vars[0];
|
do_gtod.varp = &do_gtod.vars[0];
|
||||||
do_gtod.var_idx = 0;
|
do_gtod.var_idx = 0;
|
||||||
do_gtod.varp->tb_orig_stamp = tb_last_stamp;
|
do_gtod.varp->tb_orig_stamp = tb_last_stamp;
|
||||||
get_paca()->next_jiffy_update_tb = tb_last_stamp + tb_ticks_per_jiffy;
|
__get_cpu_var(last_jiffy) = tb_last_stamp;
|
||||||
do_gtod.varp->stamp_xsec = xtime.tv_sec * XSEC_PER_SEC;
|
do_gtod.varp->stamp_xsec = (u64) xtime.tv_sec * XSEC_PER_SEC;
|
||||||
do_gtod.tb_ticks_per_sec = tb_ticks_per_sec;
|
do_gtod.tb_ticks_per_sec = tb_ticks_per_sec;
|
||||||
do_gtod.varp->tb_to_xs = tb_to_xs;
|
do_gtod.varp->tb_to_xs = tb_to_xs;
|
||||||
do_gtod.tb_to_us = tb_to_us;
|
do_gtod.tb_to_us = tb_to_us;
|
||||||
|
#ifdef CONFIG_PPC64
|
||||||
systemcfg->tb_orig_stamp = tb_last_stamp;
|
systemcfg->tb_orig_stamp = tb_last_stamp;
|
||||||
systemcfg->tb_update_count = 0;
|
systemcfg->tb_update_count = 0;
|
||||||
systemcfg->tb_ticks_per_sec = tb_ticks_per_sec;
|
systemcfg->tb_ticks_per_sec = tb_ticks_per_sec;
|
||||||
systemcfg->stamp_xsec = xtime.tv_sec * XSEC_PER_SEC;
|
systemcfg->stamp_xsec = xtime.tv_sec * XSEC_PER_SEC;
|
||||||
systemcfg->tb_to_xs = tb_to_xs;
|
systemcfg->tb_to_xs = tb_to_xs;
|
||||||
|
#endif
|
||||||
|
|
||||||
time_freq = 0;
|
time_freq = 0;
|
||||||
|
|
||||||
xtime.tv_nsec = 0;
|
/* If platform provided a timezone (pmac), we correct the time */
|
||||||
|
if (timezone_offset) {
|
||||||
|
sys_tz.tz_minuteswest = -timezone_offset / 60;
|
||||||
|
sys_tz.tz_dsttime = 0;
|
||||||
|
xtime.tv_sec -= timezone_offset;
|
||||||
|
}
|
||||||
|
|
||||||
last_rtc_update = xtime.tv_sec;
|
last_rtc_update = xtime.tv_sec;
|
||||||
set_normalized_timespec(&wall_to_monotonic,
|
set_normalized_timespec(&wall_to_monotonic,
|
||||||
-xtime.tv_sec, -xtime.tv_nsec);
|
-xtime.tv_sec, -xtime.tv_nsec);
|
||||||
|
@ -602,25 +712,28 @@ void __init time_init(void)
|
||||||
|
|
||||||
void ppc_adjtimex(void)
|
void ppc_adjtimex(void)
|
||||||
{
|
{
|
||||||
unsigned long den, new_tb_ticks_per_sec, tb_ticks, old_xsec, new_tb_to_xs, new_xsec, new_stamp_xsec;
|
#ifdef CONFIG_PPC64
|
||||||
|
unsigned long den, new_tb_ticks_per_sec, tb_ticks, old_xsec,
|
||||||
|
new_tb_to_xs, new_xsec, new_stamp_xsec;
|
||||||
unsigned long tb_ticks_per_sec_delta;
|
unsigned long tb_ticks_per_sec_delta;
|
||||||
long delta_freq, ltemp;
|
long delta_freq, ltemp;
|
||||||
struct div_result divres;
|
struct div_result divres;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct gettimeofday_vars * temp_varp;
|
|
||||||
unsigned temp_idx;
|
|
||||||
long singleshot_ppm = 0;
|
long singleshot_ppm = 0;
|
||||||
|
|
||||||
/* Compute parts per million frequency adjustment to accomplish the time adjustment
|
/*
|
||||||
implied by time_offset to be applied over the elapsed time indicated by time_constant.
|
* Compute parts per million frequency adjustment to
|
||||||
Use SHIFT_USEC to get it into the same units as time_freq. */
|
* accomplish the time adjustment implied by time_offset to be
|
||||||
|
* applied over the elapsed time indicated by time_constant.
|
||||||
|
* Use SHIFT_USEC to get it into the same units as
|
||||||
|
* time_freq.
|
||||||
|
*/
|
||||||
if ( time_offset < 0 ) {
|
if ( time_offset < 0 ) {
|
||||||
ltemp = -time_offset;
|
ltemp = -time_offset;
|
||||||
ltemp <<= SHIFT_USEC - SHIFT_UPDATE;
|
ltemp <<= SHIFT_USEC - SHIFT_UPDATE;
|
||||||
ltemp >>= SHIFT_KG + time_constant;
|
ltemp >>= SHIFT_KG + time_constant;
|
||||||
ltemp = -ltemp;
|
ltemp = -ltemp;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
ltemp = time_offset;
|
ltemp = time_offset;
|
||||||
ltemp <<= SHIFT_USEC - SHIFT_UPDATE;
|
ltemp <<= SHIFT_USEC - SHIFT_UPDATE;
|
||||||
ltemp >>= SHIFT_KG + time_constant;
|
ltemp >>= SHIFT_KG + time_constant;
|
||||||
|
@ -637,7 +750,10 @@ void ppc_adjtimex(void)
|
||||||
|
|
||||||
adjusting_time = 1;
|
adjusting_time = 1;
|
||||||
|
|
||||||
/* Compute parts per million frequency adjustment to match time_adjust */
|
/*
|
||||||
|
* Compute parts per million frequency adjustment
|
||||||
|
* to match time_adjust
|
||||||
|
*/
|
||||||
singleshot_ppm = tickadj * HZ;
|
singleshot_ppm = tickadj * HZ;
|
||||||
/*
|
/*
|
||||||
* The adjustment should be tickadj*HZ to match the code in
|
* The adjustment should be tickadj*HZ to match the code in
|
||||||
|
@ -645,7 +761,7 @@ void ppc_adjtimex(void)
|
||||||
* large. 3/4 of tickadj*HZ seems about right
|
* large. 3/4 of tickadj*HZ seems about right
|
||||||
*/
|
*/
|
||||||
singleshot_ppm -= singleshot_ppm / 4;
|
singleshot_ppm -= singleshot_ppm / 4;
|
||||||
/* Use SHIFT_USEC to get it into the same units as time_freq */
|
/* Use SHIFT_USEC to get it into the same units as time_freq */
|
||||||
singleshot_ppm <<= SHIFT_USEC;
|
singleshot_ppm <<= SHIFT_USEC;
|
||||||
if ( time_adjust < 0 )
|
if ( time_adjust < 0 )
|
||||||
singleshot_ppm = -singleshot_ppm;
|
singleshot_ppm = -singleshot_ppm;
|
||||||
|
@ -661,7 +777,10 @@ void ppc_adjtimex(void)
|
||||||
/* Add up all of the frequency adjustments */
|
/* Add up all of the frequency adjustments */
|
||||||
delta_freq = time_freq + ltemp + singleshot_ppm;
|
delta_freq = time_freq + ltemp + singleshot_ppm;
|
||||||
|
|
||||||
/* Compute a new value for tb_ticks_per_sec based on the frequency adjustment */
|
/*
|
||||||
|
* Compute a new value for tb_ticks_per_sec based on
|
||||||
|
* the frequency adjustment
|
||||||
|
*/
|
||||||
den = 1000000 * (1 << (SHIFT_USEC - 8));
|
den = 1000000 * (1 << (SHIFT_USEC - 8));
|
||||||
if ( delta_freq < 0 ) {
|
if ( delta_freq < 0 ) {
|
||||||
tb_ticks_per_sec_delta = ( tb_ticks_per_sec * ( (-delta_freq) >> (SHIFT_USEC - 8))) / den;
|
tb_ticks_per_sec_delta = ( tb_ticks_per_sec * ( (-delta_freq) >> (SHIFT_USEC - 8))) / den;
|
||||||
|
@ -676,61 +795,37 @@ void ppc_adjtimex(void)
|
||||||
printk("ppc_adjtimex: ltemp = %ld, time_freq = %ld, singleshot_ppm = %ld\n", ltemp, time_freq, singleshot_ppm);
|
printk("ppc_adjtimex: ltemp = %ld, time_freq = %ld, singleshot_ppm = %ld\n", ltemp, time_freq, singleshot_ppm);
|
||||||
printk("ppc_adjtimex: tb_ticks_per_sec - base = %ld new = %ld\n", tb_ticks_per_sec, new_tb_ticks_per_sec);
|
printk("ppc_adjtimex: tb_ticks_per_sec - base = %ld new = %ld\n", tb_ticks_per_sec, new_tb_ticks_per_sec);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Compute a new value of tb_to_xs (used to convert tb to microseconds and a new value of
|
|
||||||
stamp_xsec which is the time (in 1/2^20 second units) corresponding to tb_orig_stamp. This
|
|
||||||
new value of stamp_xsec compensates for the change in frequency (implied by the new tb_to_xs)
|
|
||||||
which guarantees that the current time remains the same */
|
|
||||||
write_seqlock_irqsave( &xtime_lock, flags );
|
|
||||||
tb_ticks = get_tb() - do_gtod.varp->tb_orig_stamp;
|
|
||||||
div128_by_32( 1024*1024, 0, new_tb_ticks_per_sec, &divres );
|
|
||||||
new_tb_to_xs = divres.result_low;
|
|
||||||
new_xsec = mulhdu( tb_ticks, new_tb_to_xs );
|
|
||||||
|
|
||||||
old_xsec = mulhdu( tb_ticks, do_gtod.varp->tb_to_xs );
|
|
||||||
new_stamp_xsec = do_gtod.varp->stamp_xsec + old_xsec - new_xsec;
|
|
||||||
|
|
||||||
/* There are two copies of tb_to_xs and stamp_xsec so that no lock is needed to access and use these
|
|
||||||
values in do_gettimeofday. We alternate the copies and as long as a reasonable time elapses between
|
|
||||||
changes, there will never be inconsistent values. ntpd has a minimum of one minute between updates */
|
|
||||||
|
|
||||||
temp_idx = (do_gtod.var_idx == 0);
|
|
||||||
temp_varp = &do_gtod.vars[temp_idx];
|
|
||||||
|
|
||||||
temp_varp->tb_to_xs = new_tb_to_xs;
|
|
||||||
temp_varp->stamp_xsec = new_stamp_xsec;
|
|
||||||
temp_varp->tb_orig_stamp = do_gtod.varp->tb_orig_stamp;
|
|
||||||
smp_mb();
|
|
||||||
do_gtod.varp = temp_varp;
|
|
||||||
do_gtod.var_idx = temp_idx;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* tb_update_count is used to allow the problem state gettimeofday code
|
* Compute a new value of tb_to_xs (used to convert tb to
|
||||||
* to assure itself that it sees a consistent view of the tb_to_xs and
|
* microseconds) and a new value of stamp_xsec which is the
|
||||||
* stamp_xsec variables. It reads the tb_update_count, then reads
|
* time (in 1/2^20 second units) corresponding to
|
||||||
* tb_to_xs and stamp_xsec and then reads tb_update_count again. If
|
* tb_orig_stamp. This new value of stamp_xsec compensates
|
||||||
* the two values of tb_update_count match and are even then the
|
* for the change in frequency (implied by the new tb_to_xs)
|
||||||
* tb_to_xs and stamp_xsec values are consistent. If not, then it
|
* which guarantees that the current time remains the same.
|
||||||
* loops back and reads them again until this criteria is met.
|
|
||||||
*/
|
*/
|
||||||
++(systemcfg->tb_update_count);
|
write_seqlock_irqsave( &xtime_lock, flags );
|
||||||
smp_wmb();
|
tb_ticks = get_tb() - do_gtod.varp->tb_orig_stamp;
|
||||||
systemcfg->tb_to_xs = new_tb_to_xs;
|
div128_by_32(1024*1024, 0, new_tb_ticks_per_sec, &divres);
|
||||||
systemcfg->stamp_xsec = new_stamp_xsec;
|
new_tb_to_xs = divres.result_low;
|
||||||
smp_wmb();
|
new_xsec = mulhdu(tb_ticks, new_tb_to_xs);
|
||||||
++(systemcfg->tb_update_count);
|
|
||||||
|
old_xsec = mulhdu(tb_ticks, do_gtod.varp->tb_to_xs);
|
||||||
|
new_stamp_xsec = do_gtod.varp->stamp_xsec + old_xsec - new_xsec;
|
||||||
|
|
||||||
|
update_gtod(do_gtod.varp->tb_orig_stamp, new_stamp_xsec, new_tb_to_xs);
|
||||||
|
|
||||||
write_sequnlock_irqrestore( &xtime_lock, flags );
|
write_sequnlock_irqrestore( &xtime_lock, flags );
|
||||||
|
#endif /* CONFIG_PPC64 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define TICK_SIZE tick
|
|
||||||
#define FEBRUARY 2
|
#define FEBRUARY 2
|
||||||
#define STARTOFTIME 1970
|
#define STARTOFTIME 1970
|
||||||
#define SECDAY 86400L
|
#define SECDAY 86400L
|
||||||
#define SECYR (SECDAY * 365)
|
#define SECYR (SECDAY * 365)
|
||||||
#define leapyear(year) ((year) % 4 == 0)
|
#define leapyear(year) ((year) % 4 == 0 && \
|
||||||
|
((year) % 100 != 0 || (year) % 400 == 0))
|
||||||
#define days_in_year(a) (leapyear(a) ? 366 : 365)
|
#define days_in_year(a) (leapyear(a) ? 366 : 365)
|
||||||
#define days_in_month(a) (month_days[(a) - 1])
|
#define days_in_month(a) (month_days[(a) - 1])
|
||||||
|
|
||||||
|
@ -748,37 +843,25 @@ void GregorianDay(struct rtc_time * tm)
|
||||||
int day;
|
int day;
|
||||||
int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
|
int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
|
||||||
|
|
||||||
lastYear=tm->tm_year-1;
|
lastYear = tm->tm_year - 1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Number of leap corrections to apply up to end of last year
|
* Number of leap corrections to apply up to end of last year
|
||||||
*/
|
*/
|
||||||
leapsToDate = lastYear/4 - lastYear/100 + lastYear/400;
|
leapsToDate = lastYear / 4 - lastYear / 100 + lastYear / 400;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This year is a leap year if it is divisible by 4 except when it is
|
* This year is a leap year if it is divisible by 4 except when it is
|
||||||
* divisible by 100 unless it is divisible by 400
|
* divisible by 100 unless it is divisible by 400
|
||||||
*
|
*
|
||||||
* e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be
|
* e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 was
|
||||||
*/
|
*/
|
||||||
if((tm->tm_year%4==0) &&
|
day = tm->tm_mon > 2 && leapyear(tm->tm_year);
|
||||||
((tm->tm_year%100!=0) || (tm->tm_year%400==0)) &&
|
|
||||||
(tm->tm_mon>2))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* We are past Feb. 29 in a leap year
|
|
||||||
*/
|
|
||||||
day=1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
day=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] +
|
day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] +
|
||||||
tm->tm_mday;
|
tm->tm_mday;
|
||||||
|
|
||||||
tm->tm_wday=day%7;
|
tm->tm_wday = day % 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
void to_tm(int tim, struct rtc_time * tm)
|
void to_tm(int tim, struct rtc_time * tm)
|
||||||
|
@ -824,14 +907,16 @@ void to_tm(int tim, struct rtc_time * tm)
|
||||||
* oscillators and the precision with which the timebase frequency
|
* oscillators and the precision with which the timebase frequency
|
||||||
* is measured but does not harm.
|
* is measured but does not harm.
|
||||||
*/
|
*/
|
||||||
unsigned mulhwu_scale_factor(unsigned inscale, unsigned outscale) {
|
unsigned mulhwu_scale_factor(unsigned inscale, unsigned outscale)
|
||||||
|
{
|
||||||
unsigned mlt=0, tmp, err;
|
unsigned mlt=0, tmp, err;
|
||||||
/* No concern for performance, it's done once: use a stupid
|
/* No concern for performance, it's done once: use a stupid
|
||||||
* but safe and compact method to find the multiplier.
|
* but safe and compact method to find the multiplier.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
for (tmp = 1U<<31; tmp != 0; tmp >>= 1) {
|
for (tmp = 1U<<31; tmp != 0; tmp >>= 1) {
|
||||||
if (mulhwu(inscale, mlt|tmp) < outscale) mlt|=tmp;
|
if (mulhwu(inscale, mlt|tmp) < outscale)
|
||||||
|
mlt |= tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We might still be off by 1 for the best approximation.
|
/* We might still be off by 1 for the best approximation.
|
||||||
|
@ -841,39 +926,53 @@ unsigned mulhwu_scale_factor(unsigned inscale, unsigned outscale) {
|
||||||
* some might have been forgotten in the test however.
|
* some might have been forgotten in the test however.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
err = inscale*(mlt+1);
|
err = inscale * (mlt+1);
|
||||||
if (err <= inscale/2) mlt++;
|
if (err <= inscale/2)
|
||||||
|
mlt++;
|
||||||
return mlt;
|
return mlt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Divide a 128-bit dividend by a 32-bit divisor, leaving a 128 bit
|
* Divide a 128-bit dividend by a 32-bit divisor, leaving a 128 bit
|
||||||
* result.
|
* result.
|
||||||
*/
|
*/
|
||||||
|
void div128_by_32(u64 dividend_high, u64 dividend_low,
|
||||||
void div128_by_32( unsigned long dividend_high, unsigned long dividend_low,
|
unsigned divisor, struct div_result *dr)
|
||||||
unsigned divisor, struct div_result *dr )
|
|
||||||
{
|
{
|
||||||
unsigned long a,b,c,d, w,x,y,z, ra,rb,rc;
|
unsigned long a, b, c, d;
|
||||||
|
unsigned long w, x, y, z;
|
||||||
|
u64 ra, rb, rc;
|
||||||
|
|
||||||
a = dividend_high >> 32;
|
a = dividend_high >> 32;
|
||||||
b = dividend_high & 0xffffffff;
|
b = dividend_high & 0xffffffff;
|
||||||
c = dividend_low >> 32;
|
c = dividend_low >> 32;
|
||||||
d = dividend_low & 0xffffffff;
|
d = dividend_low & 0xffffffff;
|
||||||
|
|
||||||
w = a/divisor;
|
w = a / divisor;
|
||||||
ra = (a - (w * divisor)) << 32;
|
ra = ((u64)(a - (w * divisor)) << 32) + b;
|
||||||
|
|
||||||
x = (ra + b)/divisor;
|
#ifdef CONFIG_PPC64
|
||||||
rb = ((ra + b) - (x * divisor)) << 32;
|
x = ra / divisor;
|
||||||
|
rb = ((ra - (x * divisor)) << 32) + c;
|
||||||
|
|
||||||
y = (rb + c)/divisor;
|
y = rb / divisor;
|
||||||
rc = ((rb + b) - (y * divisor)) << 32;
|
rc = ((rb - (y * divisor)) << 32) + d;
|
||||||
|
|
||||||
z = (rc + d)/divisor;
|
z = rc / divisor;
|
||||||
|
#else
|
||||||
|
/* for 32-bit, use do_div from div64.h */
|
||||||
|
rb = ((u64) do_div(ra, divisor) << 32) + c;
|
||||||
|
x = ra;
|
||||||
|
|
||||||
dr->result_high = (w << 32) + x;
|
rc = ((u64) do_div(rb, divisor) << 32) + d;
|
||||||
dr->result_low = (y << 32) + z;
|
y = rb;
|
||||||
|
|
||||||
|
do_div(rc, divisor);
|
||||||
|
z = rc;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
dr->result_high = ((u64)w << 32) + x;
|
||||||
|
dr->result_low = ((u64)y << 32) + z;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,15 +110,6 @@ static inline void local_delay(unsigned long ms)
|
||||||
msleep(ms);
|
msleep(ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void wakeup_decrementer(void)
|
|
||||||
{
|
|
||||||
set_dec(tb_ticks_per_jiffy);
|
|
||||||
/* No currently-supported powerbook has a 601,
|
|
||||||
* so use get_tbl, not native
|
|
||||||
*/
|
|
||||||
last_jiffy_stamp(0) = tb_last_stamp = get_tbl();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_FREQ
|
#ifdef DEBUG_FREQ
|
||||||
static inline void debug_calc_bogomips(void)
|
static inline void debug_calc_bogomips(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
*
|
*
|
||||||
* Paul Mackerras August 1996.
|
* Paul Mackerras August 1996.
|
||||||
* Copyright (C) 1996 Paul Mackerras.
|
* Copyright (C) 1996 Paul Mackerras.
|
||||||
|
* Copyright (C) 2003-2005 Benjamin Herrenschmidt.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
#include <linux/config.h>
|
#include <linux/config.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
|
@ -19,7 +21,9 @@
|
||||||
#include <linux/adb.h>
|
#include <linux/adb.h>
|
||||||
#include <linux/cuda.h>
|
#include <linux/cuda.h>
|
||||||
#include <linux/pmu.h>
|
#include <linux/pmu.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
#include <linux/hardirq.h>
|
#include <linux/hardirq.h>
|
||||||
|
#include <linux/rtc.h>
|
||||||
|
|
||||||
#include <asm/sections.h>
|
#include <asm/sections.h>
|
||||||
#include <asm/prom.h>
|
#include <asm/prom.h>
|
||||||
|
@ -30,6 +34,14 @@
|
||||||
#include <asm/time.h>
|
#include <asm/time.h>
|
||||||
#include <asm/nvram.h>
|
#include <asm/nvram.h>
|
||||||
|
|
||||||
|
#undef DEBUG
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define DBG(x...) printk(x)
|
||||||
|
#else
|
||||||
|
#define DBG(x...)
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Apparently the RTC stores seconds since 1 Jan 1904 */
|
/* Apparently the RTC stores seconds since 1 Jan 1904 */
|
||||||
#define RTC_OFFSET 2082844800
|
#define RTC_OFFSET 2082844800
|
||||||
|
|
||||||
|
@ -54,10 +66,7 @@
|
||||||
/* Bits in IFR and IER */
|
/* Bits in IFR and IER */
|
||||||
#define T1_INT 0x40 /* Timer 1 interrupt */
|
#define T1_INT 0x40 /* Timer 1 interrupt */
|
||||||
|
|
||||||
extern struct timezone sys_tz;
|
long __init pmac_time_init(void)
|
||||||
|
|
||||||
long __init
|
|
||||||
pmac_time_init(void)
|
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_NVRAM
|
#ifdef CONFIG_NVRAM
|
||||||
s32 delta = 0;
|
s32 delta = 0;
|
||||||
|
@ -210,7 +219,7 @@ via_calibrate_decr(void)
|
||||||
tb_ticks_per_jiffy = (dstart - dend) / ((6 * HZ)/100);
|
tb_ticks_per_jiffy = (dstart - dend) / ((6 * HZ)/100);
|
||||||
tb_to_us = mulhwu_scale_factor(dstart - dend, 60000);
|
tb_to_us = mulhwu_scale_factor(dstart - dend, 60000);
|
||||||
|
|
||||||
printk(KERN_INFO "via_calibrate_decr: ticks per jiffy = %u (%u ticks)\n",
|
printk(KERN_INFO "via_calibrate_decr: ticks per jiffy = %lu (%u ticks)\n",
|
||||||
tb_ticks_per_jiffy, dstart - dend);
|
tb_ticks_per_jiffy, dstart - dend);
|
||||||
|
|
||||||
iounmap(via);
|
iounmap(via);
|
||||||
|
@ -228,6 +237,7 @@ time_sleep_notify(struct pmu_sleep_notifier *self, int when)
|
||||||
static unsigned long time_diff;
|
static unsigned long time_diff;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned long seq;
|
unsigned long seq;
|
||||||
|
struct timespec tv;
|
||||||
|
|
||||||
switch (when) {
|
switch (when) {
|
||||||
case PBOOK_SLEEP_NOW:
|
case PBOOK_SLEEP_NOW:
|
||||||
|
@ -237,11 +247,9 @@ time_sleep_notify(struct pmu_sleep_notifier *self, int when)
|
||||||
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
|
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
|
||||||
break;
|
break;
|
||||||
case PBOOK_WAKE:
|
case PBOOK_WAKE:
|
||||||
write_seqlock_irqsave(&xtime_lock, flags);
|
tv.tv_sec = pmac_get_boot_time() + time_diff;
|
||||||
xtime.tv_sec = pmac_get_rtc_time() + time_diff;
|
tv.tv_nsec = 0;
|
||||||
xtime.tv_nsec = 0;
|
do_settimeofday(&tv);
|
||||||
last_rtc_update = xtime.tv_sec;
|
|
||||||
write_sequnlock_irqrestore(&xtime_lock, flags);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return PBOOK_SLEEP_OK;
|
return PBOOK_SLEEP_OK;
|
||||||
|
|
|
@ -37,7 +37,7 @@ endif
|
||||||
# These are here while we do the architecture merge
|
# These are here while we do the architecture merge
|
||||||
|
|
||||||
else
|
else
|
||||||
obj-y := irq.o idle.o time.o \
|
obj-y := irq.o idle.o \
|
||||||
align.o perfmon.o
|
align.o perfmon.o
|
||||||
obj-$(CONFIG_6xx) += l2cr.o cpu_setup_6xx.o
|
obj-$(CONFIG_6xx) += l2cr.o cpu_setup_6xx.o
|
||||||
obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o
|
obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o
|
||||||
|
|
|
@ -121,6 +121,15 @@ unsigned long profile_pc(struct pt_regs *regs)
|
||||||
EXPORT_SYMBOL(profile_pc);
|
EXPORT_SYMBOL(profile_pc);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void wakeup_decrementer(void)
|
||||||
|
{
|
||||||
|
set_dec(tb_ticks_per_jiffy);
|
||||||
|
/* No currently-supported powerbook has a 601,
|
||||||
|
* so use get_tbl, not native
|
||||||
|
*/
|
||||||
|
last_jiffy_stamp(0) = tb_last_stamp = get_tbl();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* timer_interrupt - gets called when the decrementer overflows,
|
* timer_interrupt - gets called when the decrementer overflows,
|
||||||
* with interrupts disabled.
|
* with interrupts disabled.
|
||||||
|
|
|
@ -12,7 +12,7 @@ obj-y := setup.o entry.o misc.o prom.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
obj-y += irq.o idle.o dma.o \
|
obj-y += irq.o idle.o dma.o \
|
||||||
time.o signal.o \
|
signal.o \
|
||||||
align.o bitops.o pacaData.o \
|
align.o bitops.o pacaData.o \
|
||||||
udbg.o ioctl32.o \
|
udbg.o ioctl32.o \
|
||||||
rtc.o \
|
rtc.o \
|
||||||
|
|
|
@ -180,7 +180,5 @@ void __init pmac_calibrate_decr(void)
|
||||||
if (fp == 0)
|
if (fp == 0)
|
||||||
panic("can't get cpu processor frequency");
|
panic("can't get cpu processor frequency");
|
||||||
ppc_proc_freq = *fp;
|
ppc_proc_freq = *fp;
|
||||||
|
|
||||||
setup_default_decr();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1083,15 +1083,6 @@ void ppc64_terminate_msg(unsigned int src, const char *msg)
|
||||||
printk("[terminate]%04x %s\n", src, msg);
|
printk("[terminate]%04x %s\n", src, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This should only be called on processor 0 during calibrate decr */
|
|
||||||
void __init setup_default_decr(void)
|
|
||||||
{
|
|
||||||
struct paca_struct *lpaca = get_paca();
|
|
||||||
|
|
||||||
lpaca->default_decr = tb_ticks_per_jiffy;
|
|
||||||
lpaca->next_jiffy_update_tb = get_tb() + tb_ticks_per_jiffy;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef CONFIG_PPC_ISERIES
|
#ifndef CONFIG_PPC_ISERIES
|
||||||
/*
|
/*
|
||||||
* This function can be used by platforms to "find" legacy serial ports.
|
* This function can be used by platforms to "find" legacy serial ports.
|
||||||
|
|
|
@ -588,17 +588,6 @@ pmu_get_model(void)
|
||||||
return pmu_kind;
|
return pmu_kind;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef CONFIG_PPC64
|
|
||||||
static inline void wakeup_decrementer(void)
|
|
||||||
{
|
|
||||||
set_dec(tb_ticks_per_jiffy);
|
|
||||||
/* No currently-supported powerbook has a 601,
|
|
||||||
* so use get_tbl, not native
|
|
||||||
*/
|
|
||||||
last_jiffy_stamp(0) = tb_last_stamp = get_tbl();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void pmu_set_server_mode(int server_mode)
|
static void pmu_set_server_mode(int server_mode)
|
||||||
{
|
{
|
||||||
struct adb_request req;
|
struct adb_request req;
|
||||||
|
|
|
@ -496,5 +496,7 @@ extern int call_handle_IRQ_event(int irq, struct pt_regs *regs,
|
||||||
|
|
||||||
#endif /* CONFIG_IRQSTACKS */
|
#endif /* CONFIG_IRQSTACKS */
|
||||||
|
|
||||||
|
extern void do_IRQ(struct pt_regs *regs);
|
||||||
|
|
||||||
#endif /* _ASM_IRQ_H */
|
#endif /* _ASM_IRQ_H */
|
||||||
#endif /* __KERNEL__ */
|
#endif /* __KERNEL__ */
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* Real-time clock definitions and interfaces
|
||||||
|
*
|
||||||
|
* Author: Tom Rini <trini@mvista.com>
|
||||||
|
*
|
||||||
|
* 2002 (c) MontaVista, Software, Inc. This file is licensed under
|
||||||
|
* the terms of the GNU General Public License version 2. This program
|
||||||
|
* is licensed "as is" without any warranty of any kind, whether express
|
||||||
|
* or implied.
|
||||||
|
*
|
||||||
|
* Based on:
|
||||||
|
* include/asm-m68k/rtc.h
|
||||||
|
*
|
||||||
|
* Copyright Richard Zidlicky
|
||||||
|
* implementation details for genrtc/q40rtc driver
|
||||||
|
*
|
||||||
|
* And the old drivers/macintosh/rtc.c which was heavily based on:
|
||||||
|
* Linux/SPARC Real Time Clock Driver
|
||||||
|
* Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu)
|
||||||
|
*
|
||||||
|
* With additional work by Paul Mackerras and Franz Sirl.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ASM_POWERPC_RTC_H__
|
||||||
|
#define __ASM_POWERPC_RTC_H__
|
||||||
|
|
||||||
|
#ifdef __KERNEL__
|
||||||
|
|
||||||
|
#include <linux/rtc.h>
|
||||||
|
|
||||||
|
#include <asm/machdep.h>
|
||||||
|
#include <asm/time.h>
|
||||||
|
|
||||||
|
#define RTC_PIE 0x40 /* periodic interrupt enable */
|
||||||
|
#define RTC_AIE 0x20 /* alarm interrupt enable */
|
||||||
|
#define RTC_UIE 0x10 /* update-finished interrupt enable */
|
||||||
|
|
||||||
|
/* some dummy definitions */
|
||||||
|
#define RTC_BATT_BAD 0x100 /* battery bad */
|
||||||
|
#define RTC_SQWE 0x08 /* enable square-wave output */
|
||||||
|
#define RTC_DM_BINARY 0x04 /* all time/date values are BCD if clear */
|
||||||
|
#define RTC_24H 0x02 /* 24 hour mode - else hours bit 7 means pm */
|
||||||
|
#define RTC_DST_EN 0x01 /* auto switch DST - works f. USA only */
|
||||||
|
|
||||||
|
static inline unsigned int get_rtc_time(struct rtc_time *time)
|
||||||
|
{
|
||||||
|
if (ppc_md.get_rtc_time)
|
||||||
|
ppc_md.get_rtc_time(time);
|
||||||
|
return RTC_24H;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the current date and time in the real time clock. */
|
||||||
|
static inline int set_rtc_time(struct rtc_time *time)
|
||||||
|
{
|
||||||
|
if (ppc_md.get_rtc_time) {
|
||||||
|
ppc_md.set_rtc_time(time);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int get_rtc_ss(void)
|
||||||
|
{
|
||||||
|
struct rtc_time h;
|
||||||
|
|
||||||
|
get_rtc_time(&h);
|
||||||
|
return h.tm_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int get_rtc_pll(struct rtc_pll_info *pll)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
static inline int set_rtc_pll(struct rtc_pll_info *pll)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __KERNEL__ */
|
||||||
|
#endif /* __ASM_POWERPC_RTC_H__ */
|
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* Common time prototypes and such for all ppc machines.
|
||||||
|
*
|
||||||
|
* Written by Cort Dougan (cort@cs.nmt.edu) to merge
|
||||||
|
* Paul Mackerras' version and mine for PReP and Pmac.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __POWERPC_TIME_H
|
||||||
|
#define __POWERPC_TIME_H
|
||||||
|
|
||||||
|
#ifdef __KERNEL__
|
||||||
|
#include <linux/config.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/percpu.h>
|
||||||
|
|
||||||
|
#include <asm/processor.h>
|
||||||
|
#ifdef CONFIG_PPC64
|
||||||
|
#include <asm/paca.h>
|
||||||
|
#include <asm/iSeries/HvCall.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* time.c */
|
||||||
|
extern unsigned long tb_ticks_per_jiffy;
|
||||||
|
extern unsigned long tb_ticks_per_usec;
|
||||||
|
extern unsigned long tb_ticks_per_sec;
|
||||||
|
extern u64 tb_to_xs;
|
||||||
|
extern unsigned tb_to_us;
|
||||||
|
extern u64 tb_last_stamp;
|
||||||
|
|
||||||
|
DECLARE_PER_CPU(unsigned long, last_jiffy);
|
||||||
|
|
||||||
|
struct rtc_time;
|
||||||
|
extern void to_tm(int tim, struct rtc_time * tm);
|
||||||
|
extern time_t last_rtc_update;
|
||||||
|
|
||||||
|
extern void generic_calibrate_decr(void);
|
||||||
|
extern void wakeup_decrementer(void);
|
||||||
|
|
||||||
|
/* Some sane defaults: 125 MHz timebase, 1GHz processor */
|
||||||
|
extern unsigned long ppc_proc_freq;
|
||||||
|
#define DEFAULT_PROC_FREQ (DEFAULT_TB_FREQ * 8)
|
||||||
|
extern unsigned long ppc_tb_freq;
|
||||||
|
#define DEFAULT_TB_FREQ 125000000UL
|
||||||
|
|
||||||
|
/*
|
||||||
|
* By putting all of this stuff into a single struct we
|
||||||
|
* reduce the number of cache lines touched by do_gettimeofday.
|
||||||
|
* Both by collecting all of the data in one cache line and
|
||||||
|
* by touching only one TOC entry on ppc64.
|
||||||
|
*/
|
||||||
|
struct gettimeofday_vars {
|
||||||
|
u64 tb_to_xs;
|
||||||
|
u64 stamp_xsec;
|
||||||
|
u64 tb_orig_stamp;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gettimeofday_struct {
|
||||||
|
unsigned long tb_ticks_per_sec;
|
||||||
|
struct gettimeofday_vars vars[2];
|
||||||
|
struct gettimeofday_vars * volatile varp;
|
||||||
|
unsigned var_idx;
|
||||||
|
unsigned tb_to_us;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct div_result {
|
||||||
|
u64 result_high;
|
||||||
|
u64 result_low;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Accessor functions for the timebase (RTC on 601) registers. */
|
||||||
|
/* If one day CONFIG_POWER is added just define __USE_RTC as 1 */
|
||||||
|
#ifdef CONFIG_6xx
|
||||||
|
#define __USE_RTC() cpu_has_feature(CPU_FTR_USE_TB)
|
||||||
|
#else
|
||||||
|
#define __USE_RTC() 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* On ppc64 this gets us the whole timebase; on ppc32 just the lower half */
|
||||||
|
static inline unsigned long get_tbl(void)
|
||||||
|
{
|
||||||
|
unsigned long tbl;
|
||||||
|
|
||||||
|
#if defined(CONFIG_403GCX)
|
||||||
|
asm volatile("mfspr %0, 0x3dd" : "=r" (tbl));
|
||||||
|
#else
|
||||||
|
asm volatile("mftb %0" : "=r" (tbl));
|
||||||
|
#endif
|
||||||
|
return tbl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int get_tbu(void)
|
||||||
|
{
|
||||||
|
unsigned int tbu;
|
||||||
|
|
||||||
|
#if defined(CONFIG_403GCX)
|
||||||
|
asm volatile("mfspr %0, 0x3dc" : "=r" (tbu));
|
||||||
|
#else
|
||||||
|
asm volatile("mftbu %0" : "=r" (tbu));
|
||||||
|
#endif
|
||||||
|
return tbu;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int get_rtcl(void)
|
||||||
|
{
|
||||||
|
unsigned int rtcl;
|
||||||
|
|
||||||
|
asm volatile("mfrtcl %0" : "=r" (rtcl));
|
||||||
|
return rtcl;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PPC64
|
||||||
|
static inline u64 get_tb(void)
|
||||||
|
{
|
||||||
|
return mftb();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline u64 get_tb(void)
|
||||||
|
{
|
||||||
|
unsigned int tbhi, tblo, tbhi2;
|
||||||
|
|
||||||
|
do {
|
||||||
|
tbhi = get_tbu();
|
||||||
|
tblo = get_tbl();
|
||||||
|
tbhi2 = get_tbu();
|
||||||
|
} while (tbhi != tbhi2);
|
||||||
|
|
||||||
|
return ((u64)tbhi << 32) | tblo;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline void set_tb(unsigned int upper, unsigned int lower)
|
||||||
|
{
|
||||||
|
mtspr(SPRN_TBWL, 0);
|
||||||
|
mtspr(SPRN_TBWU, upper);
|
||||||
|
mtspr(SPRN_TBWL, lower);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Accessor functions for the decrementer register.
|
||||||
|
* The 4xx doesn't even have a decrementer. I tried to use the
|
||||||
|
* generic timer interrupt code, which seems OK, with the 4xx PIT
|
||||||
|
* in auto-reload mode. The problem is PIT stops counting when it
|
||||||
|
* hits zero. If it would wrap, we could use it just like a decrementer.
|
||||||
|
*/
|
||||||
|
static inline unsigned int get_dec(void)
|
||||||
|
{
|
||||||
|
#if defined(CONFIG_40x)
|
||||||
|
return (mfspr(SPRN_PIT));
|
||||||
|
#else
|
||||||
|
return (mfspr(SPRN_DEC));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void set_dec(int val)
|
||||||
|
{
|
||||||
|
#if defined(CONFIG_40x)
|
||||||
|
return; /* Have to let it auto-reload */
|
||||||
|
#elif defined(CONFIG_8xx_CPU6)
|
||||||
|
set_dec_cpu6(val);
|
||||||
|
#else
|
||||||
|
#ifdef CONFIG_PPC_ISERIES
|
||||||
|
struct paca_struct *lpaca = get_paca();
|
||||||
|
int cur_dec;
|
||||||
|
|
||||||
|
if (lpaca->lppaca.shared_proc) {
|
||||||
|
lpaca->lppaca.virtual_decr = val;
|
||||||
|
cur_dec = get_dec();
|
||||||
|
if (cur_dec > val)
|
||||||
|
HvCall_setVirtualDecr();
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
mtspr(SPRN_DEC, val);
|
||||||
|
#endif /* not 40x or 8xx_CPU6 */
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned long tb_ticks_since(unsigned long tstamp)
|
||||||
|
{
|
||||||
|
if (__USE_RTC()) {
|
||||||
|
int delta = get_rtcl() - (unsigned int) tstamp;
|
||||||
|
return delta < 0 ? delta + 1000000000 : delta;
|
||||||
|
}
|
||||||
|
return get_tbl() - tstamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define mulhwu(x,y) \
|
||||||
|
({unsigned z; asm ("mulhwu %0,%1,%2" : "=r" (z) : "r" (x), "r" (y)); z;})
|
||||||
|
|
||||||
|
#ifdef CONFIG_PPC64
|
||||||
|
#define mulhdu(x,y) \
|
||||||
|
({unsigned long z; asm ("mulhdu %0,%1,%2" : "=r" (z) : "r" (x), "r" (y)); z;})
|
||||||
|
#else
|
||||||
|
extern u64 mulhdu(u64, u64);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
unsigned mulhwu_scale_factor(unsigned, unsigned);
|
||||||
|
void div128_by_32(u64 dividend_high, u64 dividend_low,
|
||||||
|
unsigned divisor, struct div_result *dr);
|
||||||
|
|
||||||
|
/* Used to store Processor Utilization register (purr) values */
|
||||||
|
|
||||||
|
struct cpu_usage {
|
||||||
|
u64 current_tb; /* Holds the current purr register values */
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_PER_CPU(struct cpu_usage, cpu_usage_array);
|
||||||
|
|
||||||
|
#endif /* __KERNEL__ */
|
||||||
|
#endif /* __PPC64_TIME_H */
|
|
@ -1,124 +0,0 @@
|
||||||
/*
|
|
||||||
* Common time prototypes and such for all ppc machines.
|
|
||||||
*
|
|
||||||
* Written by Cort Dougan (cort@cs.nmt.edu) to merge
|
|
||||||
* Paul Mackerras' version and mine for PReP and Pmac.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __PPC64_TIME_H
|
|
||||||
#define __PPC64_TIME_H
|
|
||||||
|
|
||||||
#ifdef __KERNEL__
|
|
||||||
#include <linux/config.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/mc146818rtc.h>
|
|
||||||
|
|
||||||
#include <asm/processor.h>
|
|
||||||
#include <asm/paca.h>
|
|
||||||
#include <asm/iSeries/HvCall.h>
|
|
||||||
|
|
||||||
/* time.c */
|
|
||||||
extern unsigned long tb_ticks_per_jiffy;
|
|
||||||
extern unsigned long tb_ticks_per_usec;
|
|
||||||
extern unsigned long tb_ticks_per_sec;
|
|
||||||
extern unsigned long tb_to_xs;
|
|
||||||
extern unsigned tb_to_us;
|
|
||||||
extern unsigned long tb_last_stamp;
|
|
||||||
|
|
||||||
struct rtc_time;
|
|
||||||
extern void to_tm(int tim, struct rtc_time * tm);
|
|
||||||
extern time_t last_rtc_update;
|
|
||||||
|
|
||||||
void generic_calibrate_decr(void);
|
|
||||||
void setup_default_decr(void);
|
|
||||||
|
|
||||||
/* Some sane defaults: 125 MHz timebase, 1GHz processor */
|
|
||||||
extern unsigned long ppc_proc_freq;
|
|
||||||
#define DEFAULT_PROC_FREQ (DEFAULT_TB_FREQ * 8)
|
|
||||||
extern unsigned long ppc_tb_freq;
|
|
||||||
#define DEFAULT_TB_FREQ 125000000UL
|
|
||||||
|
|
||||||
/*
|
|
||||||
* By putting all of this stuff into a single struct we
|
|
||||||
* reduce the number of cache lines touched by do_gettimeofday.
|
|
||||||
* Both by collecting all of the data in one cache line and
|
|
||||||
* by touching only one TOC entry
|
|
||||||
*/
|
|
||||||
struct gettimeofday_vars {
|
|
||||||
unsigned long tb_to_xs;
|
|
||||||
unsigned long stamp_xsec;
|
|
||||||
unsigned long tb_orig_stamp;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct gettimeofday_struct {
|
|
||||||
unsigned long tb_ticks_per_sec;
|
|
||||||
struct gettimeofday_vars vars[2];
|
|
||||||
struct gettimeofday_vars * volatile varp;
|
|
||||||
unsigned var_idx;
|
|
||||||
unsigned tb_to_us;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct div_result {
|
|
||||||
unsigned long result_high;
|
|
||||||
unsigned long result_low;
|
|
||||||
};
|
|
||||||
|
|
||||||
int via_calibrate_decr(void);
|
|
||||||
|
|
||||||
static __inline__ unsigned long get_tb(void)
|
|
||||||
{
|
|
||||||
return mftb();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Accessor functions for the decrementer register. */
|
|
||||||
static __inline__ unsigned int get_dec(void)
|
|
||||||
{
|
|
||||||
return (mfspr(SPRN_DEC));
|
|
||||||
}
|
|
||||||
|
|
||||||
static __inline__ void set_dec(int val)
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_PPC_ISERIES
|
|
||||||
struct paca_struct *lpaca = get_paca();
|
|
||||||
int cur_dec;
|
|
||||||
|
|
||||||
if (lpaca->lppaca.shared_proc) {
|
|
||||||
lpaca->lppaca.virtual_decr = val;
|
|
||||||
cur_dec = get_dec();
|
|
||||||
if (cur_dec > val)
|
|
||||||
HvCall_setVirtualDecr();
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
mtspr(SPRN_DEC, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline unsigned long tb_ticks_since(unsigned long tstamp)
|
|
||||||
{
|
|
||||||
return get_tb() - tstamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define mulhwu(x,y) \
|
|
||||||
({unsigned z; asm ("mulhwu %0,%1,%2" : "=r" (z) : "r" (x), "r" (y)); z;})
|
|
||||||
#define mulhdu(x,y) \
|
|
||||||
({unsigned long z; asm ("mulhdu %0,%1,%2" : "=r" (z) : "r" (x), "r" (y)); z;})
|
|
||||||
|
|
||||||
|
|
||||||
unsigned mulhwu_scale_factor(unsigned, unsigned);
|
|
||||||
void div128_by_32( unsigned long dividend_high, unsigned long dividend_low,
|
|
||||||
unsigned divisor, struct div_result *dr );
|
|
||||||
|
|
||||||
/* Used to store Processor Utilization register (purr) values */
|
|
||||||
|
|
||||||
struct cpu_usage {
|
|
||||||
u64 current_tb; /* Holds the current purr register values */
|
|
||||||
};
|
|
||||||
|
|
||||||
DECLARE_PER_CPU(struct cpu_usage, cpu_usage_array);
|
|
||||||
|
|
||||||
#endif /* __KERNEL__ */
|
|
||||||
#endif /* __PPC64_TIME_H */
|
|
Загрузка…
Ссылка в новой задаче