Thanks a lot, Konrad! On Thu, 2012-03-15 at 11:33 +0100, Konrad Eisele wrote: > 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; > } > > -- 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