2005-04-16 15:20:36 -07:00
/*
* Precise Delay Loops for S390
*
2012-07-20 11:15:04 +02:00
* Copyright IBM Corp . 1999 , 2008
2008-10-03 21:54:59 +02:00
* Author ( s ) : Martin Schwidefsky < schwidefsky @ de . ibm . com > ,
* Heiko Carstens < heiko . carstens @ de . ibm . com > ,
2005-04-16 15:20:36 -07:00
*/
# include <linux/sched.h>
# include <linux/delay.h>
2007-02-05 21:18:19 +01:00
# include <linux/timex.h>
2009-03-26 15:24:04 +01:00
# include <linux/module.h>
2007-02-05 21:18:19 +01:00
# include <linux/irqflags.h>
2007-02-21 10:55:00 +01:00
# include <linux/interrupt.h>
2012-07-20 11:15:08 +02:00
# include <asm/vtimer.h>
2011-05-26 09:48:28 +02:00
# include <asm/div64.h>
2005-04-16 15:20:36 -07: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 16:56:43 +02:00
asm volatile ( " 0: brct %0,0b " : : " d " ( ( loops / 2 ) + 1 ) ) ;
2005-04-16 15:20:36 -07:00
}
2009-10-06 10:34:05 +02:00
static void __udelay_disabled ( unsigned long long usecs )
2005-04-16 15:20:36 -07:00
{
2012-03-11 11:59:27 -04:00
unsigned long cr0 , cr6 , new ;
u64 clock_saved , end ;
2007-02-05 21:18:19 +01:00
2013-01-30 09:49:40 +01:00
end = get_tod_clock ( ) + ( usecs < < 12 ) ;
2008-10-03 21:54:59 +02:00
clock_saved = local_tick_disable ( ) ;
2012-03-11 11:59:27 -04:00
__ctl_store ( cr0 , 0 , 0 ) ;
__ctl_store ( cr6 , 6 , 6 ) ;
new = ( cr0 & 0xffff00e0 ) | 0x00000800 ;
__ctl_load ( new , 0 , 0 ) ;
new = 0 ;
__ctl_load ( new , 6 , 6 ) ;
2009-07-07 16:37:05 +02:00
lockdep_off ( ) ;
2010-11-25 09:52:45 +01:00
do {
set_clock_comparator ( end ) ;
2012-03-11 11:59:27 -04:00
vtime_stop_cpu ( ) ;
2010-11-25 09:52:45 +01:00
local_irq_disable ( ) ;
2013-01-30 09:49:40 +01:00
} while ( get_tod_clock ( ) < end ) ;
2009-07-07 16:37:05 +02:00
lockdep_on ( ) ;
2012-03-11 11:59:27 -04:00
__ctl_load ( cr0 , 0 , 0 ) ;
__ctl_load ( cr6 , 6 , 6 ) ;
2008-10-03 21:54:59 +02:00
local_tick_enable ( clock_saved ) ;
}
2007-02-05 21:18:19 +01:00
2009-10-06 10:34:05 +02:00
static void __udelay_enabled ( unsigned long long usecs )
2008-10-03 21:54:59 +02:00
{
2012-03-11 11:59:27 -04:00
u64 clock_saved , end ;
2008-10-03 21:54:59 +02:00
2013-01-30 09:49:40 +01:00
end = get_tod_clock ( ) + ( usecs < < 12 ) ;
2007-02-05 21:18:19 +01:00
do {
2009-10-06 10:34:04 +02:00
clock_saved = 0 ;
if ( end < S390_lowcore . clock_comparator ) {
clock_saved = local_tick_disable ( ) ;
set_clock_comparator ( end ) ;
}
2012-03-11 11:59:27 -04:00
vtime_stop_cpu ( ) ;
2007-02-05 21:18:19 +01:00
local_irq_disable ( ) ;
2009-10-06 10:34:04 +02:00
if ( clock_saved )
local_tick_enable ( clock_saved ) ;
2013-01-30 09:49:40 +01:00
} while ( get_tod_clock ( ) < end ) ;
2008-10-03 21:54:59 +02:00
}
2005-04-16 15:20:36 -07:00
2008-10-03 21:54:59 +02:00
/*
* Waits for ' usecs ' microseconds using the TOD clock comparator .
*/
2009-10-06 10:34:05 +02:00
void __udelay ( unsigned long long usecs )
2008-10-03 21:54:59 +02: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 21:18:19 +01:00
}
2008-10-03 21:54:59 +02:00
if ( raw_irqs_disabled_flags ( flags ) ) {
local_bh_disable ( ) ;
__udelay_disabled ( usecs ) ;
2007-02-21 10:55:00 +01:00
_local_bh_enable ( ) ;
2008-10-03 21:54:59 +02:00
goto out ;
}
__udelay_enabled ( usecs ) ;
out :
2007-02-05 21:18:19 +01:00
local_irq_restore ( flags ) ;
2008-10-03 21:54:59 +02:00
preempt_enable ( ) ;
2005-04-16 15:20:36 -07:00
}
2009-03-26 15:24:04 +01:00
EXPORT_SYMBOL ( __udelay ) ;
2008-10-10 21:33:22 +02:00
/*
* Simple udelay variant . To be used on startup and reboot
* when the interrupt handler isn ' t working .
*/
2009-10-06 10:34:05 +02:00
void udelay_simple ( unsigned long long usecs )
2008-10-10 21:33:22 +02:00
{
u64 end ;
2013-01-30 09:49:40 +01:00
end = get_tod_clock ( ) + ( usecs < < 12 ) ;
while ( get_tod_clock ( ) < end )
2008-10-10 21:33:22 +02:00
cpu_relax ( ) ;
}
2011-05-26 09:48:28 +02:00
void __ndelay ( unsigned long long nsecs )
{
u64 end ;
nsecs < < = 9 ;
do_div ( nsecs , 125 ) ;
2013-01-30 09:49:40 +01:00
end = get_tod_clock ( ) + nsecs ;
2011-05-26 09:48:28 +02:00
if ( nsecs & ~ 0xfffUL )
__udelay ( nsecs > > 12 ) ;
2013-01-30 09:49:40 +01:00
while ( get_tod_clock ( ) < end )
2011-05-26 09:48:28 +02:00
barrier ( ) ;
}
EXPORT_SYMBOL ( __ndelay ) ;