xref: /kernel/linux/linux-6.6/arch/arm64/lib/delay.c (revision 62306a36)
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/delay.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/timex.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <clocksource/arm_arch_timer.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define USECS_TO_CYCLES(time_usecs)			\
1962306a36Sopenharmony_ci	xloops_to_cycles((time_usecs) * 0x10C7UL)
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic inline unsigned long xloops_to_cycles(unsigned long xloops)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	return (xloops * loops_per_jiffy * HZ) >> 32;
2462306a36Sopenharmony_ci}
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_civoid __delay(unsigned long cycles)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	cycles_t start = get_cycles();
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	if (cpus_have_const_cap(ARM64_HAS_WFXT)) {
3162306a36Sopenharmony_ci		u64 end = start + cycles;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci		/*
3462306a36Sopenharmony_ci		 * Start with WFIT. If an interrupt makes us resume
3562306a36Sopenharmony_ci		 * early, use a WFET loop to complete the delay.
3662306a36Sopenharmony_ci		 */
3762306a36Sopenharmony_ci		wfit(end);
3862306a36Sopenharmony_ci		while ((get_cycles() - start) < cycles)
3962306a36Sopenharmony_ci			wfet(end);
4062306a36Sopenharmony_ci	} else 	if (arch_timer_evtstrm_available()) {
4162306a36Sopenharmony_ci		const cycles_t timer_evt_period =
4262306a36Sopenharmony_ci			USECS_TO_CYCLES(ARCH_TIMER_EVT_STREAM_PERIOD_US);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci		while ((get_cycles() - start + timer_evt_period) < cycles)
4562306a36Sopenharmony_ci			wfe();
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	while ((get_cycles() - start) < cycles)
4962306a36Sopenharmony_ci		cpu_relax();
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ciEXPORT_SYMBOL(__delay);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ciinline void __const_udelay(unsigned long xloops)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	__delay(xloops_to_cycles(xloops));
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ciEXPORT_SYMBOL(__const_udelay);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_civoid __udelay(unsigned long usecs)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	__const_udelay(usecs * 0x10C7UL); /* 2**32 / 1000000 (rounded up) */
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ciEXPORT_SYMBOL(__udelay);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_civoid __ndelay(unsigned long nsecs)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	__const_udelay(nsecs * 0x5UL); /* 2**32 / 1000000000 (rounded up) */
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ciEXPORT_SYMBOL(__ndelay);
70