2006-06-26 11:25:09 +04:00
/*
2007-10-13 05:10:53 +04:00
* 8253 / PIT functions
2006-06-26 11:25:09 +04:00
*
*/
2007-02-16 12:28:04 +03:00
# include <linux/clockchips.h>
2007-07-21 15:37:34 +04:00
# include <linux/interrupt.h>
2009-03-21 14:26:10 +03:00
# include <linux/spinlock.h>
2006-06-26 11:25:09 +04:00
# include <linux/jiffies.h>
# include <linux/module.h>
2009-03-21 14:26:10 +03:00
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/io.h>
2006-06-26 11:25:09 +04:00
# include <asm/i8253.h>
2008-01-30 15:30:02 +03:00
# include <asm/hpet.h>
2009-03-21 14:26:10 +03:00
# include <asm/smp.h>
2006-06-26 11:25:09 +04:00
DEFINE_SPINLOCK ( i8253_lock ) ;
EXPORT_SYMBOL ( i8253_lock ) ;
2008-01-30 15:30:03 +03:00
# ifdef CONFIG_X86_32
static void pit_disable_clocksource ( void ) ;
# else
static inline void pit_disable_clocksource ( void ) { }
# endif
2007-02-16 12:28:04 +03:00
/*
* HPET replaces the PIT , when enabled . So we need to know , which of
* the two timers is used
*/
struct clock_event_device * global_clock_event ;
/*
* Initialize the PIT timer .
*
* This is also called after resume to bring the PIT into operation again .
*/
static void init_pit_timer ( enum clock_event_mode mode ,
struct clock_event_device * evt )
{
2008-01-30 15:30:47 +03:00
spin_lock ( & i8253_lock ) ;
2007-02-16 12:28:04 +03:00
2009-03-21 14:26:10 +03:00
switch ( mode ) {
2007-02-16 12:28:04 +03:00
case CLOCK_EVT_MODE_PERIODIC :
/* binary, mode 2, LSB/MSB, ch 0 */
2008-01-30 15:33:14 +03:00
outb_pit ( 0x34 , PIT_MODE ) ;
outb_pit ( LATCH & 0xff , PIT_CH0 ) ; /* LSB */
outb_pit ( LATCH > > 8 , PIT_CH0 ) ; /* MSB */
2007-02-16 12:28:04 +03:00
break ;
case CLOCK_EVT_MODE_SHUTDOWN :
case CLOCK_EVT_MODE_UNUSED :
2007-07-21 15:37:38 +04:00
if ( evt - > mode = = CLOCK_EVT_MODE_PERIODIC | |
evt - > mode = = CLOCK_EVT_MODE_ONESHOT ) {
2008-01-30 15:33:14 +03:00
outb_pit ( 0x30 , PIT_MODE ) ;
outb_pit ( 0 , PIT_CH0 ) ;
outb_pit ( 0 , PIT_CH0 ) ;
2007-07-21 15:37:38 +04:00
}
2008-01-30 15:30:03 +03:00
pit_disable_clocksource ( ) ;
2007-07-21 15:37:34 +04:00
break ;
2007-03-23 00:46:18 +03:00
case CLOCK_EVT_MODE_ONESHOT :
2007-02-16 12:28:04 +03:00
/* One shot setup */
2008-01-30 15:30:03 +03:00
pit_disable_clocksource ( ) ;
2008-01-30 15:33:14 +03:00
outb_pit ( 0x38 , PIT_MODE ) ;
2007-07-21 15:37:34 +04:00
break ;
case CLOCK_EVT_MODE_RESUME :
/* Nothing to do here */
2007-02-16 12:28:04 +03:00
break ;
}
2008-01-30 15:30:47 +03:00
spin_unlock ( & i8253_lock ) ;
2007-02-16 12:28:04 +03:00
}
/*
* Program the next event in oneshot mode
*
* Delta is given in PIT ticks
*/
static int pit_next_event ( unsigned long delta , struct clock_event_device * evt )
2006-06-26 11:25:09 +04:00
{
2008-01-30 15:30:47 +03:00
spin_lock ( & i8253_lock ) ;
2008-01-30 15:33:14 +03:00
outb_pit ( delta & 0xff , PIT_CH0 ) ; /* LSB */
outb_pit ( delta > > 8 , PIT_CH0 ) ; /* MSB */
2008-01-30 15:30:47 +03:00
spin_unlock ( & i8253_lock ) ;
2007-02-16 12:28:04 +03:00
return 0 ;
}
/*
* On UP the PIT can serve all of the possible timer functions . On SMP systems
* it can be solely used for the global tick .
*
2007-10-20 03:13:56 +04:00
* The profiling and update capabilities are switched off once the local apic is
2007-02-16 12:28:04 +03:00
* registered . This mechanism replaces the previous # ifdef LOCAL_APIC -
* ! using_apic_timer decisions in do_timer_interrupt_hook ( )
*/
2009-03-21 14:26:10 +03:00
static struct clock_event_device pit_ce = {
2007-02-16 12:28:04 +03:00
. name = " pit " ,
. features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT ,
. set_mode = init_pit_timer ,
. set_next_event = pit_next_event ,
. shift = 32 ,
. irq = 0 ,
} ;
/*
* Initialize the conversion factor and the min / max deltas of the clock event
* structure and register the clock event source with the framework .
*/
void __init setup_pit_timer ( void )
{
/*
* Start pit with the boot cpu mask and make it global after the
* IO_APIC has been initialized .
*/
2009-03-21 14:26:10 +03:00
pit_ce . cpumask = cpumask_of ( smp_processor_id ( ) ) ;
pit_ce . mult = div_sc ( CLOCK_TICK_RATE , NSEC_PER_SEC , pit_ce . shift ) ;
pit_ce . max_delta_ns = clockevent_delta2ns ( 0x7FFF , & pit_ce ) ;
pit_ce . min_delta_ns = clockevent_delta2ns ( 0xF , & pit_ce ) ;
clockevents_register_device ( & pit_ce ) ;
global_clock_event = & pit_ce ;
2006-06-26 11:25:09 +04:00
}
2006-06-26 11:25:12 +04:00
2007-10-13 01:04:06 +04:00
# ifndef CONFIG_X86_64
2006-06-26 11:25:12 +04:00
/*
* Since the PIT overflows every tick , its not very useful
* to just read by itself . So use jiffies to emulate a free
* running counter :
*/
2009-04-21 23:24:00 +04:00
static cycle_t pit_read ( struct clocksource * cs )
2006-06-26 11:25:12 +04:00
{
2009-03-21 14:26:10 +03:00
static int old_count ;
static u32 old_jifs ;
2006-06-26 11:25:12 +04:00
unsigned long flags ;
int count ;
2006-06-26 11:25:16 +04:00
u32 jifs ;
2006-06-26 11:25:12 +04:00
spin_lock_irqsave ( & i8253_lock , flags ) ;
2007-02-16 12:28:04 +03:00
/*
2006-06-26 11:25:16 +04:00
* Although our caller may have the read side of xtime_lock ,
* this is now a seqlock , and we are cheating in this routine
* by having side effects on state that we cannot undo if
* there is a collision on the seqlock and our caller has to
* retry . ( Namely , old_jifs and old_count . ) So we must treat
* jiffies as volatile despite the lock . We read jiffies
* before latching the timer count to guarantee that although
* the jiffies value might be older than the count ( that is ,
* the counter may underflow between the last point where
* jiffies was incremented and the point where we latch the
* count ) , it cannot be newer .
*/
jifs = jiffies ;
2008-01-30 15:33:14 +03:00
outb_pit ( 0x00 , PIT_MODE ) ; /* latch the count ASAP */
count = inb_pit ( PIT_CH0 ) ; /* read the latched count */
count | = inb_pit ( PIT_CH0 ) < < 8 ;
2006-06-26 11:25:12 +04:00
/* VIA686a test code... reset the latch if count > max + 1 */
if ( count > LATCH ) {
2008-01-30 15:33:14 +03:00
outb_pit ( 0x34 , PIT_MODE ) ;
outb_pit ( LATCH & 0xff , PIT_CH0 ) ;
outb_pit ( LATCH > > 8 , PIT_CH0 ) ;
2006-06-26 11:25:12 +04:00
count = LATCH - 1 ;
}
2006-06-26 11:25:16 +04:00
/*
* It ' s possible for count to appear to go the wrong way for a
* couple of reasons :
*
* 1. The timer counter underflows , but we haven ' t handled the
* resulting interrupt and incremented jiffies yet .
* 2. Hardware problem with the timer , not giving us continuous time ,
* the counter does small " jumps " upwards on some Pentium systems ,
* ( see c ' t 95 / 10 page 335 for Neptun bug . )
*
* Previous attempts to handle these cases intelligently were
* buggy , so we just do the simple thing now .
*/
2009-03-21 14:26:10 +03:00
if ( count > old_count & & jifs = = old_jifs )
2006-06-26 11:25:16 +04:00
count = old_count ;
2009-03-21 14:26:10 +03:00
2006-06-26 11:25:16 +04:00
old_count = count ;
old_jifs = jifs ;
spin_unlock_irqrestore ( & i8253_lock , flags ) ;
2006-06-26 11:25:12 +04:00
2006-06-26 11:25:16 +04:00
count = ( LATCH - 1 ) - count ;
2006-06-26 11:25:12 +04:00
return ( cycle_t ) ( jifs * LATCH ) + count ;
}
2009-03-21 14:26:10 +03:00
static struct clocksource pit_cs = {
. name = " pit " ,
. rating = 110 ,
. read = pit_read ,
. mask = CLOCKSOURCE_MASK ( 32 ) ,
. mult = 0 ,
. shift = 20 ,
2006-06-26 11:25:12 +04:00
} ;
2008-01-30 15:30:03 +03:00
static void pit_disable_clocksource ( void )
{
/*
* Use mult to check whether it is registered or not
*/
2009-03-21 14:26:10 +03:00
if ( pit_cs . mult ) {
clocksource_unregister ( & pit_cs ) ;
pit_cs . mult = 0 ;
2008-01-30 15:30:03 +03:00
}
}
2006-06-26 11:25:12 +04:00
static int __init init_pit_clocksource ( void )
{
2008-01-30 15:30:02 +03:00
/*
* Several reasons not to register PIT as a clocksource :
*
* - On SMP PIT does not scale due to i8253_lock
* - when HPET is enabled
* - when local APIC timer is active ( PIT is switched off )
*/
if ( num_possible_cpus ( ) > 1 | | is_hpet_enabled ( ) | |
2009-03-21 14:26:10 +03:00
pit_ce . mode ! = CLOCK_EVT_MODE_PERIODIC )
2006-06-26 11:25:12 +04:00
return 0 ;
2009-03-21 14:26:10 +03:00
pit_cs . mult = clocksource_hz2mult ( CLOCK_TICK_RATE , pit_cs . shift ) ;
return clocksource_register ( & pit_cs ) ;
2006-06-26 11:25:12 +04:00
}
2007-03-05 11:30:50 +03:00
arch_initcall ( init_pit_clocksource ) ;
2007-10-13 01:04:06 +04:00
2009-03-21 14:26:10 +03:00
# endif /* !CONFIG_X86_64 */