forked from Minki/linux
sparc: convert to arch_gettimeoffset()
This patch converts sparc (specifically sparc32) to use GENERIC_TIME via the arch_getoffset() infrastructure, reducing the amount of arch specific code we need to maintain. The sparc architecture is one of the last 3 arches that need to be converted. This patch applies on top of Linus' current -git tree I've taken my best swing at converting this, but I'm not 100% confident I got it right. My cross-compiler is now out of date (gcc4.2) so I wasn't able to check if it compiled. Any assistance from arch maintainers or testers to get this merged would be great. Signed-off-by: John Stultz <johnstul@us.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
0931714652
commit
0299b1371d
@ -64,8 +64,11 @@ config BITS
|
||||
default 64 if SPARC64
|
||||
|
||||
config GENERIC_TIME
|
||||
def_bool y
|
||||
|
||||
config ARCH_USES_GETTIMEOFFSET
|
||||
bool
|
||||
default y if SPARC64
|
||||
default y if SPARC32
|
||||
|
||||
config GENERIC_CMOS_UPDATE
|
||||
bool
|
||||
|
@ -12,4 +12,5 @@
|
||||
typedef unsigned long cycles_t;
|
||||
#define get_cycles() (0)
|
||||
|
||||
extern u32 (*do_arch_gettimeoffset)(void);
|
||||
#endif
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <asm/oplib.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/pcic.h>
|
||||
#include <asm/timex.h>
|
||||
#include <asm/timer.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/irq_regs.h>
|
||||
@ -163,8 +164,6 @@ void __iomem *pcic_regs;
|
||||
volatile int pcic_speculative;
|
||||
volatile int pcic_trapped;
|
||||
|
||||
static void pci_do_gettimeofday(struct timeval *tv);
|
||||
static int pci_do_settimeofday(struct timespec *tv);
|
||||
|
||||
#define CONFIG_CMD(bus, device_fn, where) (0x80000000 | (((unsigned int)bus) << 16) | (((unsigned int)device_fn) << 8) | (where & ~3))
|
||||
|
||||
@ -716,19 +715,27 @@ static irqreturn_t pcic_timer_handler (int irq, void *h)
|
||||
#define USECS_PER_JIFFY 10000 /* We have 100HZ "standard" timer for sparc */
|
||||
#define TICK_TIMER_LIMIT ((100*1000000/4)/100)
|
||||
|
||||
u32 pci_gettimeoffset(void)
|
||||
{
|
||||
/*
|
||||
* We divide all by 100
|
||||
* to have microsecond resolution and to avoid overflow
|
||||
*/
|
||||
unsigned long count =
|
||||
readl(pcic0.pcic_regs+PCI_SYS_COUNTER) & ~PCI_SYS_COUNTER_OVERFLOW;
|
||||
count = ((count/100)*USECS_PER_JIFFY) / (TICK_TIMER_LIMIT/100);
|
||||
return count * 1000;
|
||||
}
|
||||
|
||||
|
||||
void __init pci_time_init(void)
|
||||
{
|
||||
struct linux_pcic *pcic = &pcic0;
|
||||
unsigned long v;
|
||||
int timer_irq, irq;
|
||||
|
||||
/* A hack until do_gettimeofday prototype is moved to arch specific headers
|
||||
and btfixupped. Patch do_gettimeofday with ba pci_do_gettimeofday; nop */
|
||||
((unsigned int *)do_gettimeofday)[0] =
|
||||
0x10800000 | ((((unsigned long)pci_do_gettimeofday -
|
||||
(unsigned long)do_gettimeofday) >> 2) & 0x003fffff);
|
||||
((unsigned int *)do_gettimeofday)[1] = 0x01000000;
|
||||
BTFIXUPSET_CALL(bus_do_settimeofday, pci_do_settimeofday, BTFIXUPCALL_NORM);
|
||||
do_arch_gettimeoffset = pci_gettimeoffset;
|
||||
|
||||
btfixup();
|
||||
|
||||
writel (TICK_TIMER_LIMIT, pcic->pcic_regs+PCI_SYS_LIMIT);
|
||||
@ -746,84 +753,6 @@ void __init pci_time_init(void)
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
static inline unsigned long do_gettimeoffset(void)
|
||||
{
|
||||
/*
|
||||
* We divide all by 100
|
||||
* to have microsecond resolution and to avoid overflow
|
||||
*/
|
||||
unsigned long count =
|
||||
readl(pcic0.pcic_regs+PCI_SYS_COUNTER) & ~PCI_SYS_COUNTER_OVERFLOW;
|
||||
count = ((count/100)*USECS_PER_JIFFY) / (TICK_TIMER_LIMIT/100);
|
||||
return count;
|
||||
}
|
||||
|
||||
static void pci_do_gettimeofday(struct timeval *tv)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long seq;
|
||||
unsigned long usec, sec;
|
||||
unsigned long max_ntp_tick = tick_usec - tickadj;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin_irqsave(&xtime_lock, flags);
|
||||
usec = do_gettimeoffset();
|
||||
|
||||
/*
|
||||
* If time_adjust is negative then NTP is slowing the clock
|
||||
* so make sure not to go into next possible interval.
|
||||
* Better to lose some accuracy than have time go backwards..
|
||||
*/
|
||||
if (unlikely(time_adjust < 0))
|
||||
usec = min(usec, max_ntp_tick);
|
||||
|
||||
sec = xtime.tv_sec;
|
||||
usec += (xtime.tv_nsec / 1000);
|
||||
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
|
||||
|
||||
while (usec >= 1000000) {
|
||||
usec -= 1000000;
|
||||
sec++;
|
||||
}
|
||||
|
||||
tv->tv_sec = sec;
|
||||
tv->tv_usec = usec;
|
||||
}
|
||||
|
||||
static int pci_do_settimeofday(struct timespec *tv)
|
||||
{
|
||||
if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* 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_nsec -= 1000 * do_gettimeoffset();
|
||||
while (tv->tv_nsec < 0) {
|
||||
tv->tv_nsec += NSEC_PER_SEC;
|
||||
tv->tv_sec--;
|
||||
}
|
||||
|
||||
wall_to_monotonic.tv_sec += xtime.tv_sec - tv->tv_sec;
|
||||
wall_to_monotonic.tv_nsec += xtime.tv_nsec - tv->tv_nsec;
|
||||
|
||||
if (wall_to_monotonic.tv_nsec > NSEC_PER_SEC) {
|
||||
wall_to_monotonic.tv_nsec -= NSEC_PER_SEC;
|
||||
wall_to_monotonic.tv_sec++;
|
||||
}
|
||||
if (wall_to_monotonic.tv_nsec < 0) {
|
||||
wall_to_monotonic.tv_nsec += NSEC_PER_SEC;
|
||||
wall_to_monotonic.tv_sec--;
|
||||
}
|
||||
|
||||
xtime.tv_sec = tv->tv_sec;
|
||||
xtime.tv_nsec = tv->tv_nsec;
|
||||
ntp_clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void watchdog_reset() {
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/oplib.h>
|
||||
#include <asm/timex.h>
|
||||
#include <asm/timer.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/irq.h>
|
||||
@ -51,7 +52,6 @@ DEFINE_SPINLOCK(rtc_lock);
|
||||
EXPORT_SYMBOL(rtc_lock);
|
||||
|
||||
static int set_rtc_mmss(unsigned long);
|
||||
static int sbus_do_settimeofday(struct timespec *tv);
|
||||
|
||||
unsigned long profile_pc(struct pt_regs *regs)
|
||||
{
|
||||
@ -76,6 +76,8 @@ EXPORT_SYMBOL(profile_pc);
|
||||
|
||||
__volatile__ unsigned int *master_l10_counter;
|
||||
|
||||
u32 (*do_arch_gettimeoffset)(void);
|
||||
|
||||
/*
|
||||
* timer_interrupt() needs to keep up the real-time clock,
|
||||
* as well as call the "do_timer()" routine every clocktick
|
||||
@ -196,17 +198,37 @@ static int __init clock_init(void)
|
||||
{
|
||||
return of_register_driver(&clock_driver, &of_platform_bus_type);
|
||||
}
|
||||
|
||||
/* Must be after subsys_initcall() so that busses are probed. Must
|
||||
* be before device_initcall() because things like the RTC driver
|
||||
* need to see the clock registers.
|
||||
*/
|
||||
fs_initcall(clock_init);
|
||||
|
||||
|
||||
u32 sbus_do_gettimeoffset(void)
|
||||
{
|
||||
unsigned long val = *master_l10_counter;
|
||||
unsigned long usec = (val >> 10) & 0x1fffff;
|
||||
|
||||
/* Limit hit? */
|
||||
if (val & 0x80000000)
|
||||
usec += 1000000 / HZ;
|
||||
|
||||
return usec * 1000;
|
||||
}
|
||||
|
||||
|
||||
u32 arch_gettimeoffset(void)
|
||||
{
|
||||
if (unlikely(!do_arch_gettimeoffset))
|
||||
return 0;
|
||||
return do_arch_gettimeoffset();
|
||||
}
|
||||
|
||||
static void __init sbus_time_init(void)
|
||||
{
|
||||
do_arch_gettimeoffset = sbus_do_gettimeoffset;
|
||||
|
||||
BTFIXUPSET_CALL(bus_do_settimeofday, sbus_do_settimeofday, BTFIXUPCALL_NORM);
|
||||
btfixup();
|
||||
|
||||
sparc_init_timers(timer_interrupt);
|
||||
@ -224,94 +246,6 @@ void __init time_init(void)
|
||||
sbus_time_init();
|
||||
}
|
||||
|
||||
static inline unsigned long do_gettimeoffset(void)
|
||||
{
|
||||
unsigned long val = *master_l10_counter;
|
||||
unsigned long usec = (val >> 10) & 0x1fffff;
|
||||
|
||||
/* Limit hit? */
|
||||
if (val & 0x80000000)
|
||||
usec += 1000000 / HZ;
|
||||
|
||||
return usec;
|
||||
}
|
||||
|
||||
/* Ok, my cute asm atomicity trick doesn't work anymore.
|
||||
* There are just too many variables that need to be protected
|
||||
* now (both members of xtime, et al.)
|
||||
*/
|
||||
void do_gettimeofday(struct timeval *tv)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long seq;
|
||||
unsigned long usec, sec;
|
||||
unsigned long max_ntp_tick = tick_usec - tickadj;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin_irqsave(&xtime_lock, flags);
|
||||
usec = do_gettimeoffset();
|
||||
|
||||
/*
|
||||
* If time_adjust is negative then NTP is slowing the clock
|
||||
* so make sure not to go into next possible interval.
|
||||
* Better to lose some accuracy than have time go backwards..
|
||||
*/
|
||||
if (unlikely(time_adjust < 0))
|
||||
usec = min(usec, max_ntp_tick);
|
||||
|
||||
sec = xtime.tv_sec;
|
||||
usec += (xtime.tv_nsec / 1000);
|
||||
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
|
||||
|
||||
while (usec >= 1000000) {
|
||||
usec -= 1000000;
|
||||
sec++;
|
||||
}
|
||||
|
||||
tv->tv_sec = sec;
|
||||
tv->tv_usec = usec;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(do_gettimeofday);
|
||||
|
||||
int do_settimeofday(struct timespec *tv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
write_seqlock_irq(&xtime_lock);
|
||||
ret = bus_do_settimeofday(tv);
|
||||
write_sequnlock_irq(&xtime_lock);
|
||||
clock_was_set();
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(do_settimeofday);
|
||||
|
||||
static int sbus_do_settimeofday(struct timespec *tv)
|
||||
{
|
||||
time_t wtm_sec, sec = tv->tv_sec;
|
||||
long wtm_nsec, nsec = tv->tv_nsec;
|
||||
|
||||
if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
nsec -= 1000 * do_gettimeoffset();
|
||||
|
||||
wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
|
||||
wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
|
||||
|
||||
set_normalized_timespec(&xtime, sec, nsec);
|
||||
set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
|
||||
|
||||
ntp_clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_rtc_mmss(unsigned long secs)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user