2006-05-16 14:54:37 +04:00
/*
* arch / arm / mach - pnx4008 / time . c
*
* PNX4008 Timers
*
* Authors : Vitaly Wool , Dmitry Chigirev , Grigory Tolstolytkin < source @ mvista . com >
*
* 2005 ( c ) MontaVista Software , Inc . 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 .
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/sched.h>
# include <linux/spinlock.h>
# include <linux/module.h>
# include <linux/kallsyms.h>
2006-07-05 17:47:20 +04:00
# include <linux/time.h>
# include <linux/timex.h>
# include <linux/irq.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2006-05-16 14:54:37 +04:00
# include <asm/system.h>
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2006-05-16 14:54:37 +04:00
# include <asm/leds.h>
# include <asm/mach/time.h>
# include <asm/errno.h>
2009-11-21 14:43:33 +03:00
# include "time.h"
2006-05-16 14:54:37 +04:00
/*! Note: all timers are UPCOUNTING */
/*!
* Returns number of us since last clock interrupt . Note that interrupts
* will have been disabled by do_gettimeoffset ( )
*/
static unsigned long pnx4008_gettimeoffset ( void )
{
u32 ticks_to_match =
__raw_readl ( HSTIM_MATCH0 ) - __raw_readl ( HSTIM_COUNTER ) ;
u32 elapsed = LATCH - ticks_to_match ;
return ( elapsed * ( tick_nsec / 1000 ) ) / LATCH ;
}
/*!
* IRQ handler for the timer
*/
2006-10-06 21:53:39 +04:00
static irqreturn_t pnx4008_timer_interrupt ( int irq , void * dev_id )
2006-05-16 14:54:37 +04:00
{
if ( __raw_readl ( HSTIM_INT ) & MATCH0_INT ) {
do {
2006-10-06 21:53:39 +04:00
timer_tick ( ) ;
2006-05-16 14:54:37 +04:00
/*
* this algorithm takes care of possible delay
* for this interrupt handling longer than a normal
* timer period
*/
__raw_writel ( __raw_readl ( HSTIM_MATCH0 ) + LATCH ,
HSTIM_MATCH0 ) ;
__raw_writel ( MATCH0_INT , HSTIM_INT ) ; /* clear interrupt */
/*
* The goal is to keep incrementing HSTIM_MATCH0
* register until HSTIM_MATCH0 indicates time after
* what HSTIM_COUNTER indicates .
*/
} while ( ( signed )
( __raw_readl ( HSTIM_MATCH0 ) -
__raw_readl ( HSTIM_COUNTER ) ) < 0 ) ;
}
return IRQ_HANDLED ;
}
static struct irqaction pnx4008_timer_irq = {
. name = " PNX4008 Tick Timer " ,
2007-05-08 11:35:39 +04:00
. flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL ,
2006-05-16 14:54:37 +04:00
. handler = pnx4008_timer_interrupt
} ;
/*!
* Set up timer and timer interrupt .
*/
static __init void pnx4008_setup_timer ( void )
{
__raw_writel ( RESET_COUNT , MSTIM_CTRL ) ;
while ( __raw_readl ( MSTIM_COUNTER ) ) ; /* wait for reset to complete. 100% guarantee event */
__raw_writel ( 0 , MSTIM_CTRL ) ; /* stop the timer */
__raw_writel ( 0 , MSTIM_MCTRL ) ;
__raw_writel ( RESET_COUNT , HSTIM_CTRL ) ;
while ( __raw_readl ( HSTIM_COUNTER ) ) ; /* wait for reset to complete. 100% guarantee event */
__raw_writel ( 0 , HSTIM_CTRL ) ;
__raw_writel ( 0 , HSTIM_MCTRL ) ;
__raw_writel ( 0 , HSTIM_CCR ) ;
__raw_writel ( 12 , HSTIM_PMATCH ) ; /* scale down to 1 MHZ */
__raw_writel ( LATCH , HSTIM_MATCH0 ) ;
__raw_writel ( MR0_INT , HSTIM_MCTRL ) ;
setup_irq ( HSTIMER_INT , & pnx4008_timer_irq ) ;
__raw_writel ( COUNT_ENAB | DEBUG_EN , HSTIM_CTRL ) ; /*start timer, stop when JTAG active */
}
/* Timer Clock Control in PM register */
# define TIMCLK_CTRL_REG IO_ADDRESS((PNX4008_PWRMAN_BASE + 0xBC))
# define WATCHDOG_CLK_EN 1
# define TIMER_CLK_EN 2 /* HS and MS timers? */
static u32 timclk_ctrl_reg_save ;
void pnx4008_timer_suspend ( void )
{
timclk_ctrl_reg_save = __raw_readl ( TIMCLK_CTRL_REG ) ;
__raw_writel ( 0 , TIMCLK_CTRL_REG ) ; /* disable timers */
}
void pnx4008_timer_resume ( void )
{
__raw_writel ( timclk_ctrl_reg_save , TIMCLK_CTRL_REG ) ; /* enable timers */
}
struct sys_timer pnx4008_timer = {
. init = pnx4008_setup_timer ,
. offset = pnx4008_gettimeoffset ,
. suspend = pnx4008_timer_suspend ,
. resume = pnx4008_timer_resume ,
} ;