162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/bitops.h>
362306a36Sopenharmony_ci#include <linux/bug.h>
462306a36Sopenharmony_ci#include <linux/export.h>
562306a36Sopenharmony_ci#include <linux/limits.h>
662306a36Sopenharmony_ci#include <linux/math.h>
762306a36Sopenharmony_ci#include <linux/minmax.h>
862306a36Sopenharmony_ci#include <linux/types.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/reciprocal_div.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/*
1362306a36Sopenharmony_ci * For a description of the algorithm please have a look at
1462306a36Sopenharmony_ci * include/linux/reciprocal_div.h
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistruct reciprocal_value reciprocal_value(u32 d)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	struct reciprocal_value R;
2062306a36Sopenharmony_ci	u64 m;
2162306a36Sopenharmony_ci	int l;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	l = fls(d - 1);
2462306a36Sopenharmony_ci	m = ((1ULL << 32) * ((1ULL << l) - d));
2562306a36Sopenharmony_ci	do_div(m, d);
2662306a36Sopenharmony_ci	++m;
2762306a36Sopenharmony_ci	R.m = (u32)m;
2862306a36Sopenharmony_ci	R.sh1 = min(l, 1);
2962306a36Sopenharmony_ci	R.sh2 = max(l - 1, 0);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	return R;
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ciEXPORT_SYMBOL(reciprocal_value);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistruct reciprocal_value_adv reciprocal_value_adv(u32 d, u8 prec)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct reciprocal_value_adv R;
3862306a36Sopenharmony_ci	u32 l, post_shift;
3962306a36Sopenharmony_ci	u64 mhigh, mlow;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	/* ceil(log2(d)) */
4262306a36Sopenharmony_ci	l = fls(d - 1);
4362306a36Sopenharmony_ci	/* NOTE: mlow/mhigh could overflow u64 when l == 32. This case needs to
4462306a36Sopenharmony_ci	 * be handled before calling "reciprocal_value_adv", please see the
4562306a36Sopenharmony_ci	 * comment at include/linux/reciprocal_div.h.
4662306a36Sopenharmony_ci	 */
4762306a36Sopenharmony_ci	WARN(l == 32,
4862306a36Sopenharmony_ci	     "ceil(log2(0x%08x)) == 32, %s doesn't support such divisor",
4962306a36Sopenharmony_ci	     d, __func__);
5062306a36Sopenharmony_ci	post_shift = l;
5162306a36Sopenharmony_ci	mlow = 1ULL << (32 + l);
5262306a36Sopenharmony_ci	do_div(mlow, d);
5362306a36Sopenharmony_ci	mhigh = (1ULL << (32 + l)) + (1ULL << (32 + l - prec));
5462306a36Sopenharmony_ci	do_div(mhigh, d);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	for (; post_shift > 0; post_shift--) {
5762306a36Sopenharmony_ci		u64 lo = mlow >> 1, hi = mhigh >> 1;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci		if (lo >= hi)
6062306a36Sopenharmony_ci			break;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci		mlow = lo;
6362306a36Sopenharmony_ci		mhigh = hi;
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	R.m = (u32)mhigh;
6762306a36Sopenharmony_ci	R.sh = post_shift;
6862306a36Sopenharmony_ci	R.exp = l;
6962306a36Sopenharmony_ci	R.is_wide_m = mhigh > U32_MAX;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	return R;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ciEXPORT_SYMBOL(reciprocal_value_adv);
74