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:
John Stultz 2010-01-15 01:34:28 -08:00 committed by David S. Miller
parent 0931714652
commit 0299b1371d
4 changed files with 46 additions and 179 deletions

View File

@ -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

View File

@ -12,4 +12,5 @@
typedef unsigned long cycles_t;
#define get_cycles() (0)
extern u32 (*do_arch_gettimeoffset)(void);
#endif

View File

@ -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() {

View File

@ -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)
{