1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Precise Delay Loops for S390 4 * 5 * Copyright IBM Corp. 1999, 2008 6 * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>, 7 * Heiko Carstens <heiko.carstens@de.ibm.com>, 8 */ 9 10 #include <linux/sched.h> 11 #include <linux/delay.h> 12 #include <linux/timex.h> 13 #include <linux/export.h> 14 #include <linux/irqflags.h> 15 #include <linux/interrupt.h> 16 #include <linux/jump_label.h> 17 #include <linux/irq.h> 18 #include <asm/vtimer.h> 19 #include <asm/div64.h> 20 #include <asm/idle.h> 21 22 static DEFINE_STATIC_KEY_FALSE(udelay_ready); 23 24 void __init udelay_enable(void) 25 { 26 static_branch_enable(&udelay_ready); 27 } 28 29 void __delay(unsigned long loops) 30 { 31 /* 32 * To end the bloody studid and useless discussion about the 33 * BogoMips number I took the liberty to define the __delay 34 * function in a way that that resulting BogoMips number will 35 * yield the megahertz number of the cpu. The important function 36 * is udelay and that is done using the tod clock. -- martin. 37 */ 38 asm volatile("0: brct %0,0b" : : "d" ((loops/2) + 1)); 39 } 40 EXPORT_SYMBOL(__delay); 41 42 static void __udelay_disabled(unsigned long long usecs) 43 { 44 unsigned long cr0, cr0_new, psw_mask; 45 struct s390_idle_data idle; 46 u64 end; 47 48 end = get_tod_clock() + (usecs << 12); 49 __ctl_store(cr0, 0, 0); 50 cr0_new = cr0 & ~CR0_IRQ_SUBCLASS_MASK; 51 cr0_new |= (1UL << (63 - 52)); /* enable clock comparator irq */ 52 __ctl_load(cr0_new, 0, 0); 53 psw_mask = __extract_psw() | PSW_MASK_EXT | PSW_MASK_WAIT; 54 set_clock_comparator(end); 55 set_cpu_flag(CIF_IGNORE_IRQ); 56 psw_idle(&idle, psw_mask); 57 trace_hardirqs_off(); 58 clear_cpu_flag(CIF_IGNORE_IRQ); 59 set_clock_comparator(S390_lowcore.clock_comparator); 60 __ctl_load(cr0, 0, 0); 61 } 62 63 static void __udelay_enabled(unsigned long long usecs) 64 { 65 u64 clock_saved, end; 66 67 end = get_tod_clock_fast() + (usecs << 12); 68 do { 69 clock_saved = 0; 70 if (tod_after(S390_lowcore.clock_comparator, end)) { 71 clock_saved = local_tick_disable(); 72 set_clock_comparator(end); 73 } 74 enabled_wait(); 75 if (clock_saved) 76 local_tick_enable(clock_saved); 77 } while (get_tod_clock_fast() < end); 78 } 79 80 /* 81 * Waits for 'usecs' microseconds using the TOD clock comparator. 82 */ 83 void __udelay(unsigned long long usecs) 84 { 85 unsigned long flags; 86 87 if (!static_branch_likely(&udelay_ready)) { 88 udelay_simple(usecs); 89 return; 90 } 91 92 preempt_disable(); 93 local_irq_save(flags); 94 if (in_irq()) { 95 __udelay_disabled(usecs); 96 goto out; 97 } 98 if (in_softirq()) { 99 if (raw_irqs_disabled_flags(flags)) 100 __udelay_disabled(usecs); 101 else 102 __udelay_enabled(usecs); 103 goto out; 104 } 105 if (raw_irqs_disabled_flags(flags)) { 106 local_bh_disable(); 107 __udelay_disabled(usecs); 108 _local_bh_enable(); 109 goto out; 110 } 111 __udelay_enabled(usecs); 112 out: 113 local_irq_restore(flags); 114 preempt_enable(); 115 } 116 EXPORT_SYMBOL(__udelay); 117 118 /* 119 * Simple udelay variant. To be used on startup and reboot 120 * when the interrupt handler isn't working. 121 */ 122 void udelay_simple(unsigned long long usecs) 123 { 124 u64 end; 125 126 end = get_tod_clock_fast() + (usecs << 12); 127 while (get_tod_clock_fast() < end) 128 cpu_relax(); 129 } 130 131 void __ndelay(unsigned long long nsecs) 132 { 133 u64 end; 134 135 nsecs <<= 9; 136 do_div(nsecs, 125); 137 end = get_tod_clock_fast() + nsecs; 138 if (nsecs & ~0xfffUL) 139 __udelay(nsecs >> 12); 140 while (get_tod_clock_fast() < end) 141 barrier(); 142 } 143 EXPORT_SYMBOL(__ndelay); 144