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