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_DAT | PSW_MASK_WAIT | 36 PSW_MASK_EXT | PSW_MASK_MCHECK; 37 end = get_clock() + (usecs << 12); 38 clock_saved = local_tick_disable(); 39 __ctl_store(cr0_saved, 0, 0); 40 cr0 = (cr0_saved & 0xffff00e0) | 0x00000800; 41 __ctl_load(cr0 , 0, 0); 42 lockdep_off(); 43 do { 44 set_clock_comparator(end); 45 trace_hardirqs_on(); 46 __load_psw_mask(mask); 47 local_irq_disable(); 48 } while (get_clock() < end); 49 lockdep_on(); 50 __ctl_load(cr0_saved, 0, 0); 51 local_tick_enable(clock_saved); 52 } 53 54 static void __udelay_enabled(unsigned long long usecs) 55 { 56 unsigned long mask; 57 u64 clock_saved; 58 u64 end; 59 60 mask = psw_kernel_bits | PSW_MASK_WAIT | PSW_MASK_EXT | PSW_MASK_IO; 61 end = get_clock() + (usecs << 12); 62 do { 63 clock_saved = 0; 64 if (end < S390_lowcore.clock_comparator) { 65 clock_saved = local_tick_disable(); 66 set_clock_comparator(end); 67 } 68 trace_hardirqs_on(); 69 __load_psw_mask(mask); 70 local_irq_disable(); 71 if (clock_saved) 72 local_tick_enable(clock_saved); 73 } while (get_clock() < end); 74 } 75 76 /* 77 * Waits for 'usecs' microseconds using the TOD clock comparator. 78 */ 79 void __udelay(unsigned long long usecs) 80 { 81 unsigned long flags; 82 83 preempt_disable(); 84 local_irq_save(flags); 85 if (in_irq()) { 86 __udelay_disabled(usecs); 87 goto out; 88 } 89 if (in_softirq()) { 90 if (raw_irqs_disabled_flags(flags)) 91 __udelay_disabled(usecs); 92 else 93 __udelay_enabled(usecs); 94 goto out; 95 } 96 if (raw_irqs_disabled_flags(flags)) { 97 local_bh_disable(); 98 __udelay_disabled(usecs); 99 _local_bh_enable(); 100 goto out; 101 } 102 __udelay_enabled(usecs); 103 out: 104 local_irq_restore(flags); 105 preempt_enable(); 106 } 107 EXPORT_SYMBOL(__udelay); 108 109 /* 110 * Simple udelay variant. To be used on startup and reboot 111 * when the interrupt handler isn't working. 112 */ 113 void udelay_simple(unsigned long long usecs) 114 { 115 u64 end; 116 117 end = get_clock() + (usecs << 12); 118 while (get_clock() < end) 119 cpu_relax(); 120 } 121 122 void __ndelay(unsigned long long nsecs) 123 { 124 u64 end; 125 126 nsecs <<= 9; 127 do_div(nsecs, 125); 128 end = get_clock() + nsecs; 129 if (nsecs & ~0xfffUL) 130 __udelay(nsecs >> 12); 131 while (get_clock() < end) 132 barrier(); 133 } 134 EXPORT_SYMBOL(__ndelay); 135