Add GENERIC_CLOCKEVENTS support for SPARC-LEON. The late_time_init initialization has been moved to enable overriding it for SPARC-LEON. Signed-off-by: Konrad Eisele <konrad@xxxxxxxxxxx> --- arch/sparc/include/asm/leon.h | 3 +- arch/sparc/kernel/leon_kernel.c | 189 ++++++++++++++++++++++++++++++++++++++- arch/sparc/kernel/leon_smp.c | 34 +------ arch/sparc/kernel/time_32.c | 4 +- 4 files changed, 193 insertions(+), 37 deletions(-) diff --git a/arch/sparc/include/asm/leon.h b/arch/sparc/include/asm/leon.h index a4e457f..9b9b24f 100644 --- a/arch/sparc/include/asm/leon.h +++ b/arch/sparc/include/asm/leon.h @@ -323,7 +323,7 @@ extern void leon_update_virq_handling(unsigned int virq, const char *name, int do_ack); extern void leon_clear_clock_irq(void); extern void leon_load_profile_irq(int cpu, unsigned int limit); -extern void leon_init_timers(irq_handler_t counter_fn); +extern void leon_init_timers(void); extern void leon_clear_clock_irq(void); extern void leon_load_profile_irq(int cpu, unsigned int limit); extern void leon_trans_init(struct device_node *dp); @@ -351,6 +351,7 @@ extern void init_IRQ(void); extern void cpu_panic(void); extern int __leon_processor_id(void); void leon_enable_irq_cpu(unsigned int irq_nr, unsigned int cpu); +extern void leon_register_percpu_ce(int cpu); extern irqreturn_t leon_percpu_timer_interrupt(int irq, void *unused); extern unsigned int real_irq_entry[]; diff --git a/arch/sparc/kernel/leon_kernel.c b/arch/sparc/kernel/leon_kernel.c index a19c8a0..62afe3f 100644 --- a/arch/sparc/kernel/leon_kernel.c +++ b/arch/sparc/kernel/leon_kernel.c @@ -10,6 +10,8 @@ #include <linux/of_platform.h> #include <linux/interrupt.h> #include <linux/of_device.h> +#include <linux/clocksource.h> +#include <linux/clockchips.h> #include <asm/oplib.h> #include <asm/timer.h> @@ -27,12 +29,18 @@ struct leon3_irqctrl_regs_map *leon3_irqctrl_regs; /* interrupt controller base address */ struct leon3_gptimer_regs_map *leon3_gptimer_regs; /* timer controller base address */ +static __cacheline_aligned_in_smp DEFINE_SEQLOCK(leon_timer_cs_lock); +static __volatile__ u64 leon_timer_cs_internal_counter = 0; int leondebug_irq_disable; int leon_debug_irqout; static int dummy_master_l10_counter; unsigned long amba_system_id; static DEFINE_SPINLOCK(leon_irq_lock); +static char leon_timer_cs_enabled = 0; +#ifndef CONFIG_SMP +static char leon_timer_ce_enabled = 0; +#endif unsigned long leon3_gptimer_irq; /* interrupt controller irq number */ unsigned long leon3_gptimer_idx; /* Timer Index (0..6) within Timer Core */ int leon3_ticker_irq; /* Timer ticker IRQ */ @@ -250,7 +258,177 @@ void leon_update_virq_handling(unsigned int virq, irq_set_chip_data(virq, (void *)mask); } -void __init leon_init_timers(irq_handler_t counter_fn) +static u32 leon_cycles_offset(void) +{ + u32 rld, val, off; + rld = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].rld); + val = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].val); + off = rld - val; + return rld - val; +} + +static cycle_t leon_timer_cs_read(struct clocksource *cs) +{ + unsigned int seq, offset; + u64 cycles; + + do { + seq = read_seqbegin(&leon_timer_cs_lock); + cycles = leon_timer_cs_internal_counter; + offset = leon_cycles_offset(); + } while (read_seqretry(&leon_timer_cs_lock, seq)); + + cycles *= timer_cs_period; + cycles += offset; + return cycles; +} + +static struct clocksource leon_timer_cs = { + .name = "grtimer-cs", + .rating = 200, + .read = leon_timer_cs_read, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +#ifndef CONFIG_SMP + +static void leon_timer_ce_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + case CLOCK_EVT_MODE_RESUME: + leon_timer_ce_enabled = 1; + break; + case CLOCK_EVT_MODE_SHUTDOWN: + leon_timer_ce_enabled = 0; + break; + default: + break; + } + smp_mb(); +} + +static struct clock_event_device leon_timer_ce = { + .name = "grtimer-ce", + .rating = 100, + .features = CLOCK_EVT_FEAT_PERIODIC, + .set_mode = leon_timer_ce_set_mode, + .shift = 32 +}; + +#endif /* !CONFIG_SMP */ + +static void __init leon_late_time_init(void) +{ + leon_timer_cs_enabled = 1; + + clocksource_register_hz(&leon_timer_cs, 1000000); + +#ifdef CONFIG_SMP + leon_register_percpu_ce(smp_processor_id()); +#else + BUG_ON(smp_processor_id() != boot_cpu_id); + leon_timer_ce.cpumask = cpu_possible_mask; + leon_timer_ce.mult = div_sc(1000000, NSEC_PER_SEC, + leon_timer_ce.shift); + clockevents_register_device(&leon_timer_ce); +#endif /* CONFIG_SMP */ +} + +/* clocksource irq, non-smp clockevent */ +irqreturn_t notrace leon_timer_interrupt(int dummy, void *dev_id) +{ + if (leon_timer_cs_enabled) { + write_seqlock(&leon_timer_cs_lock); + leon_timer_cs_internal_counter++; + write_sequnlock(&leon_timer_cs_lock); + } +#ifndef CONFIG_SMP + if (leon_timer_ce_enabled) { + if (leon_timer_ce.event_handler) + leon_timer_ce.event_handler(&leon_timer_ce); + } + +#endif + return IRQ_HANDLED; +} + +#ifdef CONFIG_SMP + +/* smp clockevent irq */ +irqreturn_t leon_percpu_timer_ce_interrupt(int irq, void *unused) +{ + struct clock_event_device *ce; + int cpu = smp_processor_id(); + + leon_clear_profile_irq(cpu); + + ce = &per_cpu(sparc32_clockevent, cpu); + + irq_enter(); + if (ce->event_handler) + ce->event_handler(ce); + irq_exit(); + + return IRQ_HANDLED; +} + +static void leon_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: + leon_load_profile_irq(cpu, (1000000 / HZ)); + break; + case CLOCK_EVT_MODE_ONESHOT: + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: + leon_load_profile_irq(cpu, 0); + break; + default: + break; + } +} + +static int leon_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; + leon_load_profile_irq(cpu, next); + return 0; +} + +void leon_register_percpu_ce(int cpu) +{ + struct clock_event_device *ce = &per_cpu(sparc32_clockevent, cpu); + unsigned int features = CLOCK_EVT_FEAT_PERIODIC; + + if (sparc_irq_config.features & FEAT_L14_OS) + features |= CLOCK_EVT_FEAT_ONESHOT; + + ce->name = "grtimer-ce"; + ce->rating = 200; + ce->features = features; + ce->set_mode = leon_percpu_ce_setup; + ce->set_next_event = leon_percpu_ce_set_next_event; + ce->cpumask = cpumask_of(cpu); + ce->shift = 32; + ce->mult = div_sc(1000000, NSEC_PER_SEC, + ce->shift); + ce->max_delta_ns = clockevent_delta2ns(1000000, ce); + ce->min_delta_ns = clockevent_delta2ns(100, ce); + + clockevents_register_device(ce); +} + +#endif /* CONFIG_SMP */ + +void __init leon_init_timers(void) { int irq, eirq; struct device_node *rootnp, *np, *nnp; @@ -260,6 +438,10 @@ void __init leon_init_timers(irq_handler_t counter_fn) int ampopts; int err; + late_time_init = leon_late_time_init; + get_cycles_offset = leon_cycles_offset; + timer_cs_period = 1000000 / HZ; + leondebug_irq_disable = 0; leon_debug_irqout = 0; master_l10_counter = (unsigned int *)&dummy_master_l10_counter; @@ -369,7 +551,7 @@ void __init leon_init_timers(irq_handler_t counter_fn) leon_eirq_setup(eirq); irq = _leon_build_device_irq(NULL, leon3_gptimer_irq+leon3_gptimer_idx); - err = request_irq(irq, counter_fn, IRQF_TIMER, "timer", NULL); + err = request_irq(irq, leon_timer_interrupt, IRQF_TIMER, "timer", NULL); if (err) { printk(KERN_ERR "unable to attach timer IRQ%d\n", irq); prom_halt(); @@ -401,7 +583,7 @@ void __init leon_init_timers(irq_handler_t counter_fn) /* Install per-cpu IRQ handler for broadcasted ticker */ irq = leon_build_device_irq(leon3_ticker_irq, handle_percpu_irq, "per-cpu", 0); - err = request_irq(irq, leon_percpu_timer_interrupt, + err = request_irq(irq, leon_percpu_timer_ce_interrupt, IRQF_PERCPU | IRQF_TIMER, "ticker", NULL); if (err) { @@ -428,7 +610,6 @@ void leon_clear_clock_irq(void) void leon_load_profile_irq(int cpu, unsigned int limit) { - BUG(); } void __init leon_trans_init(struct device_node *dp) diff --git a/arch/sparc/kernel/leon_smp.c b/arch/sparc/kernel/leon_smp.c index 1210fde..0bda9b1 100644 --- a/arch/sparc/kernel/leon_smp.c +++ b/arch/sparc/kernel/leon_smp.c @@ -23,6 +23,8 @@ #include <linux/pm.h> #include <linux/delay.h> #include <linux/gfp.h> +#include <linux/cpu.h> +#include <linux/clockchips.h> #include <asm/cacheflush.h> #include <asm/tlbflush.h> @@ -42,6 +44,7 @@ #include <asm/asi.h> #include <asm/leon.h> #include <asm/leon_amba.h> +#include <asm/timer.h> #include "kernel.h" @@ -68,8 +71,6 @@ static inline unsigned long do_swap(volatile unsigned long *ptr, return val; } -static void smp_setup_percpu_timer(void); - void __cpuinit leon_callin(void) { int cpuid = hard_smpleon_processor_id(); @@ -79,7 +80,7 @@ void __cpuinit leon_callin(void) leon_configure_cache_smp(); /* Get our local ticker going. */ - smp_setup_percpu_timer(); + leon_register_percpu_ce(cpuid); calibrate_delay(); smp_store_cpu_info(cpuid); @@ -196,7 +197,6 @@ void __init leon_boot_cpus(void) leon_smp_setbroadcast(1 << LEON3_IRQ_TICKER); leon_configure_cache_smp(); - smp_setup_percpu_timer(); local_flush_cache_all(); } @@ -489,32 +489,6 @@ void leon_cross_call_irq(void) ccall_info.processors_out[i] = 1; } -irqreturn_t leon_percpu_timer_interrupt(int irq, void *unused) -{ - int cpu = smp_processor_id(); - - leon_clear_profile_irq(cpu); - - profile_tick(CPU_PROFILING); - - if (!--prof_counter(cpu)) { - int user = user_mode(get_irq_regs()); - - update_process_times(user); - - prof_counter(cpu) = prof_multiplier(cpu); - } - - return IRQ_HANDLED; -} - -static void __init smp_setup_percpu_timer(void) -{ - int cpu = smp_processor_id(); - - prof_counter(cpu) = prof_multiplier(cpu) = 1; -} - void __init leon_blackbox_id(unsigned *addr) { int rd = *addr & 0x3e000000; diff --git a/arch/sparc/kernel/time_32.c b/arch/sparc/kernel/time_32.c index fc66bff..7427a49 100644 --- a/arch/sparc/kernel/time_32.c +++ b/arch/sparc/kernel/time_32.c @@ -367,12 +367,12 @@ void __init time_init(void) sparc_irq_config.features = 0; + late_time_init = sparc32_late_time_init; + if (pcic_present()) pci_time_init(); else sbus_time_init(); - - late_time_init = sparc32_late_time_init; } -- 1.6.1.3 -- To unsubscribe from this list: send the line "unsubscribe sparclinux" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html