2005-04-16 15:20:36 -07:00
/*
* Precise Delay Loops for S390
*
2008-10-03 21:54:59 +02:00
* Copyright IBM Corp . 1999 , 2008
* 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>
# include <linux/irqflags.h>
2007-02-21 10:55:00 +01:00
# include <linux/interrupt.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
}
2008-10-03 21:54:59 +02:00
static void __udelay_disabled ( unsigned long usecs )
2005-04-16 15:20:36 -07:00
{
2008-10-03 21:54:59 +02:00
unsigned long mask , cr0 , cr0_saved ;
u64 clock_saved ;
2007-02-05 21:18:19 +01:00
2008-10-03 21:54:59 +02:00
clock_saved = local_tick_disable ( ) ;
set_clock_comparator ( get_clock ( ) + ( ( u64 ) usecs < < 12 ) ) ;
__ctl_store ( cr0_saved , 0 , 0 ) ;
cr0 = ( cr0_saved & 0xffff00e0 ) | 0x00000800 ;
__ctl_load ( cr0 , 0 , 0 ) ;
mask = psw_kernel_bits | PSW_MASK_WAIT | PSW_MASK_EXT ;
trace_hardirqs_on ( ) ;
__load_psw_mask ( mask ) ;
local_irq_disable ( ) ;
__ctl_load ( cr0_saved , 0 , 0 ) ;
local_tick_enable ( clock_saved ) ;
set_clock_comparator ( S390_lowcore . clock_comparator ) ;
}
2007-02-05 21:18:19 +01:00
2008-10-03 21:54:59 +02:00
static void __udelay_enabled ( unsigned long usecs )
{
unsigned long mask ;
u64 end , time ;
mask = psw_kernel_bits | PSW_MASK_WAIT | PSW_MASK_EXT | PSW_MASK_IO ;
2007-02-05 21:18:19 +01:00
end = get_clock ( ) + ( ( u64 ) usecs < < 12 ) ;
do {
2008-04-17 07:46:25 +02:00
time = end < S390_lowcore . clock_comparator ?
end : S390_lowcore . clock_comparator ;
2007-02-05 21:18:19 +01:00
set_clock_comparator ( time ) ;
trace_hardirqs_on ( ) ;
__load_psw_mask ( mask ) ;
local_irq_disable ( ) ;
} while ( get_clock ( ) < end ) ;
2008-10-03 21:54:59 +02:00
set_clock_comparator ( S390_lowcore . clock_comparator ) ;
}
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 .
*/
void __udelay ( unsigned long usecs )
{
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
}