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>
2017-02-09 15:20:25 -05:00
# include <linux/export.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>
2015-08-15 11:42:21 +02:00
# include <linux/irq.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>
2015-08-15 11:42:21 +02:00
# include <asm/idle.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
}
2015-08-04 23:38:23 -07:00
EXPORT_SYMBOL ( __delay ) ;
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
{
2015-08-15 11:42:21 +02:00
unsigned long cr0 , cr0_new , psw_mask ;
struct s390_idle_data idle ;
u64 end ;
2007-02-05 21:18:19 +01:00
2013-01-30 09:49:40 +01:00
end = get_tod_clock ( ) + ( usecs < < 12 ) ;
2012-03-11 11:59:27 -04:00
__ctl_store ( cr0 , 0 , 0 ) ;
2015-08-15 11:42:21 +02:00
cr0_new = cr0 & ~ CR0_IRQ_SUBCLASS_MASK ;
cr0_new | = ( 1UL < < ( 63 - 52 ) ) ; /* enable clock comparator irq */
__ctl_load ( cr0_new , 0 , 0 ) ;
psw_mask = __extract_psw ( ) | PSW_MASK_EXT | PSW_MASK_WAIT ;
set_clock_comparator ( end ) ;
set_cpu_flag ( CIF_IGNORE_IRQ ) ;
psw_idle ( & idle , psw_mask ) ;
clear_cpu_flag ( CIF_IGNORE_IRQ ) ;
set_clock_comparator ( S390_lowcore . clock_comparator ) ;
2012-03-11 11:59:27 -04:00
__ctl_load ( cr0 , 0 , 0 ) ;
2008-10-03 21:54:59 +02:00
}
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-10-17 12:38:17 +02:00
end = get_tod_clock_fast ( ) + ( usecs < < 12 ) ;
2007-02-05 21:18:19 +01:00
do {
2009-10-06 10:34:04 +02:00
clock_saved = 0 ;
2016-10-27 12:41:39 +02:00
if ( tod_after ( S390_lowcore . clock_comparator , end ) ) {
2009-10-06 10:34:04 +02:00
clock_saved = local_tick_disable ( ) ;
set_clock_comparator ( end ) ;
}
2014-10-01 10:57:57 +02:00
enabled_wait ( ) ;
2009-10-06 10:34:04 +02:00
if ( clock_saved )
local_tick_enable ( clock_saved ) ;
2013-10-17 12:38:17 +02:00
} while ( get_tod_clock_fast ( ) < 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-10-17 12:38:17 +02:00
end = get_tod_clock_fast ( ) + ( usecs < < 12 ) ;
while ( get_tod_clock_fast ( ) < 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-10-17 12:38:17 +02:00
end = get_tod_clock_fast ( ) + nsecs ;
2011-05-26 09:48:28 +02:00
if ( nsecs & ~ 0xfffUL )
__udelay ( nsecs > > 12 ) ;
2013-10-17 12:38:17 +02:00
while ( get_tod_clock_fast ( ) < end )
2011-05-26 09:48:28 +02:00
barrier ( ) ;
}
EXPORT_SYMBOL ( __ndelay ) ;