1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Delay loops based on the OpenRISC implementation. 4 * 5 * Copyright (C) 2012 ARM Limited 6 * 7 * Author: Will Deacon <will.deacon@arm.com> 8 */ 9 10 #include <linux/clocksource.h> 11 #include <linux/delay.h> 12 #include <linux/init.h> 13 #include <linux/kernel.h> 14 #include <linux/module.h> 15 16 /* 17 * Default to the loop-based delay implementation. 18 */ 19 struct arm_delay_ops arm_delay_ops __ro_after_init = { 20 .delay = __loop_delay, 21 .const_udelay = __loop_const_udelay, 22 .udelay = __loop_udelay, 23 }; 24 25 static const struct delay_timer *delay_timer; 26 static bool delay_calibrated; 27 static u64 delay_res; 28 29 bool delay_read_timer(unsigned long *timer_val) 30 { 31 if (!delay_timer) 32 return false; 33 *timer_val = delay_timer->read_current_timer(); 34 return true; 35 } 36 EXPORT_SYMBOL_GPL(delay_read_timer); 37 38 static inline u64 cyc_to_ns(u64 cyc, u32 mult, u32 shift) 39 { 40 return (cyc * mult) >> shift; 41 } 42 43 static void __timer_delay(unsigned long cycles) 44 { 45 cycles_t start = get_cycles(); 46 47 while ((get_cycles() - start) < cycles) 48 cpu_relax(); 49 } 50 51 static void __timer_const_udelay(unsigned long xloops) 52 { 53 unsigned long long loops = xloops; 54 loops *= arm_delay_ops.ticks_per_jiffy; 55 __timer_delay(loops >> UDELAY_SHIFT); 56 } 57 58 static void __timer_udelay(unsigned long usecs) 59 { 60 __timer_const_udelay(usecs * UDELAY_MULT); 61 } 62 63 void __init register_current_timer_delay(const struct delay_timer *timer) 64 { 65 u32 new_mult, new_shift; 66 u64 res; 67 68 clocks_calc_mult_shift(&new_mult, &new_shift, timer->freq, 69 NSEC_PER_SEC, 3600); 70 res = cyc_to_ns(1ULL, new_mult, new_shift); 71 72 if (res > 1000) { 73 pr_err("Ignoring delay timer %ps, which has insufficient resolution of %lluns\n", 74 timer, res); 75 return; 76 } 77 78 if (!delay_calibrated && (!delay_res || (res < delay_res))) { 79 pr_info("Switching to timer-based delay loop, resolution %lluns\n", res); 80 delay_timer = timer; 81 lpj_fine = timer->freq / HZ; 82 delay_res = res; 83 84 /* cpufreq may scale loops_per_jiffy, so keep a private copy */ 85 arm_delay_ops.ticks_per_jiffy = lpj_fine; 86 arm_delay_ops.delay = __timer_delay; 87 arm_delay_ops.const_udelay = __timer_const_udelay; 88 arm_delay_ops.udelay = __timer_udelay; 89 } else { 90 pr_info("Ignoring duplicate/late registration of read_current_timer delay\n"); 91 } 92 } 93 94 unsigned long calibrate_delay_is_known(void) 95 { 96 delay_calibrated = true; 97 return lpj_fine; 98 } 99 100 void calibration_delay_done(void) 101 { 102 delay_calibrated = true; 103 } 104