projects/linux·blob master · refs

103 lines · 2.4 KiB

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 <[email protected]>
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 */
19struct 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
25static const struct delay_timer *delay_timer;
26static bool delay_calibrated;
27static u64 delay_res;
28
29bool 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}
36EXPORT_SYMBOL_GPL(delay_read_timer);
37
38static inline u64 cyc_to_ns(u64 cyc, u32 mult, u32 shift)
39{
40 return (cyc * mult) >> shift;
41}
42
43static 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
51static 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
58static void __timer_udelay(unsigned long usecs)
59{
60 __timer_const_udelay(usecs * UDELAY_MULT);
61}
62
63void __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
94unsigned long calibrate_delay_is_known(void)
95{
96 delay_calibrated = true;
97 return lpj_fine;
98}
99
100void calibration_delay_done(void)
101{
102 delay_calibrated = true;
103}