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>
2013-11-12 23:56:02 +04:00
# include <linux/kernel.h>
2005-04-17 02:20:36 +04:00
# 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>
2013-06-02 10:39:40 +04:00
# include <linux/sched_clock.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>
2012-02-24 03:06:51 +04:00
# include <mach/irqs.h>
2005-04-17 02:20:36 +04:00
2013-11-12 23:56:02 +04:00
# define SA1100_CLOCK_FREQ 3686400
# define SA1100_LATCH DIV_ROUND_CLOSEST(SA1100_CLOCK_FREQ, HZ)
2013-11-16 03:26:20 +04:00
static u64 notrace sa1100_read_sched_clock ( void )
2010-12-16 00:49:06 +03:00
{
2012-06-06 14:42:36 +04:00
return readl_relaxed ( OSCR ) ;
2010-12-16 00:49:06 +03: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. */
2012-06-06 14:42:36 +04:00
writel_relaxed ( readl_relaxed ( OIER ) & ~ OIER_E0 , OIER ) ;
writel_relaxed ( OSSR_M0 , OSSR ) ;
2008-04-15 02:03:10 +04:00
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
{
2009-12-17 14:43:29 +03:00
unsigned long next , oscr ;
2005-04-17 02:20:36 +04:00
2012-06-06 14:42:36 +04: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 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 )
{
switch ( mode ) {
case CLOCK_EVT_MODE_ONESHOT :
case CLOCK_EVT_MODE_UNUSED :
case CLOCK_EVT_MODE_SHUTDOWN :
2012-06-06 14:42:36 +04:00
writel_relaxed ( readl_relaxed ( OIER ) & ~ OIER_E0 , OIER ) ;
writel_relaxed ( OSSR_M0 , OSSR ) ;
2008-04-15 02:03:10 +04:00
break ;
case CLOCK_EVT_MODE_RESUME :
case CLOCK_EVT_MODE_PERIODIC :
break ;
}
2005-04-17 02:20:36 +04:00
}
2012-11-08 03:35:11 +04: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
*/
2013-11-12 23:56:02 +04:00
writel_relaxed ( OSMR0 - SA1100_LATCH , OSCR ) ;
2012-11-08 03:35:11 +04:00
}
# else
# define sa1100_timer_suspend NULL
# define sa1100_timer_resume NULL
# endif
2008-04-15 02:03:10 +04: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-08 03:35:11 +04:00
. suspend = sa1100_timer_suspend ,
. resume = sa1100_timer_resume ,
2005-04-17 02:20:36 +04:00
} ;
2008-04-15 02:03:10 +04:00
static struct irqaction sa1100_timer_irq = {
. name = " ost0 " ,
2014-03-05 01:04:50 +04:00
. flags = IRQF_TIMER | IRQF_IRQPOLL ,
2008-04-15 02:03:10 +04:00
. handler = sa1100_ost0_interrupt ,
. dev_id = & ckevt_sa1100_osmr0 ,
} ;
2012-11-08 23:40:59 +04:00
void __init sa1100_timer_init ( void )
2005-04-17 02:20:36 +04:00
{
2012-06-06 14:42:36 +04:00
writel_relaxed ( 0 , OIER ) ;
writel_relaxed ( OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3 , OSSR ) ;
2008-04-15 02:03:10 +04:00
2013-11-16 03:26:20 +04:00
sched_clock_register ( sa1100_read_sched_clock , 32 , 3686400 ) ;
2010-12-16 00:49:06 +03:00
2008-12-13 13:50:26 +03:00
ckevt_sa1100_osmr0 . cpumask = cpumask_of ( 0 ) ;
2007-11-13 00:55:12 +03:00
2008-04-15 02:03:10 +04:00
setup_irq ( IRQ_OST0 , & sa1100_timer_irq ) ;
2005-09-01 15:48:48 +04:00
2013-11-12 23:56:02 +04:00
clocksource_mmio_init ( OSCR , " oscr " , SA1100_CLOCK_FREQ , 200 , 32 ,
2011-05-08 17:09:47 +04:00
clocksource_mmio_readl_up ) ;
2013-01-14 22:20:02 +04:00
clockevents_config_and_register ( & ckevt_sa1100_osmr0 , 3686400 ,
MIN_OSCR_DELTA * 2 , 0x7fffffff ) ;
2005-09-01 15:48:48 +04:00
}