2005-04-16 15:20:36 -07:00
/*
* linux / arch / arm / mach - sa1100 / time . c
*
* Copyright ( C ) 1998 Deborah Wallach .
2008-11-26 20:58:43 +01:00
* Twiddles ( C ) 1999 Hugo Fiennes < hugo @ empeg . com >
*
2009-09-14 03:25:28 -04:00
* 2000 / 03 / 29 ( C ) Nicolas Pitre < nico @ fluxnic . net >
2005-04-16 15:20:36 -07:00
* Rewritten : big cleanup , much simpler , better HZ accuracy .
*
*/
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/interrupt.h>
2006-07-01 22:32:38 +01:00
# include <linux/irq.h>
2005-04-16 15:20:36 -07:00
# include <linux/timex.h>
2008-04-14 23:03:10 +01:00
# include <linux/clockchips.h>
2005-04-16 15:20:36 -07:00
# include <asm/mach/time.h>
2008-08-05 16:14:15 +01:00
# include <mach/hardware.h>
2005-04-16 15:20:36 -07:00
2008-04-14 23:03:10 +01:00
# define MIN_OSCR_DELTA 2
2005-04-16 15:20:36 -07:00
2008-04-14 23:03:10 +01:00
static irqreturn_t sa1100_ost0_interrupt ( int irq , void * dev_id )
2005-04-16 15:20:36 -07:00
{
2008-04-14 23:03:10 +01:00
struct clock_event_device * c = dev_id ;
2005-04-16 15:20:36 -07:00
2008-04-14 23:03:10 +01:00
/* Disarm the compare/match, signal the event. */
OIER & = ~ OIER_E0 ;
OSSR = OSSR_M0 ;
c - > event_handler ( c ) ;
2005-04-16 15:20:36 -07:00
2008-04-14 23:03:10 +01:00
return IRQ_HANDLED ;
}
2005-09-01 12:48:48 +01:00
2008-04-14 23:03:10 +01:00
static int
sa1100_osmr0_set_next_event ( unsigned long delta , struct clock_event_device * c )
2005-04-16 15:20:36 -07:00
{
2009-12-17 12:43:29 +01:00
unsigned long next , oscr ;
2005-04-16 15:20:36 -07:00
2008-04-14 23:03:10 +01:00
OIER | = OIER_E0 ;
next = OSCR + delta ;
OSMR0 = next ;
oscr = OSCR ;
2005-09-01 12:48:48 +01:00
2008-04-14 23:03:10 +01:00
return ( signed ) ( next - oscr ) < = MIN_OSCR_DELTA ? - ETIME : 0 ;
}
2005-04-16 15:20:36 -07:00
2008-04-14 23:03:10 +01:00
static void
sa1100_osmr0_set_mode ( enum clock_event_mode mode , struct clock_event_device * c )
{
switch ( mode ) {
case CLOCK_EVT_MODE_ONESHOT :
case CLOCK_EVT_MODE_UNUSED :
case CLOCK_EVT_MODE_SHUTDOWN :
OIER & = ~ OIER_E0 ;
OSSR = OSSR_M0 ;
break ;
case CLOCK_EVT_MODE_RESUME :
case CLOCK_EVT_MODE_PERIODIC :
break ;
}
2005-04-16 15:20:36 -07:00
}
2008-04-14 23:03:10 +01: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-16 15:20:36 -07:00
} ;
2009-09-27 17:32:47 +01:00
static cycle_t sa1100_read_oscr ( struct clocksource * s )
2007-11-12 21:55:12 +00: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-14 23:03:10 +01: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-16 15:20:36 -07:00
static void __init sa1100_timer_init ( void )
{
2005-11-08 22:43:06 +00:00
OIER = 0 ; /* disable any timer interrupts */
2005-04-16 15:20:36 -07:00
OSSR = 0xf ; /* clear status on all timers */
2008-04-14 23:03:10 +01: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 21:20:26 +10:30
ckevt_sa1100_osmr0 . cpumask = cpumask_of ( 0 ) ;
2007-11-12 21:55:12 +00:00
cksrc_sa1100_oscr . mult =
clocksource_hz2mult ( CLOCK_TICK_RATE , cksrc_sa1100_oscr . shift ) ;
2008-04-14 23:03:10 +01:00
setup_irq ( IRQ_OST0 , & sa1100_timer_irq ) ;
2005-09-01 12:48:48 +01:00
2008-04-14 23:03:10 +01:00
clocksource_register ( & cksrc_sa1100_oscr ) ;
clockevents_register_device ( & ckevt_sa1100_osmr0 ) ;
2005-09-01 12:48:48 +01:00
}
2005-04-16 15:20:36 -07: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 ,
} ;