linux/arch/s390/lib/delay.c
Martin Schwidefsky bf6f6aa46f [S390] prevent softirqs if delay is called disabled
The new delay implementation uses the clock comparator and an external
interrupt even if it is called disabled for interrupts. To do this
all external interrupt source except clock comparator are switched of
before enabling external interrupts. The external interrupt at the
end of the delay period may not execute softirqs or we can end up in a
dead-lock.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
2007-02-21 10:55:00 +01:00

74 lines
2.1 KiB
C

/*
* arch/s390/lib/delay.c
* Precise Delay Loops for S390
*
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
*
* Derived from "arch/i386/lib/delay.c"
* Copyright (C) 1993 Linus Torvalds
* Copyright (C) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
*/
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/timex.h>
#include <linux/irqflags.h>
#include <linux/interrupt.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));
}
/*
* Waits for 'usecs' microseconds using the TOD clock comparator.
*/
void __udelay(unsigned long usecs)
{
u64 end, time, jiffy_timer = 0;
unsigned long flags, cr0, mask, dummy;
int irq_context;
irq_context = in_interrupt();
if (!irq_context)
local_bh_disable();
local_irq_save(flags);
if (raw_irqs_disabled_flags(flags)) {
jiffy_timer = S390_lowcore.jiffy_timer;
S390_lowcore.jiffy_timer = -1ULL - (4096 << 12);
__ctl_store(cr0, 0, 0);
dummy = (cr0 & 0xffff00e0) | 0x00000800;
__ctl_load(dummy , 0, 0);
mask = psw_kernel_bits | PSW_MASK_WAIT | PSW_MASK_EXT;
} else
mask = psw_kernel_bits | PSW_MASK_WAIT |
PSW_MASK_EXT | PSW_MASK_IO;
end = get_clock() + ((u64) usecs << 12);
do {
time = end < S390_lowcore.jiffy_timer ?
end : S390_lowcore.jiffy_timer;
set_clock_comparator(time);
trace_hardirqs_on();
__load_psw_mask(mask);
local_irq_disable();
} while (get_clock() < end);
if (raw_irqs_disabled_flags(flags)) {
__ctl_load(cr0, 0, 0);
S390_lowcore.jiffy_timer = jiffy_timer;
}
if (!irq_context)
_local_bh_enable();
set_clock_comparator(S390_lowcore.jiffy_timer);
local_irq_restore(flags);
}