xref: /linux/arch/arm/lib/delay.c (revision 59e6295fac26b8e85c1ea859cdd89fa1e47519d7)
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