18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Delay loops based on the OpenRISC implementation.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2012 ARM Limited
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Will Deacon <will.deacon@arm.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/clocksource.h>
118c2ecf20Sopenharmony_ci#include <linux/delay.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/timex.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/*
188c2ecf20Sopenharmony_ci * Default to the loop-based delay implementation.
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_cistruct arm_delay_ops arm_delay_ops __ro_after_init = {
218c2ecf20Sopenharmony_ci	.delay		= __loop_delay,
228c2ecf20Sopenharmony_ci	.const_udelay	= __loop_const_udelay,
238c2ecf20Sopenharmony_ci	.udelay		= __loop_udelay,
248c2ecf20Sopenharmony_ci};
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic const struct delay_timer *delay_timer;
278c2ecf20Sopenharmony_cistatic bool delay_calibrated;
288c2ecf20Sopenharmony_cistatic u64 delay_res;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ciint read_current_timer(unsigned long *timer_val)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	if (!delay_timer)
338c2ecf20Sopenharmony_ci		return -ENXIO;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	*timer_val = delay_timer->read_current_timer();
368c2ecf20Sopenharmony_ci	return 0;
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(read_current_timer);
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic inline u64 cyc_to_ns(u64 cyc, u32 mult, u32 shift)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	return (cyc * mult) >> shift;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic void __timer_delay(unsigned long cycles)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	cycles_t start = get_cycles();
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	while ((get_cycles() - start) < cycles)
508c2ecf20Sopenharmony_ci		cpu_relax();
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void __timer_const_udelay(unsigned long xloops)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	unsigned long long loops = xloops;
568c2ecf20Sopenharmony_ci	loops *= arm_delay_ops.ticks_per_jiffy;
578c2ecf20Sopenharmony_ci	__timer_delay(loops >> UDELAY_SHIFT);
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic void __timer_udelay(unsigned long usecs)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	__timer_const_udelay(usecs * UDELAY_MULT);
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_civoid __init register_current_timer_delay(const struct delay_timer *timer)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	u32 new_mult, new_shift;
688c2ecf20Sopenharmony_ci	u64 res;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	clocks_calc_mult_shift(&new_mult, &new_shift, timer->freq,
718c2ecf20Sopenharmony_ci			       NSEC_PER_SEC, 3600);
728c2ecf20Sopenharmony_ci	res = cyc_to_ns(1ULL, new_mult, new_shift);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (res > 1000) {
758c2ecf20Sopenharmony_ci		pr_err("Ignoring delay timer %ps, which has insufficient resolution of %lluns\n",
768c2ecf20Sopenharmony_ci			timer, res);
778c2ecf20Sopenharmony_ci		return;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (!delay_calibrated && (!delay_res || (res < delay_res))) {
818c2ecf20Sopenharmony_ci		pr_info("Switching to timer-based delay loop, resolution %lluns\n", res);
828c2ecf20Sopenharmony_ci		delay_timer			= timer;
838c2ecf20Sopenharmony_ci		lpj_fine			= timer->freq / HZ;
848c2ecf20Sopenharmony_ci		delay_res			= res;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci		/* cpufreq may scale loops_per_jiffy, so keep a private copy */
878c2ecf20Sopenharmony_ci		arm_delay_ops.ticks_per_jiffy	= lpj_fine;
888c2ecf20Sopenharmony_ci		arm_delay_ops.delay		= __timer_delay;
898c2ecf20Sopenharmony_ci		arm_delay_ops.const_udelay	= __timer_const_udelay;
908c2ecf20Sopenharmony_ci		arm_delay_ops.udelay		= __timer_udelay;
918c2ecf20Sopenharmony_ci	} else {
928c2ecf20Sopenharmony_ci		pr_info("Ignoring duplicate/late registration of read_current_timer delay\n");
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ciunsigned long calibrate_delay_is_known(void)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	delay_calibrated = true;
998c2ecf20Sopenharmony_ci	return lpj_fine;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_civoid calibration_delay_done(void)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	delay_calibrated = true;
1058c2ecf20Sopenharmony_ci}
106