2005-04-17 02:20:36 +04:00
/*
* arch / arm / mach - pxa / time . c
*
2007-07-21 06:39:36 +04: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-17 02:20:36 +04: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 06:39:36 +04:00
# include <linux/clockchips.h>
2007-08-17 19:55:22 +04:00
# include <linux/sched.h>
2008-09-24 20:48:26 +04:00
# include <linux/cnt32_to_63.h>
2007-07-21 06:39:36 +04:00
2007-08-17 19:55:22 +04:00
# include <asm/div64.h>
2005-04-17 02:20:36 +04:00
# include <asm/mach/irq.h>
# include <asm/mach/time.h>
2009-01-20 06:04:16 +03:00
# include <mach/regs-ost.h>
2005-04-17 02:20:36 +04:00
2007-08-17 19:55:22 +04: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 .
*/
# define OSCR2NS_SCALE_FACTOR 10
static unsigned long oscr2ns_scale ;
static void __init set_oscr2ns_scale ( unsigned long oscr_rate )
{
unsigned long long v = 1000000000ULL < < OSCR2NS_SCALE_FACTOR ;
do_div ( v , oscr_rate ) ;
oscr2ns_scale = v ;
/*
* We want an even value to automatically clear the top bit
* returned by cnt32_to_63 ( ) without an additional run time
* instruction . So if the LSB is 1 then round it up .
*/
if ( oscr2ns_scale & 1 )
oscr2ns_scale + + ;
}
unsigned long long sched_clock ( void )
{
unsigned long long v = cnt32_to_63 ( OSCR ) ;
return ( v * oscr2ns_scale ) > > OSCR2NS_SCALE_FACTOR ;
}
2007-11-13 01:45:16 +03:00
# define MIN_OSCR_DELTA 16
2005-04-17 02:20:36 +04:00
static irqreturn_t
2007-07-21 06:39:36 +04:00
pxa_ost0_interrupt ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
2007-07-21 06:39:36 +04:00
struct clock_event_device * c = dev_id ;
2007-11-13 01:45:16 +03:00
/* Disarm the compare/match, signal the event. */
OIER & = ~ OIER_E0 ;
OSSR = OSSR_M0 ;
c - > event_handler ( c ) ;
2005-04-17 02:20:36 +04:00
return IRQ_HANDLED ;
}
2007-07-21 06:39:36 +04:00
static int
pxa_osmr0_set_next_event ( unsigned long delta , struct clock_event_device * dev )
{
2007-11-09 02:35:46 +03:00
unsigned long flags , next , oscr ;
2007-07-21 06:39:36 +04:00
2007-11-09 02:35:46 +03:00
raw_local_irq_save ( flags ) ;
2007-07-21 06:39:36 +04:00
OIER | = OIER_E0 ;
2007-11-09 02:35:46 +03:00
next = OSCR + delta ;
OSMR0 = next ;
oscr = OSCR ;
raw_local_irq_restore ( flags ) ;
return ( signed ) ( next - oscr ) < = MIN_OSCR_DELTA ? - ETIME : 0 ;
2007-07-21 06:39:36 +04:00
}
static void
pxa_osmr0_set_mode ( enum clock_event_mode mode , struct clock_event_device * dev )
{
unsigned long irqflags ;
switch ( mode ) {
case CLOCK_EVT_MODE_ONESHOT :
raw_local_irq_save ( irqflags ) ;
OIER & = ~ OIER_E0 ;
2007-11-09 02:35:46 +03:00
OSSR = OSSR_M0 ;
2007-07-21 06:39:36 +04:00
raw_local_irq_restore ( irqflags ) ;
break ;
case CLOCK_EVT_MODE_UNUSED :
case CLOCK_EVT_MODE_SHUTDOWN :
/* initializing, released, or preparing for suspend */
raw_local_irq_save ( irqflags ) ;
OIER & = ~ OIER_E0 ;
2007-11-09 02:35:46 +03:00
OSSR = OSSR_M0 ;
2007-07-21 06:39:36 +04:00
raw_local_irq_restore ( irqflags ) ;
break ;
2007-10-27 18:15:49 +04:00
case CLOCK_EVT_MODE_RESUME :
2007-11-13 01:45:16 +03:00
case CLOCK_EVT_MODE_PERIODIC :
2007-10-27 18:15:49 +04:00
break ;
2007-07-21 06:39:36 +04:00
}
}
static struct clock_event_device ckevt_pxa_osmr0 = {
. name = " osmr0 " ,
2007-11-13 01:45:16 +03:00
. features = CLOCK_EVT_FEAT_ONESHOT ,
2007-07-21 06:39:36 +04:00
. shift = 32 ,
. rating = 200 ,
. set_next_event = pxa_osmr0_set_next_event ,
. set_mode = pxa_osmr0_set_mode ,
2005-04-17 02:20:36 +04:00
} ;
2009-04-21 23:24:00 +04:00
static cycle_t pxa_read_oscr ( struct clocksource * cs )
2006-12-12 11:21:50 +03:00
{
return OSCR ;
}
2007-07-21 06:39:36 +04:00
static struct clocksource cksrc_pxa_oscr0 = {
. name = " oscr0 " ,
2006-12-12 11:21:50 +03:00
. rating = 200 ,
2007-07-21 06:39:36 +04:00
. read = pxa_read_oscr ,
2006-12-12 11:21:50 +03:00
. mask = CLOCKSOURCE_MASK ( 32 ) ,
. shift = 20 ,
2007-02-16 12:27:37 +03:00
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
2006-12-12 11:21:50 +03:00
} ;
2007-07-21 06:39:36 +04: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-17 02:20:36 +04:00
static void __init pxa_timer_init ( void )
{
2008-12-18 06:10:32 +03:00
unsigned long clock_tick_rate = get_clock_tick_rate ( ) ;
2007-09-02 00:12:50 +04:00
2007-07-21 06:39:36 +04:00
OIER = 0 ;
OSSR = OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3 ;
2005-04-17 02:20:36 +04:00
2007-09-02 00:12:50 +04:00
set_oscr2ns_scale ( clock_tick_rate ) ;
2007-08-17 19:55:22 +04:00
2007-07-21 06:39:36 +04:00
ckevt_pxa_osmr0 . mult =
2007-09-02 00:12:50 +04:00
div_sc ( clock_tick_rate , NSEC_PER_SEC , ckevt_pxa_osmr0 . shift ) ;
2007-07-21 06:39:36 +04:00
ckevt_pxa_osmr0 . max_delta_ns =
clockevent_delta2ns ( 0x7fffffff , & ckevt_pxa_osmr0 ) ;
ckevt_pxa_osmr0 . min_delta_ns =
2008-01-23 15:34:16 +03:00
clockevent_delta2ns ( MIN_OSCR_DELTA * 2 , & ckevt_pxa_osmr0 ) + 1 ;
2008-12-13 13:50:26 +03:00
ckevt_pxa_osmr0 . cpumask = cpumask_of ( 0 ) ;
2005-04-17 02:20:36 +04:00
2007-07-21 06:39:36 +04:00
cksrc_pxa_oscr0 . mult =
2007-09-02 00:12:50 +04:00
clocksource_hz2mult ( clock_tick_rate , cksrc_pxa_oscr0 . shift ) ;
2005-09-01 15:48:40 +04:00
2007-07-21 06:39:36 +04:00
setup_irq ( IRQ_OST0 , & pxa_ost0_irq ) ;
2005-09-01 15:48:40 +04:00
2007-07-21 06:39:36 +04:00
clocksource_register ( & cksrc_pxa_oscr0 ) ;
clockevents_register_device ( & ckevt_pxa_osmr0 ) ;
2005-09-01 15:48:40 +04:00
}
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PM
2007-11-13 01:48:12 +03:00
static unsigned long osmr [ 4 ] , oier , oscr ;
2005-04-17 02:20:36 +04:00
static void pxa_timer_suspend ( void )
{
osmr [ 0 ] = OSMR0 ;
osmr [ 1 ] = OSMR1 ;
osmr [ 2 ] = OSMR2 ;
osmr [ 3 ] = OSMR3 ;
oier = OIER ;
2007-11-13 01:48:12 +03:00
oscr = OSCR ;
2005-04-17 02:20:36 +04:00
}
static void pxa_timer_resume ( void )
{
2007-11-13 01:48:12 +03: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-17 02:20:36 +04:00
OSMR0 = osmr [ 0 ] ;
OSMR1 = osmr [ 1 ] ;
OSMR2 = osmr [ 2 ] ;
OSMR3 = osmr [ 3 ] ;
OIER = oier ;
2007-11-13 01:48:12 +03:00
OSCR = oscr ;
2005-04-17 02:20:36 +04: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 ,
} ;