Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
1da177e4 LT |
2 | * Precise Delay Loops for S390 |
3 | * | |
d3d238c7 HC |
4 | * Copyright IBM Corp. 1999,2008 |
5 | * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>, | |
6 | * Heiko Carstens <heiko.carstens@de.ibm.com>, | |
1da177e4 LT |
7 | */ |
8 | ||
1da177e4 LT |
9 | #include <linux/sched.h> |
10 | #include <linux/delay.h> | |
d54853ef | 11 | #include <linux/timex.h> |
1485c5c8 | 12 | #include <linux/module.h> |
d54853ef | 13 | #include <linux/irqflags.h> |
bf6f6aa4 | 14 | #include <linux/interrupt.h> |
1da177e4 LT |
15 | |
16 | void __delay(unsigned long loops) | |
17 | { | |
18 | /* | |
19 | * To end the bloody studid and useless discussion about the | |
20 | * BogoMips number I took the liberty to define the __delay | |
21 | * function in a way that that resulting BogoMips number will | |
22 | * yield the megahertz number of the cpu. The important function | |
23 | * is udelay and that is done using the tod clock. -- martin. | |
24 | */ | |
94c12cc7 | 25 | asm volatile("0: brct %0,0b" : : "d" ((loops/2) + 1)); |
1da177e4 LT |
26 | } |
27 | ||
0cd6a403 | 28 | static void __udelay_disabled(unsigned long long usecs) |
1da177e4 | 29 | { |
d3d238c7 HC |
30 | unsigned long mask, cr0, cr0_saved; |
31 | u64 clock_saved; | |
d54853ef | 32 | |
d3d238c7 | 33 | clock_saved = local_tick_disable(); |
0cd6a403 | 34 | set_clock_comparator(get_clock() + (usecs << 12)); |
d3d238c7 HC |
35 | __ctl_store(cr0_saved, 0, 0); |
36 | cr0 = (cr0_saved & 0xffff00e0) | 0x00000800; | |
37 | __ctl_load(cr0 , 0, 0); | |
38 | mask = psw_kernel_bits | PSW_MASK_WAIT | PSW_MASK_EXT; | |
bb8c29ca | 39 | lockdep_off(); |
d3d238c7 HC |
40 | trace_hardirqs_on(); |
41 | __load_psw_mask(mask); | |
42 | local_irq_disable(); | |
bb8c29ca | 43 | lockdep_on(); |
d3d238c7 HC |
44 | __ctl_load(cr0_saved, 0, 0); |
45 | local_tick_enable(clock_saved); | |
46 | set_clock_comparator(S390_lowcore.clock_comparator); | |
47 | } | |
d54853ef | 48 | |
0cd6a403 | 49 | static void __udelay_enabled(unsigned long long usecs) |
d3d238c7 HC |
50 | { |
51 | unsigned long mask; | |
78d81f2f CB |
52 | u64 clock_saved; |
53 | u64 end; | |
d3d238c7 HC |
54 | |
55 | mask = psw_kernel_bits | PSW_MASK_WAIT | PSW_MASK_EXT | PSW_MASK_IO; | |
0cd6a403 | 56 | end = get_clock() + (usecs << 12); |
d54853ef | 57 | do { |
78d81f2f CB |
58 | clock_saved = 0; |
59 | if (end < S390_lowcore.clock_comparator) { | |
60 | clock_saved = local_tick_disable(); | |
61 | set_clock_comparator(end); | |
62 | } | |
d54853ef MS |
63 | trace_hardirqs_on(); |
64 | __load_psw_mask(mask); | |
65 | local_irq_disable(); | |
78d81f2f CB |
66 | if (clock_saved) |
67 | local_tick_enable(clock_saved); | |
d54853ef | 68 | } while (get_clock() < end); |
d3d238c7 HC |
69 | set_clock_comparator(S390_lowcore.clock_comparator); |
70 | } | |
1da177e4 | 71 | |
d3d238c7 HC |
72 | /* |
73 | * Waits for 'usecs' microseconds using the TOD clock comparator. | |
74 | */ | |
0cd6a403 | 75 | void __udelay(unsigned long long usecs) |
d3d238c7 HC |
76 | { |
77 | unsigned long flags; | |
78 | ||
79 | preempt_disable(); | |
80 | local_irq_save(flags); | |
81 | if (in_irq()) { | |
82 | __udelay_disabled(usecs); | |
83 | goto out; | |
84 | } | |
85 | if (in_softirq()) { | |
86 | if (raw_irqs_disabled_flags(flags)) | |
87 | __udelay_disabled(usecs); | |
88 | else | |
89 | __udelay_enabled(usecs); | |
90 | goto out; | |
d54853ef | 91 | } |
d3d238c7 HC |
92 | if (raw_irqs_disabled_flags(flags)) { |
93 | local_bh_disable(); | |
94 | __udelay_disabled(usecs); | |
bf6f6aa4 | 95 | _local_bh_enable(); |
d3d238c7 HC |
96 | goto out; |
97 | } | |
98 | __udelay_enabled(usecs); | |
99 | out: | |
d54853ef | 100 | local_irq_restore(flags); |
d3d238c7 | 101 | preempt_enable(); |
1da177e4 | 102 | } |
1485c5c8 | 103 | EXPORT_SYMBOL(__udelay); |
5a0d0e65 HC |
104 | |
105 | /* | |
106 | * Simple udelay variant. To be used on startup and reboot | |
107 | * when the interrupt handler isn't working. | |
108 | */ | |
0cd6a403 | 109 | void udelay_simple(unsigned long long usecs) |
5a0d0e65 HC |
110 | { |
111 | u64 end; | |
112 | ||
0cd6a403 | 113 | end = get_clock() + (usecs << 12); |
5a0d0e65 HC |
114 | while (get_clock() < end) |
115 | cpu_relax(); | |
116 | } |