^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) #include <linux/bug.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) #include <asm/div64.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) #include <linux/reciprocal_div.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) #include <linux/export.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) #include <linux/minmax.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * For a description of the algorithm please have a look at
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * include/linux/reciprocal_div.h
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) struct reciprocal_value reciprocal_value(u32 d)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) struct reciprocal_value R;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) u64 m;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) int l;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) l = fls(d - 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) m = ((1ULL << 32) * ((1ULL << l) - d));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) do_div(m, d);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) ++m;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) R.m = (u32)m;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) R.sh1 = min(l, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) R.sh2 = max(l - 1, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) return R;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) EXPORT_SYMBOL(reciprocal_value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) struct reciprocal_value_adv reciprocal_value_adv(u32 d, u8 prec)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) struct reciprocal_value_adv R;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) u32 l, post_shift;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) u64 mhigh, mlow;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) /* ceil(log2(d)) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) l = fls(d - 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) /* NOTE: mlow/mhigh could overflow u64 when l == 32. This case needs to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) * be handled before calling "reciprocal_value_adv", please see the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) * comment at include/linux/reciprocal_div.h.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) WARN(l == 32,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) "ceil(log2(0x%08x)) == 32, %s doesn't support such divisor",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) d, __func__);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) post_shift = l;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) mlow = 1ULL << (32 + l);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) do_div(mlow, d);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) mhigh = (1ULL << (32 + l)) + (1ULL << (32 + l - prec));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) do_div(mhigh, d);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) for (; post_shift > 0; post_shift--) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) u64 lo = mlow >> 1, hi = mhigh >> 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) if (lo >= hi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) mlow = lo;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) mhigh = hi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) R.m = (u32)mhigh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) R.sh = post_shift;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) R.exp = l;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) R.is_wide_m = mhigh > U32_MAX;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) return R;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) EXPORT_SYMBOL(reciprocal_value_adv);