2008-03-27 21:51:40 +03: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 08:42:36 +04:00
# include <linux/timer.h>
2008-03-27 21:51:40 +03:00
# include <linux/clockchips.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
2013-06-02 10:39:40 +04:00
# include <linux/sched_clock.h>
2013-10-23 18:12:50 +04:00
# include <plat/time.h>
2015-10-19 18:00:35 +03:00
# include <asm/delay.h>
2008-03-27 21:51:40 +03:00
/*
2010-10-15 18:50:26 +04:00
* MBus bridge block registers .
2008-03-27 21:51:40 +03:00
*/
2010-10-15 18:50:26 +04:00
# define BRIDGE_CAUSE_OFF 0x0110
# define BRIDGE_MASK_OFF 0x0114
# define BRIDGE_INT_TIMER0 0x0002
# define BRIDGE_INT_TIMER1 0x0004
2008-03-27 21:51:40 +03:00
/*
* Timer block registers .
*/
2010-10-15 18:50:26 +04: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 21:51:40 +03:00
2009-05-12 21:30:41 +04:00
/*
* Orion ' s sched_clock implementation . It has a resolution of
2010-12-16 00:55:06 +03:00
* at least 7.5 ns ( 133 MHz TCLK ) .
2009-05-12 21:30:41 +04:00
*/
2013-11-16 03:26:24 +04:00
static u64 notrace orion_read_sched_clock ( void )
2009-05-15 08:42:36 +04:00
{
2011-12-15 15:19:23 +04:00
return ~ readl ( timer_base + TIMER0_VAL_OFF ) ;
2009-05-12 21:30:41 +04:00
}
2008-03-27 21:51:40 +03: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 18:50:26 +04:00
writel ( bridge_timer1_clr_mask , bridge_base + BRIDGE_CAUSE_OFF ) ;
2008-03-27 21:51:40 +03:00
2010-10-15 18:50:26 +04:00
u = readl ( bridge_base + BRIDGE_MASK_OFF ) ;
2008-03-27 21:51:40 +03:00
u | = BRIDGE_INT_TIMER1 ;
2010-10-15 18:50:26 +04:00
writel ( u , bridge_base + BRIDGE_MASK_OFF ) ;
2008-03-27 21:51:40 +03:00
/*
* Setup new clockevent timer value .
*/
2010-10-15 18:50:26 +04:00
writel ( delta , timer_base + TIMER1_VAL_OFF ) ;
2008-03-27 21:51:40 +03:00
/*
* Enable the timer .
*/
2010-10-15 18:50:26 +04:00
u = readl ( timer_base + TIMER_CTRL_OFF ) ;
2008-03-27 21:51:40 +03:00
u = ( u & ~ TIMER1_RELOAD_EN ) | TIMER1_EN ;
2010-10-15 18:50:26 +04:00
writel ( u , timer_base + TIMER_CTRL_OFF ) ;
2008-03-27 21:51:40 +03:00
local_irq_restore ( flags ) ;
return 0 ;
}
2015-02-27 11:09:52 +03:00
static int orion_clkevt_shutdown ( struct clock_event_device * evt )
2008-03-27 21:51:40 +03:00
{
unsigned long flags ;
u32 u ;
local_irq_save ( flags ) ;
2015-02-27 11:09:52 +03:00
/* Disable timer */
u = readl ( timer_base + TIMER_CTRL_OFF ) ;
writel ( u & ~ TIMER1_EN , timer_base + TIMER_CTRL_OFF ) ;
/* Disable timer interrupt */
u = readl ( bridge_base + BRIDGE_MASK_OFF ) ;
writel ( u & ~ BRIDGE_INT_TIMER1 , bridge_base + BRIDGE_MASK_OFF ) ;
/* ACK pending timer interrupt */
writel ( bridge_timer1_clr_mask , bridge_base + BRIDGE_CAUSE_OFF ) ;
local_irq_restore ( flags ) ;
return 0 ;
}
static int orion_clkevt_set_periodic ( struct clock_event_device * evt )
{
unsigned long flags ;
u32 u ;
local_irq_save ( flags ) ;
/* Setup timer to fire at 1/HZ intervals */
writel ( ticks_per_jiffy - 1 , timer_base + TIMER1_RELOAD_OFF ) ;
writel ( ticks_per_jiffy - 1 , timer_base + TIMER1_VAL_OFF ) ;
/* Enable timer interrupt */
u = readl ( bridge_base + BRIDGE_MASK_OFF ) ;
writel ( u | BRIDGE_INT_TIMER1 , bridge_base + BRIDGE_MASK_OFF ) ;
/* Enable timer */
u = readl ( timer_base + TIMER_CTRL_OFF ) ;
writel ( u | TIMER1_EN | TIMER1_RELOAD_EN , timer_base + TIMER_CTRL_OFF ) ;
2008-03-27 21:51:40 +03:00
local_irq_restore ( flags ) ;
2015-02-27 11:09:52 +03:00
return 0 ;
2008-03-27 21:51:40 +03:00
}
static struct clock_event_device orion_clkevt = {
2015-02-27 11:09:52 +03:00
. name = " orion_tick " ,
. features = CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_PERIODIC ,
. rating = 300 ,
. set_next_event = orion_clkevt_next_event ,
. set_state_shutdown = orion_clkevt_shutdown ,
. set_state_periodic = orion_clkevt_set_periodic ,
. set_state_oneshot = orion_clkevt_shutdown ,
. tick_resume = orion_clkevt_shutdown ,
2008-03-27 21:51:40 +03:00
} ;
static irqreturn_t orion_timer_interrupt ( int irq , void * dev_id )
{
/*
* ACK timer interrupt and call event handler .
*/
2010-10-15 18:50:26 +04:00
writel ( bridge_timer1_clr_mask , bridge_base + BRIDGE_CAUSE_OFF ) ;
2008-03-27 21:51:40 +03:00
orion_clkevt . event_handler ( & orion_clkevt ) ;
return IRQ_HANDLED ;
}
2010-10-15 18:50:26 +04:00
void __init
2012-09-11 16:27:25 +04:00
orion_time_set_base ( void __iomem * _timer_base )
2010-10-15 18:50:26 +04:00
{
2012-09-11 16:27:25 +04:00
timer_base = _timer_base ;
2010-10-15 18:50:26 +04:00
}
2015-10-19 18:00:35 +03:00
static unsigned long orion_delay_timer_read ( void )
{
return ~ readl ( timer_base + TIMER0_VAL_OFF ) ;
}
static struct delay_timer orion_delay_timer = {
. read_current_timer = orion_delay_timer_read ,
} ;
2010-10-15 18:50:26 +04:00
void __init
2012-09-11 16:27:25 +04:00
orion_time_init ( void __iomem * _bridge_base , u32 _bridge_timer1_clr_mask ,
2010-10-15 18:50:26 +04:00
unsigned int irq , unsigned int tclk )
2008-03-27 21:51:40 +03:00
{
u32 u ;
2010-10-15 18:50:26 +04:00
/*
* Set SoC - specific data .
*/
2012-09-11 16:27:25 +04:00
bridge_base = _bridge_base ;
2010-10-15 18:50:26 +04:00
bridge_timer1_clr_mask = _bridge_timer1_clr_mask ;
2008-03-27 21:51:40 +03:00
ticks_per_jiffy = ( tclk + HZ / 2 ) / HZ ;
2015-10-19 18:00:35 +03:00
orion_delay_timer . freq = tclk ;
register_current_timer_delay ( & orion_delay_timer ) ;
2009-05-12 21:30:41 +04:00
/*
2010-10-15 18:50:26 +04:00
* Set scale and timer for sched_clock .
2009-05-12 21:30:41 +04:00
*/
2013-11-16 03:26:24 +04:00
sched_clock_register ( orion_read_sched_clock , 32 , tclk ) ;
2008-03-27 21:51:40 +03:00
/*
* Setup free - running clocksource timer ( interrupts
2010-10-15 18:50:26 +04:00
* disabled ) .
2008-03-27 21:51:40 +03:00
*/
2010-10-15 18:50:26 +04: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 18:33:30 +04:00
clocksource_mmio_init ( timer_base + TIMER0_VAL_OFF , " orion_clocksource " ,
tclk , 300 , 32 , clocksource_mmio_readl_down ) ;
2008-03-27 21:51:40 +03:00
/*
2010-10-15 18:50:26 +04:00
* Setup clockevent timer ( interrupt - driven ) .
2008-03-27 21:51:40 +03:00
*/
2020-03-01 15:23:30 +03:00
if ( request_irq ( irq , orion_timer_interrupt , IRQF_TIMER , " orion_tick " ,
NULL ) )
pr_err ( " Failed to request irq %u (orion_tick) \n " , irq ) ;
2008-12-13 13:50:26 +03:00
orion_clkevt . cpumask = cpumask_of ( 0 ) ;
2013-01-12 15:50:05 +04:00
clockevents_config_and_register ( & orion_clkevt , tclk , 1 , 0xfffffffe ) ;
2008-03-27 21:51:40 +03:00
}