2009-01-20 14:15:18 +08:00
/*
* linux / arch / arm / mach - mmp / time . c
*
* Support for clocksource and clockevents
*
* Copyright ( C ) 2008 Marvell International Ltd .
* All rights reserved .
*
* 2008 - 04 - 11 : Jason Chagas < Jason . chagas @ marvell . com >
* 2008 - 10 - 08 : Bin Yang < bin . yang @ marvell . com >
*
2011-03-30 22:57:33 -03:00
* The timers module actually includes three timers , each timer with up to
2009-01-20 14:15:18 +08:00
* three match comparators . Timer # 0 is used here in free - running mode as
* the clock source , and match comparator # 1 used as clock event device .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/interrupt.h>
# include <linux/clockchips.h>
# include <linux/io.h>
# include <linux/irq.h>
2012-04-12 19:05:40 +08:00
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
2013-06-01 23:39:40 -07:00
# include <linux/sched_clock.h>
2009-01-20 14:15:18 +08:00
# include <mach/addr-map.h>
# include <mach/regs-timers.h>
2009-12-04 09:41:28 -05:00
# include <mach/regs-apbc.h>
2009-01-20 14:15:18 +08:00
# include <mach/irqs.h>
2009-12-04 09:41:28 -05:00
# include <mach/cputype.h>
# include <asm/mach/time.h>
2009-01-20 14:15:18 +08:00
# include "clock.h"
2013-11-12 20:56:02 +01:00
# ifdef CONFIG_CPU_MMP2
# define MMP_CLOCK_FREQ 6500000
# else
# define MMP_CLOCK_FREQ 3250000
# endif
2009-01-20 14:15:18 +08:00
# define TIMERS_VIRT_BASE TIMERS1_VIRT_BASE
# define MAX_DELTA (0xfffffffe)
# define MIN_DELTA (16)
2012-04-12 19:05:40 +08:00
static void __iomem * mmp_timer_base = TIMERS_VIRT_BASE ;
2009-01-20 14:15:18 +08:00
/*
* FIXME : the timer needs some delay to stablize the counter capture
*/
static inline uint32_t timer_read ( void )
{
int delay = 100 ;
2012-04-12 19:05:40 +08:00
__raw_writel ( 1 , mmp_timer_base + TMR_CVWR ( 1 ) ) ;
2009-01-20 14:15:18 +08:00
while ( delay - - )
cpu_relax ( ) ;
2012-04-12 19:05:40 +08:00
return __raw_readl ( mmp_timer_base + TMR_CVWR ( 1 ) ) ;
2009-01-20 14:15:18 +08:00
}
2013-11-15 15:26:15 -08:00
static u64 notrace mmp_read_sched_clock ( void )
2009-01-20 14:15:18 +08:00
{
2011-12-15 12:19:23 +01:00
return timer_read ( ) ;
2009-01-20 14:15:18 +08:00
}
static irqreturn_t timer_interrupt ( int irq , void * dev_id )
{
struct clock_event_device * c = dev_id ;
2011-08-10 02:37:55 +08:00
/*
* Clear pending interrupt status .
*/
2012-04-12 19:05:40 +08:00
__raw_writel ( 0x01 , mmp_timer_base + TMR_ICR ( 0 ) ) ;
2011-08-10 02:37:55 +08:00
/*
* Disable timer 0.
*/
2012-04-12 19:05:40 +08:00
__raw_writel ( 0x02 , mmp_timer_base + TMR_CER ) ;
2011-08-10 02:37:55 +08:00
2009-01-20 14:15:18 +08:00
c - > event_handler ( c ) ;
2011-08-10 02:37:55 +08:00
2009-01-20 14:15:18 +08:00
return IRQ_HANDLED ;
}
static int timer_set_next_event ( unsigned long delta ,
struct clock_event_device * dev )
{
2011-08-10 02:37:55 +08:00
unsigned long flags ;
2009-01-20 14:15:18 +08:00
local_irq_save ( flags ) ;
2011-08-10 02:37:55 +08:00
/*
* Disable timer 0.
*/
2012-04-12 19:05:40 +08:00
__raw_writel ( 0x02 , mmp_timer_base + TMR_CER ) ;
2011-08-10 02:37:55 +08:00
/*
* Clear and enable timer match 0 interrupt .
*/
2012-04-12 19:05:40 +08:00
__raw_writel ( 0x01 , mmp_timer_base + TMR_ICR ( 0 ) ) ;
__raw_writel ( 0x01 , mmp_timer_base + TMR_IER ( 0 ) ) ;
2009-01-20 14:15:18 +08:00
2011-08-10 02:37:55 +08:00
/*
* Setup new clockevent timer value .
*/
2012-04-12 19:05:40 +08:00
__raw_writel ( delta - 1 , mmp_timer_base + TMR_TN_MM ( 0 , 0 ) ) ;
2011-08-10 02:37:55 +08:00
/*
* Enable timer 0.
*/
2012-04-12 19:05:40 +08:00
__raw_writel ( 0x03 , mmp_timer_base + TMR_CER ) ;
2009-01-20 14:15:18 +08:00
local_irq_restore ( flags ) ;
2011-08-10 02:37:55 +08:00
2009-01-20 14:15:18 +08:00
return 0 ;
}
static void timer_set_mode ( enum clock_event_mode mode ,
struct clock_event_device * dev )
{
unsigned long flags ;
local_irq_save ( flags ) ;
switch ( mode ) {
case CLOCK_EVT_MODE_ONESHOT :
case CLOCK_EVT_MODE_UNUSED :
case CLOCK_EVT_MODE_SHUTDOWN :
/* disable the matching interrupt */
2012-04-12 19:05:40 +08:00
__raw_writel ( 0x00 , mmp_timer_base + TMR_IER ( 0 ) ) ;
2009-01-20 14:15:18 +08:00
break ;
case CLOCK_EVT_MODE_RESUME :
case CLOCK_EVT_MODE_PERIODIC :
break ;
}
local_irq_restore ( flags ) ;
}
static struct clock_event_device ckevt = {
. name = " clockevent " ,
. features = CLOCK_EVT_FEAT_ONESHOT ,
. rating = 200 ,
. set_next_event = timer_set_next_event ,
. set_mode = timer_set_mode ,
} ;
2009-04-23 03:04:45 +08:00
static cycle_t clksrc_read ( struct clocksource * cs )
2009-01-20 14:15:18 +08:00
{
return timer_read ( ) ;
}
static struct clocksource cksrc = {
. name = " clocksource " ,
. rating = 200 ,
. read = clksrc_read ,
. mask = CLOCKSOURCE_MASK ( 32 ) ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ;
static void __init timer_config ( void )
{
2012-04-12 19:05:40 +08:00
uint32_t ccr = __raw_readl ( mmp_timer_base + TMR_CCR ) ;
2009-01-20 14:15:18 +08:00
2012-04-12 19:05:40 +08:00
__raw_writel ( 0x0 , mmp_timer_base + TMR_CER ) ; /* disable */
2009-01-20 14:15:18 +08:00
2011-08-10 02:36:59 +08:00
ccr & = ( cpu_is_mmp2 ( ) ) ? ( TMR_CCR_CS_0 ( 0 ) | TMR_CCR_CS_1 ( 0 ) ) :
( TMR_CCR_CS_0 ( 3 ) | TMR_CCR_CS_1 ( 3 ) ) ;
2012-04-12 19:05:40 +08:00
__raw_writel ( ccr , mmp_timer_base + TMR_CCR ) ;
2009-01-20 14:15:18 +08:00
2011-08-10 02:37:55 +08:00
/* set timer 0 to periodic mode, and timer 1 to free-running mode */
2012-04-12 19:05:40 +08:00
__raw_writel ( 0x2 , mmp_timer_base + TMR_CMR ) ;
2009-01-20 14:15:18 +08:00
2012-04-12 19:05:40 +08:00
__raw_writel ( 0x1 , mmp_timer_base + TMR_PLCR ( 0 ) ) ; /* periodic */
__raw_writel ( 0x7 , mmp_timer_base + TMR_ICR ( 0 ) ) ; /* clear status */
__raw_writel ( 0x0 , mmp_timer_base + TMR_IER ( 0 ) ) ;
2009-01-20 14:15:18 +08:00
2012-04-12 19:05:40 +08:00
__raw_writel ( 0x0 , mmp_timer_base + TMR_PLCR ( 1 ) ) ; /* free-running */
__raw_writel ( 0x7 , mmp_timer_base + TMR_ICR ( 1 ) ) ; /* clear status */
__raw_writel ( 0x0 , mmp_timer_base + TMR_IER ( 1 ) ) ;
2011-08-10 02:36:59 +08:00
2011-08-10 02:37:55 +08:00
/* enable timer 1 counter */
2012-04-12 19:05:40 +08:00
__raw_writel ( 0x2 , mmp_timer_base + TMR_CER ) ;
2009-01-20 14:15:18 +08:00
}
static struct irqaction timer_irq = {
. name = " timer " ,
2014-03-04 22:07:26 +01:00
. flags = IRQF_TIMER | IRQF_IRQPOLL ,
2009-01-20 14:15:18 +08:00
. handler = timer_interrupt ,
. dev_id = & ckevt ,
} ;
void __init timer_init ( int irq )
{
timer_config ( ) ;
2014-02-18 22:19:33 -08:00
sched_clock_register ( mmp_read_sched_clock , 32 , MMP_CLOCK_FREQ ) ;
2009-01-20 14:15:18 +08:00
ckevt . cpumask = cpumask_of ( 0 ) ;
setup_irq ( irq , & timer_irq ) ;
2013-11-12 20:56:02 +01:00
clocksource_register_hz ( & cksrc , MMP_CLOCK_FREQ ) ;
clockevents_config_and_register ( & ckevt , MMP_CLOCK_FREQ ,
2013-01-12 11:50:05 +00:00
MIN_DELTA , MAX_DELTA ) ;
2009-01-20 14:15:18 +08:00
}
2012-04-12 19:05:40 +08:00
# ifdef CONFIG_OF
static struct of_device_id mmp_timer_dt_ids [ ] = {
{ . compatible = " mrvl,mmp-timer " , } ,
{ }
} ;
void __init mmp_dt_init_timer ( void )
{
struct device_node * np ;
int irq , ret ;
np = of_find_matching_node ( NULL , mmp_timer_dt_ids ) ;
if ( ! np ) {
ret = - ENODEV ;
goto out ;
}
irq = irq_of_parse_and_map ( np , 0 ) ;
if ( ! irq ) {
ret = - EINVAL ;
goto out ;
}
mmp_timer_base = of_iomap ( np , 0 ) ;
if ( ! mmp_timer_base ) {
ret = - ENOMEM ;
goto out ;
}
timer_init ( irq ) ;
return ;
out :
pr_err ( " Failed to get timer from device tree with error:%d \n " , ret ) ;
}
# endif