diff options
Diffstat (limited to 'arch/mn10300/kernel/cevt-mn10300.c')
-rw-r--r-- | arch/mn10300/kernel/cevt-mn10300.c | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/arch/mn10300/kernel/cevt-mn10300.c b/arch/mn10300/kernel/cevt-mn10300.c new file mode 100644 index 000000000..60f64ca17 --- /dev/null +++ b/arch/mn10300/kernel/cevt-mn10300.c @@ -0,0 +1,142 @@ +/* MN10300 clockevents + * + * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. + * Written by Mark Salter (msalter@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/clockchips.h> +#include <linux/interrupt.h> +#include <linux/percpu.h> +#include <linux/smp.h> +#include <asm/timex.h> +#include "internal.h" + +#ifdef CONFIG_SMP +#if (CONFIG_NR_CPUS > 2) && !defined(CONFIG_GEENERIC_CLOCKEVENTS_BROADCAST) +#error "This doesn't scale well! Need per-core local timers." +#endif +#else /* CONFIG_SMP */ +#define stop_jiffies_counter1() +#define reload_jiffies_counter1(x) +#define TMJC1IRQ TMJCIRQ +#endif + + +static int next_event(unsigned long delta, + struct clock_event_device *evt) +{ + unsigned int cpu = smp_processor_id(); + + if (cpu == 0) { + stop_jiffies_counter(); + reload_jiffies_counter(delta - 1); + } else { + stop_jiffies_counter1(); + reload_jiffies_counter1(delta - 1); + } + return 0; +} + +static void set_clock_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + /* Nothing to do ... */ +} + +static DEFINE_PER_CPU(struct clock_event_device, mn10300_clockevent_device); +static DEFINE_PER_CPU(struct irqaction, timer_irq); + +static irqreturn_t timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *cd; + unsigned int cpu = smp_processor_id(); + + if (cpu == 0) + stop_jiffies_counter(); + else + stop_jiffies_counter1(); + + cd = &per_cpu(mn10300_clockevent_device, cpu); + cd->event_handler(cd); + + return IRQ_HANDLED; +} + +static void event_handler(struct clock_event_device *dev) +{ +} + +static inline void setup_jiffies_interrupt(int irq, + struct irqaction *action) +{ + u16 tmp; + setup_irq(irq, action); + set_intr_level(irq, NUM2GxICR_LEVEL(CONFIG_TIMER_IRQ_LEVEL)); + GxICR(irq) |= GxICR_ENABLE | GxICR_DETECT | GxICR_REQUEST; + tmp = GxICR(irq); +} + +int __init init_clockevents(void) +{ + struct clock_event_device *cd; + struct irqaction *iact; + unsigned int cpu = smp_processor_id(); + + cd = &per_cpu(mn10300_clockevent_device, cpu); + + if (cpu == 0) { + stop_jiffies_counter(); + cd->irq = TMJCIRQ; + } else { + stop_jiffies_counter1(); + cd->irq = TMJC1IRQ; + } + + cd->name = "Timestamp"; + cd->features = CLOCK_EVT_FEAT_ONESHOT; + + /* Calculate shift/mult. We want to spawn at least 1 second */ + clockevents_calc_mult_shift(cd, MN10300_JCCLK, 1); + + /* Calculate the min / max delta */ + cd->max_delta_ns = clockevent_delta2ns(TMJCBR_MAX, cd); + cd->min_delta_ns = clockevent_delta2ns(100, cd); + + cd->rating = 200; + cd->cpumask = cpumask_of(smp_processor_id()); + cd->set_mode = set_clock_mode; + cd->event_handler = event_handler; + cd->set_next_event = next_event; + + iact = &per_cpu(timer_irq, cpu); + iact->flags = IRQF_SHARED | IRQF_TIMER; + iact->handler = timer_interrupt; + + clockevents_register_device(cd); + +#if defined(CONFIG_SMP) && !defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) + /* setup timer irq affinity so it only runs on this cpu */ + { + struct irq_data *data; + data = irq_get_irq_data(cd->irq); + cpumask_copy(data->affinity, cpumask_of(cpu)); + iact->flags |= IRQF_NOBALANCING; + } +#endif + + if (cpu == 0) { + reload_jiffies_counter(MN10300_JC_PER_HZ - 1); + iact->name = "CPU0 Timer"; + } else { + reload_jiffies_counter1(MN10300_JC_PER_HZ - 1); + iact->name = "CPU1 Timer"; + } + + setup_jiffies_interrupt(cd->irq, iact); + + return 0; +} |