2005-04-17 02:20:36 +04:00
/*
* linux / arch / arm / mach - sa1100 / time . c
*
* Copyright ( C ) 1998 Deborah Wallach .
2008-11-26 22:58:43 +03:00
* Twiddles ( C ) 1999 Hugo Fiennes < hugo @ empeg . com >
*
2009-09-14 11:25:28 +04:00
* 2000 / 03 / 29 ( C ) Nicolas Pitre < nico @ fluxnic . net >
2005-04-17 02:20:36 +04:00
* Rewritten : big cleanup , much simpler , better HZ accuracy .
*
*/
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/interrupt.h>
2006-07-02 01:32:38 +04:00
# include <linux/irq.h>
2005-04-17 02:20:36 +04:00
# include <linux/timex.h>
2008-04-15 02:03:10 +04:00
# include <linux/clockchips.h>
2005-04-17 02:20:36 +04:00
# include <asm/mach/time.h>
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2005-04-17 02:20:36 +04:00
2008-04-15 02:03:10 +04:00
# define MIN_OSCR_DELTA 2
2005-04-17 02:20:36 +04:00
2008-04-15 02:03:10 +04:00
static irqreturn_t sa1100_ost0_interrupt ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
2008-04-15 02:03:10 +04:00
struct clock_event_device * c = dev_id ;
2005-04-17 02:20:36 +04:00
2008-04-15 02:03:10 +04:00
/* Disarm the compare/match, signal the event. */
OIER & = ~ OIER_E0 ;
OSSR = OSSR_M0 ;
c - > event_handler ( c ) ;
2005-04-17 02:20:36 +04:00
2008-04-15 02:03:10 +04:00
return IRQ_HANDLED ;
}
2005-09-01 15:48:48 +04:00
2008-04-15 02:03:10 +04:00
static int
sa1100_osmr0_set_next_event ( unsigned long delta , struct clock_event_device * c )
2005-04-17 02:20:36 +04:00
{
2008-04-15 02:03:10 +04:00
unsigned long flags , next , oscr ;
2005-04-17 02:20:36 +04:00
2008-04-15 02:03:10 +04:00
raw_local_irq_save ( flags ) ;
OIER | = OIER_E0 ;
next = OSCR + delta ;
OSMR0 = next ;
oscr = OSCR ;
raw_local_irq_restore ( flags ) ;
2005-09-01 15:48:48 +04:00
2008-04-15 02:03:10 +04:00
return ( signed ) ( next - oscr ) < = MIN_OSCR_DELTA ? - ETIME : 0 ;
}
2005-04-17 02:20:36 +04:00
2008-04-15 02:03:10 +04:00
static void
sa1100_osmr0_set_mode ( enum clock_event_mode mode , struct clock_event_device * c )
{
unsigned long flags ;
switch ( mode ) {
case CLOCK_EVT_MODE_ONESHOT :
case CLOCK_EVT_MODE_UNUSED :
case CLOCK_EVT_MODE_SHUTDOWN :
raw_local_irq_save ( flags ) ;
OIER & = ~ OIER_E0 ;
OSSR = OSSR_M0 ;
raw_local_irq_restore ( flags ) ;
break ;
case CLOCK_EVT_MODE_RESUME :
case CLOCK_EVT_MODE_PERIODIC :
break ;
}
2005-04-17 02:20:36 +04:00
}
2008-04-15 02:03:10 +04:00
static struct clock_event_device ckevt_sa1100_osmr0 = {
. name = " osmr0 " ,
. features = CLOCK_EVT_FEAT_ONESHOT ,
. shift = 32 ,
. rating = 200 ,
. set_next_event = sa1100_osmr0_set_next_event ,
. set_mode = sa1100_osmr0_set_mode ,
2005-04-17 02:20:36 +04:00
} ;
2009-09-27 20:32:47 +04:00
static cycle_t sa1100_read_oscr ( struct clocksource * s )
2007-11-13 00:55:12 +03:00
{
return OSCR ;
}
static struct clocksource cksrc_sa1100_oscr = {
. name = " oscr " ,
. rating = 200 ,
. read = sa1100_read_oscr ,
. mask = CLOCKSOURCE_MASK ( 32 ) ,
. shift = 20 ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ;
2008-04-15 02:03:10 +04:00
static struct irqaction sa1100_timer_irq = {
. name = " ost0 " ,
. flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL ,
. handler = sa1100_ost0_interrupt ,
. dev_id = & ckevt_sa1100_osmr0 ,
} ;
2005-04-17 02:20:36 +04:00
static void __init sa1100_timer_init ( void )
{
2005-11-09 01:43:06 +03:00
OIER = 0 ; /* disable any timer interrupts */
2005-04-17 02:20:36 +04:00
OSSR = 0xf ; /* clear status on all timers */
2008-04-15 02:03:10 +04:00
ckevt_sa1100_osmr0 . mult =
div_sc ( 3686400 , NSEC_PER_SEC , ckevt_sa1100_osmr0 . shift ) ;
ckevt_sa1100_osmr0 . max_delta_ns =
clockevent_delta2ns ( 0x7fffffff , & ckevt_sa1100_osmr0 ) ;
ckevt_sa1100_osmr0 . min_delta_ns =
clockevent_delta2ns ( MIN_OSCR_DELTA * 2 , & ckevt_sa1100_osmr0 ) + 1 ;
2008-12-13 13:50:26 +03:00
ckevt_sa1100_osmr0 . cpumask = cpumask_of ( 0 ) ;
2007-11-13 00:55:12 +03:00
cksrc_sa1100_oscr . mult =
clocksource_hz2mult ( CLOCK_TICK_RATE , cksrc_sa1100_oscr . shift ) ;
2008-04-15 02:03:10 +04:00
setup_irq ( IRQ_OST0 , & sa1100_timer_irq ) ;
2005-09-01 15:48:48 +04:00
2008-04-15 02:03:10 +04:00
clocksource_register ( & cksrc_sa1100_oscr ) ;
clockevents_register_device ( & ckevt_sa1100_osmr0 ) ;
2005-09-01 15:48:48 +04:00
}
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PM
unsigned long osmr [ 4 ] , oier ;
static void sa1100_timer_suspend ( void )
{
osmr [ 0 ] = OSMR0 ;
osmr [ 1 ] = OSMR1 ;
osmr [ 2 ] = OSMR2 ;
osmr [ 3 ] = OSMR3 ;
oier = OIER ;
}
static void sa1100_timer_resume ( void )
{
OSSR = 0x0f ;
OSMR0 = osmr [ 0 ] ;
OSMR1 = osmr [ 1 ] ;
OSMR2 = osmr [ 2 ] ;
OSMR3 = osmr [ 3 ] ;
OIER = oier ;
/*
* OSMR0 is the system timer : make sure OSCR is sufficiently behind
*/
OSCR = OSMR0 - LATCH ;
}
# else
# define sa1100_timer_suspend NULL
# define sa1100_timer_resume NULL
# endif
struct sys_timer sa1100_timer = {
. init = sa1100_timer_init ,
. suspend = sa1100_timer_suspend ,
. resume = sa1100_timer_resume ,
} ;