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 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() + ((u64) 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 usecs) 50 { 51 unsigned long mask; 52 u64 end, time; 53 54 mask = psw_kernel_bits | PSW_MASK_WAIT | PSW_MASK_EXT | PSW_MASK_IO; 55 end = get_clock() + ((u64) usecs << 12); 56 do { 57 time = end < S390_lowcore.clock_comparator ? 58 end : S390_lowcore.clock_comparator; 59 set_clock_comparator(time); 60 trace_hardirqs_on(); 61 __load_psw_mask(mask); 62 local_irq_disable(); 63 } while (get_clock() < end); 64 set_clock_comparator(S390_lowcore.clock_comparator); 65 } 66 67 /* 68 * Waits for 'usecs' microseconds using the TOD clock comparator. 69 */ 70 void __udelay(unsigned long usecs) 71 { 72 unsigned long flags; 73 74 preempt_disable(); 75 local_irq_save(flags); 76 if (in_irq()) { 77 __udelay_disabled(usecs); 78 goto out; 79 } 80 if (in_softirq()) { 81 if (raw_irqs_disabled_flags(flags)) 82 __udelay_disabled(usecs); 83 else 84 __udelay_enabled(usecs); 85 goto out; 86 } 87 if (raw_irqs_disabled_flags(flags)) { 88 local_bh_disable(); 89 __udelay_disabled(usecs); 90 _local_bh_enable(); 91 goto out; 92 } 93 __udelay_enabled(usecs); 94 out: 95 local_irq_restore(flags); 96 preempt_enable(); 97 } 98 EXPORT_SYMBOL(__udelay); 99 100 /* 101 * Simple udelay variant. To be used on startup and reboot 102 * when the interrupt handler isn't working. 103 */ 104 void udelay_simple(unsigned long usecs) 105 { 106 u64 end; 107 108 end = get_clock() + ((u64) usecs << 12); 109 while (get_clock() < end) 110 cpu_relax(); 111 } 112