forked from Minki/linux
0b0ed657fe
The current code is rather complex and caused a lot of subtle and hard to debug bugs in the past. Simplify the code by calling the system_call handler with interrupts disabled, save machine state, and re-enable them later. This requires significant changes to the machine check handling code as well. When the machine check interrupt arrived while being in kernel mode the new code will signal pending machine checks with a SIGP external call. When userspace was interrupted, the handler will switch to the kernel stack and directly execute s390_handle_mcck(). Signed-off-by: Sven Schnelle <svens@linux.ibm.com> Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
132 lines
3.1 KiB
C
132 lines
3.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Precise Delay Loops for S390
|
|
*
|
|
* Copyright IBM Corp. 1999, 2008
|
|
* Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>,
|
|
* Heiko Carstens <heiko.carstens@de.ibm.com>,
|
|
*/
|
|
|
|
#include <linux/sched.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/timex.h>
|
|
#include <linux/export.h>
|
|
#include <linux/irqflags.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/irq.h>
|
|
#include <asm/vtimer.h>
|
|
#include <asm/div64.h>
|
|
#include <asm/idle.h>
|
|
|
|
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.
|
|
*/
|
|
asm volatile("0: brct %0,0b" : : "d" ((loops/2) + 1));
|
|
}
|
|
EXPORT_SYMBOL(__delay);
|
|
|
|
static void __udelay_disabled(unsigned long long usecs)
|
|
{
|
|
unsigned long cr0, cr0_new, psw_mask, flags;
|
|
struct s390_idle_data idle;
|
|
u64 end;
|
|
|
|
end = get_tod_clock() + (usecs << 12);
|
|
__ctl_store(cr0, 0, 0);
|
|
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);
|
|
local_irq_save(flags);
|
|
psw_idle(&idle, psw_mask);
|
|
local_irq_restore(flags);
|
|
clear_cpu_flag(CIF_IGNORE_IRQ);
|
|
set_clock_comparator(S390_lowcore.clock_comparator);
|
|
__ctl_load(cr0, 0, 0);
|
|
}
|
|
|
|
static void __udelay_enabled(unsigned long long usecs)
|
|
{
|
|
u64 clock_saved, end;
|
|
|
|
end = get_tod_clock_fast() + (usecs << 12);
|
|
do {
|
|
clock_saved = 0;
|
|
if (tod_after(S390_lowcore.clock_comparator, end)) {
|
|
clock_saved = local_tick_disable();
|
|
set_clock_comparator(end);
|
|
}
|
|
enabled_wait();
|
|
if (clock_saved)
|
|
local_tick_enable(clock_saved);
|
|
} while (get_tod_clock_fast() < end);
|
|
}
|
|
|
|
/*
|
|
* Waits for 'usecs' microseconds using the TOD clock comparator.
|
|
*/
|
|
void __udelay(unsigned long 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;
|
|
}
|
|
if (raw_irqs_disabled_flags(flags)) {
|
|
local_bh_disable();
|
|
__udelay_disabled(usecs);
|
|
_local_bh_enable();
|
|
goto out;
|
|
}
|
|
__udelay_enabled(usecs);
|
|
out:
|
|
local_irq_restore(flags);
|
|
preempt_enable();
|
|
}
|
|
EXPORT_SYMBOL(__udelay);
|
|
|
|
/*
|
|
* Simple udelay variant. To be used on startup and reboot
|
|
* when the interrupt handler isn't working.
|
|
*/
|
|
void udelay_simple(unsigned long long usecs)
|
|
{
|
|
u64 end;
|
|
|
|
end = get_tod_clock_fast() + (usecs << 12);
|
|
while (get_tod_clock_fast() < end)
|
|
cpu_relax();
|
|
}
|
|
|
|
void __ndelay(unsigned long long nsecs)
|
|
{
|
|
u64 end;
|
|
|
|
nsecs <<= 9;
|
|
do_div(nsecs, 125);
|
|
end = get_tod_clock_fast() + nsecs;
|
|
if (nsecs & ~0xfffUL)
|
|
__udelay(nsecs >> 12);
|
|
while (get_tod_clock_fast() < end)
|
|
barrier();
|
|
}
|
|
EXPORT_SYMBOL(__ndelay);
|