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/module.h> 13 #include <linux/irqflags.h> 14 #include <linux/interrupt.h> 15 #include <asm/div64.h> 16 17 void __delay(unsigned long loops) 18 { 19 /* 20 * To end the bloody studid and useless discussion about the 21 * BogoMips number I took the liberty to define the __delay 22 * function in a way that that resulting BogoMips number will 23 * yield the megahertz number of the cpu. The important function 24 * is udelay and that is done using the tod clock. -- martin. 25 */ 26 asm volatile("0: brct %0,0b" : : "d" ((loops/2) + 1)); 27 } 28 29 static void __udelay_disabled(unsigned long long usecs) 30 { 31 unsigned long mask, cr0, cr0_saved; 32 u64 clock_saved; 33 u64 end; 34 35 mask = psw_kernel_bits | PSW_MASK_WAIT | PSW_MASK_EXT; 36 end = get_clock() + (usecs << 12); 37 clock_saved = local_tick_disable(); 38 __ctl_store(cr0_saved, 0, 0); 39 cr0 = (cr0_saved & 0xffff00e0) | 0x00000800; 40 __ctl_load(cr0 , 0, 0); 41 lockdep_off(); 42 do { 43 set_clock_comparator(end); 44 trace_hardirqs_on(); 45 __load_psw_mask(mask); 46 local_irq_disable(); 47 } while (get_clock() < end); 48 lockdep_on(); 49 __ctl_load(cr0_saved, 0, 0); 50 local_tick_enable(clock_saved); 51 } 52 53 static void __udelay_enabled(unsigned long long usecs) 54 { 55 unsigned long mask; 56 u64 clock_saved; 57 u64 end; 58 59 mask = psw_kernel_bits | PSW_MASK_WAIT | PSW_MASK_EXT | PSW_MASK_IO; 60 end = get_clock() + (usecs << 12); 61 do { 62 clock_saved = 0; 63 if (end < S390_lowcore.clock_comparator) { 64 clock_saved = local_tick_disable(); 65 set_clock_comparator(end); 66 } 67 trace_hardirqs_on(); 68 __load_psw_mask(mask); 69 local_irq_disable(); 70 if (clock_saved) 71 local_tick_enable(clock_saved); 72 } while (get_clock() < end); 73 } 74 75 /* 76 * Waits for 'usecs' microseconds using the TOD clock comparator. 77 */ 78 void __udelay(unsigned long long usecs) 79 { 80 unsigned long flags; 81 82 preempt_disable(); 83 local_irq_save(flags); 84 if (in_irq()) { 85 __udelay_disabled(usecs); 86 goto out; 87 } 88 if (in_softirq()) { 89 if (raw_irqs_disabled_flags(flags)) 90 __udelay_disabled(usecs); 91 else 92 __udelay_enabled(usecs); 93 goto out; 94 } 95 if (raw_irqs_disabled_flags(flags)) { 96 local_bh_disable(); 97 __udelay_disabled(usecs); 98 _local_bh_enable(); 99 goto out; 100 } 101 __udelay_enabled(usecs); 102 out: 103 local_irq_restore(flags); 104 preempt_enable(); 105 } 106 EXPORT_SYMBOL(__udelay); 107 108 /* 109 * Simple udelay variant. To be used on startup and reboot 110 * when the interrupt handler isn't working. 111 */ 112 void udelay_simple(unsigned long long usecs) 113 { 114 u64 end; 115 116 end = get_clock() + (usecs << 12); 117 while (get_clock() < end) 118 cpu_relax(); 119 } 120 121 void __ndelay(unsigned long long nsecs) 122 { 123 u64 end; 124 125 nsecs <<= 9; 126 do_div(nsecs, 125); 127 end = get_clock() + nsecs; 128 if (nsecs & ~0xfffUL) 129 __udelay(nsecs >> 12); 130 while (get_clock() < end) 131 barrier(); 132 } 133 EXPORT_SYMBOL(__ndelay); 134