2005-04-17 02:20:36 +04:00
/*
* Precise Delay Loops for S390
*
2008-10-03 23:54:59 +04:00
* Copyright IBM Corp . 1999 , 2008
* Author ( s ) : Martin Schwidefsky < schwidefsky @ de . ibm . com > ,
* Heiko Carstens < heiko . carstens @ de . ibm . com > ,
2005-04-17 02:20:36 +04:00
*/
# include <linux/sched.h>
# include <linux/delay.h>
2007-02-05 23:18:19 +03:00
# include <linux/timex.h>
2009-03-26 17:24:04 +03:00
# include <linux/module.h>
2007-02-05 23:18:19 +03:00
# include <linux/irqflags.h>
2007-02-21 12:55:00 +03:00
# include <linux/interrupt.h>
2011-05-26 11:48:28 +04:00
# include <asm/div64.h>
2005-04-17 02:20:36 +04:00
void __delay ( unsigned long loops )
{
/*
* To end the bloody studid and useless discussion about the
* BogoMips number I took the liberty to define the __delay
* function in a way that that resulting BogoMips number will
* yield the megahertz number of the cpu . The important function
* is udelay and that is done using the tod clock . - - martin .
*/
2006-09-28 18:56:43 +04:00
asm volatile ( " 0: brct %0,0b " : : " d " ( ( loops / 2 ) + 1 ) ) ;
2005-04-17 02:20:36 +04:00
}
2009-10-06 12:34:05 +04:00
static void __udelay_disabled ( unsigned long long usecs )
2005-04-17 02:20:36 +04:00
{
2008-10-03 23:54:59 +04:00
unsigned long mask , cr0 , cr0_saved ;
u64 clock_saved ;
2010-11-25 11:52:45 +03:00
u64 end ;
2007-02-05 23:18:19 +03:00
2010-11-25 11:52:45 +03:00
mask = psw_kernel_bits | PSW_MASK_WAIT | PSW_MASK_EXT ;
end = get_clock ( ) + ( usecs < < 12 ) ;
2008-10-03 23:54:59 +04:00
clock_saved = local_tick_disable ( ) ;
__ctl_store ( cr0_saved , 0 , 0 ) ;
cr0 = ( cr0_saved & 0xffff00e0 ) | 0x00000800 ;
__ctl_load ( cr0 , 0 , 0 ) ;
2009-07-07 18:37:05 +04:00
lockdep_off ( ) ;
2010-11-25 11:52:45 +03:00
do {
set_clock_comparator ( end ) ;
trace_hardirqs_on ( ) ;
__load_psw_mask ( mask ) ;
local_irq_disable ( ) ;
} while ( get_clock ( ) < end ) ;
2009-07-07 18:37:05 +04:00
lockdep_on ( ) ;
2008-10-03 23:54:59 +04:00
__ctl_load ( cr0_saved , 0 , 0 ) ;
local_tick_enable ( clock_saved ) ;
}
2007-02-05 23:18:19 +03:00
2009-10-06 12:34:05 +04:00
static void __udelay_enabled ( unsigned long long usecs )
2008-10-03 23:54:59 +04:00
{
unsigned long mask ;
2009-10-06 12:34:04 +04:00
u64 clock_saved ;
u64 end ;
2008-10-03 23:54:59 +04:00
mask = psw_kernel_bits | PSW_MASK_WAIT | PSW_MASK_EXT | PSW_MASK_IO ;
2009-10-06 12:34:05 +04:00
end = get_clock ( ) + ( usecs < < 12 ) ;
2007-02-05 23:18:19 +03:00
do {
2009-10-06 12:34:04 +04:00
clock_saved = 0 ;
if ( end < S390_lowcore . clock_comparator ) {
clock_saved = local_tick_disable ( ) ;
set_clock_comparator ( end ) ;
}
2007-02-05 23:18:19 +03:00
trace_hardirqs_on ( ) ;
__load_psw_mask ( mask ) ;
local_irq_disable ( ) ;
2009-10-06 12:34:04 +04:00
if ( clock_saved )
local_tick_enable ( clock_saved ) ;
2007-02-05 23:18:19 +03:00
} while ( get_clock ( ) < end ) ;
2008-10-03 23:54:59 +04:00
}
2005-04-17 02:20:36 +04:00
2008-10-03 23:54:59 +04:00
/*
* Waits for ' usecs ' microseconds using the TOD clock comparator .
*/
2009-10-06 12:34:05 +04:00
void __udelay ( unsigned long long usecs )
2008-10-03 23:54:59 +04:00
{
unsigned long flags ;
preempt_disable ( ) ;
local_irq_save ( flags ) ;
if ( in_irq ( ) ) {
__udelay_disabled ( usecs ) ;
goto out ;
}
if ( in_softirq ( ) ) {
if ( raw_irqs_disabled_flags ( flags ) )
__udelay_disabled ( usecs ) ;
else
__udelay_enabled ( usecs ) ;
goto out ;
2007-02-05 23:18:19 +03:00
}
2008-10-03 23:54:59 +04:00
if ( raw_irqs_disabled_flags ( flags ) ) {
local_bh_disable ( ) ;
__udelay_disabled ( usecs ) ;
2007-02-21 12:55:00 +03:00
_local_bh_enable ( ) ;
2008-10-03 23:54:59 +04:00
goto out ;
}
__udelay_enabled ( usecs ) ;
out :
2007-02-05 23:18:19 +03:00
local_irq_restore ( flags ) ;
2008-10-03 23:54:59 +04:00
preempt_enable ( ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-26 17:24:04 +03:00
EXPORT_SYMBOL ( __udelay ) ;
2008-10-10 23:33:22 +04:00
/*
* Simple udelay variant . To be used on startup and reboot
* when the interrupt handler isn ' t working .
*/
2009-10-06 12:34:05 +04:00
void udelay_simple ( unsigned long long usecs )
2008-10-10 23:33:22 +04:00
{
u64 end ;
2009-10-06 12:34:05 +04:00
end = get_clock ( ) + ( usecs < < 12 ) ;
2008-10-10 23:33:22 +04:00
while ( get_clock ( ) < end )
cpu_relax ( ) ;
}
2011-05-26 11:48:28 +04:00
void __ndelay ( unsigned long long nsecs )
{
u64 end ;
nsecs < < = 9 ;
do_div ( nsecs , 125 ) ;
end = get_clock ( ) + nsecs ;
if ( nsecs & ~ 0xfffUL )
__udelay ( nsecs > > 12 ) ;
while ( get_clock ( ) < end )
barrier ( ) ;
}
EXPORT_SYMBOL ( __ndelay ) ;