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