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:
committed by
David S. Miller
parent
0931714652
commit
0299b1371d
@@ -64,8 +64,11 @@ config BITS
|
|||||||
default 64 if SPARC64
|
default 64 if SPARC64
|
||||||
|
|
||||||
config GENERIC_TIME
|
config GENERIC_TIME
|
||||||
|
def_bool y
|
||||||
|
|
||||||
|
config ARCH_USES_GETTIMEOFFSET
|
||||||
bool
|
bool
|
||||||
default y if SPARC64
|
default y if SPARC32
|
||||||
|
|
||||||
config GENERIC_CMOS_UPDATE
|
config GENERIC_CMOS_UPDATE
|
||||||
bool
|
bool
|
||||||
|
@@ -12,4 +12,5 @@
|
|||||||
typedef unsigned long cycles_t;
|
typedef unsigned long cycles_t;
|
||||||
#define get_cycles() (0)
|
#define get_cycles() (0)
|
||||||
|
|
||||||
|
extern u32 (*do_arch_gettimeoffset)(void);
|
||||||
#endif
|
#endif
|
||||||
|
@@ -30,6 +30,7 @@
|
|||||||
#include <asm/oplib.h>
|
#include <asm/oplib.h>
|
||||||
#include <asm/prom.h>
|
#include <asm/prom.h>
|
||||||
#include <asm/pcic.h>
|
#include <asm/pcic.h>
|
||||||
|
#include <asm/timex.h>
|
||||||
#include <asm/timer.h>
|
#include <asm/timer.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <asm/irq_regs.h>
|
#include <asm/irq_regs.h>
|
||||||
@@ -163,8 +164,6 @@ void __iomem *pcic_regs;
|
|||||||
volatile int pcic_speculative;
|
volatile int pcic_speculative;
|
||||||
volatile int pcic_trapped;
|
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))
|
#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 USECS_PER_JIFFY 10000 /* We have 100HZ "standard" timer for sparc */
|
||||||
#define TICK_TIMER_LIMIT ((100*1000000/4)/100)
|
#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)
|
void __init pci_time_init(void)
|
||||||
{
|
{
|
||||||
struct linux_pcic *pcic = &pcic0;
|
struct linux_pcic *pcic = &pcic0;
|
||||||
unsigned long v;
|
unsigned long v;
|
||||||
int timer_irq, irq;
|
int timer_irq, irq;
|
||||||
|
|
||||||
/* A hack until do_gettimeofday prototype is moved to arch specific headers
|
do_arch_gettimeoffset = pci_gettimeoffset;
|
||||||
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);
|
|
||||||
btfixup();
|
btfixup();
|
||||||
|
|
||||||
writel (TICK_TIMER_LIMIT, pcic->pcic_regs+PCI_SYS_LIMIT);
|
writel (TICK_TIMER_LIMIT, pcic->pcic_regs+PCI_SYS_LIMIT);
|
||||||
@@ -746,84 +753,6 @@ void __init pci_time_init(void)
|
|||||||
local_irq_enable();
|
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
|
#if 0
|
||||||
static void watchdog_reset() {
|
static void watchdog_reset() {
|
||||||
|
@@ -35,6 +35,7 @@
|
|||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
#include <asm/oplib.h>
|
#include <asm/oplib.h>
|
||||||
|
#include <asm/timex.h>
|
||||||
#include <asm/timer.h>
|
#include <asm/timer.h>
|
||||||
#include <asm/system.h>
|
#include <asm/system.h>
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
@@ -51,7 +52,6 @@ DEFINE_SPINLOCK(rtc_lock);
|
|||||||
EXPORT_SYMBOL(rtc_lock);
|
EXPORT_SYMBOL(rtc_lock);
|
||||||
|
|
||||||
static int set_rtc_mmss(unsigned long);
|
static int set_rtc_mmss(unsigned long);
|
||||||
static int sbus_do_settimeofday(struct timespec *tv);
|
|
||||||
|
|
||||||
unsigned long profile_pc(struct pt_regs *regs)
|
unsigned long profile_pc(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
@@ -76,6 +76,8 @@ EXPORT_SYMBOL(profile_pc);
|
|||||||
|
|
||||||
__volatile__ unsigned int *master_l10_counter;
|
__volatile__ unsigned int *master_l10_counter;
|
||||||
|
|
||||||
|
u32 (*do_arch_gettimeoffset)(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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
|
||||||
@@ -196,17 +198,37 @@ static int __init clock_init(void)
|
|||||||
{
|
{
|
||||||
return of_register_driver(&clock_driver, &of_platform_bus_type);
|
return of_register_driver(&clock_driver, &of_platform_bus_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Must be after subsys_initcall() so that busses are probed. Must
|
/* Must be after subsys_initcall() so that busses are probed. Must
|
||||||
* be before device_initcall() because things like the RTC driver
|
* be before device_initcall() because things like the RTC driver
|
||||||
* need to see the clock registers.
|
* need to see the clock registers.
|
||||||
*/
|
*/
|
||||||
fs_initcall(clock_init);
|
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)
|
static void __init sbus_time_init(void)
|
||||||
{
|
{
|
||||||
|
do_arch_gettimeoffset = sbus_do_gettimeoffset;
|
||||||
|
|
||||||
BTFIXUPSET_CALL(bus_do_settimeofday, sbus_do_settimeofday, BTFIXUPCALL_NORM);
|
|
||||||
btfixup();
|
btfixup();
|
||||||
|
|
||||||
sparc_init_timers(timer_interrupt);
|
sparc_init_timers(timer_interrupt);
|
||||||
@@ -224,94 +246,6 @@ void __init time_init(void)
|
|||||||
sbus_time_init();
|
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)
|
static int set_rtc_mmss(unsigned long secs)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user