1
0
Fork 0

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>
wifi-calibration
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

@ -346,7 +346,7 @@ config ARCH_FOOTBRIDGE
bool "FootBridge" bool "FootBridge"
select CPU_SA110 select CPU_SA110
select FOOTBRIDGE select FOOTBRIDGE
select ARCH_USES_GETTIMEOFFSET select GENERIC_CLOCKEVENTS
help help
Support for systems based on the DC21285 companion chip Support for systems based on the DC21285 companion chip
("FootBridge"), such as the Simtec CATS and the Rebel NetWinder. ("FootBridge"), such as the Simtec CATS and the Rebel NetWinder.

View File

@ -4,10 +4,11 @@
* Copyright (C) 1998 Russell King. * Copyright (C) 1998 Russell King.
* Copyright (C) 1998 Phil Blundell * Copyright (C) 1998 Phil Blundell
*/ */
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/spinlock.h>
#include <asm/irq.h> #include <asm/irq.h>
@ -16,32 +17,76 @@
#include "common.h" #include "common.h"
/* static cycle_t cksrc_dc21285_read(struct clocksource *cs)
* Footbridge timer 1 support.
*/
static unsigned long timer1_latch;
static unsigned long timer1_gettimeoffset (void)
{ {
unsigned long value = timer1_latch - *CSR_TIMER1_VALUE; return cs->mask - *CSR_TIMER2_VALUE;
return ((tick_nsec / 1000) * value) / timer1_latch;
} }
static irqreturn_t static int cksrc_dc21285_enable(struct clocksource *cs)
timer1_interrupt(int irq, void *dev_id)
{ {
*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; *CSR_TIMER1_CLR = 0;
timer_tick(); ce->event_handler(ce);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static struct irqaction footbridge_timer_irq = { static struct irqaction footbridge_timer_irq = {
.name = "Timer1 timer tick", .name = "dc21285_timer1",
.handler = timer1_interrupt, .handler = timer1_interrupt,
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, .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) 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; clocksource_register_hz(&cksrc_dc21285, (mem_fclk_21285 + 8) / 16);
*CSR_TIMER1_LOAD = timer1_latch;
*CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_AUTORELOAD | TIMER_CNTL_DIV16;
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 = { struct sys_timer footbridge_timer = {
.init = footbridge_timer_init, .init = footbridge_timer_init,
.offset = timer1_gettimeoffset,
}; };

View File

@ -4,10 +4,13 @@
* Copyright (C) 1998 Russell King. * Copyright (C) 1998 Russell King.
* Copyright (C) 1998 Phil Blundell * Copyright (C) 1998 Phil Blundell
*/ */
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/timex.h>
#include <asm/irq.h> #include <asm/irq.h>
@ -15,77 +18,115 @@
#include "common.h" #include "common.h"
/* #define PIT_MODE 0x43
* ISA timer tick support #define PIT_CH0 0x40
*/
#define mSEC_10_from_14 ((14318180 + 100) / 200)
static unsigned long isa_gettimeoffset(void) #define PIT_LATCH ((PIT_TICK_RATE + HZ / 2) / HZ)
static cycle_t pit_read(struct clocksource *cs)
{ {
unsigned long flags;
static int old_count;
static u32 old_jifs;
int count; int count;
u32 jifs;
static int count_p = (mSEC_10_from_14/6); /* for the first call after boot */ raw_local_irq_save(flags);
static unsigned long jiffies_p = 0;
/* jifs = jiffies;
* cache volatile jiffies temporarily; we have IRQs turned off. outb_p(0x00, PIT_MODE); /* latch the count */
*/ count = inb_p(PIT_CH0); /* read the latched count */
unsigned long jiffies_t; count |= inb_p(PIT_CH0) << 8;
/* timer count may underflow right here */ if (count > old_count && jifs == old_jifs)
outb_p(0x00, 0x43); /* latch the count ASAP */ count = old_count;
count = inb_p(0x40); /* read the latched count */ old_count = count;
old_jifs = jifs;
/* raw_local_irq_restore(flags);
* We do this guaranteed double memory access instead of a _p
* postfix in the previous port access. Wheee, hackady hack
*/
jiffies_t = jiffies;
count |= inb_p(0x40) << 8; count = (PIT_LATCH - 1) - count;
/* Detect timer underflows. If we haven't had a timer tick since return (cycle_t)(jifs * PIT_LATCH) + count;
the last time we were called, and time is apparently going
backwards, the counter must have wrapped during this routine. */
if ((jiffies_t == jiffies_p) && (count > count_p))
count -= (mSEC_10_from_14/6);
else
jiffies_p = jiffies_t;
count_p = count;
count = (((mSEC_10_from_14/6)-1) - count) * (tick_nsec / 1000);
count = (count + (mSEC_10_from_14/6)/2) / (mSEC_10_from_14/6);
return count;
} }
static irqreturn_t static struct clocksource pit_cs = {
isa_timer_interrupt(int irq, void *dev_id) .name = "pit",
.rating = 110,
.read = pit_read,
.mask = CLOCKSOURCE_MASK(32),
};
static void pit_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{ {
timer_tick(); unsigned long flags;
raw_local_irq_save(flags);
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
outb_p(0x34, PIT_MODE);
outb_p(PIT_LATCH & 0xff, PIT_CH0);
outb_p(PIT_LATCH >> 8, PIT_CH0);
break;
case CLOCK_EVT_MODE_SHUTDOWN:
case CLOCK_EVT_MODE_UNUSED:
outb_p(0x30, PIT_MODE);
outb_p(0, PIT_CH0);
outb_p(0, PIT_CH0);
break;
case CLOCK_EVT_MODE_ONESHOT:
case CLOCK_EVT_MODE_RESUME:
break;
}
local_irq_restore(flags);
}
static int pit_set_next_event(unsigned long delta,
struct clock_event_device *evt)
{
return 0;
}
static struct clock_event_device pit_ce = {
.name = "pit",
.features = CLOCK_EVT_FEAT_PERIODIC,
.set_mode = pit_set_mode,
.set_next_event = pit_set_next_event,
.shift = 32,
};
static irqreturn_t pit_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *ce = dev_id;
ce->event_handler(ce);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static struct irqaction isa_timer_irq = { static struct irqaction pit_timer_irq = {
.name = "ISA timer tick", .name = "pit",
.handler = isa_timer_interrupt, .handler = pit_timer_interrupt,
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
.dev_id = &pit_ce,
}; };
static void __init isa_timer_init(void) static void __init isa_timer_init(void)
{ {
/* enable PIT timer */ pit_ce.cpumask = cpumask_of(smp_processor_id());
/* set for periodic (4) and LSB/MSB write (0x30) */ pit_ce.mult = div_sc(PIT_TICK_RATE, NSEC_PER_SEC, pit_ce.shift);
outb(0x34, 0x43); pit_ce.max_delta_ns = clockevent_delta2ns(0x7fff, &pit_ce);
outb((mSEC_10_from_14/6) & 0xFF, 0x40); pit_ce.min_delta_ns = clockevent_delta2ns(0x000f, &pit_ce);
outb((mSEC_10_from_14/6) >> 8, 0x40);
setup_irq(IRQ_ISA_TIMER, &isa_timer_irq); clocksource_register_hz(&pit_cs, PIT_TICK_RATE);
setup_irq(pit_ce.irq, &pit_timer_irq);
clockevents_register_device(&pit_ce);
} }
struct sys_timer isa_timer = { struct sys_timer isa_timer = {
.init = isa_timer_init, .init = isa_timer_init,
.offset = isa_gettimeoffset,
}; };