103 lines · 2.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Delay loops based on the OpenRISC implementation. * * Copyright (C) 2012 ARM Limited * * Author: Will Deacon <will.deacon@arm.com> */ #include <linux/clocksource.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/module.h> /* * Default to the loop-based delay implementation. */struct arm_delay_ops arm_delay_ops __ro_after_init = {	.delay		= __loop_delay,	.const_udelay	= __loop_const_udelay,	.udelay		= __loop_udelay,}; static const struct delay_timer *delay_timer;static bool delay_calibrated;static u64 delay_res; bool delay_read_timer(unsigned long *timer_val){	if (!delay_timer)		return false;	*timer_val = delay_timer->read_current_timer();	return true;}EXPORT_SYMBOL_GPL(delay_read_timer); static inline u64 cyc_to_ns(u64 cyc, u32 mult, u32 shift){	return (cyc * mult) >> shift;} static void __timer_delay(unsigned long cycles){	cycles_t start = get_cycles(); 	while ((get_cycles() - start) < cycles)		cpu_relax();} static void __timer_const_udelay(unsigned long xloops){	unsigned long long loops = xloops;	loops *= arm_delay_ops.ticks_per_jiffy;	__timer_delay(loops >> UDELAY_SHIFT);} static void __timer_udelay(unsigned long usecs){	__timer_const_udelay(usecs * UDELAY_MULT);} void __init register_current_timer_delay(const struct delay_timer *timer){	u32 new_mult, new_shift;	u64 res; 	clocks_calc_mult_shift(&new_mult, &new_shift, timer->freq,			       NSEC_PER_SEC, 3600);	res = cyc_to_ns(1ULL, new_mult, new_shift); 	if (res > 1000) {		pr_err("Ignoring delay timer %ps, which has insufficient resolution of %lluns\n",			timer, res);		return;	} 	if (!delay_calibrated && (!delay_res || (res < delay_res))) {		pr_info("Switching to timer-based delay loop, resolution %lluns\n", res);		delay_timer			= timer;		lpj_fine			= timer->freq / HZ;		delay_res			= res; 		/* cpufreq may scale loops_per_jiffy, so keep a private copy */		arm_delay_ops.ticks_per_jiffy	= lpj_fine;		arm_delay_ops.delay		= __timer_delay;		arm_delay_ops.const_udelay	= __timer_const_udelay;		arm_delay_ops.udelay		= __timer_udelay;	} else {		pr_info("Ignoring duplicate/late registration of read_current_timer delay\n");	}} unsigned long calibrate_delay_is_known(void){	delay_calibrated = true;	return lpj_fine;} void calibration_delay_done(void){	delay_calibrated = true;}