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

Commit 000bc178 authored by Linus Walleij's avatar Linus Walleij
Browse files

ARM: ep93xx: switch to GENERIC_CLOCKEVENTS



This switches the EP93xx to use GENERIC_CLOCKEVENTS and
CLKSRC_MMIO. Also implements a sched_clock() hook.
Tested on the SIM.ONE. Use only oneshot events.

Tested-by: default avatarH Hartley Sweeten <hsweeten@visionengravers.com>
Reviewed-by: default avatarH Hartley Sweeten <hsweeten@visionengravers.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent 361c81f9
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -418,11 +418,12 @@ config ARCH_EP93XX
	bool "EP93xx-based"
	select ARCH_HAS_HOLES_MEMORYMODEL
	select ARCH_REQUIRE_GPIOLIB
	select ARCH_USES_GETTIMEOFFSET
	select ARM_AMBA
	select ARM_VIC
	select CLKDEV_LOOKUP
	select CLKSRC_MMIO
	select CPU_ARM920T
	select GENERIC_CLOCKEVENTS
	help
	  This enables support for the Cirrus EP93xx series of CPUs.

+70 −40
Original line number Diff line number Diff line
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/sched_clock.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
@@ -45,26 +48,68 @@
#define EP93XX_TIMER3_CONTROL		EP93XX_TIMER_REG(0x88)
#define EP93XX_TIMER3_CLEAR		EP93XX_TIMER_REG(0x8c)

#define EP93XX_TIMER123_CLOCK		508469
#define EP93XX_TIMER4_CLOCK		983040
#define EP93XX_TIMER123_RATE		508469
#define EP93XX_TIMER4_RATE		983040

#define TIMER1_RELOAD			((EP93XX_TIMER123_CLOCK / HZ) - 1)
#define TIMER4_TICKS_PER_JIFFY		DIV_ROUND_CLOSEST(EP93XX_TIMER4_CLOCK, HZ)
static u64 notrace ep93xx_read_sched_clock(void)
{
	u64 ret;

	ret = __raw_readl(EP93XX_TIMER4_VALUE_LOW);
	ret |= ((u64) (__raw_readl(EP93XX_TIMER4_VALUE_HIGH) & 0xff) << 32);
	return ret;
}

cycle_t ep93xx_clocksource_read(struct clocksource *c)
{
	u64 ret;

	ret = __raw_readl(EP93XX_TIMER4_VALUE_LOW);
	ret |= ((u64) (__raw_readl(EP93XX_TIMER4_VALUE_HIGH) & 0xff) << 32);
	return (cycle_t) ret;
}

static int ep93xx_clkevt_set_next_event(unsigned long next,
					struct clock_event_device *evt)
{
	/* Default mode: periodic, off, 508 kHz */
	u32 tmode = EP93XX_TIMER123_CONTROL_MODE |
		    EP93XX_TIMER123_CONTROL_CLKSEL;

	/* Clear timer */
	__raw_writel(tmode, EP93XX_TIMER1_CONTROL);

	/* Set next event */
	__raw_writel(next, EP93XX_TIMER1_LOAD);
	__raw_writel(tmode | EP93XX_TIMER123_CONTROL_ENABLE,
		     EP93XX_TIMER1_CONTROL);
        return 0;
}


static void ep93xx_clkevt_set_mode(enum clock_event_mode mode,
				   struct clock_event_device *evt)
{
	/* Disable timer */
	__raw_writel(0, EP93XX_TIMER1_CONTROL);
}

static unsigned int last_jiffy_time;
static struct clock_event_device ep93xx_clockevent = {
	.name		= "timer1",
	.features	= CLOCK_EVT_FEAT_ONESHOT,
	.set_mode	= ep93xx_clkevt_set_mode,
	.set_next_event	= ep93xx_clkevt_set_next_event,
	.rating		= 300,
};

static irqreturn_t ep93xx_timer_interrupt(int irq, void *dev_id)
{
	struct clock_event_device *evt = dev_id;

	/* Writing any value clears the timer interrupt */
	__raw_writel(1, EP93XX_TIMER1_CLEAR);

	/* Recover lost jiffies */
	while ((signed long)
		(__raw_readl(EP93XX_TIMER4_VALUE_LOW) - last_jiffy_time)
						>= TIMER4_TICKS_PER_JIFFY) {
		last_jiffy_time += TIMER4_TICKS_PER_JIFFY;
		timer_tick();
	}
	evt->event_handler(evt);

	return IRQ_HANDLED;
}
@@ -73,40 +118,25 @@ static struct irqaction ep93xx_timer_irq = {
	.name		= "ep93xx timer",
	.flags		= IRQF_TIMER | IRQF_IRQPOLL,
	.handler	= ep93xx_timer_interrupt,
	.dev_id		= &ep93xx_clockevent,
};

static u32 ep93xx_gettimeoffset(void)
{
	int offset;

	offset = __raw_readl(EP93XX_TIMER4_VALUE_LOW) - last_jiffy_time;

	/*
	 * Timer 4 is based on a 983.04 kHz reference clock,
	 * so dividing by 983040 gives the fraction of a second,
	 * so dividing by 0.983040 converts to uS.
	 * Refactor the calculation to avoid overflow.
	 * Finally, multiply by 1000 to give nS.
	 */
	return (offset + (53 * offset / 3072)) * 1000;
}

void __init ep93xx_timer_init(void)
{
	u32 tmode = EP93XX_TIMER123_CONTROL_MODE |
		    EP93XX_TIMER123_CONTROL_CLKSEL;

	arch_gettimeoffset = ep93xx_gettimeoffset;

	/* Enable periodic HZ timer.  */
	__raw_writel(tmode, EP93XX_TIMER1_CONTROL);
	__raw_writel(TIMER1_RELOAD, EP93XX_TIMER1_LOAD);
	__raw_writel(tmode | EP93XX_TIMER123_CONTROL_ENABLE,
			EP93XX_TIMER1_CONTROL);

	/* Enable lost jiffy timer.  */
	/* Enable and register clocksource and sched_clock on timer 4 */
	__raw_writel(EP93XX_TIMER4_VALUE_HIGH_ENABLE,
			EP93XX_TIMER4_VALUE_HIGH);
	clocksource_mmio_init(NULL, "timer4",
			      EP93XX_TIMER4_RATE, 200, 40,
			      ep93xx_clocksource_read);
	sched_clock_register(ep93xx_read_sched_clock, 40,
			     EP93XX_TIMER4_RATE);

	/* Set up clockevent on timer 1 */
	setup_irq(IRQ_EP93XX_TIMER1, &ep93xx_timer_irq);
	// FIXME: timer one is 16 bits 1-ffff use timer 3 1-ffffffff */
	clockevents_config_and_register(&ep93xx_clockevent,
					EP93XX_TIMER123_RATE,
					1,
					0xffffU);
}