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>
2010-12-15 21:49:06 +00:00
# include <linux/sched.h> /* just for sched_clock() - funny that */
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>
2010-12-15 21:49:06 +00:00
# include <asm/sched_clock.h>
2008-08-05 16:14:15 +01:00
# include <mach/hardware.h>
2005-04-16 15:20:36 -07:00
2010-12-15 21:49:06 +00:00
/*
* This is the SA11x0 sched_clock implementation .
*/
static DEFINE_CLOCK_DATA ( cd ) ;
/*
* Constants generated by clocks_calc_mult_shift ( m , s , 3.6864 MHz ,
* NSEC_PER_SEC , 60 ) .
* This gives a resolution of about 271 ns and a wrap period of about 19 min .
*/
# define SC_MULT 2275555556u
# define SC_SHIFT 23
unsigned long long notrace sched_clock ( void )
{
u32 cyc = OSCR ;
return cyc_to_fixed_sched_clock ( & cd , cyc , ( u32 ) ~ 0 , SC_MULT , SC_SHIFT ) ;
}
static void notrace sa1100_update_sched_clock ( void )
{
u32 cyc = OSCR ;
update_sched_clock ( & cd , cyc , ( u32 ) ~ 0 ) ;
}
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 ,
. rating = 200 ,
. set_next_event = sa1100_osmr0_set_next_event ,
. set_mode = sa1100_osmr0_set_mode ,
2005-04-16 15:20:36 -07:00
} ;
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 )
{
2011-05-08 16:14:40 +01:00
OIER = 0 ;
OSSR = OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3 ;
2008-04-14 23:03:10 +01:00
2010-12-15 21:49:06 +00:00
init_fixed_sched_clock ( & cd , sa1100_update_sched_clock , 32 ,
3686400 , SC_MULT , SC_SHIFT ) ;
2011-05-08 16:14:40 +01:00
clockevents_calc_mult_shift ( & ckevt_sa1100_osmr0 , 3686400 , 4 ) ;
2008-04-14 23:03:10 +01:00
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
2008-04-14 23:03:10 +01:00
setup_irq ( IRQ_OST0 , & sa1100_timer_irq ) ;
2005-09-01 12:48:48 +01:00
2011-05-08 14:09:47 +01:00
clocksource_mmio_init ( & OSCR , " oscr " , CLOCK_TICK_RATE , 200 , 32 ,
clocksource_mmio_readl_up ) ;
2008-04-14 23:03:10 +01:00
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 ,
} ;