2005-04-16 15:20:36 -07:00
/*
* arch / arm / mach - pxa / time . c
*
2007-07-21 03:39:36 +01:00
* PXA clocksource , clockevents , and OST interrupt handlers .
* Copyright ( c ) 2007 by Bill Gatliff < bgat @ billgatliff . com > .
*
* Derived from Nicolas Pitre ' s PXA timer handler Copyright ( c ) 2001
* by MontaVista Software , Inc . ( Nico , your code rocks ! )
2005-04-16 15:20:36 -07:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/interrupt.h>
2007-07-21 03:39:36 +01:00
# include <linux/clockchips.h>
2007-08-17 16:55:22 +01:00
# include <asm/div64.h>
2005-04-16 15:20:36 -07:00
# include <asm/mach/irq.h>
# include <asm/mach/time.h>
2010-12-15 21:48:15 +00:00
# include <asm/sched_clock.h>
2009-01-20 11:04:16 +08:00
# include <mach/regs-ost.h>
2005-04-16 15:20:36 -07:00
2007-08-17 16:55:22 +01:00
/*
* This is PXA ' s sched_clock implementation . This has a resolution
* of at least 308 ns and a maximum value of 208 days .
*
* The return value is guaranteed to be monotonic in that range as
* long as there is always less than 582 seconds between successive
* calls to sched_clock ( ) which should always be the case in practice .
*/
2011-12-15 12:19:23 +01:00
static u32 notrace pxa_read_sched_clock ( void )
2007-08-17 16:55:22 +01:00
{
2011-12-15 12:19:23 +01:00
return OSCR ;
2007-08-17 16:55:22 +01:00
}
2007-11-12 22:45:16 +00:00
# define MIN_OSCR_DELTA 16
2005-04-16 15:20:36 -07:00
static irqreturn_t
2007-07-21 03:39:36 +01:00
pxa_ost0_interrupt ( int irq , void * dev_id )
2005-04-16 15:20:36 -07:00
{
2007-07-21 03:39:36 +01:00
struct clock_event_device * c = dev_id ;
2007-11-12 22:45:16 +00: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
return IRQ_HANDLED ;
}
2007-07-21 03:39:36 +01:00
static int
pxa_osmr0_set_next_event ( unsigned long delta , struct clock_event_device * dev )
{
2009-12-17 12:43:29 +01:00
unsigned long next , oscr ;
2007-07-21 03:39:36 +01:00
OIER | = OIER_E0 ;
2007-11-08 23:35:46 +00:00
next = OSCR + delta ;
OSMR0 = next ;
oscr = OSCR ;
return ( signed ) ( next - oscr ) < = MIN_OSCR_DELTA ? - ETIME : 0 ;
2007-07-21 03:39:36 +01:00
}
static void
pxa_osmr0_set_mode ( enum clock_event_mode mode , struct clock_event_device * dev )
{
switch ( mode ) {
case CLOCK_EVT_MODE_ONESHOT :
OIER & = ~ OIER_E0 ;
2007-11-08 23:35:46 +00:00
OSSR = OSSR_M0 ;
2007-07-21 03:39:36 +01:00
break ;
case CLOCK_EVT_MODE_UNUSED :
case CLOCK_EVT_MODE_SHUTDOWN :
/* initializing, released, or preparing for suspend */
OIER & = ~ OIER_E0 ;
2007-11-08 23:35:46 +00:00
OSSR = OSSR_M0 ;
2007-07-21 03:39:36 +01:00
break ;
2007-10-27 15:15:49 +01:00
case CLOCK_EVT_MODE_RESUME :
2007-11-12 22:45:16 +00:00
case CLOCK_EVT_MODE_PERIODIC :
2007-10-27 15:15:49 +01:00
break ;
2007-07-21 03:39:36 +01:00
}
}
static struct clock_event_device ckevt_pxa_osmr0 = {
. name = " osmr0 " ,
2007-11-12 22:45:16 +00:00
. features = CLOCK_EVT_FEAT_ONESHOT ,
2007-07-21 03:39:36 +01:00
. rating = 200 ,
. set_next_event = pxa_osmr0_set_next_event ,
. set_mode = pxa_osmr0_set_mode ,
2005-04-16 15:20:36 -07:00
} ;
2007-07-21 03:39:36 +01:00
static struct irqaction pxa_ost0_irq = {
. name = " ost0 " ,
. flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL ,
. handler = pxa_ost0_interrupt ,
. dev_id = & ckevt_pxa_osmr0 ,
} ;
2005-04-16 15:20:36 -07:00
static void __init pxa_timer_init ( void )
{
2008-12-18 11:10:32 +08:00
unsigned long clock_tick_rate = get_clock_tick_rate ( ) ;
2007-09-01 21:12:50 +01:00
2007-07-21 03:39:36 +01:00
OIER = 0 ;
OSSR = OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3 ;
2005-04-16 15:20:36 -07:00
2011-12-15 12:19:23 +01:00
setup_sched_clock ( pxa_read_sched_clock , 32 , clock_tick_rate ) ;
2007-08-17 16:55:22 +01:00
2010-11-24 11:54:23 +08:00
clockevents_calc_mult_shift ( & ckevt_pxa_osmr0 , clock_tick_rate , 4 ) ;
2007-07-21 03:39:36 +01:00
ckevt_pxa_osmr0 . max_delta_ns =
clockevent_delta2ns ( 0x7fffffff , & ckevt_pxa_osmr0 ) ;
ckevt_pxa_osmr0 . min_delta_ns =
2008-01-23 12:34:16 +00:00
clockevent_delta2ns ( MIN_OSCR_DELTA * 2 , & ckevt_pxa_osmr0 ) + 1 ;
2008-12-13 21:20:26 +10:30
ckevt_pxa_osmr0 . cpumask = cpumask_of ( 0 ) ;
2005-04-16 15:20:36 -07:00
2007-07-21 03:39:36 +01:00
setup_irq ( IRQ_OST0 , & pxa_ost0_irq ) ;
2005-09-01 12:48:40 +01:00
2011-05-08 14:09:47 +01:00
clocksource_mmio_init ( & OSCR , " oscr0 " , clock_tick_rate , 200 , 32 ,
clocksource_mmio_readl_up ) ;
2007-07-21 03:39:36 +01:00
clockevents_register_device ( & ckevt_pxa_osmr0 ) ;
2005-09-01 12:48:40 +01:00
}
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_PM
2007-11-12 22:48:12 +00:00
static unsigned long osmr [ 4 ] , oier , oscr ;
2005-04-16 15:20:36 -07:00
static void pxa_timer_suspend ( void )
{
osmr [ 0 ] = OSMR0 ;
osmr [ 1 ] = OSMR1 ;
osmr [ 2 ] = OSMR2 ;
osmr [ 3 ] = OSMR3 ;
oier = OIER ;
2007-11-12 22:48:12 +00:00
oscr = OSCR ;
2005-04-16 15:20:36 -07:00
}
static void pxa_timer_resume ( void )
{
2007-11-12 22:48:12 +00:00
/*
* Ensure that we have at least MIN_OSCR_DELTA between match
* register 0 and the OSCR , to guarantee that we will receive
* the one - shot timer interrupt . We adjust OSMR0 in preference
* to OSCR to guarantee that OSCR is monotonically incrementing .
*/
if ( osmr [ 0 ] - oscr < MIN_OSCR_DELTA )
osmr [ 0 ] + = MIN_OSCR_DELTA ;
2005-04-16 15:20:36 -07:00
OSMR0 = osmr [ 0 ] ;
OSMR1 = osmr [ 1 ] ;
OSMR2 = osmr [ 2 ] ;
OSMR3 = osmr [ 3 ] ;
OIER = oier ;
2007-11-12 22:48:12 +00:00
OSCR = oscr ;
2005-04-16 15:20:36 -07:00
}
# else
# define pxa_timer_suspend NULL
# define pxa_timer_resume NULL
# endif
struct sys_timer pxa_timer = {
. init = pxa_timer_init ,
. suspend = pxa_timer_suspend ,
. resume = pxa_timer_resume ,
} ;