ARM: footbridge: convert to clockevents/clocksource

The Footbridge platforms have some reasonable timers in the host bridge,
which we use for most footbridge-based platforms.  However, NetWinder's
clock these using a spread-spectrum clock which makes them too unstable
for time keeping.  So we have to rely on the PIT.

Convert both Footbridge timers and PIT timers to use the clocksource
and clockevent infrastructure.  Tested on Netwinder.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
Russell King
2011-01-28 21:00:39 +00:00
parent 5f2c1b30c7
commit 4e8d76373c
3 changed files with 157 additions and 68 deletions

View File

@@ -4,10 +4,11 @@
* Copyright (C) 1998 Russell King.
* Copyright (C) 1998 Phil Blundell
*/
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/spinlock.h>
#include <asm/irq.h>
@@ -16,32 +17,76 @@
#include "common.h"
/*
* Footbridge timer 1 support.
*/
static unsigned long timer1_latch;
static unsigned long timer1_gettimeoffset (void)
static cycle_t cksrc_dc21285_read(struct clocksource *cs)
{
unsigned long value = timer1_latch - *CSR_TIMER1_VALUE;
return ((tick_nsec / 1000) * value) / timer1_latch;
return cs->mask - *CSR_TIMER2_VALUE;
}
static irqreturn_t
timer1_interrupt(int irq, void *dev_id)
static int cksrc_dc21285_enable(struct clocksource *cs)
{
*CSR_TIMER2_LOAD = cs->mask;
*CSR_TIMER2_CLR = 0;
*CSR_TIMER2_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV16;
return 0;
}
static int cksrc_dc21285_disable(struct clocksource *cs)
{
*CSR_TIMER2_CNTL = 0;
}
static struct clocksource cksrc_dc21285 = {
.name = "dc21285_timer2",
.rating = 200,
.read = cksrc_dc21285_read,
.enable = cksrc_dc21285_enable,
.disable = cksrc_dc21285_disable,
.mask = CLOCKSOURCE_MASK(24),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static void ckevt_dc21285_set_mode(enum clock_event_mode mode,
struct clock_event_device *c)
{
switch (mode) {
case CLOCK_EVT_MODE_RESUME:
case CLOCK_EVT_MODE_PERIODIC:
*CSR_TIMER1_CLR = 0;
*CSR_TIMER1_LOAD = (mem_fclk_21285 + 8 * HZ) / (16 * HZ);
*CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_AUTORELOAD |
TIMER_CNTL_DIV16;
break;
default:
*CSR_TIMER1_CNTL = 0;
break;
}
}
static struct clock_event_device ckevt_dc21285 = {
.name = "dc21285_timer1",
.features = CLOCK_EVT_FEAT_PERIODIC,
.rating = 200,
.irq = IRQ_TIMER1,
.set_mode = ckevt_dc21285_set_mode,
};
static irqreturn_t timer1_interrupt(int irq, void *dev_id)
{
struct clock_event_device *ce = dev_id;
*CSR_TIMER1_CLR = 0;
timer_tick();
ce->event_handler(ce);
return IRQ_HANDLED;
}
static struct irqaction footbridge_timer_irq = {
.name = "Timer1 timer tick",
.name = "dc21285_timer1",
.handler = timer1_interrupt,
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
.dev_id = &ckevt_dc21285,
};
/*
@@ -49,16 +94,19 @@ static struct irqaction footbridge_timer_irq = {
*/
static void __init footbridge_timer_init(void)
{
timer1_latch = (mem_fclk_21285 + 8 * HZ) / (16 * HZ);
struct clock_event_device *ce = &ckevt_dc21285;
*CSR_TIMER1_CLR = 0;
*CSR_TIMER1_LOAD = timer1_latch;
*CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_AUTORELOAD | TIMER_CNTL_DIV16;
clocksource_register_hz(&cksrc_dc21285, (mem_fclk_21285 + 8) / 16);
setup_irq(IRQ_TIMER1, &footbridge_timer_irq);
setup_irq(ce->irq, &footbridge_timer_irq);
clockevents_calc_mult_shift(ce, mem_fclk_21285, 5);
ce->max_delta_ns = clockevent_delta2ns(0xffffff, ce);
ce->min_delta_ns = clockevent_delta2ns(0x000004, ce);
clockevents_register_device(ce);
}
struct sys_timer footbridge_timer = {
.init = footbridge_timer_init,
.offset = timer1_gettimeoffset,
};