mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-15 12:54:27 +00:00
LinuxKPI: implement mul_u64_u64_div_u64()
Implement mul_u64_u64_div_u64() for an updated iwlwifi driver (though we do not yet use it there; it is used for in-kernel ptp on wifi). Sponsored by: The FreeBSD Foundation Submitted by: cperciva MFC after: 10 days Reviewed by: cperciva, dwmalone Differential Revision: https://reviews.freebsd.org/D40120
This commit is contained in:
parent
98fd1add67
commit
b80ea45237
|
@ -106,6 +106,54 @@ mul_u64_u32_div(uint64_t x, uint32_t y, uint32_t div)
|
|||
return ((x / div) * y + (rem * y) / div);
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
mul_u64_u64_div_u64(uint64_t x, uint64_t y, uint64_t z)
|
||||
{
|
||||
uint64_t res, rem;
|
||||
uint64_t x1, y1, y1z;
|
||||
|
||||
res = rem = 0;
|
||||
x1 = x;
|
||||
y1z = y / z;
|
||||
y1 = y - y1z * z;
|
||||
|
||||
/*
|
||||
* INVARIANT: x * y = res * z + rem + (y1 + y1z * z) * x1
|
||||
* INVARIANT: y1 < z
|
||||
* INVARIANT: rem < z
|
||||
*/
|
||||
while (x1 > 0) {
|
||||
/* Handle low bit. */
|
||||
if (x1 & 1) {
|
||||
x1 &= ~1;
|
||||
res += y1z;
|
||||
rem += y1;
|
||||
if ((rem < y1) || (rem >= z)) {
|
||||
res += 1;
|
||||
rem -= z;
|
||||
}
|
||||
}
|
||||
|
||||
/* Shift x1 right and (y1 + y1z * z) left */
|
||||
x1 >>= 1;
|
||||
if ((y1 * 2 < y1) || (y1 * 2 >= z)) {
|
||||
y1z = y1z * 2 + 1;
|
||||
y1 = y1 * 2 - z;
|
||||
} else {
|
||||
y1z *= 2;
|
||||
y1 *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
KASSERT(res * z + rem == x * y, ("%s: res %ju * z %ju + rem %ju != "
|
||||
"x %ju * y %ju", __func__, (uintmax_t)res, (uintmax_t)z,
|
||||
(uintmax_t)rem, (uintmax_t)x, (uintmax_t)y));
|
||||
KASSERT(rem < z, ("%s: rem %ju >= z %ju\n", __func__,
|
||||
(uintmax_t)rem, (uintmax_t)z);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
mul_u64_u32_shr(uint64_t x, uint32_t y, unsigned int shift)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue