xref: /kernel/linux/linux-6.6/arch/arm/lib/delay.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Delay loops based on the OpenRISC implementation.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012 ARM Limited
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Will Deacon <will.deacon@arm.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/clocksource.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/timex.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*
1862306a36Sopenharmony_ci * Default to the loop-based delay implementation.
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_cistruct arm_delay_ops arm_delay_ops __ro_after_init = {
2162306a36Sopenharmony_ci	.delay		= __loop_delay,
2262306a36Sopenharmony_ci	.const_udelay	= __loop_const_udelay,
2362306a36Sopenharmony_ci	.udelay		= __loop_udelay,
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic const struct delay_timer *delay_timer;
2762306a36Sopenharmony_cistatic bool delay_calibrated;
2862306a36Sopenharmony_cistatic u64 delay_res;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ciint read_current_timer(unsigned long *timer_val)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	if (!delay_timer)
3362306a36Sopenharmony_ci		return -ENXIO;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	*timer_val = delay_timer->read_current_timer();
3662306a36Sopenharmony_ci	return 0;
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(read_current_timer);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic inline u64 cyc_to_ns(u64 cyc, u32 mult, u32 shift)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	return (cyc * mult) >> shift;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic void __timer_delay(unsigned long cycles)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	cycles_t start = get_cycles();
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	while ((get_cycles() - start) < cycles)
5062306a36Sopenharmony_ci		cpu_relax();
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic void __timer_const_udelay(unsigned long xloops)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	unsigned long long loops = xloops;
5662306a36Sopenharmony_ci	loops *= arm_delay_ops.ticks_per_jiffy;
5762306a36Sopenharmony_ci	__timer_delay(loops >> UDELAY_SHIFT);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic void __timer_udelay(unsigned long usecs)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	__timer_const_udelay(usecs * UDELAY_MULT);
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_civoid __init register_current_timer_delay(const struct delay_timer *timer)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	u32 new_mult, new_shift;
6862306a36Sopenharmony_ci	u64 res;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	clocks_calc_mult_shift(&new_mult, &new_shift, timer->freq,
7162306a36Sopenharmony_ci			       NSEC_PER_SEC, 3600);
7262306a36Sopenharmony_ci	res = cyc_to_ns(1ULL, new_mult, new_shift);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (res > 1000) {
7562306a36Sopenharmony_ci		pr_err("Ignoring delay timer %ps, which has insufficient resolution of %lluns\n",
7662306a36Sopenharmony_ci			timer, res);
7762306a36Sopenharmony_ci		return;
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (!delay_calibrated && (!delay_res || (res < delay_res))) {
8162306a36Sopenharmony_ci		pr_info("Switching to timer-based delay loop, resolution %lluns\n", res);
8262306a36Sopenharmony_ci		delay_timer			= timer;
8362306a36Sopenharmony_ci		lpj_fine			= timer->freq / HZ;
8462306a36Sopenharmony_ci		delay_res			= res;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		/* cpufreq may scale loops_per_jiffy, so keep a private copy */
8762306a36Sopenharmony_ci		arm_delay_ops.ticks_per_jiffy	= lpj_fine;
8862306a36Sopenharmony_ci		arm_delay_ops.delay		= __timer_delay;
8962306a36Sopenharmony_ci		arm_delay_ops.const_udelay	= __timer_const_udelay;
9062306a36Sopenharmony_ci		arm_delay_ops.udelay		= __timer_udelay;
9162306a36Sopenharmony_ci	} else {
9262306a36Sopenharmony_ci		pr_info("Ignoring duplicate/late registration of read_current_timer delay\n");
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ciunsigned long calibrate_delay_is_known(void)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	delay_calibrated = true;
9962306a36Sopenharmony_ci	return lpj_fine;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_civoid calibration_delay_done(void)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	delay_calibrated = true;
10562306a36Sopenharmony_ci}
106