18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2014 Linaro Ltd.
48c2ecf20Sopenharmony_ci * Copyright (C) 2014 ZTE Corporation.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
88c2ecf20Sopenharmony_ci#include <linux/err.h>
98c2ecf20Sopenharmony_ci#include <linux/gcd.h>
108c2ecf20Sopenharmony_ci#include <linux/io.h>
118c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
148c2ecf20Sopenharmony_ci#include <asm/div64.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "clk.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define to_clk_zx_pll(_hw) container_of(_hw, struct clk_zx_pll, hw)
198c2ecf20Sopenharmony_ci#define to_clk_zx_audio(_hw) container_of(_hw, struct clk_zx_audio, hw)
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define CFG0_CFG1_OFFSET 4
228c2ecf20Sopenharmony_ci#define LOCK_FLAG 30
238c2ecf20Sopenharmony_ci#define POWER_DOWN 31
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic int rate_to_idx(struct clk_zx_pll *zx_pll, unsigned long rate)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	const struct zx_pll_config *config = zx_pll->lookup_table;
288c2ecf20Sopenharmony_ci	int i;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	for (i = 0; i < zx_pll->count; i++) {
318c2ecf20Sopenharmony_ci		if (config[i].rate > rate)
328c2ecf20Sopenharmony_ci			return i > 0 ? i - 1 : 0;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci		if (config[i].rate == rate)
358c2ecf20Sopenharmony_ci			return i;
368c2ecf20Sopenharmony_ci	}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	return i - 1;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic int hw_to_idx(struct clk_zx_pll *zx_pll)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	const struct zx_pll_config *config = zx_pll->lookup_table;
448c2ecf20Sopenharmony_ci	u32 hw_cfg0, hw_cfg1;
458c2ecf20Sopenharmony_ci	int i;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	hw_cfg0 = readl_relaxed(zx_pll->reg_base);
488c2ecf20Sopenharmony_ci	hw_cfg1 = readl_relaxed(zx_pll->reg_base + CFG0_CFG1_OFFSET);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	/* For matching the value in lookup table */
518c2ecf20Sopenharmony_ci	hw_cfg0 &= ~BIT(zx_pll->lock_bit);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	/* Check availability of pd_bit */
548c2ecf20Sopenharmony_ci	if (zx_pll->pd_bit < 32)
558c2ecf20Sopenharmony_ci		hw_cfg0 |= BIT(zx_pll->pd_bit);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	for (i = 0; i < zx_pll->count; i++) {
588c2ecf20Sopenharmony_ci		if (hw_cfg0 == config[i].cfg0 && hw_cfg1 == config[i].cfg1)
598c2ecf20Sopenharmony_ci			return i;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return -EINVAL;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic unsigned long zx_pll_recalc_rate(struct clk_hw *hw,
668c2ecf20Sopenharmony_ci					unsigned long parent_rate)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
698c2ecf20Sopenharmony_ci	int idx;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	idx = hw_to_idx(zx_pll);
728c2ecf20Sopenharmony_ci	if (unlikely(idx == -EINVAL))
738c2ecf20Sopenharmony_ci		return 0;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	return zx_pll->lookup_table[idx].rate;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic long zx_pll_round_rate(struct clk_hw *hw, unsigned long rate,
798c2ecf20Sopenharmony_ci			      unsigned long *prate)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
828c2ecf20Sopenharmony_ci	int idx;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	idx = rate_to_idx(zx_pll, rate);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return zx_pll->lookup_table[idx].rate;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic int zx_pll_set_rate(struct clk_hw *hw, unsigned long rate,
908c2ecf20Sopenharmony_ci			   unsigned long parent_rate)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	/* Assume current cpu is not running on current PLL */
938c2ecf20Sopenharmony_ci	struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
948c2ecf20Sopenharmony_ci	const struct zx_pll_config *config;
958c2ecf20Sopenharmony_ci	int idx;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	idx = rate_to_idx(zx_pll, rate);
988c2ecf20Sopenharmony_ci	config = &zx_pll->lookup_table[idx];
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	writel_relaxed(config->cfg0, zx_pll->reg_base);
1018c2ecf20Sopenharmony_ci	writel_relaxed(config->cfg1, zx_pll->reg_base + CFG0_CFG1_OFFSET);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	return 0;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic int zx_pll_enable(struct clk_hw *hw)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
1098c2ecf20Sopenharmony_ci	u32 reg;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/* If pd_bit is not available, simply return success. */
1128c2ecf20Sopenharmony_ci	if (zx_pll->pd_bit > 31)
1138c2ecf20Sopenharmony_ci		return 0;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	reg = readl_relaxed(zx_pll->reg_base);
1168c2ecf20Sopenharmony_ci	writel_relaxed(reg & ~BIT(zx_pll->pd_bit), zx_pll->reg_base);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return readl_relaxed_poll_timeout(zx_pll->reg_base, reg,
1198c2ecf20Sopenharmony_ci					  reg & BIT(zx_pll->lock_bit), 0, 100);
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic void zx_pll_disable(struct clk_hw *hw)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
1258c2ecf20Sopenharmony_ci	u32 reg;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (zx_pll->pd_bit > 31)
1288c2ecf20Sopenharmony_ci		return;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	reg = readl_relaxed(zx_pll->reg_base);
1318c2ecf20Sopenharmony_ci	writel_relaxed(reg | BIT(zx_pll->pd_bit), zx_pll->reg_base);
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic int zx_pll_is_enabled(struct clk_hw *hw)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
1378c2ecf20Sopenharmony_ci	u32 reg;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	reg = readl_relaxed(zx_pll->reg_base);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	return !(reg & BIT(zx_pll->pd_bit));
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ciconst struct clk_ops zx_pll_ops = {
1458c2ecf20Sopenharmony_ci	.recalc_rate = zx_pll_recalc_rate,
1468c2ecf20Sopenharmony_ci	.round_rate = zx_pll_round_rate,
1478c2ecf20Sopenharmony_ci	.set_rate = zx_pll_set_rate,
1488c2ecf20Sopenharmony_ci	.enable = zx_pll_enable,
1498c2ecf20Sopenharmony_ci	.disable = zx_pll_disable,
1508c2ecf20Sopenharmony_ci	.is_enabled = zx_pll_is_enabled,
1518c2ecf20Sopenharmony_ci};
1528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(zx_pll_ops);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistruct clk *clk_register_zx_pll(const char *name, const char *parent_name,
1558c2ecf20Sopenharmony_ci				unsigned long flags, void __iomem *reg_base,
1568c2ecf20Sopenharmony_ci				const struct zx_pll_config *lookup_table,
1578c2ecf20Sopenharmony_ci				int count, spinlock_t *lock)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	struct clk_zx_pll *zx_pll;
1608c2ecf20Sopenharmony_ci	struct clk *clk;
1618c2ecf20Sopenharmony_ci	struct clk_init_data init;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	zx_pll = kzalloc(sizeof(*zx_pll), GFP_KERNEL);
1648c2ecf20Sopenharmony_ci	if (!zx_pll)
1658c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	init.name = name;
1688c2ecf20Sopenharmony_ci	init.ops = &zx_pll_ops;
1698c2ecf20Sopenharmony_ci	init.flags = flags;
1708c2ecf20Sopenharmony_ci	init.parent_names = parent_name ? &parent_name : NULL;
1718c2ecf20Sopenharmony_ci	init.num_parents = parent_name ? 1 : 0;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	zx_pll->reg_base = reg_base;
1748c2ecf20Sopenharmony_ci	zx_pll->lookup_table = lookup_table;
1758c2ecf20Sopenharmony_ci	zx_pll->count = count;
1768c2ecf20Sopenharmony_ci	zx_pll->lock_bit = LOCK_FLAG;
1778c2ecf20Sopenharmony_ci	zx_pll->pd_bit = POWER_DOWN;
1788c2ecf20Sopenharmony_ci	zx_pll->lock = lock;
1798c2ecf20Sopenharmony_ci	zx_pll->hw.init = &init;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	clk = clk_register(NULL, &zx_pll->hw);
1828c2ecf20Sopenharmony_ci	if (IS_ERR(clk))
1838c2ecf20Sopenharmony_ci		kfree(zx_pll);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	return clk;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci#define BPAR 1000000
1898c2ecf20Sopenharmony_cistatic u32 calc_reg(u32 parent_rate, u32 rate)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	u32 sel, integ, fra_div, tmp;
1928c2ecf20Sopenharmony_ci	u64 tmp64 = (u64)parent_rate * BPAR;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	do_div(tmp64, rate);
1958c2ecf20Sopenharmony_ci	integ = (u32)tmp64 / BPAR;
1968c2ecf20Sopenharmony_ci	integ = integ >> 1;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	tmp = (u32)tmp64 % BPAR;
1998c2ecf20Sopenharmony_ci	sel = tmp / BPAR;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	tmp = tmp % BPAR;
2028c2ecf20Sopenharmony_ci	fra_div = tmp * 0xff / BPAR;
2038c2ecf20Sopenharmony_ci	tmp = (sel << 24) | (integ << 16) | (0xff << 8) | fra_div;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/* Set I2S integer divider as 1. This bit is reserved for SPDIF
2068c2ecf20Sopenharmony_ci	 * and do no harm.
2078c2ecf20Sopenharmony_ci	 */
2088c2ecf20Sopenharmony_ci	tmp |= BIT(28);
2098c2ecf20Sopenharmony_ci	return tmp;
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic u32 calc_rate(u32 reg, u32 parent_rate)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	u32 sel, integ, fra_div, tmp;
2158c2ecf20Sopenharmony_ci	u64 tmp64 = (u64)parent_rate * BPAR;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	tmp = reg;
2188c2ecf20Sopenharmony_ci	sel = (tmp >> 24) & BIT(0);
2198c2ecf20Sopenharmony_ci	integ = (tmp >> 16) & 0xff;
2208c2ecf20Sopenharmony_ci	fra_div = tmp & 0xff;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	tmp = fra_div * BPAR;
2238c2ecf20Sopenharmony_ci	tmp = tmp / 0xff;
2248c2ecf20Sopenharmony_ci	tmp += sel * BPAR;
2258c2ecf20Sopenharmony_ci	tmp += 2 * integ * BPAR;
2268c2ecf20Sopenharmony_ci	do_div(tmp64, tmp);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	return (u32)tmp64;
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic unsigned long zx_audio_recalc_rate(struct clk_hw *hw,
2328c2ecf20Sopenharmony_ci					  unsigned long parent_rate)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	struct clk_zx_audio *zx_audio = to_clk_zx_audio(hw);
2358c2ecf20Sopenharmony_ci	u32 reg;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	reg = readl_relaxed(zx_audio->reg_base);
2388c2ecf20Sopenharmony_ci	return calc_rate(reg, parent_rate);
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic long zx_audio_round_rate(struct clk_hw *hw, unsigned long rate,
2428c2ecf20Sopenharmony_ci				unsigned long *prate)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	u32 reg;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if (rate * 2 > *prate)
2478c2ecf20Sopenharmony_ci		return -EINVAL;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	reg = calc_reg(*prate, rate);
2508c2ecf20Sopenharmony_ci	return calc_rate(reg, *prate);
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic int zx_audio_set_rate(struct clk_hw *hw, unsigned long rate,
2548c2ecf20Sopenharmony_ci			     unsigned long parent_rate)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	struct clk_zx_audio *zx_audio = to_clk_zx_audio(hw);
2578c2ecf20Sopenharmony_ci	u32 reg;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	reg = calc_reg(parent_rate, rate);
2608c2ecf20Sopenharmony_ci	writel_relaxed(reg, zx_audio->reg_base);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	return 0;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci#define ZX_AUDIO_EN BIT(25)
2668c2ecf20Sopenharmony_cistatic int zx_audio_enable(struct clk_hw *hw)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	struct clk_zx_audio *zx_audio = to_clk_zx_audio(hw);
2698c2ecf20Sopenharmony_ci	u32 reg;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	reg = readl_relaxed(zx_audio->reg_base);
2728c2ecf20Sopenharmony_ci	writel_relaxed(reg & ~ZX_AUDIO_EN, zx_audio->reg_base);
2738c2ecf20Sopenharmony_ci	return 0;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic void zx_audio_disable(struct clk_hw *hw)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct clk_zx_audio *zx_audio = to_clk_zx_audio(hw);
2798c2ecf20Sopenharmony_ci	u32 reg;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	reg = readl_relaxed(zx_audio->reg_base);
2828c2ecf20Sopenharmony_ci	writel_relaxed(reg | ZX_AUDIO_EN, zx_audio->reg_base);
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic const struct clk_ops zx_audio_ops = {
2868c2ecf20Sopenharmony_ci	.recalc_rate = zx_audio_recalc_rate,
2878c2ecf20Sopenharmony_ci	.round_rate = zx_audio_round_rate,
2888c2ecf20Sopenharmony_ci	.set_rate = zx_audio_set_rate,
2898c2ecf20Sopenharmony_ci	.enable = zx_audio_enable,
2908c2ecf20Sopenharmony_ci	.disable = zx_audio_disable,
2918c2ecf20Sopenharmony_ci};
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistruct clk *clk_register_zx_audio(const char *name,
2948c2ecf20Sopenharmony_ci				  const char * const parent_name,
2958c2ecf20Sopenharmony_ci				  unsigned long flags,
2968c2ecf20Sopenharmony_ci				  void __iomem *reg_base)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct clk_zx_audio *zx_audio;
2998c2ecf20Sopenharmony_ci	struct clk *clk;
3008c2ecf20Sopenharmony_ci	struct clk_init_data init;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	zx_audio = kzalloc(sizeof(*zx_audio), GFP_KERNEL);
3038c2ecf20Sopenharmony_ci	if (!zx_audio)
3048c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	init.name = name;
3078c2ecf20Sopenharmony_ci	init.ops = &zx_audio_ops;
3088c2ecf20Sopenharmony_ci	init.flags = flags;
3098c2ecf20Sopenharmony_ci	init.parent_names = parent_name ? &parent_name : NULL;
3108c2ecf20Sopenharmony_ci	init.num_parents = parent_name ? 1 : 0;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	zx_audio->reg_base = reg_base;
3138c2ecf20Sopenharmony_ci	zx_audio->hw.init = &init;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	clk = clk_register(NULL, &zx_audio->hw);
3168c2ecf20Sopenharmony_ci	if (IS_ERR(clk))
3178c2ecf20Sopenharmony_ci		kfree(zx_audio);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	return clk;
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci#define CLK_AUDIO_DIV_FRAC	BIT(0)
3238c2ecf20Sopenharmony_ci#define CLK_AUDIO_DIV_INT	BIT(1)
3248c2ecf20Sopenharmony_ci#define CLK_AUDIO_DIV_UNCOMMON	BIT(1)
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci#define CLK_AUDIO_DIV_FRAC_NSHIFT	16
3278c2ecf20Sopenharmony_ci#define CLK_AUDIO_DIV_INT_FRAC_RE	BIT(16)
3288c2ecf20Sopenharmony_ci#define CLK_AUDIO_DIV_INT_FRAC_MAX	(0xffff)
3298c2ecf20Sopenharmony_ci#define CLK_AUDIO_DIV_INT_FRAC_MIN	(0x2)
3308c2ecf20Sopenharmony_ci#define CLK_AUDIO_DIV_INT_INT_SHIFT	24
3318c2ecf20Sopenharmony_ci#define CLK_AUDIO_DIV_INT_INT_WIDTH	4
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cistruct zx_clk_audio_div_table {
3348c2ecf20Sopenharmony_ci	unsigned long rate;
3358c2ecf20Sopenharmony_ci	unsigned int int_reg;
3368c2ecf20Sopenharmony_ci	unsigned int frac_reg;
3378c2ecf20Sopenharmony_ci};
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci#define to_clk_zx_audio_div(_hw) container_of(_hw, struct clk_zx_audio_divider, hw)
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic unsigned long audio_calc_rate(struct clk_zx_audio_divider *audio_div,
3428c2ecf20Sopenharmony_ci				     u32 reg_frac, u32 reg_int,
3438c2ecf20Sopenharmony_ci				     unsigned long parent_rate)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	unsigned long rate, m, n;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	m = reg_frac & 0xffff;
3488c2ecf20Sopenharmony_ci	n = (reg_frac >> 16) & 0xffff;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	m = (reg_int & 0xffff) * n + m;
3518c2ecf20Sopenharmony_ci	rate = (parent_rate * n) / m;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	return rate;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic void audio_calc_reg(struct clk_zx_audio_divider *audio_div,
3578c2ecf20Sopenharmony_ci			   struct zx_clk_audio_div_table *div_table,
3588c2ecf20Sopenharmony_ci			   unsigned long rate, unsigned long parent_rate)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	unsigned int reg_int, reg_frac;
3618c2ecf20Sopenharmony_ci	unsigned long m, n, div;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	reg_int = parent_rate / rate;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	if (reg_int > CLK_AUDIO_DIV_INT_FRAC_MAX)
3668c2ecf20Sopenharmony_ci		reg_int = CLK_AUDIO_DIV_INT_FRAC_MAX;
3678c2ecf20Sopenharmony_ci	else if (reg_int < CLK_AUDIO_DIV_INT_FRAC_MIN)
3688c2ecf20Sopenharmony_ci		reg_int = 0;
3698c2ecf20Sopenharmony_ci	m = parent_rate - rate * reg_int;
3708c2ecf20Sopenharmony_ci	n = rate;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	div = gcd(m, n);
3738c2ecf20Sopenharmony_ci	m = m / div;
3748c2ecf20Sopenharmony_ci	n = n / div;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	if ((m >> 16) || (n >> 16)) {
3778c2ecf20Sopenharmony_ci		if (m > n) {
3788c2ecf20Sopenharmony_ci			n = n * 0xffff / m;
3798c2ecf20Sopenharmony_ci			m = 0xffff;
3808c2ecf20Sopenharmony_ci		} else {
3818c2ecf20Sopenharmony_ci			m = m * 0xffff / n;
3828c2ecf20Sopenharmony_ci			n = 0xffff;
3838c2ecf20Sopenharmony_ci		}
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci	reg_frac = m | (n << 16);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	div_table->rate = parent_rate * n / (reg_int * n + m);
3888c2ecf20Sopenharmony_ci	div_table->int_reg = reg_int;
3898c2ecf20Sopenharmony_ci	div_table->frac_reg = reg_frac;
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic unsigned long zx_audio_div_recalc_rate(struct clk_hw *hw,
3938c2ecf20Sopenharmony_ci					  unsigned long parent_rate)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	struct clk_zx_audio_divider *zx_audio_div = to_clk_zx_audio_div(hw);
3968c2ecf20Sopenharmony_ci	u32 reg_frac, reg_int;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	reg_frac = readl_relaxed(zx_audio_div->reg_base);
3998c2ecf20Sopenharmony_ci	reg_int = readl_relaxed(zx_audio_div->reg_base + 0x4);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	return audio_calc_rate(zx_audio_div, reg_frac, reg_int, parent_rate);
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic long zx_audio_div_round_rate(struct clk_hw *hw, unsigned long rate,
4058c2ecf20Sopenharmony_ci				unsigned long *prate)
4068c2ecf20Sopenharmony_ci{
4078c2ecf20Sopenharmony_ci	struct clk_zx_audio_divider *zx_audio_div = to_clk_zx_audio_div(hw);
4088c2ecf20Sopenharmony_ci	struct zx_clk_audio_div_table divt;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	audio_calc_reg(zx_audio_div, &divt, rate, *prate);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	return audio_calc_rate(zx_audio_div, divt.frac_reg, divt.int_reg, *prate);
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic int zx_audio_div_set_rate(struct clk_hw *hw, unsigned long rate,
4168c2ecf20Sopenharmony_ci				    unsigned long parent_rate)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	struct clk_zx_audio_divider *zx_audio_div = to_clk_zx_audio_div(hw);
4198c2ecf20Sopenharmony_ci	struct zx_clk_audio_div_table divt;
4208c2ecf20Sopenharmony_ci	unsigned int val;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	audio_calc_reg(zx_audio_div, &divt, rate, parent_rate);
4238c2ecf20Sopenharmony_ci	if (divt.rate != rate)
4248c2ecf20Sopenharmony_ci		pr_debug("the real rate is:%ld", divt.rate);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	writel_relaxed(divt.frac_reg, zx_audio_div->reg_base);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	val = readl_relaxed(zx_audio_div->reg_base + 0x4);
4298c2ecf20Sopenharmony_ci	val &= ~0xffff;
4308c2ecf20Sopenharmony_ci	val |= divt.int_reg | CLK_AUDIO_DIV_INT_FRAC_RE;
4318c2ecf20Sopenharmony_ci	writel_relaxed(val, zx_audio_div->reg_base + 0x4);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	mdelay(1);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	val = readl_relaxed(zx_audio_div->reg_base + 0x4);
4368c2ecf20Sopenharmony_ci	val &= ~CLK_AUDIO_DIV_INT_FRAC_RE;
4378c2ecf20Sopenharmony_ci	writel_relaxed(val, zx_audio_div->reg_base + 0x4);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	return 0;
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ciconst struct clk_ops zx_audio_div_ops = {
4438c2ecf20Sopenharmony_ci	.recalc_rate = zx_audio_div_recalc_rate,
4448c2ecf20Sopenharmony_ci	.round_rate = zx_audio_div_round_rate,
4458c2ecf20Sopenharmony_ci	.set_rate = zx_audio_div_set_rate,
4468c2ecf20Sopenharmony_ci};
447