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 33 clock_saved = local_tick_disable(); 34 set_clock_comparator(get_clock() + (usecs << 12)); 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; 39 lockdep_off(); 40 trace_hardirqs_on(); 41 __load_psw_mask(mask); 42 local_irq_disable(); 43 lockdep_on(); 44 __ctl_load(cr0_saved, 0, 0); 45 local_tick_enable(clock_saved); 46 set_clock_comparator(S390_lowcore.clock_comparator); 47 } 48 49 static void __udelay_enabled(unsigned long long usecs) 50 { 51 unsigned long mask; 52 u64 clock_saved; 53 u64 end; 54 55 mask = psw_kernel_bits | PSW_MASK_WAIT | PSW_MASK_EXT | PSW_MASK_IO; 56 end = get_clock() + (usecs << 12); 57 do { 58 clock_saved = 0; 59 if (end < S390_lowcore.clock_comparator) { 60 clock_saved = local_tick_disable(); 61 set_clock_comparator(end); 62 } 63 trace_hardirqs_on(); 64 __load_psw_mask(mask); 65 local_irq_disable(); 66 if (clock_saved) 67 local_tick_enable(clock_saved); 68 } while (get_clock() < end); 69 set_clock_comparator(S390_lowcore.clock_comparator); 70 } 71 72 /* 73 * Waits for 'usecs' microseconds using the TOD clock comparator. 74 */ 75 void __udelay(unsigned long long usecs) 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; 91 } 92 if (raw_irqs_disabled_flags(flags)) { 93 local_bh_disable(); 94 __udelay_disabled(usecs); 95 _local_bh_enable(); 96 goto out; 97 } 98 __udelay_enabled(usecs); 99 out: 100 local_irq_restore(flags); 101 preempt_enable(); 102 } 103 EXPORT_SYMBOL(__udelay); 104 105 /* 106 * Simple udelay variant. To be used on startup and reboot 107 * when the interrupt handler isn't working. 108 */ 109 void udelay_simple(unsigned long long usecs) 110 { 111 u64 end; 112 113 end = get_clock() + (usecs << 12); 114 while (get_clock() < end) 115 cpu_relax(); 116 } 117