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>
2007-07-21 06:39:36 +04:00
2007-08-17 19:55:22 +04:00
# include <asm/div64.h>
# include <asm/cnt32_to_63.h>
2005-04-17 02:20:36 +04:00
# include <asm/mach/irq.h>
# include <asm/mach/time.h>
# include <asm/arch/pxa-regs.h>
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 ;
}
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
{
int next_match ;
2007-07-21 06:39:36 +04:00
struct clock_event_device * c = dev_id ;
if ( c - > mode = = CLOCK_EVT_MODE_ONESHOT ) {
/* Disarm the compare/match, signal the event. */
OIER & = ~ OIER_E0 ;
c - > event_handler ( c ) ;
} else if ( c - > mode = = CLOCK_EVT_MODE_PERIODIC ) {
/* Call the event handler as many times as necessary
* to recover missed events , if any ( if we update
* OSMR0 and OSCR0 is still ahead of us , we ' ve missed
* the event ) . As we ' re dealing with that , re - arm the
* compare / match for the next event .
*
* HACK ALERT :
*
* There ' s a latency between the instruction that
* writes to OSMR0 and the actual commit to the
* physical hardware , because the CPU doesn ' t ( have
* to ) run at bus speed , there ' s a write buffer
* between the CPU and the bus , etc . etc . So if the
* target OSCR0 is " very close " , to the OSMR0 load
* value , the update to OSMR0 might not get to the
* hardware in time and we ' ll miss that interrupt .
*
* To be safe , if the new OSMR0 is " very close " to the
* target OSCR0 value , we call the event_handler as
* though the event actually happened . According to
* Nico ' s comment in the previous version of this
* code , experience has shown that 6 OSCR ticks is
* " very close " but he went with 8. We will use 16 ,
* based on the results of testing on PXA270 .
*
* To be doubly sure , we also tell clkevt via
* clockevents_register_device ( ) not to ask for
* anything that might put us " very close " .
2005-04-17 02:20:36 +04:00
*/
2007-07-21 06:39:36 +04:00
# define MIN_OSCR_DELTA 16
2005-04-17 02:20:36 +04:00
do {
2007-07-21 06:39:36 +04:00
OSSR = OSSR_M0 ;
2005-04-17 02:20:36 +04:00
next_match = ( OSMR0 + = LATCH ) ;
2007-07-21 06:39:36 +04:00
c - > event_handler ( c ) ;
} while ( ( ( signed long ) ( next_match - OSCR ) < = MIN_OSCR_DELTA )
& & ( c - > mode = = CLOCK_EVT_MODE_PERIODIC ) ) ;
}
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 )
{
unsigned long irqflags ;
raw_local_irq_save ( irqflags ) ;
OSMR0 = OSCR + delta ;
OSSR = OSSR_M0 ;
OIER | = OIER_E0 ;
raw_local_irq_restore ( irqflags ) ;
return 0 ;
}
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_PERIODIC :
raw_local_irq_save ( irqflags ) ;
OSMR0 = OSCR + LATCH ;
OSSR = OSSR_M0 ;
OIER | = OIER_E0 ;
raw_local_irq_restore ( irqflags ) ;
break ;
case CLOCK_EVT_MODE_ONESHOT :
raw_local_irq_save ( irqflags ) ;
OIER & = ~ OIER_E0 ;
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 ;
raw_local_irq_restore ( irqflags ) ;
break ;
}
}
static struct clock_event_device ckevt_pxa_osmr0 = {
. name = " osmr0 " ,
. features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT ,
. shift = 32 ,
. rating = 200 ,
. cpumask = CPU_MASK_CPU0 ,
. set_next_event = pxa_osmr0_set_next_event ,
. set_mode = pxa_osmr0_set_mode ,
2005-04-17 02:20:36 +04:00
} ;
2007-07-21 06:39:36 +04:00
static cycle_t pxa_read_oscr ( void )
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 )
{
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-08-17 19:55:22 +04:00
set_oscr2ns_scale ( CLOCK_TICK_RATE ) ;
2007-07-21 06:39:36 +04:00
ckevt_pxa_osmr0 . mult =
div_sc ( CLOCK_TICK_RATE , NSEC_PER_SEC , ckevt_pxa_osmr0 . shift ) ;
ckevt_pxa_osmr0 . max_delta_ns =
clockevent_delta2ns ( 0x7fffffff , & ckevt_pxa_osmr0 ) ;
ckevt_pxa_osmr0 . min_delta_ns =
clockevent_delta2ns ( MIN_OSCR_DELTA , & ckevt_pxa_osmr0 ) + 1 ;
2005-04-17 02:20:36 +04:00
2007-07-21 06:39:36 +04:00
cksrc_pxa_oscr0 . mult =
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
static unsigned long osmr [ 4 ] , oier ;
static void pxa_timer_suspend ( void )
{
osmr [ 0 ] = OSMR0 ;
osmr [ 1 ] = OSMR1 ;
osmr [ 2 ] = OSMR2 ;
osmr [ 3 ] = OSMR3 ;
oier = OIER ;
}
static void pxa_timer_resume ( void )
{
OSMR0 = osmr [ 0 ] ;
OSMR1 = osmr [ 1 ] ;
OSMR2 = osmr [ 2 ] ;
OSMR3 = osmr [ 3 ] ;
OIER = oier ;
/*
2007-07-21 06:39:36 +04:00
* OSCR0 is the system timer , which has to increase
* monotonically until it rolls over in hardware . The value
* ( OSMR0 - LATCH ) is OSCR0 at the most recent system tick ,
* which is a handy value to restore to OSCR0 .
2005-04-17 02:20:36 +04:00
*/
OSCR = OSMR0 - LATCH ;
}
# 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 ,
} ;