2008-07-05 12:02:50 +04:00
/*
* linux / arch / arm / plat - mxc / time . c
*
* Copyright ( C ) 2000 - 2001 Deep Blue Solutions
* Copyright ( C ) 2002 Shane Nay ( shane @ minirl . com )
* Copyright ( C ) 2006 - 2007 Pavel Pisa ( ppisa @ pikron . com )
* Copyright ( C ) 2008 Juergen Beisert ( kernel @ pengutronix . de )
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
* 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 .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston ,
* MA 02110 - 1301 , USA .
*/
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/clockchips.h>
# include <linux/clk.h>
2012-03-09 12:29:27 +04:00
# include <linux/err.h>
2008-07-05 12:02:50 +04:00
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2011-03-17 15:44:30 +03:00
# include <asm/sched_clock.h>
2008-07-05 12:02:50 +04:00
# include <asm/mach/time.h>
2008-08-05 19:14:15 +04:00
# include <mach/common.h>
2009-02-18 22:58:40 +03:00
2009-12-04 11:34:51 +03:00
/*
* There are 2 versions of the timer hardware on Freescale MXC hardware .
* Version 1 : MX1 / MXL , MX21 , MX27 .
* Version 2 : MX25 , MX31 , MX35 , MX37 , MX51
*/
2009-02-18 22:58:40 +03:00
/* defines common for all i.MX */
# define MXC_TCTL 0x00
2009-12-04 11:34:51 +03:00
# define MXC_TCTL_TEN (1 << 0) /* Enable module */
2009-02-18 22:58:40 +03:00
# define MXC_TPRER 0x04
/* MX1, MX21, MX27 */
# define MX1_2_TCTL_CLK_PCLK1 (1 << 1)
# define MX1_2_TCTL_IRQEN (1 << 4)
# define MX1_2_TCTL_FRR (1 << 8)
# define MX1_2_TCMP 0x08
# define MX1_2_TCN 0x10
# define MX1_2_TSTAT 0x14
/* MX21, MX27 */
# define MX2_TSTAT_CAPT (1 << 1)
# define MX2_TSTAT_COMP (1 << 0)
2011-04-07 13:13:25 +04:00
/* MX31, MX35, MX25, MX5 */
2010-04-21 22:34:36 +04:00
# define V2_TCTL_WAITEN (1 << 3) /* Wait enable mode */
# define V2_TCTL_CLK_IPG (1 << 6)
2012-05-15 11:34:40 +04:00
# define V2_TCTL_CLK_PER (2 << 6)
2010-04-21 22:34:36 +04:00
# define V2_TCTL_FRR (1 << 9)
# define V2_IR 0x0c
# define V2_TSTAT 0x08
# define V2_TSTAT_OF1 (1 << 0)
# define V2_TCN 0x24
# define V2_TCMP 0x10
2008-07-05 12:02:50 +04:00
2009-12-04 11:34:51 +03:00
# define timer_is_v1() (cpu_is_mx1() || cpu_is_mx21() || cpu_is_mx27())
# define timer_is_v2() (!timer_is_v1())
2008-07-05 12:02:50 +04:00
static struct clock_event_device clockevent_mxc ;
static enum clock_event_mode clockevent_mode = CLOCK_EVT_MODE_UNUSED ;
2009-02-18 22:58:40 +03:00
static void __iomem * timer_base ;
2008-07-05 12:02:50 +04:00
2009-02-18 22:58:40 +03:00
static inline void gpt_irq_disable ( void )
2008-07-05 12:02:50 +04:00
{
2009-02-18 22:58:40 +03:00
unsigned int tmp ;
2009-12-04 11:34:51 +03:00
if ( timer_is_v2 ( ) )
2010-04-21 22:34:36 +04:00
__raw_writel ( 0 , timer_base + V2_IR ) ;
2009-02-18 22:58:40 +03:00
else {
tmp = __raw_readl ( timer_base + MXC_TCTL ) ;
__raw_writel ( tmp & ~ MX1_2_TCTL_IRQEN , timer_base + MXC_TCTL ) ;
}
}
static inline void gpt_irq_enable ( void )
{
2009-12-04 11:34:51 +03:00
if ( timer_is_v2 ( ) )
2010-04-21 22:34:36 +04:00
__raw_writel ( 1 < < 0 , timer_base + V2_IR ) ;
2009-02-18 22:58:40 +03:00
else {
__raw_writel ( __raw_readl ( timer_base + MXC_TCTL ) | MX1_2_TCTL_IRQEN ,
timer_base + MXC_TCTL ) ;
}
}
static void gpt_irq_acknowledge ( void )
{
2009-12-04 11:34:51 +03:00
if ( timer_is_v1 ( ) ) {
if ( cpu_is_mx1 ( ) )
__raw_writel ( 0 , timer_base + MX1_2_TSTAT ) ;
else
__raw_writel ( MX2_TSTAT_CAPT | MX2_TSTAT_COMP ,
timer_base + MX1_2_TSTAT ) ;
} else if ( timer_is_v2 ( ) )
2010-04-23 08:49:43 +04:00
__raw_writel ( V2_TSTAT_OF1 , timer_base + V2_TSTAT ) ;
2009-02-18 22:58:40 +03:00
}
2011-05-08 17:09:47 +04:00
static void __iomem * sched_clock_reg ;
2008-07-05 12:02:50 +04:00
2011-12-15 15:19:23 +04:00
static u32 notrace mxc_read_sched_clock ( void )
2011-03-17 15:44:30 +03:00
{
2011-12-15 15:19:23 +04:00
return sched_clock_reg ? __raw_readl ( sched_clock_reg ) : 0 ;
2011-03-17 15:44:30 +03:00
}
2009-02-16 16:36:49 +03:00
static int __init mxc_clocksource_init ( struct clk * timer_clk )
2008-07-05 12:02:50 +04:00
{
2009-01-26 18:34:51 +03:00
unsigned int c = clk_get_rate ( timer_clk ) ;
2011-05-08 17:09:47 +04:00
void __iomem * reg = timer_base + ( timer_is_v2 ( ) ? V2_TCN : MX1_2_TCN ) ;
2008-07-05 12:02:50 +04:00
2011-05-08 17:09:47 +04:00
sched_clock_reg = reg ;
2009-02-18 22:58:40 +03:00
2011-12-15 15:19:23 +04:00
setup_sched_clock ( mxc_read_sched_clock , 32 , c ) ;
2011-05-08 17:09:47 +04:00
return clocksource_mmio_init ( reg , " mxc_timer1 " , c , 200 , 32 ,
clocksource_mmio_readl_up ) ;
2008-07-05 12:02:50 +04:00
}
/* clock event */
2009-02-18 22:58:40 +03:00
static int mx1_2_set_next_event ( unsigned long evt ,
2008-07-05 12:02:50 +04:00
struct clock_event_device * unused )
{
unsigned long tcmp ;
2009-02-18 22:58:40 +03:00
tcmp = __raw_readl ( timer_base + MX1_2_TCN ) + evt ;
2008-07-05 12:02:50 +04:00
2009-02-18 22:58:40 +03:00
__raw_writel ( tcmp , timer_base + MX1_2_TCMP ) ;
return ( int ) ( tcmp - __raw_readl ( timer_base + MX1_2_TCN ) ) < 0 ?
- ETIME : 0 ;
}
2010-04-21 22:34:36 +04:00
static int v2_set_next_event ( unsigned long evt ,
2009-02-18 22:58:40 +03:00
struct clock_event_device * unused )
{
unsigned long tcmp ;
2010-04-21 22:34:36 +04:00
tcmp = __raw_readl ( timer_base + V2_TCN ) + evt ;
2009-02-18 22:58:40 +03:00
2010-04-21 22:34:36 +04:00
__raw_writel ( tcmp , timer_base + V2_TCMP ) ;
2009-02-18 22:58:40 +03:00
2010-04-21 22:34:36 +04:00
return ( int ) ( tcmp - __raw_readl ( timer_base + V2_TCN ) ) < 0 ?
2008-07-05 12:02:50 +04:00
- ETIME : 0 ;
}
# ifdef DEBUG
static const char * clock_event_mode_label [ ] = {
[ CLOCK_EVT_MODE_PERIODIC ] = " CLOCK_EVT_MODE_PERIODIC " ,
[ CLOCK_EVT_MODE_ONESHOT ] = " CLOCK_EVT_MODE_ONESHOT " ,
[ CLOCK_EVT_MODE_SHUTDOWN ] = " CLOCK_EVT_MODE_SHUTDOWN " ,
[ CLOCK_EVT_MODE_UNUSED ] = " CLOCK_EVT_MODE_UNUSED "
} ;
# endif /* DEBUG */
static void mxc_set_mode ( enum clock_event_mode mode ,
struct clock_event_device * evt )
{
unsigned long flags ;
/*
* The timer interrupt generation is disabled at least
* for enough time to call mxc_set_next_event ( )
*/
local_irq_save ( flags ) ;
/* Disable interrupt in GPT module */
gpt_irq_disable ( ) ;
if ( mode ! = clockevent_mode ) {
/* Set event time into far-far future */
2009-12-04 11:34:51 +03:00
if ( timer_is_v2 ( ) )
2010-04-21 22:34:36 +04:00
__raw_writel ( __raw_readl ( timer_base + V2_TCN ) - 3 ,
timer_base + V2_TCMP ) ;
2009-02-18 22:58:40 +03:00
else
__raw_writel ( __raw_readl ( timer_base + MX1_2_TCN ) - 3 ,
timer_base + MX1_2_TCMP ) ;
2008-07-05 12:02:50 +04:00
/* Clear pending interrupt */
gpt_irq_acknowledge ( ) ;
}
# ifdef DEBUG
printk ( KERN_INFO " mxc_set_mode: changing mode from %s to %s \n " ,
clock_event_mode_label [ clockevent_mode ] ,
clock_event_mode_label [ mode ] ) ;
# endif /* DEBUG */
/* Remember timer mode */
clockevent_mode = mode ;
local_irq_restore ( flags ) ;
switch ( mode ) {
case CLOCK_EVT_MODE_PERIODIC :
printk ( KERN_ERR " mxc_set_mode: Periodic mode is not "
" supported for i.MX \n " ) ;
break ;
case CLOCK_EVT_MODE_ONESHOT :
/*
* Do not put overhead of interrupt enable / disable into
* mxc_set_next_event ( ) , the core has about 4 minutes
* to call mxc_set_next_event ( ) or shutdown clock after
* mode switching
*/
local_irq_save ( flags ) ;
gpt_irq_enable ( ) ;
local_irq_restore ( flags ) ;
break ;
case CLOCK_EVT_MODE_SHUTDOWN :
case CLOCK_EVT_MODE_UNUSED :
case CLOCK_EVT_MODE_RESUME :
/* Left event sources disabled, no more interrupts appear */
break ;
}
}
/*
* IRQ handler for the timer
*/
static irqreturn_t mxc_timer_interrupt ( int irq , void * dev_id )
{
struct clock_event_device * evt = & clockevent_mxc ;
uint32_t tstat ;
2009-12-04 11:34:51 +03:00
if ( timer_is_v2 ( ) )
2010-04-21 22:34:36 +04:00
tstat = __raw_readl ( timer_base + V2_TSTAT ) ;
2009-04-29 15:55:13 +04:00
else
tstat = __raw_readl ( timer_base + MX1_2_TSTAT ) ;
2008-07-05 12:02:50 +04:00
gpt_irq_acknowledge ( ) ;
evt - > event_handler ( evt ) ;
return IRQ_HANDLED ;
}
static struct irqaction mxc_timer_irq = {
. name = " i.MX Timer Tick " ,
. flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL ,
. handler = mxc_timer_interrupt ,
} ;
static struct clock_event_device clockevent_mxc = {
. name = " mxc_timer1 " ,
. features = CLOCK_EVT_FEAT_ONESHOT ,
. shift = 32 ,
. set_mode = mxc_set_mode ,
2009-02-18 22:58:40 +03:00
. set_next_event = mx1_2_set_next_event ,
2008-07-05 12:02:50 +04:00
. rating = 200 ,
} ;
2009-02-16 16:36:49 +03:00
static int __init mxc_clockevent_init ( struct clk * timer_clk )
2008-07-05 12:02:50 +04:00
{
2009-01-26 18:34:51 +03:00
unsigned int c = clk_get_rate ( timer_clk ) ;
2008-07-05 12:02:50 +04:00
2009-12-04 11:34:51 +03:00
if ( timer_is_v2 ( ) )
2010-04-21 22:34:36 +04:00
clockevent_mxc . set_next_event = v2_set_next_event ;
2009-02-18 22:58:40 +03:00
2009-01-26 18:34:51 +03:00
clockevent_mxc . mult = div_sc ( c , NSEC_PER_SEC ,
2008-07-05 12:02:50 +04:00
clockevent_mxc . shift ) ;
clockevent_mxc . max_delta_ns =
clockevent_delta2ns ( 0xfffffffe , & clockevent_mxc ) ;
clockevent_mxc . min_delta_ns =
clockevent_delta2ns ( 0xff , & clockevent_mxc ) ;
2008-12-13 13:50:26 +03:00
clockevent_mxc . cpumask = cpumask_of ( 0 ) ;
2008-07-05 12:02:50 +04:00
clockevents_register_device ( & clockevent_mxc ) ;
return 0 ;
}
2009-05-25 14:21:38 +04:00
void __init mxc_timer_init ( struct clk * timer_clk , void __iomem * base , int irq )
2008-07-05 12:02:50 +04:00
{
2009-02-18 22:58:40 +03:00
uint32_t tctl_val ;
2012-03-09 12:29:27 +04:00
struct clk * timer_ipg_clk ;
if ( ! timer_clk ) {
timer_clk = clk_get_sys ( " imx-gpt.0 " , " per " ) ;
if ( IS_ERR ( timer_clk ) ) {
pr_err ( " i.MX timer: unable to get clk \n " ) ;
return ;
}
timer_ipg_clk = clk_get_sys ( " imx-gpt.0 " , " ipg " ) ;
if ( ! IS_ERR ( timer_ipg_clk ) )
clk_prepare_enable ( timer_ipg_clk ) ;
}
2009-02-18 22:58:40 +03:00
2011-11-15 10:47:57 +04:00
clk_prepare_enable ( timer_clk ) ;
2008-07-05 12:02:50 +04:00
2009-05-25 14:21:38 +04:00
timer_base = base ;
2009-02-18 22:58:40 +03:00
2008-07-05 12:02:50 +04:00
/*
* Initialise to a known state ( all timers off , and timing reset )
*/
2009-02-18 22:58:40 +03:00
__raw_writel ( 0 , timer_base + MXC_TCTL ) ;
__raw_writel ( 0 , timer_base + MXC_TPRER ) ; /* see datasheet note */
2009-12-04 11:34:51 +03:00
if ( timer_is_v2 ( ) )
2012-05-15 11:34:40 +04:00
tctl_val = V2_TCTL_CLK_PER | V2_TCTL_FRR | V2_TCTL_WAITEN | MXC_TCTL_TEN ;
2009-02-18 22:58:40 +03:00
else
tctl_val = MX1_2_TCTL_FRR | MX1_2_TCTL_CLK_PCLK1 | MXC_TCTL_TEN ;
__raw_writel ( tctl_val , timer_base + MXC_TCTL ) ;
2008-07-05 12:02:50 +04:00
/* init and register the timer to the framework */
2009-02-16 16:36:49 +03:00
mxc_clocksource_init ( timer_clk ) ;
mxc_clockevent_init ( timer_clk ) ;
2008-07-05 12:02:50 +04:00
/* Make irqs happen */
2009-02-18 22:58:40 +03:00
setup_irq ( irq , & mxc_timer_irq ) ;
2008-07-05 12:02:50 +04:00
}