2007-11-26 15:11:58 +03:00
/* linux/arch/arm/mach-msm/timer.c
*
* Copyright ( C ) 2007 Google , Inc .
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
*/
# include <linux/init.h>
# include <linux/time.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/clk.h>
# include <linux/clockchips.h>
# include <linux/delay.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2007-11-26 15:11:58 +03:00
# include <asm/mach/time.h>
2011-06-01 03:10:00 +04:00
# include <asm/hardware/gic.h>
2008-08-05 19:14:15 +04:00
# include <mach/msm_iomap.h>
2011-01-07 21:20:49 +03:00
# include <mach/cpu.h>
2007-11-26 15:11:58 +03:00
# define TIMER_MATCH_VAL 0x0000
# define TIMER_COUNT_VAL 0x0004
# define TIMER_ENABLE 0x0008
# define TIMER_ENABLE_CLR_ON_MATCH_EN 2
# define TIMER_ENABLE_EN 1
# define TIMER_CLEAR 0x000C
2010-10-06 02:23:57 +04:00
# define DGT_CLK_CTL 0x0034
enum {
DGT_CLK_CTL_DIV_1 = 0 ,
DGT_CLK_CTL_DIV_2 = 1 ,
DGT_CLK_CTL_DIV_3 = 2 ,
DGT_CLK_CTL_DIV_4 = 3 ,
} ;
2007-11-26 15:11:58 +03:00
# define CSR_PROTECTION 0x0020
# define CSR_PROTECTION_EN 1
# define GPT_HZ 32768
2010-10-06 02:23:57 +04:00
2010-12-02 23:05:12 +03:00
enum timer_location {
LOCAL_TIMER = 0 ,
GLOBAL_TIMER = 1 ,
} ;
# define MSM_GLOBAL_TIMER MSM_CLOCK_DGT
2011-01-07 21:20:49 +03:00
/* TODO: Remove these ifdefs */
2010-10-06 02:23:57 +04:00
# if defined(CONFIG_ARCH_QSD8X50)
# define DGT_HZ (19200000 / 4) /* 19.2 MHz / 4 by default */
# define MSM_DGT_SHIFT (0)
2011-04-22 03:09:11 +04:00
# elif defined(CONFIG_ARCH_MSM7X30)
2010-10-06 02:23:57 +04:00
# define DGT_HZ (24576000 / 4) /* 24.576 MHz (LPXO) / 4 by default */
# define MSM_DGT_SHIFT (0)
2011-04-22 03:09:11 +04:00
# elif defined(CONFIG_ARCH_MSM8X60) || defined(CONFIG_ARCH_MSM8960)
# define DGT_HZ (27000000 / 4) /* 27 MHz (PXO) / 4 by default */
# define MSM_DGT_SHIFT (0)
2010-10-06 02:23:57 +04:00
# else
2007-11-26 15:11:58 +03:00
# define DGT_HZ 19200000 /* 19.2 MHz or 600 KHz after shift */
2010-10-06 02:23:57 +04:00
# define MSM_DGT_SHIFT (5)
# endif
2007-11-26 15:11:58 +03:00
struct msm_clock {
struct clock_event_device clockevent ;
struct clocksource clocksource ;
2011-07-22 15:52:37 +04:00
unsigned int irq ;
2008-09-11 01:00:53 +04:00
void __iomem * regbase ;
2007-11-26 15:11:58 +03:00
uint32_t freq ;
uint32_t shift ;
2010-12-02 23:05:12 +03:00
void __iomem * global_counter ;
void __iomem * local_counter ;
2011-07-22 15:52:37 +04:00
union {
struct clock_event_device * evt ;
struct clock_event_device __percpu * * percpu_evt ;
} ;
2010-12-02 23:05:12 +03:00
} ;
enum {
MSM_CLOCK_GPT ,
MSM_CLOCK_DGT ,
NR_TIMERS ,
2007-11-26 15:11:58 +03:00
} ;
2010-12-02 23:05:12 +03:00
static struct msm_clock msm_clocks [ ] ;
2007-11-26 15:11:58 +03:00
static irqreturn_t msm_timer_interrupt ( int irq , void * dev_id )
{
2011-07-22 15:52:37 +04:00
struct clock_event_device * evt = * ( struct clock_event_device * * ) dev_id ;
2010-12-02 23:05:12 +03:00
if ( evt - > event_handler = = NULL )
return IRQ_HANDLED ;
2007-11-26 15:11:58 +03:00
evt - > event_handler ( evt ) ;
return IRQ_HANDLED ;
}
2010-12-02 23:05:12 +03:00
static cycle_t msm_read_timer_count ( struct clocksource * cs )
2007-11-26 15:11:58 +03:00
{
2010-12-02 23:05:12 +03:00
struct msm_clock * clk = container_of ( cs , struct msm_clock , clocksource ) ;
2011-06-18 00:55:38 +04:00
/*
* Shift timer count down by a constant due to unreliable lower bits
* on some targets .
*/
return readl ( clk - > global_counter ) > > clk - > shift ;
2007-11-26 15:11:58 +03:00
}
2010-12-02 23:05:12 +03:00
static struct msm_clock * clockevent_to_clock ( struct clock_event_device * evt )
2007-11-26 15:11:58 +03:00
{
2010-12-02 23:05:12 +03:00
# ifdef CONFIG_SMP
int i ;
for ( i = 0 ; i < NR_TIMERS ; i + + )
if ( evt = = & ( msm_clocks [ i ] . clockevent ) )
return & msm_clocks [ i ] ;
return & msm_clocks [ MSM_GLOBAL_TIMER ] ;
# else
return container_of ( evt , struct msm_clock , clockevent ) ;
# endif
2007-11-26 15:11:58 +03:00
}
static int msm_timer_set_next_event ( unsigned long cycles ,
struct clock_event_device * evt )
{
2010-12-02 23:05:12 +03:00
struct msm_clock * clock = clockevent_to_clock ( evt ) ;
uint32_t now = readl ( clock - > local_counter ) ;
2007-11-26 15:11:58 +03:00
uint32_t alarm = now + ( cycles < < clock - > shift ) ;
writel ( alarm , clock - > regbase + TIMER_MATCH_VAL ) ;
return 0 ;
}
static void msm_timer_set_mode ( enum clock_event_mode mode ,
struct clock_event_device * evt )
{
2010-12-02 23:05:12 +03:00
struct msm_clock * clock = clockevent_to_clock ( evt ) ;
2007-11-26 15:11:58 +03:00
switch ( mode ) {
case CLOCK_EVT_MODE_RESUME :
case CLOCK_EVT_MODE_PERIODIC :
break ;
case CLOCK_EVT_MODE_ONESHOT :
writel ( TIMER_ENABLE_EN , clock - > regbase + TIMER_ENABLE ) ;
break ;
case CLOCK_EVT_MODE_UNUSED :
case CLOCK_EVT_MODE_SHUTDOWN :
writel ( 0 , clock - > regbase + TIMER_ENABLE ) ;
break ;
}
}
static struct msm_clock msm_clocks [ ] = {
2010-12-02 23:05:12 +03:00
[ MSM_CLOCK_GPT ] = {
2007-11-26 15:11:58 +03:00
. clockevent = {
. name = " gp_timer " ,
. features = CLOCK_EVT_FEAT_ONESHOT ,
. shift = 32 ,
. rating = 200 ,
. set_next_event = msm_timer_set_next_event ,
. set_mode = msm_timer_set_mode ,
} ,
. clocksource = {
. name = " gp_timer " ,
. rating = 200 ,
2010-12-02 23:05:12 +03:00
. read = msm_read_timer_count ,
2007-11-26 15:11:58 +03:00
. mask = CLOCKSOURCE_MASK ( 32 ) ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ,
2011-07-22 15:52:37 +04:00
. irq = INT_GP_TIMER_EXP ,
2010-12-02 23:05:12 +03:00
. freq = GPT_HZ ,
2007-11-26 15:11:58 +03:00
} ,
2010-12-02 23:05:12 +03:00
[ MSM_CLOCK_DGT ] = {
2007-11-26 15:11:58 +03:00
. clockevent = {
. name = " dg_timer " ,
. features = CLOCK_EVT_FEAT_ONESHOT ,
. shift = 32 + MSM_DGT_SHIFT ,
. rating = 300 ,
. set_next_event = msm_timer_set_next_event ,
. set_mode = msm_timer_set_mode ,
} ,
. clocksource = {
. name = " dg_timer " ,
. rating = 300 ,
2010-12-02 23:05:12 +03:00
. read = msm_read_timer_count ,
2007-11-26 15:11:58 +03:00
. mask = CLOCKSOURCE_MASK ( ( 32 - MSM_DGT_SHIFT ) ) ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ,
2011-07-22 15:52:37 +04:00
. irq = INT_DEBUG_TIMER_EXP ,
2007-11-26 15:11:58 +03:00
. freq = DGT_HZ > > MSM_DGT_SHIFT ,
2010-12-02 23:05:12 +03:00
. shift = MSM_DGT_SHIFT ,
2007-11-26 15:11:58 +03:00
}
} ;
static void __init msm_timer_init ( void )
{
int i ;
int res ;
2011-01-07 21:20:49 +03:00
int global_offset = 0 ;
if ( cpu_is_msm7x01 ( ) ) {
msm_clocks [ MSM_CLOCK_GPT ] . regbase = MSM_CSR_BASE ;
msm_clocks [ MSM_CLOCK_DGT ] . regbase = MSM_CSR_BASE + 0x10 ;
} else if ( cpu_is_msm7x30 ( ) ) {
msm_clocks [ MSM_CLOCK_GPT ] . regbase = MSM_CSR_BASE + 0x04 ;
msm_clocks [ MSM_CLOCK_DGT ] . regbase = MSM_CSR_BASE + 0x24 ;
} else if ( cpu_is_qsd8x50 ( ) ) {
msm_clocks [ MSM_CLOCK_GPT ] . regbase = MSM_CSR_BASE ;
msm_clocks [ MSM_CLOCK_DGT ] . regbase = MSM_CSR_BASE + 0x10 ;
2010-12-02 06:25:14 +03:00
} else if ( cpu_is_msm8x60 ( ) | | cpu_is_msm8960 ( ) ) {
2011-01-07 21:20:49 +03:00
msm_clocks [ MSM_CLOCK_GPT ] . regbase = MSM_TMR_BASE + 0x04 ;
msm_clocks [ MSM_CLOCK_DGT ] . regbase = MSM_TMR_BASE + 0x24 ;
/* Use CPU0's timer as the global timer. */
global_offset = MSM_TMR0_BASE - MSM_TMR_BASE ;
} else
BUG ( ) ;
2007-11-26 15:11:58 +03:00
2010-12-02 23:05:12 +03:00
# ifdef CONFIG_ARCH_MSM_SCORPIONMP
2010-10-06 02:23:57 +04:00
writel ( DGT_CLK_CTL_DIV_4 , MSM_TMR_BASE + DGT_CLK_CTL ) ;
# endif
2007-11-26 15:11:58 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( msm_clocks ) ; i + + ) {
struct msm_clock * clock = & msm_clocks [ i ] ;
struct clock_event_device * ce = & clock - > clockevent ;
struct clocksource * cs = & clock - > clocksource ;
2011-01-07 21:20:49 +03:00
clock - > local_counter = clock - > regbase + TIMER_COUNT_VAL ;
clock - > global_counter = clock - > local_counter + global_offset ;
2007-11-26 15:11:58 +03:00
writel ( 0 , clock - > regbase + TIMER_ENABLE ) ;
writel ( 0 , clock - > regbase + TIMER_CLEAR ) ;
writel ( ~ 0 , clock - > regbase + TIMER_MATCH_VAL ) ;
ce - > mult = div_sc ( clock - > freq , NSEC_PER_SEC , ce - > shift ) ;
/* allow at least 10 seconds to notice that the timer wrapped */
ce - > max_delta_ns =
clockevent_delta2ns ( 0xf0000000 > > clock - > shift , ce ) ;
/* 4 gets rounded down to 3 */
ce - > min_delta_ns = clockevent_delta2ns ( 4 , ce ) ;
2008-12-13 13:50:26 +03:00
ce - > cpumask = cpumask_of ( 0 ) ;
2007-11-26 15:11:58 +03:00
2010-12-13 16:18:12 +03:00
res = clocksource_register_hz ( cs , clock - > freq ) ;
2007-11-26 15:11:58 +03:00
if ( res )
printk ( KERN_ERR " msm_timer_init: clocksource_register "
" failed for %s \n " , cs - > name ) ;
2011-07-22 15:52:37 +04:00
ce - > irq = clock - > irq ;
if ( cpu_is_msm8x60 ( ) | | cpu_is_msm8960 ( ) ) {
clock - > percpu_evt = alloc_percpu ( struct clock_event_device * ) ;
if ( ! clock - > percpu_evt ) {
pr_err ( " msm_timer_init: memory allocation "
" failed for %s \n " , ce - > name ) ;
continue ;
}
* __this_cpu_ptr ( clock - > percpu_evt ) = ce ;
res = request_percpu_irq ( ce - > irq , msm_timer_interrupt ,
ce - > name , clock - > percpu_evt ) ;
if ( ! res )
enable_percpu_irq ( ce - > irq , 0 ) ;
} else {
clock - > evt = ce ;
res = request_irq ( ce - > irq , msm_timer_interrupt ,
IRQF_TIMER | IRQF_NOBALANCING | IRQF_TRIGGER_RISING ,
ce - > name , & clock - > evt ) ;
}
2007-11-26 15:11:58 +03:00
if ( res )
2011-07-22 15:52:37 +04:00
pr_err ( " msm_timer_init: request_irq failed for %s \n " ,
ce - > name ) ;
2007-11-26 15:11:58 +03:00
clockevents_register_device ( ce ) ;
}
}
2011-11-08 22:34:03 +04:00
# ifdef CONFIG_LOCAL_TIMERS
2011-02-23 20:53:15 +03:00
int __cpuinit local_timer_setup ( struct clock_event_device * evt )
2010-12-02 23:05:12 +03:00
{
2011-07-22 15:52:37 +04:00
static bool local_timer_inited ;
2010-12-02 23:05:12 +03:00
struct msm_clock * clock = & msm_clocks [ MSM_GLOBAL_TIMER ] ;
/* Use existing clock_event for cpu 0 */
if ( ! smp_processor_id ( ) )
2011-03-30 22:26:57 +04:00
return 0 ;
2010-12-02 23:05:12 +03:00
writel ( DGT_CLK_CTL_DIV_4 , MSM_TMR_BASE + DGT_CLK_CTL ) ;
2011-07-22 15:52:37 +04:00
if ( ! local_timer_inited ) {
2010-12-02 23:05:12 +03:00
writel ( 0 , clock - > regbase + TIMER_ENABLE ) ;
writel ( 0 , clock - > regbase + TIMER_CLEAR ) ;
writel ( ~ 0 , clock - > regbase + TIMER_MATCH_VAL ) ;
2011-07-22 15:52:37 +04:00
local_timer_inited = true ;
2010-12-02 23:05:12 +03:00
}
2011-07-22 15:52:37 +04:00
evt - > irq = clock - > irq ;
2010-12-02 23:05:12 +03:00
evt - > name = " local_timer " ;
evt - > features = CLOCK_EVT_FEAT_ONESHOT ;
evt - > rating = clock - > clockevent . rating ;
evt - > set_mode = msm_timer_set_mode ;
evt - > set_next_event = msm_timer_set_next_event ;
evt - > shift = clock - > clockevent . shift ;
evt - > mult = div_sc ( clock - > freq , NSEC_PER_SEC , evt - > shift ) ;
evt - > max_delta_ns =
clockevent_delta2ns ( 0xf0000000 > > clock - > shift , evt ) ;
evt - > min_delta_ns = clockevent_delta2ns ( 4 , evt ) ;
2011-07-22 15:52:37 +04:00
* __this_cpu_ptr ( clock - > percpu_evt ) = evt ;
enable_percpu_irq ( evt - > irq , 0 ) ;
2010-12-02 23:05:12 +03:00
clockevents_register_device ( evt ) ;
2011-02-23 20:53:15 +03:00
return 0 ;
2010-12-02 23:05:12 +03:00
}
2011-07-22 15:52:37 +04:00
void local_timer_stop ( struct clock_event_device * evt )
2010-12-02 23:05:12 +03:00
{
2011-07-22 15:52:37 +04:00
evt - > set_mode ( CLOCK_EVT_MODE_UNUSED , evt ) ;
disable_percpu_irq ( evt - > irq ) ;
2010-12-02 23:05:12 +03:00
}
2011-11-08 22:34:03 +04:00
# endif /* CONFIG_LOCAL_TIMERS */
2010-12-02 23:05:12 +03:00
2007-11-26 15:11:58 +03:00
struct sys_timer msm_timer = {
. init = msm_timer_init
} ;