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-06-17 02:31:12 +04:00
# include <linux/timex.h>
2009-03-21 14:26:10 +03:00
# include <linux/delay.h>
2011-06-01 22:04:57 +04:00
# include <linux/i8253.h>
2009-03-21 14:26:10 +03:00
# include <linux/init.h>
# include <linux/io.h>
2006-06-26 11:25:09 +04:00
2008-01-30 15:30:02 +03:00
# include <asm/hpet.h>
2011-06-01 22:05:06 +04:00
# include <asm/time.h>
2009-03-21 14:26:10 +03:00
# include <asm/smp.h>
2006-06-26 11:25:09 +04:00
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 )
{
2010-02-17 19:47:10 +03:00
raw_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
}
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: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 ;
}
2010-02-17 19:47:10 +03:00
raw_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
{
2010-02-17 19:47:10 +03:00
raw_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 */
2010-02-17 19:47:10 +03:00
raw_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 ,
. 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 ( ) ) ;
2011-05-19 01:33:42 +04:00
clockevents_config_and_register ( & pit_ce , CLOCK_TICK_RATE , 0xF , 0x7FFF ) ;
2009-03-21 14:26:10 +03:00
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
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 ;
2011-05-08 21:55:19 +04:00
return clocksource_i8253_init ( ) ;
2006-06-26 11:25:12 +04:00
}
2007-03-05 11:30:50 +03:00
arch_initcall ( init_pit_clocksource ) ;
2009-03-21 14:26:10 +03:00
# endif /* !CONFIG_X86_64 */