2012-06-13 20:58:09 +04:00
/*
* Marvell Armada 370 / XP SoC timer handling .
*
* Copyright ( C ) 2012 Marvell
*
* Lior Amsalem < alior @ marvell . com >
* Gregory CLEMENT < gregory . clement @ free - electrons . com >
* Thomas Petazzoni < thomas . petazzoni @ free - electrons . com >
*
* 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 .
2013-08-13 18:43:13 +04:00
*
* - - -
* Clocksource driver for Armada 370 and Armada XP SoC .
* This driver implements one compatible string for each SoC , given
* each has its own characteristics :
*
* * Armada 370 has no 25 MHz fixed timer .
*
* * Armada XP cannot work properly without such 25 MHz fixed timer as
* doing otherwise leads to using a clocksource whose frequency varies
* when doing cpufreq frequency changes .
*
* See Documentation / devicetree / bindings / timer / marvell , armada - 370 - xp - timer . txt
2012-06-13 20:58:09 +04:00
*/
# include <linux/init.h>
# include <linux/platform_device.h>
# include <linux/kernel.h>
2012-11-17 18:22:25 +04:00
# include <linux/clk.h>
2013-02-16 05:02:16 +04:00
# include <linux/cpu.h>
2012-06-13 20:58:09 +04:00
# include <linux/timer.h>
# include <linux/clockchips.h>
# include <linux/interrupt.h>
# include <linux/of.h>
# include <linux/of_irq.h>
# include <linux/of_address.h>
# include <linux/irq.h>
# include <linux/module.h>
2013-06-02 10:39:40 +04:00
# include <linux/sched_clock.h>
2013-01-25 21:32:42 +04:00
# include <linux/percpu.h>
2014-11-21 19:00:01 +03:00
# include <linux/syscore_ops.h>
2012-06-13 20:58:09 +04:00
2015-10-19 18:19:20 +03:00
# include <asm/delay.h>
2012-06-13 20:58:09 +04:00
/*
* Timer block registers .
*/
# define TIMER_CTRL_OFF 0x0000
2013-08-13 18:43:10 +04:00
# define TIMER0_EN BIT(0)
# define TIMER0_RELOAD_EN BIT(1)
# define TIMER0_25MHZ BIT(11)
2012-06-13 20:58:09 +04:00
# define TIMER0_DIV(div) ((div) << 19)
2013-08-13 18:43:10 +04:00
# define TIMER1_EN BIT(2)
# define TIMER1_RELOAD_EN BIT(3)
# define TIMER1_25MHZ BIT(12)
2012-06-13 20:58:09 +04:00
# define TIMER1_DIV(div) ((div) << 22)
# define TIMER_EVENTS_STATUS 0x0004
# define TIMER0_CLR_MASK (~0x1)
# define TIMER1_CLR_MASK (~0x100)
# define TIMER0_RELOAD_OFF 0x0010
# define TIMER0_VAL_OFF 0x0014
# define TIMER1_RELOAD_OFF 0x0018
# define TIMER1_VAL_OFF 0x001c
2013-01-25 21:32:42 +04:00
# define LCL_TIMER_EVENTS_STATUS 0x0028
2012-06-13 20:58:09 +04:00
/* Global timers are connected to the coherency fabric clock, and the
below divider reduces their incrementing frequency . */
# define TIMER_DIVIDER_SHIFT 5
# define TIMER_DIVIDER (1 << TIMER_DIVIDER_SHIFT)
/*
* SoC - specific data .
*/
2013-01-25 21:32:42 +04:00
static void __iomem * timer_base , * local_base ;
static unsigned int timer_clk ;
static bool timer25Mhz = true ;
2013-12-02 14:39:56 +04:00
static u32 enable_mask ;
2012-06-13 20:58:09 +04:00
/*
* Number of timer ticks per jiffy .
*/
static u32 ticks_per_jiffy ;
2013-02-16 05:02:16 +04:00
static struct clock_event_device __percpu * armada_370_xp_evt ;
2013-01-25 21:32:42 +04:00
2013-08-13 18:43:11 +04:00
static void local_timer_ctrl_clrset ( u32 clr , u32 set )
{
writel ( ( readl ( local_base + TIMER_CTRL_OFF ) & ~ clr ) | set ,
local_base + TIMER_CTRL_OFF ) ;
}
2013-01-25 21:32:42 +04:00
2013-07-19 03:21:27 +04:00
static u64 notrace armada_370_xp_read_sched_clock ( void )
2012-06-13 20:58:09 +04:00
{
return ~ readl ( timer_base + TIMER0_VAL_OFF ) ;
}
/*
* Clockevent handling .
*/
static int
armada_370_xp_clkevt_next_event ( unsigned long delta ,
struct clock_event_device * dev )
{
/*
* Clear clockevent timer interrupt .
*/
2013-01-25 21:32:42 +04:00
writel ( TIMER0_CLR_MASK , local_base + LCL_TIMER_EVENTS_STATUS ) ;
2012-06-13 20:58:09 +04:00
/*
* Setup new clockevent timer value .
*/
2013-01-25 21:32:42 +04:00
writel ( delta , local_base + TIMER0_VAL_OFF ) ;
2012-06-13 20:58:09 +04:00
/*
* Enable the timer .
*/
2013-12-02 14:39:56 +04:00
local_timer_ctrl_clrset ( TIMER0_RELOAD_EN , enable_mask ) ;
2012-06-13 20:58:09 +04:00
return 0 ;
}
2015-06-18 13:54:40 +03:00
static int armada_370_xp_clkevt_shutdown ( struct clock_event_device * evt )
2012-06-13 20:58:09 +04:00
{
2015-06-18 13:54:40 +03:00
/*
* Disable timer .
*/
local_timer_ctrl_clrset ( TIMER0_EN , 0 ) ;
/*
* ACK pending timer interrupt .
*/
writel ( TIMER0_CLR_MASK , local_base + LCL_TIMER_EVENTS_STATUS ) ;
return 0 ;
}
static int armada_370_xp_clkevt_set_periodic ( struct clock_event_device * evt )
{
/*
* Setup timer to fire at 1 / HZ intervals .
*/
writel ( ticks_per_jiffy - 1 , local_base + TIMER0_RELOAD_OFF ) ;
writel ( ticks_per_jiffy - 1 , local_base + TIMER0_VAL_OFF ) ;
/*
* Enable timer .
*/
local_timer_ctrl_clrset ( 0 , TIMER0_RELOAD_EN | enable_mask ) ;
return 0 ;
2012-06-13 20:58:09 +04:00
}
2013-02-16 05:02:16 +04:00
static int armada_370_xp_clkevt_irq ;
2012-06-13 20:58:09 +04:00
static irqreturn_t armada_370_xp_timer_interrupt ( int irq , void * dev_id )
{
/*
* ACK timer interrupt and call event handler .
*/
2013-02-16 05:02:16 +04:00
struct clock_event_device * evt = dev_id ;
2012-06-13 20:58:09 +04:00
2013-01-25 21:32:42 +04:00
writel ( TIMER0_CLR_MASK , local_base + LCL_TIMER_EVENTS_STATUS ) ;
evt - > event_handler ( evt ) ;
2012-06-13 20:58:09 +04:00
return IRQ_HANDLED ;
}
2013-01-25 21:32:42 +04:00
/*
* Setup the local clock events for a CPU .
*/
2016-07-13 20:17:06 +03:00
static int armada_370_xp_timer_starting_cpu ( unsigned int cpu )
2013-01-25 21:32:42 +04:00
{
2016-07-13 20:17:06 +03:00
struct clock_event_device * evt = per_cpu_ptr ( armada_370_xp_evt , cpu ) ;
2013-08-13 18:43:11 +04:00
u32 clr = 0 , set = 0 ;
2013-01-25 21:32:42 +04:00
if ( timer25Mhz )
2013-08-13 18:43:11 +04:00
set = TIMER0_25MHZ ;
2013-01-25 21:32:42 +04:00
else
2013-08-13 18:43:11 +04:00
clr = TIMER0_25MHZ ;
local_timer_ctrl_clrset ( clr , set ) ;
2013-01-25 21:32:42 +04:00
2013-02-16 05:02:16 +04:00
evt - > name = " armada_370_xp_per_cpu_tick " ,
evt - > features = CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_PERIODIC ;
evt - > shift = 32 ,
evt - > rating = 300 ,
2013-01-25 21:32:42 +04:00
evt - > set_next_event = armada_370_xp_clkevt_next_event ,
2015-06-18 13:54:40 +03:00
evt - > set_state_shutdown = armada_370_xp_clkevt_shutdown ;
evt - > set_state_periodic = armada_370_xp_clkevt_set_periodic ;
evt - > set_state_oneshot = armada_370_xp_clkevt_shutdown ;
evt - > tick_resume = armada_370_xp_clkevt_shutdown ;
2013-02-16 05:02:16 +04:00
evt - > irq = armada_370_xp_clkevt_irq ;
2013-01-25 21:32:42 +04:00
evt - > cpumask = cpumask_of ( cpu ) ;
clockevents_config_and_register ( evt , timer_clk , 1 , 0xfffffffe ) ;
enable_percpu_irq ( evt - > irq , 0 ) ;
return 0 ;
}
2016-07-13 20:17:06 +03:00
static int armada_370_xp_timer_dying_cpu ( unsigned int cpu )
2013-01-25 21:32:42 +04:00
{
2016-07-13 20:17:06 +03:00
struct clock_event_device * evt = per_cpu_ptr ( armada_370_xp_evt , cpu ) ;
2015-06-18 13:54:40 +03:00
evt - > set_state_shutdown ( evt ) ;
2013-01-25 21:32:42 +04:00
disable_percpu_irq ( evt - > irq ) ;
2016-07-13 20:17:06 +03:00
return 0 ;
2013-01-25 21:32:42 +04:00
}
2014-11-21 19:00:01 +03:00
static u32 timer0_ctrl_reg , timer0_local_ctrl_reg ;
static int armada_370_xp_timer_suspend ( void )
{
timer0_ctrl_reg = readl ( timer_base + TIMER_CTRL_OFF ) ;
timer0_local_ctrl_reg = readl ( local_base + TIMER_CTRL_OFF ) ;
return 0 ;
}
static void armada_370_xp_timer_resume ( void )
{
writel ( 0xffffffff , timer_base + TIMER0_VAL_OFF ) ;
writel ( 0xffffffff , timer_base + TIMER0_RELOAD_OFF ) ;
writel ( timer0_ctrl_reg , timer_base + TIMER_CTRL_OFF ) ;
writel ( timer0_local_ctrl_reg , local_base + TIMER_CTRL_OFF ) ;
}
2016-06-16 16:47:49 +03:00
static struct syscore_ops armada_370_xp_timer_syscore_ops = {
2014-11-21 19:00:01 +03:00
. suspend = armada_370_xp_timer_suspend ,
. resume = armada_370_xp_timer_resume ,
} ;
2015-10-19 18:19:20 +03:00
static unsigned long armada_370_delay_timer_read ( void )
{
return ~ readl ( timer_base + TIMER0_VAL_OFF ) ;
}
static struct delay_timer armada_370_delay_timer = {
. read_current_timer = armada_370_delay_timer_read ,
} ;
2016-06-06 19:00:01 +03:00
static int __init armada_370_xp_timer_common_init ( struct device_node * np )
2012-06-13 20:58:09 +04:00
{
2013-08-13 18:43:11 +04:00
u32 clr = 0 , set = 0 ;
2013-01-25 21:32:42 +04:00
int res ;
2012-06-13 20:58:09 +04:00
timer_base = of_iomap ( np , 0 ) ;
2016-06-06 19:00:01 +03:00
if ( ! timer_base ) {
pr_err ( " Failed to iomap " ) ;
return - ENXIO ;
}
2013-01-25 21:32:42 +04:00
local_base = of_iomap ( np , 1 ) ;
2016-06-06 19:00:01 +03:00
if ( ! local_base ) {
pr_err ( " Failed to iomap " ) ;
return - ENXIO ;
}
2012-06-13 20:58:09 +04:00
2013-12-02 14:39:56 +04:00
if ( timer25Mhz ) {
2013-09-17 00:10:26 +04:00
set = TIMER0_25MHZ ;
2013-12-02 14:39:56 +04:00
enable_mask = TIMER0_EN ;
} else {
2013-08-13 18:43:11 +04:00
clr = TIMER0_25MHZ ;
2013-12-02 14:39:56 +04:00
enable_mask = TIMER0_EN | TIMER0_DIV ( TIMER_DIVIDER_SHIFT ) ;
}
2014-02-20 00:05:26 +04:00
atomic_io_modify ( timer_base + TIMER_CTRL_OFF , clr | set , set ) ;
2013-08-13 18:43:11 +04:00
local_timer_ctrl_clrset ( clr , set ) ;
2012-06-13 20:58:09 +04:00
2013-01-25 21:32:42 +04:00
/*
* We use timer 0 as clocksource , and private ( local ) timer 0
* for clockevents
*/
2013-02-16 05:02:16 +04:00
armada_370_xp_clkevt_irq = irq_of_parse_and_map ( np , 4 ) ;
2012-06-13 20:58:09 +04:00
ticks_per_jiffy = ( timer_clk + HZ / 2 ) / HZ ;
/*
* Setup free - running clocksource timer ( interrupts
* disabled ) .
*/
writel ( 0xffffffff , timer_base + TIMER0_VAL_OFF ) ;
writel ( 0xffffffff , timer_base + TIMER0_RELOAD_OFF ) ;
2014-02-20 00:05:26 +04:00
atomic_io_modify ( timer_base + TIMER_CTRL_OFF ,
TIMER0_RELOAD_EN | enable_mask ,
TIMER0_RELOAD_EN | enable_mask ) ;
2012-06-13 20:58:09 +04:00
2015-10-19 18:19:20 +03:00
armada_370_delay_timer . freq = timer_clk ;
register_current_timer_delay ( & armada_370_delay_timer ) ;
clocksource: armada-370-xp: Register sched_clock after the counter reset
This commit registers the sched_clock _after_ the counter reset
(instead of before). This removes the timestamp 'jump' in kernel
log messages.
Before this change:
[ 0.000000] sched_clock: 32 bits at 25MHz, resolution 40ns, wraps every 171798691800ns
[ 0.000000] Initializing Coherency fabric
[ 0.000000] Aurora cache controller enabled
[ 0.000000] l2x0: 16 ways, CACHE_ID 0x00000100, AUX_CTRL 0x1a696b12, Cache size: 1024 kB
[ 163.507447] Calibrating delay loop... 1325.05 BogoMIPS (lpj=662528)
[ 163.521419] pid_max: default: 32768 minimum: 301
[ 163.526185] Mount-cache hash table entries: 512
[ 163.531095] CPU: Testing write buffer coherency: ok
After this change:
[ 0.000000] sched_clock: 32 bits at 25MHz, resolution 40ns, wraps every 171798691800ns
[ 0.000000] Initializing Coherency fabric
[ 0.000000] Aurora cache controller enabled
[ 0.000000] l2x0: 16 ways, CACHE_ID 0x00000100, AUX_CTRL 0x1a696b12, Cache size: 1024 kB
[ 0.016849] Calibrating delay loop... 1325.05 BogoMIPS (lpj=662528)
[ 0.030820] pid_max: default: 32768 minimum: 301
[ 0.035588] Mount-cache hash table entries: 512
[ 0.040500] CPU: Testing write buffer coherency: ok
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Acked-by: Jason Cooper <jason@lakedaemon.net>
2013-11-27 01:20:14 +04:00
/*
* Set scale and timer for sched_clock .
*/
sched_clock_register ( armada_370_xp_read_sched_clock , 32 , timer_clk ) ;
2016-06-06 19:00:01 +03:00
res = clocksource_mmio_init ( timer_base + TIMER0_VAL_OFF ,
" armada_370_xp_clocksource " ,
timer_clk , 300 , 32 , clocksource_mmio_readl_down ) ;
if ( res ) {
pr_err ( " Failed to initialize clocksource mmio " ) ;
return res ;
}
2012-06-13 20:58:09 +04:00
2013-02-16 05:02:16 +04:00
armada_370_xp_evt = alloc_percpu ( struct clock_event_device ) ;
2016-06-06 19:00:01 +03:00
if ( ! armada_370_xp_evt )
return - ENOMEM ;
2013-01-25 21:32:42 +04:00
/*
* Setup clockevent timer ( interrupt - driven ) .
*/
2013-02-16 05:02:16 +04:00
res = request_percpu_irq ( armada_370_xp_clkevt_irq ,
2013-01-25 21:32:42 +04:00
armada_370_xp_timer_interrupt ,
2013-02-16 05:02:16 +04:00
" armada_370_xp_per_cpu_tick " ,
armada_370_xp_evt ) ;
/* Immediately configure the timer on the boot CPU */
2016-06-06 19:00:01 +03:00
if ( res ) {
pr_err ( " Failed to request percpu irq " ) ;
return res ;
}
2016-07-13 20:17:06 +03:00
res = cpuhp_setup_state ( CPUHP_AP_ARMADA_TIMER_STARTING ,
2016-12-21 22:19:54 +03:00
" clockevents/armada:starting " ,
2016-07-13 20:17:06 +03:00
armada_370_xp_timer_starting_cpu ,
armada_370_xp_timer_dying_cpu ) ;
2016-07-12 17:40:09 +03:00
if ( res ) {
2016-07-13 20:17:06 +03:00
pr_err ( " Failed to setup hotplug state and timer " ) ;
2016-06-06 19:00:01 +03:00
return res ;
}
2014-11-21 19:00:01 +03:00
register_syscore_ops ( & armada_370_xp_timer_syscore_ops ) ;
2016-06-06 19:00:01 +03:00
return 0 ;
2013-01-25 21:32:42 +04:00
}
2013-08-13 18:43:13 +04:00
2016-06-06 19:00:01 +03:00
static int __init armada_xp_timer_init ( struct device_node * np )
2013-08-13 18:43:13 +04:00
{
2013-08-20 19:45:53 +04:00
struct clk * clk = of_clk_get_by_name ( np , " fixed " ) ;
2016-06-06 19:00:01 +03:00
int ret ;
if ( IS_ERR ( clk ) ) {
pr_err ( " Failed to get clock " ) ;
return PTR_ERR ( clk ) ;
}
ret = clk_prepare_enable ( clk ) ;
if ( ret )
return ret ;
2013-08-20 19:45:53 +04:00
timer_clk = clk_get_rate ( clk ) ;
2013-08-13 18:43:13 +04:00
2016-06-06 19:00:01 +03:00
return armada_370_xp_timer_common_init ( np ) ;
2013-08-13 18:43:13 +04:00
}
2016-06-07 01:27:44 +03:00
CLOCKSOURCE_OF_DECLARE ( armada_xp , " marvell,armada-xp-timer " ,
2013-08-13 18:43:13 +04:00
armada_xp_timer_init ) ;
2016-06-06 19:00:01 +03:00
static int __init armada_375_timer_init ( struct device_node * np )
2014-11-04 16:21:33 +03:00
{
struct clk * clk ;
2016-06-06 19:00:01 +03:00
int ret ;
2014-11-04 16:21:33 +03:00
clk = of_clk_get_by_name ( np , " fixed " ) ;
if ( ! IS_ERR ( clk ) ) {
2016-06-06 19:00:01 +03:00
ret = clk_prepare_enable ( clk ) ;
if ( ret )
return ret ;
2014-11-04 16:21:33 +03:00
timer_clk = clk_get_rate ( clk ) ;
} else {
/*
* This fallback is required in order to retain proper
* devicetree backwards compatibility .
*/
clk = of_clk_get ( np , 0 ) ;
/* Must have at least a clock */
2016-06-06 19:00:01 +03:00
if ( IS_ERR ( clk ) ) {
pr_err ( " Failed to get clock " ) ;
return PTR_ERR ( clk ) ;
}
ret = clk_prepare_enable ( clk ) ;
if ( ret )
return ret ;
2014-11-04 16:21:33 +03:00
timer_clk = clk_get_rate ( clk ) / TIMER_DIVIDER ;
timer25Mhz = false ;
}
2016-06-06 19:00:01 +03:00
return armada_370_xp_timer_common_init ( np ) ;
2014-11-04 16:21:33 +03:00
}
2016-06-07 01:27:44 +03:00
CLOCKSOURCE_OF_DECLARE ( armada_375 , " marvell,armada-375-timer " ,
2014-11-04 16:21:33 +03:00
armada_375_timer_init ) ;
2016-06-06 19:00:01 +03:00
static int __init armada_370_timer_init ( struct device_node * np )
2013-08-13 18:43:13 +04:00
{
2016-06-06 19:00:01 +03:00
struct clk * clk ;
int ret ;
clk = of_clk_get ( np , 0 ) ;
if ( IS_ERR ( clk ) ) {
pr_err ( " Failed to get clock " ) ;
return PTR_ERR ( clk ) ;
}
ret = clk_prepare_enable ( clk ) ;
if ( ret )
return ret ;
2013-08-13 18:43:13 +04:00
timer_clk = clk_get_rate ( clk ) / TIMER_DIVIDER ;
timer25Mhz = false ;
2016-06-06 19:00:01 +03:00
return armada_370_xp_timer_common_init ( np ) ;
2013-01-25 21:32:42 +04:00
}
2016-06-07 01:27:44 +03:00
CLOCKSOURCE_OF_DECLARE ( armada_370 , " marvell,armada-370-timer " ,
2013-08-13 18:43:13 +04:00
armada_370_timer_init ) ;