Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4e8d7637 authored by Russell King's avatar Russell King
Browse files

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: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 5f2c1b30
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -346,7 +346,7 @@ config ARCH_FOOTBRIDGE
	bool "FootBridge"
	select CPU_SA110
	select FOOTBRIDGE
	select ARCH_USES_GETTIMEOFFSET
	select GENERIC_CLOCKEVENTS
	help
	  Support for systems based on the DC21285 companion chip
	  ("FootBridge"), such as the Simtec CATS and the Rebel NetWinder.
+66 −18
Original line number Diff line number Diff line
@@ -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 cycle_t cksrc_dc21285_read(struct clocksource *cs)
{
	return cs->mask - *CSR_TIMER2_VALUE;
}

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 unsigned long timer1_gettimeoffset (void)
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)
{
	unsigned long value = timer1_latch - *CSR_TIMER1_VALUE;
	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;

	return ((tick_nsec / 1000) * value) / timer1_latch;
	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)
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(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);

	setup_irq(IRQ_TIMER1, &footbridge_timer_irq);
	clockevents_register_device(ce);
}

struct sys_timer footbridge_timer = {
	.init		= footbridge_timer_init,
	.offset		= timer1_gettimeoffset,
};
+85 −44
Original line number Diff line number Diff line
@@ -4,10 +4,13 @@
 *  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/io.h>
#include <linux/timex.h>

#include <asm/irq.h>

@@ -15,77 +18,115 @@

#include "common.h"

/*
 * ISA timer tick support
 */
#define mSEC_10_from_14 ((14318180 + 100) / 200)
#define PIT_MODE	0x43
#define PIT_CH0		0x40

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;
	u32 jifs;

	static int count_p = (mSEC_10_from_14/6);    /* for the first call after boot */
	static unsigned long jiffies_p = 0;
	raw_local_irq_save(flags);

	/*
	 * cache volatile jiffies temporarily; we have IRQs turned off. 
	 */
	unsigned long jiffies_t;
	jifs = jiffies;
	outb_p(0x00, PIT_MODE);		/* latch the count */
	count = inb_p(PIT_CH0);		/* read the latched count */
	count |= inb_p(PIT_CH0) << 8;

	/* timer count may underflow right here */
	outb_p(0x00, 0x43);	/* latch the count ASAP */
	if (count > old_count && jifs == old_jifs)
		count = old_count;

	count = inb_p(0x40);	/* read the latched count */
	old_count = count;
	old_jifs = jifs;

	/*
	 * We do this guaranteed double memory access instead of a _p 
	 * postfix in the previous port access. Wheee, hackady hack
	 */
 	jiffies_t = jiffies;
	raw_local_irq_restore(flags);

	count |= inb_p(0x40) << 8;
	count = (PIT_LATCH - 1) - count;

	/* Detect timer underflows.  If we haven't had a timer tick since 
	   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;
	return (cycle_t)(jifs * PIT_LATCH) + count;
}

	count_p = count;
static struct clocksource pit_cs = {
	.name		= "pit",
	.rating		= 110,
	.read		= pit_read,
	.mask		= CLOCKSOURCE_MASK(32),
};

	count = (((mSEC_10_from_14/6)-1) - count) * (tick_nsec / 1000);
	count = (count + (mSEC_10_from_14/6)/2) / (mSEC_10_from_14/6);
static void pit_set_mode(enum clock_event_mode mode,
	struct clock_event_device *evt)
{
	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);
}

	return count;
static int pit_set_next_event(unsigned long delta,
	struct clock_event_device *evt)
{
	return 0;
}

static irqreturn_t
isa_timer_interrupt(int irq, void *dev_id)
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)
{
	timer_tick();
	struct clock_event_device *ce = dev_id;
	ce->event_handler(ce);
	return IRQ_HANDLED;
}

static struct irqaction isa_timer_irq = {
	.name		= "ISA timer tick",
	.handler	= isa_timer_interrupt,
static struct irqaction pit_timer_irq = {
	.name		= "pit",
	.handler	= pit_timer_interrupt,
	.flags		= IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
	.dev_id		= &pit_ce,
};

static void __init isa_timer_init(void)
{
	/* enable PIT timer */
	/* set for periodic (4) and LSB/MSB write (0x30) */
	outb(0x34, 0x43);
	outb((mSEC_10_from_14/6) & 0xFF, 0x40);
	outb((mSEC_10_from_14/6) >> 8, 0x40);
	pit_ce.cpumask = cpumask_of(smp_processor_id());
	pit_ce.mult = div_sc(PIT_TICK_RATE, NSEC_PER_SEC, pit_ce.shift);
	pit_ce.max_delta_ns = clockevent_delta2ns(0x7fff, &pit_ce);
	pit_ce.min_delta_ns = clockevent_delta2ns(0x000f, &pit_ce);

	clocksource_register_hz(&pit_cs, PIT_TICK_RATE);

	setup_irq(IRQ_ISA_TIMER, &isa_timer_irq);
	setup_irq(pit_ce.irq, &pit_timer_irq);
	clockevents_register_device(&pit_ce);
}

struct sys_timer isa_timer = {
	.init		= isa_timer_init,
	.offset		= isa_gettimeoffset,
};