18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/bug.h>
38c2ecf20Sopenharmony_ci#include <linux/kernel.h>
48c2ecf20Sopenharmony_ci#include <asm/div64.h>
58c2ecf20Sopenharmony_ci#include <linux/reciprocal_div.h>
68c2ecf20Sopenharmony_ci#include <linux/export.h>
78c2ecf20Sopenharmony_ci#include <linux/minmax.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/*
108c2ecf20Sopenharmony_ci * For a description of the algorithm please have a look at
118c2ecf20Sopenharmony_ci * include/linux/reciprocal_div.h
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistruct reciprocal_value reciprocal_value(u32 d)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	struct reciprocal_value R;
178c2ecf20Sopenharmony_ci	u64 m;
188c2ecf20Sopenharmony_ci	int l;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	l = fls(d - 1);
218c2ecf20Sopenharmony_ci	m = ((1ULL << 32) * ((1ULL << l) - d));
228c2ecf20Sopenharmony_ci	do_div(m, d);
238c2ecf20Sopenharmony_ci	++m;
248c2ecf20Sopenharmony_ci	R.m = (u32)m;
258c2ecf20Sopenharmony_ci	R.sh1 = min(l, 1);
268c2ecf20Sopenharmony_ci	R.sh2 = max(l - 1, 0);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	return R;
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ciEXPORT_SYMBOL(reciprocal_value);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct reciprocal_value_adv reciprocal_value_adv(u32 d, u8 prec)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	struct reciprocal_value_adv R;
358c2ecf20Sopenharmony_ci	u32 l, post_shift;
368c2ecf20Sopenharmony_ci	u64 mhigh, mlow;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	/* ceil(log2(d)) */
398c2ecf20Sopenharmony_ci	l = fls(d - 1);
408c2ecf20Sopenharmony_ci	/* NOTE: mlow/mhigh could overflow u64 when l == 32. This case needs to
418c2ecf20Sopenharmony_ci	 * be handled before calling "reciprocal_value_adv", please see the
428c2ecf20Sopenharmony_ci	 * comment at include/linux/reciprocal_div.h.
438c2ecf20Sopenharmony_ci	 */
448c2ecf20Sopenharmony_ci	WARN(l == 32,
458c2ecf20Sopenharmony_ci	     "ceil(log2(0x%08x)) == 32, %s doesn't support such divisor",
468c2ecf20Sopenharmony_ci	     d, __func__);
478c2ecf20Sopenharmony_ci	post_shift = l;
488c2ecf20Sopenharmony_ci	mlow = 1ULL << (32 + l);
498c2ecf20Sopenharmony_ci	do_div(mlow, d);
508c2ecf20Sopenharmony_ci	mhigh = (1ULL << (32 + l)) + (1ULL << (32 + l - prec));
518c2ecf20Sopenharmony_ci	do_div(mhigh, d);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	for (; post_shift > 0; post_shift--) {
548c2ecf20Sopenharmony_ci		u64 lo = mlow >> 1, hi = mhigh >> 1;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci		if (lo >= hi)
578c2ecf20Sopenharmony_ci			break;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci		mlow = lo;
608c2ecf20Sopenharmony_ci		mhigh = hi;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	R.m = (u32)mhigh;
648c2ecf20Sopenharmony_ci	R.sh = post_shift;
658c2ecf20Sopenharmony_ci	R.exp = l;
668c2ecf20Sopenharmony_ci	R.is_wide_m = mhigh > U32_MAX;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	return R;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(reciprocal_value_adv);
71