diff options
Diffstat (limited to 'arch/sparc/kernel/time_32.c')
| -rw-r--r-- | arch/sparc/kernel/time_32.c | 248 | 
1 files changed, 188 insertions, 60 deletions
diff --git a/arch/sparc/kernel/time_32.c b/arch/sparc/kernel/time_32.c index 9c743b1886f..5923d1e4e7c 100644 --- a/arch/sparc/kernel/time_32.c +++ b/arch/sparc/kernel/time_32.c @@ -26,6 +26,8 @@  #include <linux/rtc.h>  #include <linux/rtc/m48t59.h>  #include <linux/timex.h> +#include <linux/clocksource.h> +#include <linux/clockchips.h>  #include <linux/init.h>  #include <linux/pci.h>  #include <linux/ioport.h> @@ -34,20 +36,32 @@  #include <linux/of_device.h>  #include <linux/platform_device.h> +#include <asm/mc146818rtc.h>  #include <asm/oplib.h>  #include <asm/timex.h>  #include <asm/timer.h> -#include <asm/system.h>  #include <asm/irq.h>  #include <asm/io.h>  #include <asm/idprom.h> -#include <asm/machines.h>  #include <asm/page.h>  #include <asm/pcic.h>  #include <asm/irq_regs.h> +#include <asm/setup.h> +#include "kernel.h"  #include "irq.h" +static __cacheline_aligned_in_smp DEFINE_SEQLOCK(timer_cs_lock); +static __volatile__ u64 timer_cs_internal_counter = 0; +static char timer_cs_enabled = 0; + +static struct clock_event_device timer_ce; +static char timer_ce_enabled = 0; + +#ifdef CONFIG_SMP +DEFINE_PER_CPU(struct clock_event_device, sparc32_clockevent); +#endif +  DEFINE_SPINLOCK(rtc_lock);  EXPORT_SYMBOL(rtc_lock); @@ -56,7 +70,6 @@ static int set_rtc_mmss(unsigned long);  unsigned long profile_pc(struct pt_regs *regs)  {  	extern char __copy_user_begin[], __copy_user_end[]; -	extern char __atomic_begin[], __atomic_end[];  	extern char __bzero_begin[], __bzero_end[];  	unsigned long pc = regs->pc; @@ -64,8 +77,6 @@ unsigned long profile_pc(struct pt_regs *regs)  	if (in_lock_functions(pc) ||  	    (pc >= (unsigned long) __copy_user_begin &&  	     pc < (unsigned long) __copy_user_end) || -	    (pc >= (unsigned long) __atomic_begin && -	     pc < (unsigned long) __atomic_end) ||  	    (pc >= (unsigned long) __bzero_begin &&  	     pc < (unsigned long) __bzero_end))  		pc = regs->u_regs[UREG_RETPC]; @@ -74,43 +85,170 @@ unsigned long profile_pc(struct pt_regs *regs)  EXPORT_SYMBOL(profile_pc); -__volatile__ unsigned int *master_l10_counter; - -u32 (*do_arch_gettimeoffset)(void); +volatile u32 __iomem *master_l10_counter;  int update_persistent_clock(struct timespec now)  {  	return set_rtc_mmss(now.tv_sec);  } -/* - * timer_interrupt() needs to keep up the real-time clock, - * as well as call the "do_timer()" routine every clocktick - */ +irqreturn_t notrace timer_interrupt(int dummy, void *dev_id) +{ +	if (timer_cs_enabled) { +		write_seqlock(&timer_cs_lock); +		timer_cs_internal_counter++; +		sparc_config.clear_clock_irq(); +		write_sequnlock(&timer_cs_lock); +	} else { +		sparc_config.clear_clock_irq(); +	} -#define TICK_SIZE (tick_nsec / 1000) +	if (timer_ce_enabled) +		timer_ce.event_handler(&timer_ce); -static irqreturn_t timer_interrupt(int dummy, void *dev_id) +	return IRQ_HANDLED; +} + +static void timer_ce_set_mode(enum clock_event_mode mode, +			      struct clock_event_device *evt)  { -#ifndef CONFIG_SMP -	profile_tick(CPU_PROFILING); -#endif +	switch (mode) { +		case CLOCK_EVT_MODE_PERIODIC: +		case CLOCK_EVT_MODE_RESUME: +			timer_ce_enabled = 1; +			break; +		case CLOCK_EVT_MODE_SHUTDOWN: +			timer_ce_enabled = 0; +			break; +		default: +			break; +	} +	smp_mb(); +} + +static __init void setup_timer_ce(void) +{ +	struct clock_event_device *ce = &timer_ce; + +	BUG_ON(smp_processor_id() != boot_cpu_id); + +	ce->name     = "timer_ce"; +	ce->rating   = 100; +	ce->features = CLOCK_EVT_FEAT_PERIODIC; +	ce->set_mode = timer_ce_set_mode; +	ce->cpumask  = cpu_possible_mask; +	ce->shift    = 32; +	ce->mult     = div_sc(sparc_config.clock_rate, NSEC_PER_SEC, +	                      ce->shift); +	clockevents_register_device(ce); +} + +static unsigned int sbus_cycles_offset(void) +{ +	u32 val, offset; -	/* Protect counter clear so that do_gettimeoffset works */ -	write_seqlock(&xtime_lock); +	val = sbus_readl(master_l10_counter); +	offset = (val >> TIMER_VALUE_SHIFT) & TIMER_VALUE_MASK; -	clear_clock_irq(); +	/* Limit hit? */ +	if (val & TIMER_LIMIT_BIT) +		offset += sparc_config.cs_period; -	do_timer(1); +	return offset; +} -	write_sequnlock(&xtime_lock); +static cycle_t timer_cs_read(struct clocksource *cs) +{ +	unsigned int seq, offset; +	u64 cycles; -#ifndef CONFIG_SMP -	update_process_times(user_mode(get_irq_regs())); -#endif -	return IRQ_HANDLED; +	do { +		seq = read_seqbegin(&timer_cs_lock); + +		cycles = timer_cs_internal_counter; +		offset = sparc_config.get_cycles_offset(); +	} while (read_seqretry(&timer_cs_lock, seq)); + +	/* Count absolute cycles */ +	cycles *= sparc_config.cs_period; +	cycles += offset; + +	return cycles; +} + +static struct clocksource timer_cs = { +	.name	= "timer_cs", +	.rating	= 100, +	.read	= timer_cs_read, +	.mask	= CLOCKSOURCE_MASK(64), +	.shift	= 2, +	.flags	= CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static __init int setup_timer_cs(void) +{ +	timer_cs_enabled = 1; +	timer_cs.mult = clocksource_hz2mult(sparc_config.clock_rate, +	                                    timer_cs.shift); + +	return clocksource_register(&timer_cs); +} + +#ifdef CONFIG_SMP +static void percpu_ce_setup(enum clock_event_mode mode, +			struct clock_event_device *evt) +{ +	int cpu = __first_cpu(evt->cpumask); + +	switch (mode) { +		case CLOCK_EVT_MODE_PERIODIC: +			sparc_config.load_profile_irq(cpu, +						      SBUS_CLOCK_RATE / HZ); +			break; +		case CLOCK_EVT_MODE_ONESHOT: +		case CLOCK_EVT_MODE_SHUTDOWN: +		case CLOCK_EVT_MODE_UNUSED: +			sparc_config.load_profile_irq(cpu, 0); +			break; +		default: +			break; +	}  } +static int percpu_ce_set_next_event(unsigned long delta, +				    struct clock_event_device *evt) +{ +	int cpu = __first_cpu(evt->cpumask); +	unsigned int next = (unsigned int)delta; + +	sparc_config.load_profile_irq(cpu, next); +	return 0; +} + +void register_percpu_ce(int cpu) +{ +	struct clock_event_device *ce = &per_cpu(sparc32_clockevent, cpu); +	unsigned int features = CLOCK_EVT_FEAT_PERIODIC; + +	if (sparc_config.features & FEAT_L14_ONESHOT) +		features |= CLOCK_EVT_FEAT_ONESHOT; + +	ce->name           = "percpu_ce"; +	ce->rating         = 200; +	ce->features       = features; +	ce->set_mode       = percpu_ce_setup; +	ce->set_next_event = percpu_ce_set_next_event; +	ce->cpumask        = cpumask_of(cpu); +	ce->shift          = 32; +	ce->mult           = div_sc(sparc_config.clock_rate, NSEC_PER_SEC, +	                            ce->shift); +	ce->max_delta_ns   = clockevent_delta2ns(sparc_config.clock_rate, ce); +	ce->min_delta_ns   = clockevent_delta2ns(100, ce); + +	clockevents_register_device(ce); +} +#endif +  static unsigned char mostek_read_byte(struct device *dev, u32 ofs)  {  	struct platform_device *pdev = to_platform_device(dev); @@ -142,7 +280,7 @@ static struct platform_device m48t59_rtc = {  	},  }; -static int __devinit clock_probe(struct platform_device *op, const struct of_device_id *match) +static int clock_probe(struct platform_device *op)  {  	struct device_node *dp = op->dev.of_node;  	const char *model = of_get_property(dp, "model", NULL); @@ -150,6 +288,10 @@ static int __devinit clock_probe(struct platform_device *op, const struct of_dev  	if (!model)  		return -ENODEV; +	/* Only the primary RTC has an address property */ +	if (!of_find_property(dp, "address", NULL)) +		return -ENODEV; +  	m48t59_rtc.resource = &op->resource[0];  	if (!strcmp(model, "mk48t02")) {  		/* Map the clock register io area read-only */ @@ -169,14 +311,14 @@ static int __devinit clock_probe(struct platform_device *op, const struct of_dev  	return 0;  } -static struct of_device_id __initdata clock_match[] = { +static struct of_device_id clock_match[] = {  	{  		.name = "eeprom",  	},  	{},  }; -static struct of_platform_driver clock_driver = { +static struct platform_driver clock_driver = {  	.probe		= clock_probe,  	.driver = {  		.name = "rtc", @@ -189,7 +331,7 @@ static struct of_platform_driver clock_driver = {  /* Probe for the mostek real time clock chip. */  static int __init clock_init(void)  { -	return of_register_platform_driver(&clock_driver); +	return platform_driver_register(&clock_driver);  }  /* Must be after subsys_initcall() so that busses are probed.  Must   * be before device_initcall() because things like the RTC driver @@ -197,46 +339,32 @@ static int __init clock_init(void)   */  fs_initcall(clock_init); - -u32 sbus_do_gettimeoffset(void) +static void __init sparc32_late_time_init(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(); +	if (sparc_config.features & FEAT_L10_CLOCKEVENT) +		setup_timer_ce(); +	if (sparc_config.features & FEAT_L10_CLOCKSOURCE) +		setup_timer_cs(); +#ifdef CONFIG_SMP +	register_percpu_ce(smp_processor_id()); +#endif  }  static void __init sbus_time_init(void)  { -	do_arch_gettimeoffset = sbus_do_gettimeoffset; - -	btfixup(); - -	sparc_init_timers(timer_interrupt); +	sparc_config.get_cycles_offset = sbus_cycles_offset; +	sparc_config.init_timers();  }  void __init time_init(void)  { -#ifdef CONFIG_PCI -	extern void pci_time_init(void); -	if (pcic_present()) { +	sparc_config.features = 0; +	late_time_init = sparc32_late_time_init; + +	if (pcic_present())  		pci_time_init(); -		return; -	} -#endif -	sbus_time_init(); +	else +		sbus_time_init();  }  | 
