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>
2013-06-01 23:39:40 -07:00
# include <linux/sched_clock.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>
2012-02-24 00:06:51 +01:00
# include <mach/irqs.h>
2005-04-16 15:20:36 -07:00
2012-01-04 11:42:19 +01:00
static u32 notrace sa1100_read_sched_clock ( void )
2010-12-15 21:49:06 +00:00
{
2012-06-06 11:42:36 +01:00
return readl_relaxed ( OSCR ) ;
2010-12-15 21:49:06 +00: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. */
2012-06-06 11:42:36 +01:00
writel_relaxed ( readl_relaxed ( OIER ) & ~ OIER_E0 , OIER ) ;
writel_relaxed ( OSSR_M0 , OSSR ) ;
2008-04-14 23:03:10 +01:00
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
2012-06-06 11:42:36 +01:00
writel_relaxed ( readl_relaxed ( OIER ) | OIER_E0 , OIER ) ;
next = readl_relaxed ( OSCR ) + delta ;
writel_relaxed ( next , OSMR0 ) ;
oscr = readl_relaxed ( 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 :
2012-06-06 11:42:36 +01:00
writel_relaxed ( readl_relaxed ( OIER ) & ~ OIER_E0 , OIER ) ;
writel_relaxed ( OSSR_M0 , OSSR ) ;
2008-04-14 23:03:10 +01:00
break ;
case CLOCK_EVT_MODE_RESUME :
case CLOCK_EVT_MODE_PERIODIC :
break ;
}
2005-04-16 15:20:36 -07:00
}
2012-11-07 16:35:11 -07:00
# ifdef CONFIG_PM
unsigned long osmr [ 4 ] , oier ;
static void sa1100_timer_suspend ( struct clock_event_device * cedev )
{
osmr [ 0 ] = readl_relaxed ( OSMR0 ) ;
osmr [ 1 ] = readl_relaxed ( OSMR1 ) ;
osmr [ 2 ] = readl_relaxed ( OSMR2 ) ;
osmr [ 3 ] = readl_relaxed ( OSMR3 ) ;
oier = readl_relaxed ( OIER ) ;
}
static void sa1100_timer_resume ( struct clock_event_device * cedev )
{
writel_relaxed ( 0x0f , OSSR ) ;
writel_relaxed ( osmr [ 0 ] , OSMR0 ) ;
writel_relaxed ( osmr [ 1 ] , OSMR1 ) ;
writel_relaxed ( osmr [ 2 ] , OSMR2 ) ;
writel_relaxed ( osmr [ 3 ] , OSMR3 ) ;
writel_relaxed ( oier , OIER ) ;
/*
* OSMR0 is the system timer : make sure OSCR is sufficiently behind
*/
writel_relaxed ( OSMR0 - LATCH , OSCR ) ;
}
# else
# define sa1100_timer_suspend NULL
# define sa1100_timer_resume NULL
# endif
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 ,
2012-11-07 16:35:11 -07:00
. suspend = sa1100_timer_suspend ,
. resume = sa1100_timer_resume ,
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 ,
} ;
2012-11-08 12:40:59 -07:00
void __init sa1100_timer_init ( void )
2005-04-16 15:20:36 -07:00
{
2012-06-06 11:42:36 +01:00
writel_relaxed ( 0 , OIER ) ;
writel_relaxed ( OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3 , OSSR ) ;
2008-04-14 23:03:10 +01:00
2011-12-15 12:19:23 +01:00
setup_sched_clock ( sa1100_read_sched_clock , 32 , 3686400 ) ;
2010-12-15 21:49:06 +00:00
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
2012-06-06 11:42:36 +01:00
clocksource_mmio_init ( OSCR , " oscr " , CLOCK_TICK_RATE , 200 , 32 ,
2011-05-08 14:09:47 +01:00
clocksource_mmio_readl_up ) ;
2013-01-14 10:20:02 -08:00
clockevents_config_and_register ( & ckevt_sa1100_osmr0 , 3686400 ,
MIN_OSCR_DELTA * 2 , 0x7fffffff ) ;
2005-09-01 12:48:48 +01:00
}