18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Authors:
68c2ecf20Sopenharmony_ci *   Serge Semin <Sergey.Semin@baikalelectronics.ru>
78c2ecf20Sopenharmony_ci *   Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Baikal-T1 CCU PLL interface driver
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "bt1-ccu-pll: " fmt
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/printk.h>
168c2ecf20Sopenharmony_ci#include <linux/limits.h>
178c2ecf20Sopenharmony_ci#include <linux/bits.h>
188c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
218c2ecf20Sopenharmony_ci#include <linux/of.h>
228c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
238c2ecf20Sopenharmony_ci#include <linux/regmap.h>
248c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
258c2ecf20Sopenharmony_ci#include <linux/time64.h>
268c2ecf20Sopenharmony_ci#include <linux/rational.h>
278c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include "ccu-pll.h"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define CCU_PLL_CTL			0x000
328c2ecf20Sopenharmony_ci#define CCU_PLL_CTL_EN			BIT(0)
338c2ecf20Sopenharmony_ci#define CCU_PLL_CTL_RST			BIT(1)
348c2ecf20Sopenharmony_ci#define CCU_PLL_CTL_CLKR_FLD		2
358c2ecf20Sopenharmony_ci#define CCU_PLL_CTL_CLKR_MASK		GENMASK(7, CCU_PLL_CTL_CLKR_FLD)
368c2ecf20Sopenharmony_ci#define CCU_PLL_CTL_CLKF_FLD		8
378c2ecf20Sopenharmony_ci#define CCU_PLL_CTL_CLKF_MASK		GENMASK(20, CCU_PLL_CTL_CLKF_FLD)
388c2ecf20Sopenharmony_ci#define CCU_PLL_CTL_CLKOD_FLD		21
398c2ecf20Sopenharmony_ci#define CCU_PLL_CTL_CLKOD_MASK		GENMASK(24, CCU_PLL_CTL_CLKOD_FLD)
408c2ecf20Sopenharmony_ci#define CCU_PLL_CTL_BYPASS		BIT(30)
418c2ecf20Sopenharmony_ci#define CCU_PLL_CTL_LOCK		BIT(31)
428c2ecf20Sopenharmony_ci#define CCU_PLL_CTL1			0x004
438c2ecf20Sopenharmony_ci#define CCU_PLL_CTL1_BWADJ_FLD		3
448c2ecf20Sopenharmony_ci#define CCU_PLL_CTL1_BWADJ_MASK		GENMASK(14, CCU_PLL_CTL1_BWADJ_FLD)
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define CCU_PLL_LOCK_CHECK_RETRIES	50
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define CCU_PLL_NR_MAX \
498c2ecf20Sopenharmony_ci	((CCU_PLL_CTL_CLKR_MASK >> CCU_PLL_CTL_CLKR_FLD) + 1)
508c2ecf20Sopenharmony_ci#define CCU_PLL_NF_MAX \
518c2ecf20Sopenharmony_ci	((CCU_PLL_CTL_CLKF_MASK >> (CCU_PLL_CTL_CLKF_FLD + 1)) + 1)
528c2ecf20Sopenharmony_ci#define CCU_PLL_OD_MAX \
538c2ecf20Sopenharmony_ci	((CCU_PLL_CTL_CLKOD_MASK >> CCU_PLL_CTL_CLKOD_FLD) + 1)
548c2ecf20Sopenharmony_ci#define CCU_PLL_NB_MAX \
558c2ecf20Sopenharmony_ci	((CCU_PLL_CTL1_BWADJ_MASK >> CCU_PLL_CTL1_BWADJ_FLD) + 1)
568c2ecf20Sopenharmony_ci#define CCU_PLL_FDIV_MIN		427000UL
578c2ecf20Sopenharmony_ci#define CCU_PLL_FDIV_MAX		3500000000UL
588c2ecf20Sopenharmony_ci#define CCU_PLL_FOUT_MIN		200000000UL
598c2ecf20Sopenharmony_ci#define CCU_PLL_FOUT_MAX		2500000000UL
608c2ecf20Sopenharmony_ci#define CCU_PLL_FVCO_MIN		700000000UL
618c2ecf20Sopenharmony_ci#define CCU_PLL_FVCO_MAX		3500000000UL
628c2ecf20Sopenharmony_ci#define CCU_PLL_CLKOD_FACTOR		2
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic inline unsigned long ccu_pll_lock_delay_us(unsigned long ref_clk,
658c2ecf20Sopenharmony_ci						  unsigned long nr)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	u64 us = 500ULL * nr * USEC_PER_SEC;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	do_div(us, ref_clk);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	return us;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic inline unsigned long ccu_pll_calc_freq(unsigned long ref_clk,
758c2ecf20Sopenharmony_ci					      unsigned long nr,
768c2ecf20Sopenharmony_ci					      unsigned long nf,
778c2ecf20Sopenharmony_ci					      unsigned long od)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	u64 tmp = ref_clk;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	do_div(tmp, nr);
828c2ecf20Sopenharmony_ci	tmp *= nf;
838c2ecf20Sopenharmony_ci	do_div(tmp, od);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	return tmp;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic int ccu_pll_reset(struct ccu_pll *pll, unsigned long ref_clk,
898c2ecf20Sopenharmony_ci			 unsigned long nr)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	unsigned long ud, ut;
928c2ecf20Sopenharmony_ci	u32 val;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	ud = ccu_pll_lock_delay_us(ref_clk, nr);
958c2ecf20Sopenharmony_ci	ut = ud * CCU_PLL_LOCK_CHECK_RETRIES;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	regmap_update_bits(pll->sys_regs, pll->reg_ctl,
988c2ecf20Sopenharmony_ci			   CCU_PLL_CTL_RST, CCU_PLL_CTL_RST);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return regmap_read_poll_timeout_atomic(pll->sys_regs, pll->reg_ctl, val,
1018c2ecf20Sopenharmony_ci					       val & CCU_PLL_CTL_LOCK, ud, ut);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int ccu_pll_enable(struct clk_hw *hw)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct clk_hw *parent_hw = clk_hw_get_parent(hw);
1078c2ecf20Sopenharmony_ci	struct ccu_pll *pll = to_ccu_pll(hw);
1088c2ecf20Sopenharmony_ci	unsigned long flags;
1098c2ecf20Sopenharmony_ci	u32 val = 0;
1108c2ecf20Sopenharmony_ci	int ret;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (!parent_hw) {
1138c2ecf20Sopenharmony_ci		pr_err("Can't enable '%s' with no parent", clk_hw_get_name(hw));
1148c2ecf20Sopenharmony_ci		return -EINVAL;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	regmap_read(pll->sys_regs, pll->reg_ctl, &val);
1188c2ecf20Sopenharmony_ci	if (val & CCU_PLL_CTL_EN)
1198c2ecf20Sopenharmony_ci		return 0;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pll->lock, flags);
1228c2ecf20Sopenharmony_ci	regmap_write(pll->sys_regs, pll->reg_ctl, val | CCU_PLL_CTL_EN);
1238c2ecf20Sopenharmony_ci	ret = ccu_pll_reset(pll, clk_hw_get_rate(parent_hw),
1248c2ecf20Sopenharmony_ci			    FIELD_GET(CCU_PLL_CTL_CLKR_MASK, val) + 1);
1258c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pll->lock, flags);
1268c2ecf20Sopenharmony_ci	if (ret)
1278c2ecf20Sopenharmony_ci		pr_err("PLL '%s' reset timed out\n", clk_hw_get_name(hw));
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	return ret;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic void ccu_pll_disable(struct clk_hw *hw)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	struct ccu_pll *pll = to_ccu_pll(hw);
1358c2ecf20Sopenharmony_ci	unsigned long flags;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pll->lock, flags);
1388c2ecf20Sopenharmony_ci	regmap_update_bits(pll->sys_regs, pll->reg_ctl, CCU_PLL_CTL_EN, 0);
1398c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pll->lock, flags);
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic int ccu_pll_is_enabled(struct clk_hw *hw)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	struct ccu_pll *pll = to_ccu_pll(hw);
1458c2ecf20Sopenharmony_ci	u32 val = 0;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	regmap_read(pll->sys_regs, pll->reg_ctl, &val);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	return !!(val & CCU_PLL_CTL_EN);
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic unsigned long ccu_pll_recalc_rate(struct clk_hw *hw,
1538c2ecf20Sopenharmony_ci					 unsigned long parent_rate)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	struct ccu_pll *pll = to_ccu_pll(hw);
1568c2ecf20Sopenharmony_ci	unsigned long nr, nf, od;
1578c2ecf20Sopenharmony_ci	u32 val = 0;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	regmap_read(pll->sys_regs, pll->reg_ctl, &val);
1608c2ecf20Sopenharmony_ci	nr = FIELD_GET(CCU_PLL_CTL_CLKR_MASK, val) + 1;
1618c2ecf20Sopenharmony_ci	nf = FIELD_GET(CCU_PLL_CTL_CLKF_MASK, val) + 1;
1628c2ecf20Sopenharmony_ci	od = FIELD_GET(CCU_PLL_CTL_CLKOD_MASK, val) + 1;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return ccu_pll_calc_freq(parent_rate, nr, nf, od);
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic void ccu_pll_calc_factors(unsigned long rate, unsigned long parent_rate,
1688c2ecf20Sopenharmony_ci				 unsigned long *nr, unsigned long *nf,
1698c2ecf20Sopenharmony_ci				 unsigned long *od)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	unsigned long err, freq, min_err = ULONG_MAX;
1728c2ecf20Sopenharmony_ci	unsigned long num, denom, n1, d1, nri;
1738c2ecf20Sopenharmony_ci	unsigned long nr_max, nf_max, od_max;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/*
1768c2ecf20Sopenharmony_ci	 * Make sure PLL is working with valid input signal (Fdiv). If
1778c2ecf20Sopenharmony_ci	 * you want to speed the function up just reduce CCU_PLL_NR_MAX.
1788c2ecf20Sopenharmony_ci	 * This will cause a worse approximation though.
1798c2ecf20Sopenharmony_ci	 */
1808c2ecf20Sopenharmony_ci	nri = (parent_rate / CCU_PLL_FDIV_MAX) + 1;
1818c2ecf20Sopenharmony_ci	nr_max = min(parent_rate / CCU_PLL_FDIV_MIN, CCU_PLL_NR_MAX);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/*
1848c2ecf20Sopenharmony_ci	 * Find a closest [nr;nf;od] vector taking into account the
1858c2ecf20Sopenharmony_ci	 * limitations like: 1) 700MHz <= Fvco <= 3.5GHz, 2) PLL Od is
1868c2ecf20Sopenharmony_ci	 * either 1 or even number within the acceptable range (alas 1s
1878c2ecf20Sopenharmony_ci	 * is also excluded by the next loop).
1888c2ecf20Sopenharmony_ci	 */
1898c2ecf20Sopenharmony_ci	for (; nri <= nr_max; ++nri) {
1908c2ecf20Sopenharmony_ci		/* Use Od factor to fulfill the limitation 2). */
1918c2ecf20Sopenharmony_ci		num = CCU_PLL_CLKOD_FACTOR * rate;
1928c2ecf20Sopenharmony_ci		denom = parent_rate / nri;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci		/*
1958c2ecf20Sopenharmony_ci		 * Make sure Fvco is within the acceptable range to fulfill
1968c2ecf20Sopenharmony_ci		 * the condition 1). Note due to the CCU_PLL_CLKOD_FACTOR value
1978c2ecf20Sopenharmony_ci		 * the actual upper limit is also divided by that factor.
1988c2ecf20Sopenharmony_ci		 * It's not big problem for us since practically there is no
1998c2ecf20Sopenharmony_ci		 * need in clocks with that high frequency.
2008c2ecf20Sopenharmony_ci		 */
2018c2ecf20Sopenharmony_ci		nf_max = min(CCU_PLL_FVCO_MAX / denom, CCU_PLL_NF_MAX);
2028c2ecf20Sopenharmony_ci		od_max = CCU_PLL_OD_MAX / CCU_PLL_CLKOD_FACTOR;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci		/*
2058c2ecf20Sopenharmony_ci		 * Bypass the out-of-bound values, which can't be properly
2068c2ecf20Sopenharmony_ci		 * handled by the rational fraction approximation algorithm.
2078c2ecf20Sopenharmony_ci		 */
2088c2ecf20Sopenharmony_ci		if (num / denom >= nf_max) {
2098c2ecf20Sopenharmony_ci			n1 = nf_max;
2108c2ecf20Sopenharmony_ci			d1 = 1;
2118c2ecf20Sopenharmony_ci		} else if (denom / num >= od_max) {
2128c2ecf20Sopenharmony_ci			n1 = 1;
2138c2ecf20Sopenharmony_ci			d1 = od_max;
2148c2ecf20Sopenharmony_ci		} else {
2158c2ecf20Sopenharmony_ci			rational_best_approximation(num, denom, nf_max, od_max,
2168c2ecf20Sopenharmony_ci						    &n1, &d1);
2178c2ecf20Sopenharmony_ci		}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci		/* Select the best approximation of the target rate. */
2208c2ecf20Sopenharmony_ci		freq = ccu_pll_calc_freq(parent_rate, nri, n1, d1);
2218c2ecf20Sopenharmony_ci		err = abs((int64_t)freq - num);
2228c2ecf20Sopenharmony_ci		if (err < min_err) {
2238c2ecf20Sopenharmony_ci			min_err = err;
2248c2ecf20Sopenharmony_ci			*nr = nri;
2258c2ecf20Sopenharmony_ci			*nf = n1;
2268c2ecf20Sopenharmony_ci			*od = CCU_PLL_CLKOD_FACTOR * d1;
2278c2ecf20Sopenharmony_ci		}
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic long ccu_pll_round_rate(struct clk_hw *hw, unsigned long rate,
2328c2ecf20Sopenharmony_ci			       unsigned long *parent_rate)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	unsigned long nr = 1, nf = 1, od = 1;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	ccu_pll_calc_factors(rate, *parent_rate, &nr, &nf, &od);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	return ccu_pll_calc_freq(*parent_rate, nr, nf, od);
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci/*
2428c2ecf20Sopenharmony_ci * This method is used for PLLs, which support the on-the-fly dividers
2438c2ecf20Sopenharmony_ci * adjustment. So there is no need in gating such clocks.
2448c2ecf20Sopenharmony_ci */
2458c2ecf20Sopenharmony_cistatic int ccu_pll_set_rate_reset(struct clk_hw *hw, unsigned long rate,
2468c2ecf20Sopenharmony_ci				  unsigned long parent_rate)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	struct ccu_pll *pll = to_ccu_pll(hw);
2498c2ecf20Sopenharmony_ci	unsigned long nr, nf, od;
2508c2ecf20Sopenharmony_ci	unsigned long flags;
2518c2ecf20Sopenharmony_ci	u32 mask, val;
2528c2ecf20Sopenharmony_ci	int ret;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	ccu_pll_calc_factors(rate, parent_rate, &nr, &nf, &od);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	mask = CCU_PLL_CTL_CLKR_MASK | CCU_PLL_CTL_CLKF_MASK |
2578c2ecf20Sopenharmony_ci	       CCU_PLL_CTL_CLKOD_MASK;
2588c2ecf20Sopenharmony_ci	val = FIELD_PREP(CCU_PLL_CTL_CLKR_MASK, nr - 1) |
2598c2ecf20Sopenharmony_ci	      FIELD_PREP(CCU_PLL_CTL_CLKF_MASK, nf - 1) |
2608c2ecf20Sopenharmony_ci	      FIELD_PREP(CCU_PLL_CTL_CLKOD_MASK, od - 1);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pll->lock, flags);
2638c2ecf20Sopenharmony_ci	regmap_update_bits(pll->sys_regs, pll->reg_ctl, mask, val);
2648c2ecf20Sopenharmony_ci	ret = ccu_pll_reset(pll, parent_rate, nr);
2658c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pll->lock, flags);
2668c2ecf20Sopenharmony_ci	if (ret)
2678c2ecf20Sopenharmony_ci		pr_err("PLL '%s' reset timed out\n", clk_hw_get_name(hw));
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	return ret;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci/*
2738c2ecf20Sopenharmony_ci * This method is used for PLLs, which don't support the on-the-fly dividers
2748c2ecf20Sopenharmony_ci * adjustment. So the corresponding clocks are supposed to be gated first.
2758c2ecf20Sopenharmony_ci */
2768c2ecf20Sopenharmony_cistatic int ccu_pll_set_rate_norst(struct clk_hw *hw, unsigned long rate,
2778c2ecf20Sopenharmony_ci				  unsigned long parent_rate)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	struct ccu_pll *pll = to_ccu_pll(hw);
2808c2ecf20Sopenharmony_ci	unsigned long nr, nf, od;
2818c2ecf20Sopenharmony_ci	unsigned long flags;
2828c2ecf20Sopenharmony_ci	u32 mask, val;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	ccu_pll_calc_factors(rate, parent_rate, &nr, &nf, &od);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	/*
2878c2ecf20Sopenharmony_ci	 * Disable PLL if it was enabled by default or left enabled by the
2888c2ecf20Sopenharmony_ci	 * system bootloader.
2898c2ecf20Sopenharmony_ci	 */
2908c2ecf20Sopenharmony_ci	mask = CCU_PLL_CTL_CLKR_MASK | CCU_PLL_CTL_CLKF_MASK |
2918c2ecf20Sopenharmony_ci	       CCU_PLL_CTL_CLKOD_MASK | CCU_PLL_CTL_EN;
2928c2ecf20Sopenharmony_ci	val = FIELD_PREP(CCU_PLL_CTL_CLKR_MASK, nr - 1) |
2938c2ecf20Sopenharmony_ci	      FIELD_PREP(CCU_PLL_CTL_CLKF_MASK, nf - 1) |
2948c2ecf20Sopenharmony_ci	      FIELD_PREP(CCU_PLL_CTL_CLKOD_MASK, od - 1);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pll->lock, flags);
2978c2ecf20Sopenharmony_ci	regmap_update_bits(pll->sys_regs, pll->reg_ctl, mask, val);
2988c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pll->lock, flags);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	return 0;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistruct ccu_pll_dbgfs_bit {
3068c2ecf20Sopenharmony_ci	struct ccu_pll *pll;
3078c2ecf20Sopenharmony_ci	const char *name;
3088c2ecf20Sopenharmony_ci	unsigned int reg;
3098c2ecf20Sopenharmony_ci	u32 mask;
3108c2ecf20Sopenharmony_ci};
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistruct ccu_pll_dbgfs_fld {
3138c2ecf20Sopenharmony_ci	struct ccu_pll *pll;
3148c2ecf20Sopenharmony_ci	const char *name;
3158c2ecf20Sopenharmony_ci	unsigned int reg;
3168c2ecf20Sopenharmony_ci	unsigned int lsb;
3178c2ecf20Sopenharmony_ci	u32 mask;
3188c2ecf20Sopenharmony_ci	u32 min;
3198c2ecf20Sopenharmony_ci	u32 max;
3208c2ecf20Sopenharmony_ci};
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci#define CCU_PLL_DBGFS_BIT_ATTR(_name, _reg, _mask)	\
3238c2ecf20Sopenharmony_ci	{						\
3248c2ecf20Sopenharmony_ci		.name = _name,				\
3258c2ecf20Sopenharmony_ci		.reg = _reg,				\
3268c2ecf20Sopenharmony_ci		.mask = _mask				\
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci#define CCU_PLL_DBGFS_FLD_ATTR(_name, _reg, _lsb, _mask, _min, _max)	\
3308c2ecf20Sopenharmony_ci	{								\
3318c2ecf20Sopenharmony_ci		.name = _name,						\
3328c2ecf20Sopenharmony_ci		.reg = _reg,						\
3338c2ecf20Sopenharmony_ci		.lsb = _lsb,						\
3348c2ecf20Sopenharmony_ci		.mask = _mask,						\
3358c2ecf20Sopenharmony_ci		.min = _min,						\
3368c2ecf20Sopenharmony_ci		.max = _max						\
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic const struct ccu_pll_dbgfs_bit ccu_pll_bits[] = {
3408c2ecf20Sopenharmony_ci	CCU_PLL_DBGFS_BIT_ATTR("pll_en", CCU_PLL_CTL, CCU_PLL_CTL_EN),
3418c2ecf20Sopenharmony_ci	CCU_PLL_DBGFS_BIT_ATTR("pll_rst", CCU_PLL_CTL, CCU_PLL_CTL_RST),
3428c2ecf20Sopenharmony_ci	CCU_PLL_DBGFS_BIT_ATTR("pll_bypass", CCU_PLL_CTL, CCU_PLL_CTL_BYPASS),
3438c2ecf20Sopenharmony_ci	CCU_PLL_DBGFS_BIT_ATTR("pll_lock", CCU_PLL_CTL, CCU_PLL_CTL_LOCK)
3448c2ecf20Sopenharmony_ci};
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci#define CCU_PLL_DBGFS_BIT_NUM	ARRAY_SIZE(ccu_pll_bits)
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic const struct ccu_pll_dbgfs_fld ccu_pll_flds[] = {
3498c2ecf20Sopenharmony_ci	CCU_PLL_DBGFS_FLD_ATTR("pll_nr", CCU_PLL_CTL, CCU_PLL_CTL_CLKR_FLD,
3508c2ecf20Sopenharmony_ci				CCU_PLL_CTL_CLKR_MASK, 1, CCU_PLL_NR_MAX),
3518c2ecf20Sopenharmony_ci	CCU_PLL_DBGFS_FLD_ATTR("pll_nf", CCU_PLL_CTL, CCU_PLL_CTL_CLKF_FLD,
3528c2ecf20Sopenharmony_ci				CCU_PLL_CTL_CLKF_MASK, 1, CCU_PLL_NF_MAX),
3538c2ecf20Sopenharmony_ci	CCU_PLL_DBGFS_FLD_ATTR("pll_od", CCU_PLL_CTL, CCU_PLL_CTL_CLKOD_FLD,
3548c2ecf20Sopenharmony_ci				CCU_PLL_CTL_CLKOD_MASK, 1, CCU_PLL_OD_MAX),
3558c2ecf20Sopenharmony_ci	CCU_PLL_DBGFS_FLD_ATTR("pll_nb", CCU_PLL_CTL1, CCU_PLL_CTL1_BWADJ_FLD,
3568c2ecf20Sopenharmony_ci				CCU_PLL_CTL1_BWADJ_MASK, 1, CCU_PLL_NB_MAX)
3578c2ecf20Sopenharmony_ci};
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci#define CCU_PLL_DBGFS_FLD_NUM	ARRAY_SIZE(ccu_pll_flds)
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci/*
3628c2ecf20Sopenharmony_ci * It can be dangerous to change the PLL settings behind clock framework back,
3638c2ecf20Sopenharmony_ci * therefore we don't provide any kernel config based compile time option for
3648c2ecf20Sopenharmony_ci * this feature to enable.
3658c2ecf20Sopenharmony_ci */
3668c2ecf20Sopenharmony_ci#undef CCU_PLL_ALLOW_WRITE_DEBUGFS
3678c2ecf20Sopenharmony_ci#ifdef CCU_PLL_ALLOW_WRITE_DEBUGFS
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic int ccu_pll_dbgfs_bit_set(void *priv, u64 val)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	const struct ccu_pll_dbgfs_bit *bit = priv;
3728c2ecf20Sopenharmony_ci	struct ccu_pll *pll = bit->pll;
3738c2ecf20Sopenharmony_ci	unsigned long flags;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pll->lock, flags);
3768c2ecf20Sopenharmony_ci	regmap_update_bits(pll->sys_regs, pll->reg_ctl + bit->reg,
3778c2ecf20Sopenharmony_ci			   bit->mask, val ? bit->mask : 0);
3788c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pll->lock, flags);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	return 0;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic int ccu_pll_dbgfs_fld_set(void *priv, u64 val)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	struct ccu_pll_dbgfs_fld *fld = priv;
3868c2ecf20Sopenharmony_ci	struct ccu_pll *pll = fld->pll;
3878c2ecf20Sopenharmony_ci	unsigned long flags;
3888c2ecf20Sopenharmony_ci	u32 data;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	val = clamp_t(u64, val, fld->min, fld->max);
3918c2ecf20Sopenharmony_ci	data = ((val - 1) << fld->lsb) & fld->mask;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pll->lock, flags);
3948c2ecf20Sopenharmony_ci	regmap_update_bits(pll->sys_regs, pll->reg_ctl + fld->reg, fld->mask,
3958c2ecf20Sopenharmony_ci			   data);
3968c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pll->lock, flags);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	return 0;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci#define ccu_pll_dbgfs_mode	0644
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci#else /* !CCU_PLL_ALLOW_WRITE_DEBUGFS */
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci#define ccu_pll_dbgfs_bit_set	NULL
4068c2ecf20Sopenharmony_ci#define ccu_pll_dbgfs_fld_set	NULL
4078c2ecf20Sopenharmony_ci#define ccu_pll_dbgfs_mode	0444
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci#endif /* !CCU_PLL_ALLOW_WRITE_DEBUGFS */
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic int ccu_pll_dbgfs_bit_get(void *priv, u64 *val)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	struct ccu_pll_dbgfs_bit *bit = priv;
4148c2ecf20Sopenharmony_ci	struct ccu_pll *pll = bit->pll;
4158c2ecf20Sopenharmony_ci	u32 data = 0;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	regmap_read(pll->sys_regs, pll->reg_ctl + bit->reg, &data);
4188c2ecf20Sopenharmony_ci	*val = !!(data & bit->mask);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	return 0;
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(ccu_pll_dbgfs_bit_fops,
4238c2ecf20Sopenharmony_ci	ccu_pll_dbgfs_bit_get, ccu_pll_dbgfs_bit_set, "%llu\n");
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic int ccu_pll_dbgfs_fld_get(void *priv, u64 *val)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	struct ccu_pll_dbgfs_fld *fld = priv;
4288c2ecf20Sopenharmony_ci	struct ccu_pll *pll = fld->pll;
4298c2ecf20Sopenharmony_ci	u32 data = 0;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	regmap_read(pll->sys_regs, pll->reg_ctl + fld->reg, &data);
4328c2ecf20Sopenharmony_ci	*val = ((data & fld->mask) >> fld->lsb) + 1;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	return 0;
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(ccu_pll_dbgfs_fld_fops,
4378c2ecf20Sopenharmony_ci	ccu_pll_dbgfs_fld_get, ccu_pll_dbgfs_fld_set, "%llu\n");
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic void ccu_pll_debug_init(struct clk_hw *hw, struct dentry *dentry)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	struct ccu_pll *pll = to_ccu_pll(hw);
4428c2ecf20Sopenharmony_ci	struct ccu_pll_dbgfs_bit *bits;
4438c2ecf20Sopenharmony_ci	struct ccu_pll_dbgfs_fld *flds;
4448c2ecf20Sopenharmony_ci	int idx;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	bits = kcalloc(CCU_PLL_DBGFS_BIT_NUM, sizeof(*bits), GFP_KERNEL);
4478c2ecf20Sopenharmony_ci	if (!bits)
4488c2ecf20Sopenharmony_ci		return;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	for (idx = 0; idx < CCU_PLL_DBGFS_BIT_NUM; ++idx) {
4518c2ecf20Sopenharmony_ci		bits[idx] = ccu_pll_bits[idx];
4528c2ecf20Sopenharmony_ci		bits[idx].pll = pll;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci		debugfs_create_file_unsafe(bits[idx].name, ccu_pll_dbgfs_mode,
4558c2ecf20Sopenharmony_ci					   dentry, &bits[idx],
4568c2ecf20Sopenharmony_ci					   &ccu_pll_dbgfs_bit_fops);
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	flds = kcalloc(CCU_PLL_DBGFS_FLD_NUM, sizeof(*flds), GFP_KERNEL);
4608c2ecf20Sopenharmony_ci	if (!flds)
4618c2ecf20Sopenharmony_ci		return;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	for (idx = 0; idx < CCU_PLL_DBGFS_FLD_NUM; ++idx) {
4648c2ecf20Sopenharmony_ci		flds[idx] = ccu_pll_flds[idx];
4658c2ecf20Sopenharmony_ci		flds[idx].pll = pll;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci		debugfs_create_file_unsafe(flds[idx].name, ccu_pll_dbgfs_mode,
4688c2ecf20Sopenharmony_ci					   dentry, &flds[idx],
4698c2ecf20Sopenharmony_ci					   &ccu_pll_dbgfs_fld_fops);
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci#else /* !CONFIG_DEBUG_FS */
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci#define ccu_pll_debug_init NULL
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci#endif /* !CONFIG_DEBUG_FS */
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_cistatic const struct clk_ops ccu_pll_gate_to_set_ops = {
4808c2ecf20Sopenharmony_ci	.enable = ccu_pll_enable,
4818c2ecf20Sopenharmony_ci	.disable = ccu_pll_disable,
4828c2ecf20Sopenharmony_ci	.is_enabled = ccu_pll_is_enabled,
4838c2ecf20Sopenharmony_ci	.recalc_rate = ccu_pll_recalc_rate,
4848c2ecf20Sopenharmony_ci	.round_rate = ccu_pll_round_rate,
4858c2ecf20Sopenharmony_ci	.set_rate = ccu_pll_set_rate_norst,
4868c2ecf20Sopenharmony_ci	.debug_init = ccu_pll_debug_init
4878c2ecf20Sopenharmony_ci};
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_cistatic const struct clk_ops ccu_pll_straight_set_ops = {
4908c2ecf20Sopenharmony_ci	.enable = ccu_pll_enable,
4918c2ecf20Sopenharmony_ci	.disable = ccu_pll_disable,
4928c2ecf20Sopenharmony_ci	.is_enabled = ccu_pll_is_enabled,
4938c2ecf20Sopenharmony_ci	.recalc_rate = ccu_pll_recalc_rate,
4948c2ecf20Sopenharmony_ci	.round_rate = ccu_pll_round_rate,
4958c2ecf20Sopenharmony_ci	.set_rate = ccu_pll_set_rate_reset,
4968c2ecf20Sopenharmony_ci	.debug_init = ccu_pll_debug_init
4978c2ecf20Sopenharmony_ci};
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cistruct ccu_pll *ccu_pll_hw_register(const struct ccu_pll_init_data *pll_init)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	struct clk_parent_data parent_data = { };
5028c2ecf20Sopenharmony_ci	struct clk_init_data hw_init = { };
5038c2ecf20Sopenharmony_ci	struct ccu_pll *pll;
5048c2ecf20Sopenharmony_ci	int ret;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	if (!pll_init)
5078c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
5108c2ecf20Sopenharmony_ci	if (!pll)
5118c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	/*
5148c2ecf20Sopenharmony_ci	 * Note since Baikal-T1 System Controller registers are MMIO-backed
5158c2ecf20Sopenharmony_ci	 * we won't check the regmap IO operations return status, because it
5168c2ecf20Sopenharmony_ci	 * must be zero anyway.
5178c2ecf20Sopenharmony_ci	 */
5188c2ecf20Sopenharmony_ci	pll->hw.init = &hw_init;
5198c2ecf20Sopenharmony_ci	pll->reg_ctl = pll_init->base + CCU_PLL_CTL;
5208c2ecf20Sopenharmony_ci	pll->reg_ctl1 = pll_init->base + CCU_PLL_CTL1;
5218c2ecf20Sopenharmony_ci	pll->sys_regs = pll_init->sys_regs;
5228c2ecf20Sopenharmony_ci	pll->id = pll_init->id;
5238c2ecf20Sopenharmony_ci	spin_lock_init(&pll->lock);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	hw_init.name = pll_init->name;
5268c2ecf20Sopenharmony_ci	hw_init.flags = pll_init->flags;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	if (hw_init.flags & CLK_SET_RATE_GATE)
5298c2ecf20Sopenharmony_ci		hw_init.ops = &ccu_pll_gate_to_set_ops;
5308c2ecf20Sopenharmony_ci	else
5318c2ecf20Sopenharmony_ci		hw_init.ops = &ccu_pll_straight_set_ops;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	if (!pll_init->parent_name) {
5348c2ecf20Sopenharmony_ci		ret = -EINVAL;
5358c2ecf20Sopenharmony_ci		goto err_free_pll;
5368c2ecf20Sopenharmony_ci	}
5378c2ecf20Sopenharmony_ci	parent_data.fw_name = pll_init->parent_name;
5388c2ecf20Sopenharmony_ci	hw_init.parent_data = &parent_data;
5398c2ecf20Sopenharmony_ci	hw_init.num_parents = 1;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	ret = of_clk_hw_register(pll_init->np, &pll->hw);
5428c2ecf20Sopenharmony_ci	if (ret)
5438c2ecf20Sopenharmony_ci		goto err_free_pll;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	return pll;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_cierr_free_pll:
5488c2ecf20Sopenharmony_ci	kfree(pll);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
5518c2ecf20Sopenharmony_ci}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_civoid ccu_pll_hw_unregister(struct ccu_pll *pll)
5548c2ecf20Sopenharmony_ci{
5558c2ecf20Sopenharmony_ci	clk_hw_unregister(&pll->hw);
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	kfree(pll);
5588c2ecf20Sopenharmony_ci}
559