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