2006-04-02 17:46:21 +01:00
/*
2008-03-13 08:47:21 +02:00
* linux / arch / arm / mach - omap1 / timer32k . c
2006-04-02 17:46:21 +01:00
*
* OMAP 32 K Timer
*
* Copyright ( C ) 2004 - 2005 Nokia Corporation
* Partial timer rewrite and additional dynamic tick timer support by
* Tony Lindgen < tony @ atomide . com > and
* Tuukka Tikkanen < tuukka . tikkanen @ elektrobit . com >
2006-06-26 16:16:12 -07:00
* OMAP Dual - mode timer framework support by Timo Teras
2006-04-02 17:46:21 +01:00
*
* MPU timer code based on the older MPU timer code for OMAP
* Copyright ( C ) 2000 RidgeRun , Inc .
* Author : Greg Lonnon < glonnon @ ridgerun . com >
*
* 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 SOFTWARE IS PROVIDED ` ` AS IS ' ' AND ANY EXPRESS OR IMPLIED
* WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF
* USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* 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 . ,
* 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# 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/err.h>
# include <linux/clk.h>
2007-03-08 20:32:19 +01:00
# include <linux/clocksource.h>
# include <linux/clockchips.h>
2008-09-06 12:10:45 +01:00
# include <linux/io.h>
2006-04-02 17:46:21 +01:00
# include <asm/leds.h>
# include <asm/irq.h>
# include <asm/mach/irq.h>
# include <asm/mach/time.h>
2012-02-24 10:34:34 -08:00
2009-10-20 09:40:47 -07:00
# include <plat/dmtimer.h>
2006-04-02 17:46:21 +01:00
2012-02-24 10:34:34 -08:00
# include <mach/hardware.h>
# include "common.h"
2006-04-02 17:46:21 +01:00
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* 32 KHz OS timer
*
* This currently works only on 16 xx , as 1510 does not have the continuous
* 32 KHz synchronous timer . The 32 KHz synchronous timer is used to keep track
* of time in addition to the 32 KHz OS timer . Using only the 32 KHz OS timer
* on 1510 would be possible , but the timer would not be as accurate as
* with the 32 KHz synchronized timer .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
/* 16xx specific defines */
# define OMAP1_32K_TIMER_BASE 0xfffb9000
# define OMAP1_32K_TIMER_CR 0x08
# define OMAP1_32K_TIMER_TVR 0x00
# define OMAP1_32K_TIMER_TCR 0x04
2007-03-08 20:32:19 +01:00
# define OMAP_32K_TICKS_PER_SEC (32768)
2006-04-02 17:46:21 +01:00
/*
* TRM says 1 / HZ = ( TVR + 1 ) / 32768 , so TRV = ( 32768 / HZ ) - 1
* so with HZ = 128 , TVR = 255.
*/
2007-03-08 20:32:19 +01:00
# define OMAP_32K_TIMER_TICK_PERIOD ((OMAP_32K_TICKS_PER_SEC / HZ) - 1)
2006-04-02 17:46:21 +01:00
# define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \
( ( ( nr_jiffies ) * ( clock_rate ) ) / HZ )
static inline void omap_32k_timer_write ( int val , int reg )
{
2006-06-26 16:16:12 -07:00
omap_writew ( val , OMAP1_32K_TIMER_BASE + reg ) ;
2006-04-02 17:46:21 +01:00
}
static inline unsigned long omap_32k_timer_read ( int reg )
{
2006-06-26 16:16:12 -07:00
return omap_readl ( OMAP1_32K_TIMER_BASE + reg ) & 0xffffff ;
}
2006-04-02 17:46:21 +01:00
2006-06-26 16:16:12 -07:00
static inline void omap_32k_timer_start ( unsigned long load_val )
{
2006-09-25 12:41:21 +03:00
if ( ! load_val )
load_val = 1 ;
2006-06-26 16:16:12 -07:00
omap_32k_timer_write ( load_val , OMAP1_32K_TIMER_TVR ) ;
omap_32k_timer_write ( 0x0f , OMAP1_32K_TIMER_CR ) ;
2006-04-02 17:46:21 +01:00
}
2006-06-26 16:16:12 -07:00
static inline void omap_32k_timer_stop ( void )
2006-04-02 17:46:21 +01:00
{
2006-06-26 16:16:12 -07:00
omap_32k_timer_write ( 0x0 , OMAP1_32K_TIMER_CR ) ;
2006-04-02 17:46:21 +01:00
}
2006-06-26 16:16:12 -07:00
# define omap_32k_timer_ack_irq()
2008-03-13 08:47:21 +02:00
static int omap_32k_timer_set_next_event ( unsigned long delta ,
struct clock_event_device * dev )
{
omap_32k_timer_start ( delta ) ;
return 0 ;
}
2007-03-08 20:32:19 +01:00
static void omap_32k_timer_set_mode ( enum clock_event_mode mode ,
struct clock_event_device * evt )
{
2007-05-16 08:52:05 -07:00
omap_32k_timer_stop ( ) ;
2007-03-08 20:32:19 +01:00
switch ( mode ) {
case CLOCK_EVT_MODE_PERIODIC :
omap_32k_timer_start ( OMAP_32K_TIMER_TICK_PERIOD ) ;
break ;
2007-05-16 08:52:05 -07:00
case CLOCK_EVT_MODE_ONESHOT :
2007-03-08 20:32:19 +01:00
case CLOCK_EVT_MODE_UNUSED :
case CLOCK_EVT_MODE_SHUTDOWN :
break ;
2007-07-21 04:37:34 -07:00
case CLOCK_EVT_MODE_RESUME :
break ;
2007-03-08 20:32:19 +01:00
}
}
static struct clock_event_device clockevent_32k_timer = {
. name = " 32k-timer " ,
2008-03-13 08:47:21 +02:00
. features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT ,
2007-03-08 20:32:19 +01:00
. shift = 32 ,
2008-03-13 08:47:21 +02:00
. set_next_event = omap_32k_timer_set_next_event ,
2007-03-08 20:32:19 +01:00
. set_mode = omap_32k_timer_set_mode ,
} ;
2006-10-06 10:53:39 -07:00
static irqreturn_t omap_32k_timer_interrupt ( int irq , void * dev_id )
2006-09-25 12:41:40 +03:00
{
2007-03-08 20:32:19 +01:00
struct clock_event_device * evt = & clockevent_32k_timer ;
omap_32k_timer_ack_irq ( ) ;
2006-09-25 12:41:40 +03:00
2007-03-08 20:32:19 +01:00
evt - > event_handler ( evt ) ;
2006-04-02 17:46:21 +01:00
return IRQ_HANDLED ;
}
static struct irqaction omap_32k_timer_irq = {
. name = " 32KHz timer " ,
2007-05-08 00:35:39 -07:00
. flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL ,
2006-04-02 17:46:21 +01:00
. handler = omap_32k_timer_interrupt ,
} ;
static __init void omap_init_32k_timer ( void )
{
2008-03-13 08:47:21 +02:00
setup_irq ( INT_OS_TIMER , & omap_32k_timer_irq ) ;
2007-03-08 20:32:19 +01:00
clockevent_32k_timer . mult = div_sc ( OMAP_32K_TICKS_PER_SEC ,
NSEC_PER_SEC ,
clockevent_32k_timer . shift ) ;
clockevent_32k_timer . max_delta_ns =
clockevent_delta2ns ( 0xfffffffe , & clockevent_32k_timer ) ;
clockevent_32k_timer . min_delta_ns =
clockevent_delta2ns ( 1 , & clockevent_32k_timer ) ;
2008-12-13 21:20:26 +10:30
clockevent_32k_timer . cpumask = cpumask_of ( 0 ) ;
2007-03-08 20:32:19 +01:00
clockevents_register_device ( & clockevent_32k_timer ) ;
2006-04-02 17:46:21 +01:00
}
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Timer initialization
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
2011-01-18 12:42:23 -08:00
bool __init omap_32k_timer_init ( void )
2006-04-02 17:46:21 +01:00
{
2011-01-18 12:42:23 -08:00
omap_init_clocksource_32k ( ) ;
2006-04-02 17:46:21 +01:00
omap_init_32k_timer ( ) ;
2011-01-18 12:42:23 -08:00
return true ;
}