mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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: H Hartley Sweeten <hsweeten@visionengravers.com> Reviewed-by: H Hartley Sweeten <hsweeten@visionengravers.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
		
							parent
							
								
									361c81f996
								
							
						
					
					
						commit
						000bc17817
					
				
					 2 changed files with 72 additions and 41 deletions
				
			
		| 
						 | 
				
			
			@ -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.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,8 @@
 | 
			
		|||
#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;
 | 
			
		||||
 | 
			
		||||
static unsigned int last_jiffy_time;
 | 
			
		||||
	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 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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue