forked from Minki/linux
5f664e2b1b
In preparation for making the clockevents core NTP correction aware, all clockevent device drivers must set ->min_delta_ticks and ->max_delta_ticks rather than ->min_delta_ns and ->max_delta_ns: a clockevent device's rate is going to change dynamically and thus, the ratio of ns to ticks ceases to stay invariant. Make the mn10300 arch's clockevent driver initialize these fields properly. This patch alone doesn't introduce any change in functionality as the clockevents core still looks exclusively at the (untouched) ->min_delta_ns and ->max_delta_ns. As soon as this has changed, a followup patch will purge the initialization of ->min_delta_ns and ->max_delta_ns from this driver. Cc: Ingo Molnar <mingo@redhat.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Daniel Lezcano <daniel.lezcano@linaro.org> Cc: Richard Cochran <richardcochran@gmail.com> Cc: Prarit Bhargava <prarit@redhat.com> Cc: Stephen Boyd <sboyd@codeaurora.org> Cc: David Howells <dhowells@redhat.com> Cc: linux-am33-list@redhat.com Signed-off-by: Nicolai Stange <nicstange@gmail.com> Signed-off-by: John Stultz <john.stultz@linaro.org>
138 lines
3.4 KiB
C
138 lines
3.4 KiB
C
/* 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 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->max_delta_ticks = TMJCBR_MAX;
|
|
cd->min_delta_ns = clockevent_delta2ns(100, cd);
|
|
cd->min_delta_ticks = 100;
|
|
|
|
cd->rating = 200;
|
|
cd->cpumask = cpumask_of(smp_processor_id());
|
|
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(irq_data_get_affinity_mask(data), 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;
|
|
}
|