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