18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci#ifndef _ASM_X86_DIV64_H
38c2ecf20Sopenharmony_ci#define _ASM_X86_DIV64_H
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_32
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/types.h>
88c2ecf20Sopenharmony_ci#include <linux/log2.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci/*
118c2ecf20Sopenharmony_ci * do_div() is NOT a C function. It wants to return
128c2ecf20Sopenharmony_ci * two values (the quotient and the remainder), but
138c2ecf20Sopenharmony_ci * since that doesn't work very well in C, what it
148c2ecf20Sopenharmony_ci * does is:
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * - modifies the 64-bit dividend _in_place_
178c2ecf20Sopenharmony_ci * - returns the 32-bit remainder
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * This ends up being the most efficient "calling
208c2ecf20Sopenharmony_ci * convention" on x86.
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci#define do_div(n, base)						\
238c2ecf20Sopenharmony_ci({								\
248c2ecf20Sopenharmony_ci	unsigned long __upper, __low, __high, __mod, __base;	\
258c2ecf20Sopenharmony_ci	__base = (base);					\
268c2ecf20Sopenharmony_ci	if (__builtin_constant_p(__base) && is_power_of_2(__base)) { \
278c2ecf20Sopenharmony_ci		__mod = n & (__base - 1);			\
288c2ecf20Sopenharmony_ci		n >>= ilog2(__base);				\
298c2ecf20Sopenharmony_ci	} else {						\
308c2ecf20Sopenharmony_ci		asm("" : "=a" (__low), "=d" (__high) : "A" (n));\
318c2ecf20Sopenharmony_ci		__upper = __high;				\
328c2ecf20Sopenharmony_ci		if (__high) {					\
338c2ecf20Sopenharmony_ci			__upper = __high % (__base);		\
348c2ecf20Sopenharmony_ci			__high = __high / (__base);		\
358c2ecf20Sopenharmony_ci		}						\
368c2ecf20Sopenharmony_ci		asm("divl %2" : "=a" (__low), "=d" (__mod)	\
378c2ecf20Sopenharmony_ci			: "rm" (__base), "0" (__low), "1" (__upper));	\
388c2ecf20Sopenharmony_ci		asm("" : "=A" (n) : "a" (__low), "d" (__high));	\
398c2ecf20Sopenharmony_ci	}							\
408c2ecf20Sopenharmony_ci	__mod;							\
418c2ecf20Sopenharmony_ci})
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	union {
468c2ecf20Sopenharmony_ci		u64 v64;
478c2ecf20Sopenharmony_ci		u32 v32[2];
488c2ecf20Sopenharmony_ci	} d = { dividend };
498c2ecf20Sopenharmony_ci	u32 upper;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	upper = d.v32[1];
528c2ecf20Sopenharmony_ci	d.v32[1] = 0;
538c2ecf20Sopenharmony_ci	if (upper >= divisor) {
548c2ecf20Sopenharmony_ci		d.v32[1] = upper / divisor;
558c2ecf20Sopenharmony_ci		upper %= divisor;
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci	asm ("divl %2" : "=a" (d.v32[0]), "=d" (*remainder) :
588c2ecf20Sopenharmony_ci		"rm" (divisor), "0" (d.v32[0]), "1" (upper));
598c2ecf20Sopenharmony_ci	return d.v64;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci#define div_u64_rem	div_u64_rem
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic inline u64 mul_u32_u32(u32 a, u32 b)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	u32 high, low;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	asm ("mull %[b]" : "=a" (low), "=d" (high)
688c2ecf20Sopenharmony_ci			 : [a] "a" (a), [b] "rm" (b) );
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	return low | ((u64)high) << 32;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci#define mul_u32_u32 mul_u32_u32
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#else
758c2ecf20Sopenharmony_ci# include <asm-generic/div64.h>
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/*
788c2ecf20Sopenharmony_ci * Will generate an #DE when the result doesn't fit u64, could fix with an
798c2ecf20Sopenharmony_ci * __ex_table[] entry when it becomes an issue.
808c2ecf20Sopenharmony_ci */
818c2ecf20Sopenharmony_cistatic inline u64 mul_u64_u64_div_u64(u64 a, u64 mul, u64 div)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	u64 q;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	asm ("mulq %2; divq %3" : "=a" (q)
868c2ecf20Sopenharmony_ci				: "a" (a), "rm" (mul), "rm" (div)
878c2ecf20Sopenharmony_ci				: "rdx");
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return q;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci#define mul_u64_u64_div_u64 mul_u64_u64_div_u64
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic inline u64 mul_u64_u32_div(u64 a, u32 mul, u32 div)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	return mul_u64_u64_div_u64(a, mul, div);
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci#define mul_u64_u32_div	mul_u64_u32_div
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci#endif /* CONFIG_X86_32 */
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci#endif /* _ASM_X86_DIV64_H */
102