2008-03-27 14:51:40 -04:00
/*
* arch / arm / plat - orion / time . c
*
* Marvell Orion SoC timer handling .
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*
* Timer 0 is used as free - running clocksource , while timer 1 is
* used as clock_event_device .
*/
# include <linux/kernel.h>
2009-05-15 00:42:36 -04:00
# include <linux/timer.h>
2008-03-27 14:51:40 -04:00
# include <linux/clockchips.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
2013-06-01 23:39:40 -07:00
# include <linux/sched_clock.h>
2013-10-23 16:12:50 +02:00
# include <plat/time.h>
2008-03-27 14:51:40 -04:00
/*
2010-10-15 16:50:26 +02:00
* MBus bridge block registers .
2008-03-27 14:51:40 -04:00
*/
2010-10-15 16:50:26 +02:00
# define BRIDGE_CAUSE_OFF 0x0110
# define BRIDGE_MASK_OFF 0x0114
# define BRIDGE_INT_TIMER0 0x0002
# define BRIDGE_INT_TIMER1 0x0004
2008-03-27 14:51:40 -04:00
/*
* Timer block registers .
*/
2010-10-15 16:50:26 +02:00
# define TIMER_CTRL_OFF 0x0000
# define TIMER0_EN 0x0001
# define TIMER0_RELOAD_EN 0x0002
# define TIMER1_EN 0x0004
# define TIMER1_RELOAD_EN 0x0008
# define TIMER0_RELOAD_OFF 0x0010
# define TIMER0_VAL_OFF 0x0014
# define TIMER1_RELOAD_OFF 0x0018
# define TIMER1_VAL_OFF 0x001c
/*
* SoC - specific data .
*/
static void __iomem * bridge_base ;
static u32 bridge_timer1_clr_mask ;
static void __iomem * timer_base ;
/*
* Number of timer ticks per jiffy .
*/
static u32 ticks_per_jiffy ;
2008-03-27 14:51:40 -04:00
2009-05-12 10:30:41 -07:00
/*
* Orion ' s sched_clock implementation . It has a resolution of
2010-12-15 21:55:06 +00:00
* at least 7.5 ns ( 133 MHz TCLK ) .
2009-05-12 10:30:41 -07:00
*/
2013-11-15 15:26:24 -08:00
static u64 notrace orion_read_sched_clock ( void )
2009-05-15 00:42:36 -04:00
{
2011-12-15 12:19:23 +01:00
return ~ readl ( timer_base + TIMER0_VAL_OFF ) ;
2009-05-12 10:30:41 -07:00
}
2008-03-27 14:51:40 -04:00
/*
* Clockevent handling .
*/
static int
orion_clkevt_next_event ( unsigned long delta , struct clock_event_device * dev )
{
unsigned long flags ;
u32 u ;
if ( delta = = 0 )
return - ETIME ;
local_irq_save ( flags ) ;
/*
* Clear and enable clockevent timer interrupt .
*/
2010-10-15 16:50:26 +02:00
writel ( bridge_timer1_clr_mask , bridge_base + BRIDGE_CAUSE_OFF ) ;
2008-03-27 14:51:40 -04:00
2010-10-15 16:50:26 +02:00
u = readl ( bridge_base + BRIDGE_MASK_OFF ) ;
2008-03-27 14:51:40 -04:00
u | = BRIDGE_INT_TIMER1 ;
2010-10-15 16:50:26 +02:00
writel ( u , bridge_base + BRIDGE_MASK_OFF ) ;
2008-03-27 14:51:40 -04:00
/*
* Setup new clockevent timer value .
*/
2010-10-15 16:50:26 +02:00
writel ( delta , timer_base + TIMER1_VAL_OFF ) ;
2008-03-27 14:51:40 -04:00
/*
* Enable the timer .
*/
2010-10-15 16:50:26 +02:00
u = readl ( timer_base + TIMER_CTRL_OFF ) ;
2008-03-27 14:51:40 -04:00
u = ( u & ~ TIMER1_RELOAD_EN ) | TIMER1_EN ;
2010-10-15 16:50:26 +02:00
writel ( u , timer_base + TIMER_CTRL_OFF ) ;
2008-03-27 14:51:40 -04:00
local_irq_restore ( flags ) ;
return 0 ;
}
static void
orion_clkevt_mode ( enum clock_event_mode mode , struct clock_event_device * dev )
{
unsigned long flags ;
u32 u ;
local_irq_save ( flags ) ;
if ( mode = = CLOCK_EVT_MODE_PERIODIC ) {
/*
* Setup timer to fire at 1 / HZ intervals .
*/
2010-10-15 16:50:26 +02:00
writel ( ticks_per_jiffy - 1 , timer_base + TIMER1_RELOAD_OFF ) ;
writel ( ticks_per_jiffy - 1 , timer_base + TIMER1_VAL_OFF ) ;
2008-03-27 14:51:40 -04:00
/*
* Enable timer interrupt .
*/
2010-10-15 16:50:26 +02:00
u = readl ( bridge_base + BRIDGE_MASK_OFF ) ;
writel ( u | BRIDGE_INT_TIMER1 , bridge_base + BRIDGE_MASK_OFF ) ;
2008-03-27 14:51:40 -04:00
/*
* Enable timer .
*/
2010-10-15 16:50:26 +02:00
u = readl ( timer_base + TIMER_CTRL_OFF ) ;
writel ( u | TIMER1_EN | TIMER1_RELOAD_EN ,
timer_base + TIMER_CTRL_OFF ) ;
2008-03-27 14:51:40 -04:00
} else {
/*
* Disable timer .
*/
2010-10-15 16:50:26 +02:00
u = readl ( timer_base + TIMER_CTRL_OFF ) ;
writel ( u & ~ TIMER1_EN , timer_base + TIMER_CTRL_OFF ) ;
2008-03-27 14:51:40 -04:00
/*
* Disable timer interrupt .
*/
2010-10-15 16:50:26 +02:00
u = readl ( bridge_base + BRIDGE_MASK_OFF ) ;
writel ( u & ~ BRIDGE_INT_TIMER1 , bridge_base + BRIDGE_MASK_OFF ) ;
2008-03-27 14:51:40 -04:00
/*
* ACK pending timer interrupt .
*/
2010-10-15 16:50:26 +02:00
writel ( bridge_timer1_clr_mask , bridge_base + BRIDGE_CAUSE_OFF ) ;
2008-03-27 14:51:40 -04:00
}
local_irq_restore ( flags ) ;
}
static struct clock_event_device orion_clkevt = {
. name = " orion_tick " ,
. features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC ,
. rating = 300 ,
. set_next_event = orion_clkevt_next_event ,
. set_mode = orion_clkevt_mode ,
} ;
static irqreturn_t orion_timer_interrupt ( int irq , void * dev_id )
{
/*
* ACK timer interrupt and call event handler .
*/
2010-10-15 16:50:26 +02:00
writel ( bridge_timer1_clr_mask , bridge_base + BRIDGE_CAUSE_OFF ) ;
2008-03-27 14:51:40 -04:00
orion_clkevt . event_handler ( & orion_clkevt ) ;
return IRQ_HANDLED ;
}
static struct irqaction orion_timer_irq = {
. name = " orion_tick " ,
2013-10-12 05:49:20 +02:00
. flags = IRQF_TIMER ,
2008-03-27 14:51:40 -04:00
. handler = orion_timer_interrupt
} ;
2010-10-15 16:50:26 +02:00
void __init
2012-09-11 14:27:25 +02:00
orion_time_set_base ( void __iomem * _timer_base )
2010-10-15 16:50:26 +02:00
{
2012-09-11 14:27:25 +02:00
timer_base = _timer_base ;
2010-10-15 16:50:26 +02:00
}
void __init
2012-09-11 14:27:25 +02:00
orion_time_init ( void __iomem * _bridge_base , u32 _bridge_timer1_clr_mask ,
2010-10-15 16:50:26 +02:00
unsigned int irq , unsigned int tclk )
2008-03-27 14:51:40 -04:00
{
u32 u ;
2010-10-15 16:50:26 +02:00
/*
* Set SoC - specific data .
*/
2012-09-11 14:27:25 +02:00
bridge_base = _bridge_base ;
2010-10-15 16:50:26 +02:00
bridge_timer1_clr_mask = _bridge_timer1_clr_mask ;
2008-03-27 14:51:40 -04:00
ticks_per_jiffy = ( tclk + HZ / 2 ) / HZ ;
2009-05-12 10:30:41 -07:00
/*
2010-10-15 16:50:26 +02:00
* Set scale and timer for sched_clock .
2009-05-12 10:30:41 -07:00
*/
2013-11-15 15:26:24 -08:00
sched_clock_register ( orion_read_sched_clock , 32 , tclk ) ;
2008-03-27 14:51:40 -04:00
/*
* Setup free - running clocksource timer ( interrupts
2010-10-15 16:50:26 +02:00
* disabled ) .
2008-03-27 14:51:40 -04:00
*/
2010-10-15 16:50:26 +02:00
writel ( 0xffffffff , timer_base + TIMER0_VAL_OFF ) ;
writel ( 0xffffffff , timer_base + TIMER0_RELOAD_OFF ) ;
u = readl ( bridge_base + BRIDGE_MASK_OFF ) ;
writel ( u & ~ BRIDGE_INT_TIMER0 , bridge_base + BRIDGE_MASK_OFF ) ;
u = readl ( timer_base + TIMER_CTRL_OFF ) ;
writel ( u | TIMER0_EN | TIMER0_RELOAD_EN , timer_base + TIMER_CTRL_OFF ) ;
2011-05-08 15:33:30 +01:00
clocksource_mmio_init ( timer_base + TIMER0_VAL_OFF , " orion_clocksource " ,
tclk , 300 , 32 , clocksource_mmio_readl_down ) ;
2008-03-27 14:51:40 -04:00
/*
2010-10-15 16:50:26 +02:00
* Setup clockevent timer ( interrupt - driven ) .
2008-03-27 14:51:40 -04:00
*/
setup_irq ( irq , & orion_timer_irq ) ;
2008-12-13 21:20:26 +10:30
orion_clkevt . cpumask = cpumask_of ( 0 ) ;
2013-01-12 11:50:05 +00:00
clockevents_config_and_register ( & orion_clkevt , tclk , 1 , 0xfffffffe ) ;
2008-03-27 14:51:40 -04:00
}