mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2024-12-29 17:23:36 +00:00
4505153954
Based on 1 normalized pattern(s): this program is free software you can redistribute it and or modify it under the terms of the gnu general public license version 2 as published by the free software foundation this program is distributed in the hope that it will be useful but without any warranty without even the implied warranty of merchantability or fitness for a particular purpose see the gnu general public license for more details you should have received a copy of the gnu general public license along with this program if not write to the free software foundation inc 59 temple place suite 330 boston ma 02111 1307 usa extracted by the scancode license scanner the SPDX license identifier GPL-2.0-only has been chosen to replace the boilerplate/reference in 136 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Alexios Zavras <alexios.zavras@intel.com> Reviewed-by: Allison Randal <allison@lohutok.net> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190530000436.384967451@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
106 lines
2.4 KiB
C
106 lines
2.4 KiB
C
// 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>
|
|
#include <linux/timex.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;
|
|
|
|
int read_current_timer(unsigned long *timer_val)
|
|
{
|
|
if (!delay_timer)
|
|
return -ENXIO;
|
|
|
|
*timer_val = delay_timer->read_current_timer();
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(read_current_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;
|
|
}
|