2010-02-27 02:53:41 +03:00
/*
* arch / arm / mach - lpc32xx / timer . c
*
* Author : Kevin Wells < kevin . wells @ nxp . com >
*
* Copyright ( C ) 2009 - 2010 NXP Semiconductors
* Copyright ( C ) 2009 Fontys University of Applied Sciences , Eindhoven
* Ed Schouten < e . schouten @ fontys . nl >
* Laurens Timmermans < l . timmermans @ fontys . nl >
*
* 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 .
*/
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/time.h>
# include <linux/err.h>
# include <linux/clockchips.h>
# include <asm/mach/time.h>
# include <mach/hardware.h>
# include <mach/platform.h>
# include "common.h"
static int lpc32xx_clkevt_next_event ( unsigned long delta ,
struct clock_event_device * dev )
{
2012-02-27 20:31:24 +04:00
__raw_writel ( LPC32XX_TIMER_CNTR_TCR_RESET ,
LPC32XX_TIMER_TCR ( LPC32XX_TIMER0_BASE ) ) ;
__raw_writel ( delta , LPC32XX_TIMER_PR ( LPC32XX_TIMER0_BASE ) ) ;
__raw_writel ( LPC32XX_TIMER_CNTR_TCR_EN ,
LPC32XX_TIMER_TCR ( LPC32XX_TIMER0_BASE ) ) ;
2010-02-27 02:53:41 +03:00
return 0 ;
}
static void lpc32xx_clkevt_mode ( enum clock_event_mode mode ,
struct clock_event_device * dev )
{
switch ( mode ) {
case CLOCK_EVT_MODE_PERIODIC :
WARN_ON ( 1 ) ;
break ;
case CLOCK_EVT_MODE_ONESHOT :
case CLOCK_EVT_MODE_SHUTDOWN :
/*
* Disable the timer . When using oneshot , we must also
* disable the timer to wait for the first call to
* set_next_event ( ) .
*/
2012-02-27 20:31:24 +04:00
__raw_writel ( 0 , LPC32XX_TIMER_TCR ( LPC32XX_TIMER0_BASE ) ) ;
2010-02-27 02:53:41 +03:00
break ;
case CLOCK_EVT_MODE_UNUSED :
case CLOCK_EVT_MODE_RESUME :
break ;
}
}
static struct clock_event_device lpc32xx_clkevt = {
. name = " lpc32xx_clkevt " ,
. features = CLOCK_EVT_FEAT_ONESHOT ,
. rating = 300 ,
. set_next_event = lpc32xx_clkevt_next_event ,
. set_mode = lpc32xx_clkevt_mode ,
} ;
static irqreturn_t lpc32xx_timer_interrupt ( int irq , void * dev_id )
{
struct clock_event_device * evt = & lpc32xx_clkevt ;
/* Clear match */
2012-02-27 20:31:24 +04:00
__raw_writel ( LPC32XX_TIMER_CNTR_MTCH_BIT ( 0 ) ,
LPC32XX_TIMER_IR ( LPC32XX_TIMER0_BASE ) ) ;
2010-02-27 02:53:41 +03:00
evt - > event_handler ( evt ) ;
return IRQ_HANDLED ;
}
static struct irqaction lpc32xx_timer_irq = {
. name = " LPC32XX Timer Tick " ,
. flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL ,
. handler = lpc32xx_timer_interrupt ,
} ;
/*
* The clock management driver isn ' t initialized at this point , so the
* clocks need to be enabled here manually and then tagged as used in
* the clock driver initialization
*/
2012-11-08 23:40:59 +04:00
void __init lpc32xx_timer_init ( void )
2010-02-27 02:53:41 +03:00
{
u32 clkrate , pllreg ;
/* Enable timer clock */
__raw_writel ( LPC32XX_CLKPWR_TMRPWMCLK_TIMER0_EN |
LPC32XX_CLKPWR_TMRPWMCLK_TIMER1_EN ,
LPC32XX_CLKPWR_TIMERS_PWMS_CLK_CTRL_1 ) ;
/*
* The clock driver isn ' t initialized at this point . So determine if
* the SYSCLK is driven from the PLL397 or main oscillator and then use
* it to compute the PLL frequency and the PCLK divider to get the base
* timer rates . This rate is needed to compute the tick rate .
*/
if ( clk_is_sysclk_mainosc ( ) ! = 0 )
clkrate = LPC32XX_MAIN_OSC_FREQ ;
else
clkrate = 397 * LPC32XX_CLOCK_OSC_FREQ ;
/* Get ARM HCLKPLL register and convert it into a frequency */
pllreg = __raw_readl ( LPC32XX_CLKPWR_HCLKPLL_CTRL ) & 0x1FFFF ;
clkrate = clk_get_pllrate_from_reg ( clkrate , pllreg ) ;
/* Get PCLK divider and divide ARM PLL clock by it to get timer rate */
clkrate = clkrate / clk_get_pclk_div ( ) ;
/* Initial timer setup */
2012-02-27 20:31:24 +04:00
__raw_writel ( 0 , LPC32XX_TIMER_TCR ( LPC32XX_TIMER0_BASE ) ) ;
__raw_writel ( LPC32XX_TIMER_CNTR_MTCH_BIT ( 0 ) ,
LPC32XX_TIMER_IR ( LPC32XX_TIMER0_BASE ) ) ;
__raw_writel ( 1 , LPC32XX_TIMER_MR0 ( LPC32XX_TIMER0_BASE ) ) ;
__raw_writel ( LPC32XX_TIMER_CNTR_MCR_MTCH ( 0 ) |
LPC32XX_TIMER_CNTR_MCR_STOP ( 0 ) |
LPC32XX_TIMER_CNTR_MCR_RESET ( 0 ) ,
LPC32XX_TIMER_MCR ( LPC32XX_TIMER0_BASE ) ) ;
2010-02-27 02:53:41 +03:00
/* Setup tick interrupt */
setup_irq ( IRQ_LPC32XX_TIMER0 , & lpc32xx_timer_irq ) ;
/* Setup the clockevent structure. */
lpc32xx_clkevt . cpumask = cpumask_of ( 0 ) ;
2013-01-12 15:50:05 +04:00
clockevents_config_and_register ( & lpc32xx_clkevt , clkrate , 1 , - 1 ) ;
2010-02-27 02:53:41 +03:00
/* Use timer1 as clock source. */
2012-02-27 20:31:24 +04:00
__raw_writel ( LPC32XX_TIMER_CNTR_TCR_RESET ,
LPC32XX_TIMER_TCR ( LPC32XX_TIMER1_BASE ) ) ;
__raw_writel ( 0 , LPC32XX_TIMER_PR ( LPC32XX_TIMER1_BASE ) ) ;
__raw_writel ( 0 , LPC32XX_TIMER_MCR ( LPC32XX_TIMER1_BASE ) ) ;
__raw_writel ( LPC32XX_TIMER_CNTR_TCR_EN ,
LPC32XX_TIMER_TCR ( LPC32XX_TIMER1_BASE ) ) ;
clocksource_mmio_init ( LPC32XX_TIMER_TC ( LPC32XX_TIMER1_BASE ) ,
2011-05-08 17:09:47 +04:00
" lpc32xx_clksrc " , clkrate , 300 , 32 , clocksource_mmio_readl_up ) ;
2010-02-27 02:53:41 +03:00
}