70 lines
1.5 KiB
C
70 lines
1.5 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/delay.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/timex.h>
|
|
|
|
#include <clocksource/arm_arch_timer.h>
|
|
|
|
#define USECS_TO_CYCLES(time_usecs) \
|
|
xloops_to_cycles((time_usecs) * 0x10C7UL)
|
|
|
|
static inline unsigned long xloops_to_cycles(unsigned long xloops)
|
|
{
|
|
return (xloops * loops_per_jiffy * HZ) >> 32;
|
|
}
|
|
|
|
void __delay(unsigned long cycles)
|
|
{
|
|
cycles_t start = get_cycles();
|
|
|
|
if (cpus_have_const_cap(ARM64_HAS_WFXT)) {
|
|
u64 end = start + cycles;
|
|
|
|
/*
|
|
* Start with WFIT. If an interrupt makes us resume
|
|
* early, use a WFET loop to complete the delay.
|
|
*/
|
|
wfit(end);
|
|
while ((get_cycles() - start) < cycles)
|
|
wfet(end);
|
|
} else if (arch_timer_evtstrm_available()) {
|
|
const cycles_t timer_evt_period =
|
|
USECS_TO_CYCLES(ARCH_TIMER_EVT_STREAM_PERIOD_US);
|
|
|
|
while ((get_cycles() - start + timer_evt_period) < cycles)
|
|
wfe();
|
|
}
|
|
|
|
while ((get_cycles() - start) < cycles)
|
|
cpu_relax();
|
|
}
|
|
EXPORT_SYMBOL(__delay);
|
|
|
|
inline void __const_udelay(unsigned long xloops)
|
|
{
|
|
__delay(xloops_to_cycles(xloops));
|
|
}
|
|
EXPORT_SYMBOL(__const_udelay);
|
|
|
|
void __udelay(unsigned long usecs)
|
|
{
|
|
__const_udelay(usecs * 0x10C7UL); /* 2**32 / 1000000 (rounded up) */
|
|
}
|
|
EXPORT_SYMBOL(__udelay);
|
|
|
|
void __ndelay(unsigned long nsecs)
|
|
{
|
|
__const_udelay(nsecs * 0x5UL); /* 2**32 / 1000000000 (rounded up) */
|
|
}
|
|
EXPORT_SYMBOL(__ndelay);
|