162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors:
662306a36Sopenharmony_ci *   Serge Semin <Sergey.Semin@baikalelectronics.ru>
762306a36Sopenharmony_ci *   Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Baikal-T1 CCU PLL interface driver
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define pr_fmt(fmt) "bt1-ccu-pll: " fmt
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/printk.h>
1662306a36Sopenharmony_ci#include <linux/limits.h>
1762306a36Sopenharmony_ci#include <linux/bits.h>
1862306a36Sopenharmony_ci#include <linux/bitfield.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <linux/clk-provider.h>
2162306a36Sopenharmony_ci#include <linux/of.h>
2262306a36Sopenharmony_ci#include <linux/spinlock.h>
2362306a36Sopenharmony_ci#include <linux/regmap.h>
2462306a36Sopenharmony_ci#include <linux/iopoll.h>
2562306a36Sopenharmony_ci#include <linux/time64.h>
2662306a36Sopenharmony_ci#include <linux/rational.h>
2762306a36Sopenharmony_ci#include <linux/debugfs.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include "ccu-pll.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define CCU_PLL_CTL			0x000
3262306a36Sopenharmony_ci#define CCU_PLL_CTL_EN			BIT(0)
3362306a36Sopenharmony_ci#define CCU_PLL_CTL_RST			BIT(1)
3462306a36Sopenharmony_ci#define CCU_PLL_CTL_CLKR_FLD		2
3562306a36Sopenharmony_ci#define CCU_PLL_CTL_CLKR_MASK		GENMASK(7, CCU_PLL_CTL_CLKR_FLD)
3662306a36Sopenharmony_ci#define CCU_PLL_CTL_CLKF_FLD		8
3762306a36Sopenharmony_ci#define CCU_PLL_CTL_CLKF_MASK		GENMASK(20, CCU_PLL_CTL_CLKF_FLD)
3862306a36Sopenharmony_ci#define CCU_PLL_CTL_CLKOD_FLD		21
3962306a36Sopenharmony_ci#define CCU_PLL_CTL_CLKOD_MASK		GENMASK(24, CCU_PLL_CTL_CLKOD_FLD)
4062306a36Sopenharmony_ci#define CCU_PLL_CTL_BYPASS		BIT(30)
4162306a36Sopenharmony_ci#define CCU_PLL_CTL_LOCK		BIT(31)
4262306a36Sopenharmony_ci#define CCU_PLL_CTL1			0x004
4362306a36Sopenharmony_ci#define CCU_PLL_CTL1_BWADJ_FLD		3
4462306a36Sopenharmony_ci#define CCU_PLL_CTL1_BWADJ_MASK		GENMASK(14, CCU_PLL_CTL1_BWADJ_FLD)
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define CCU_PLL_LOCK_CHECK_RETRIES	50
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define CCU_PLL_NR_MAX \
4962306a36Sopenharmony_ci	((CCU_PLL_CTL_CLKR_MASK >> CCU_PLL_CTL_CLKR_FLD) + 1)
5062306a36Sopenharmony_ci#define CCU_PLL_NF_MAX \
5162306a36Sopenharmony_ci	((CCU_PLL_CTL_CLKF_MASK >> (CCU_PLL_CTL_CLKF_FLD + 1)) + 1)
5262306a36Sopenharmony_ci#define CCU_PLL_OD_MAX \
5362306a36Sopenharmony_ci	((CCU_PLL_CTL_CLKOD_MASK >> CCU_PLL_CTL_CLKOD_FLD) + 1)
5462306a36Sopenharmony_ci#define CCU_PLL_NB_MAX \
5562306a36Sopenharmony_ci	((CCU_PLL_CTL1_BWADJ_MASK >> CCU_PLL_CTL1_BWADJ_FLD) + 1)
5662306a36Sopenharmony_ci#define CCU_PLL_FDIV_MIN		427000UL
5762306a36Sopenharmony_ci#define CCU_PLL_FDIV_MAX		3500000000UL
5862306a36Sopenharmony_ci#define CCU_PLL_FOUT_MIN		200000000UL
5962306a36Sopenharmony_ci#define CCU_PLL_FOUT_MAX		2500000000UL
6062306a36Sopenharmony_ci#define CCU_PLL_FVCO_MIN		700000000UL
6162306a36Sopenharmony_ci#define CCU_PLL_FVCO_MAX		3500000000UL
6262306a36Sopenharmony_ci#define CCU_PLL_CLKOD_FACTOR		2
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic inline unsigned long ccu_pll_lock_delay_us(unsigned long ref_clk,
6562306a36Sopenharmony_ci						  unsigned long nr)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	u64 us = 500ULL * nr * USEC_PER_SEC;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	do_div(us, ref_clk);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	return us;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic inline unsigned long ccu_pll_calc_freq(unsigned long ref_clk,
7562306a36Sopenharmony_ci					      unsigned long nr,
7662306a36Sopenharmony_ci					      unsigned long nf,
7762306a36Sopenharmony_ci					      unsigned long od)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	u64 tmp = ref_clk;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	do_div(tmp, nr);
8262306a36Sopenharmony_ci	tmp *= nf;
8362306a36Sopenharmony_ci	do_div(tmp, od);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return tmp;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int ccu_pll_reset(struct ccu_pll *pll, unsigned long ref_clk,
8962306a36Sopenharmony_ci			 unsigned long nr)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	unsigned long ud, ut;
9262306a36Sopenharmony_ci	u32 val;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	ud = ccu_pll_lock_delay_us(ref_clk, nr);
9562306a36Sopenharmony_ci	ut = ud * CCU_PLL_LOCK_CHECK_RETRIES;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	regmap_update_bits(pll->sys_regs, pll->reg_ctl,
9862306a36Sopenharmony_ci			   CCU_PLL_CTL_RST, CCU_PLL_CTL_RST);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	return regmap_read_poll_timeout_atomic(pll->sys_regs, pll->reg_ctl, val,
10162306a36Sopenharmony_ci					       val & CCU_PLL_CTL_LOCK, ud, ut);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic int ccu_pll_enable(struct clk_hw *hw)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct clk_hw *parent_hw = clk_hw_get_parent(hw);
10762306a36Sopenharmony_ci	struct ccu_pll *pll = to_ccu_pll(hw);
10862306a36Sopenharmony_ci	unsigned long flags;
10962306a36Sopenharmony_ci	u32 val = 0;
11062306a36Sopenharmony_ci	int ret;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (!parent_hw) {
11362306a36Sopenharmony_ci		pr_err("Can't enable '%s' with no parent", clk_hw_get_name(hw));
11462306a36Sopenharmony_ci		return -EINVAL;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	regmap_read(pll->sys_regs, pll->reg_ctl, &val);
11862306a36Sopenharmony_ci	if (val & CCU_PLL_CTL_EN)
11962306a36Sopenharmony_ci		return 0;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	spin_lock_irqsave(&pll->lock, flags);
12262306a36Sopenharmony_ci	regmap_write(pll->sys_regs, pll->reg_ctl, val | CCU_PLL_CTL_EN);
12362306a36Sopenharmony_ci	ret = ccu_pll_reset(pll, clk_hw_get_rate(parent_hw),
12462306a36Sopenharmony_ci			    FIELD_GET(CCU_PLL_CTL_CLKR_MASK, val) + 1);
12562306a36Sopenharmony_ci	spin_unlock_irqrestore(&pll->lock, flags);
12662306a36Sopenharmony_ci	if (ret)
12762306a36Sopenharmony_ci		pr_err("PLL '%s' reset timed out\n", clk_hw_get_name(hw));
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return ret;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic void ccu_pll_disable(struct clk_hw *hw)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct ccu_pll *pll = to_ccu_pll(hw);
13562306a36Sopenharmony_ci	unsigned long flags;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	spin_lock_irqsave(&pll->lock, flags);
13862306a36Sopenharmony_ci	regmap_update_bits(pll->sys_regs, pll->reg_ctl, CCU_PLL_CTL_EN, 0);
13962306a36Sopenharmony_ci	spin_unlock_irqrestore(&pll->lock, flags);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int ccu_pll_is_enabled(struct clk_hw *hw)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct ccu_pll *pll = to_ccu_pll(hw);
14562306a36Sopenharmony_ci	u32 val = 0;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	regmap_read(pll->sys_regs, pll->reg_ctl, &val);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return !!(val & CCU_PLL_CTL_EN);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic unsigned long ccu_pll_recalc_rate(struct clk_hw *hw,
15362306a36Sopenharmony_ci					 unsigned long parent_rate)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct ccu_pll *pll = to_ccu_pll(hw);
15662306a36Sopenharmony_ci	unsigned long nr, nf, od;
15762306a36Sopenharmony_ci	u32 val = 0;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	regmap_read(pll->sys_regs, pll->reg_ctl, &val);
16062306a36Sopenharmony_ci	nr = FIELD_GET(CCU_PLL_CTL_CLKR_MASK, val) + 1;
16162306a36Sopenharmony_ci	nf = FIELD_GET(CCU_PLL_CTL_CLKF_MASK, val) + 1;
16262306a36Sopenharmony_ci	od = FIELD_GET(CCU_PLL_CTL_CLKOD_MASK, val) + 1;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return ccu_pll_calc_freq(parent_rate, nr, nf, od);
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic void ccu_pll_calc_factors(unsigned long rate, unsigned long parent_rate,
16862306a36Sopenharmony_ci				 unsigned long *nr, unsigned long *nf,
16962306a36Sopenharmony_ci				 unsigned long *od)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	unsigned long err, freq, min_err = ULONG_MAX;
17262306a36Sopenharmony_ci	unsigned long num, denom, n1, d1, nri;
17362306a36Sopenharmony_ci	unsigned long nr_max, nf_max, od_max;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/*
17662306a36Sopenharmony_ci	 * Make sure PLL is working with valid input signal (Fdiv). If
17762306a36Sopenharmony_ci	 * you want to speed the function up just reduce CCU_PLL_NR_MAX.
17862306a36Sopenharmony_ci	 * This will cause a worse approximation though.
17962306a36Sopenharmony_ci	 */
18062306a36Sopenharmony_ci	nri = (parent_rate / CCU_PLL_FDIV_MAX) + 1;
18162306a36Sopenharmony_ci	nr_max = min(parent_rate / CCU_PLL_FDIV_MIN, CCU_PLL_NR_MAX);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/*
18462306a36Sopenharmony_ci	 * Find a closest [nr;nf;od] vector taking into account the
18562306a36Sopenharmony_ci	 * limitations like: 1) 700MHz <= Fvco <= 3.5GHz, 2) PLL Od is
18662306a36Sopenharmony_ci	 * either 1 or even number within the acceptable range (alas 1s
18762306a36Sopenharmony_ci	 * is also excluded by the next loop).
18862306a36Sopenharmony_ci	 */
18962306a36Sopenharmony_ci	for (; nri <= nr_max; ++nri) {
19062306a36Sopenharmony_ci		/* Use Od factor to fulfill the limitation 2). */
19162306a36Sopenharmony_ci		num = CCU_PLL_CLKOD_FACTOR * rate;
19262306a36Sopenharmony_ci		denom = parent_rate / nri;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci		/*
19562306a36Sopenharmony_ci		 * Make sure Fvco is within the acceptable range to fulfill
19662306a36Sopenharmony_ci		 * the condition 1). Note due to the CCU_PLL_CLKOD_FACTOR value
19762306a36Sopenharmony_ci		 * the actual upper limit is also divided by that factor.
19862306a36Sopenharmony_ci		 * It's not big problem for us since practically there is no
19962306a36Sopenharmony_ci		 * need in clocks with that high frequency.
20062306a36Sopenharmony_ci		 */
20162306a36Sopenharmony_ci		nf_max = min(CCU_PLL_FVCO_MAX / denom, CCU_PLL_NF_MAX);
20262306a36Sopenharmony_ci		od_max = CCU_PLL_OD_MAX / CCU_PLL_CLKOD_FACTOR;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci		/*
20562306a36Sopenharmony_ci		 * Bypass the out-of-bound values, which can't be properly
20662306a36Sopenharmony_ci		 * handled by the rational fraction approximation algorithm.
20762306a36Sopenharmony_ci		 */
20862306a36Sopenharmony_ci		if (num / denom >= nf_max) {
20962306a36Sopenharmony_ci			n1 = nf_max;
21062306a36Sopenharmony_ci			d1 = 1;
21162306a36Sopenharmony_ci		} else if (denom / num >= od_max) {
21262306a36Sopenharmony_ci			n1 = 1;
21362306a36Sopenharmony_ci			d1 = od_max;
21462306a36Sopenharmony_ci		} else {
21562306a36Sopenharmony_ci			rational_best_approximation(num, denom, nf_max, od_max,
21662306a36Sopenharmony_ci						    &n1, &d1);
21762306a36Sopenharmony_ci		}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		/* Select the best approximation of the target rate. */
22062306a36Sopenharmony_ci		freq = ccu_pll_calc_freq(parent_rate, nri, n1, d1);
22162306a36Sopenharmony_ci		err = abs((int64_t)freq - num);
22262306a36Sopenharmony_ci		if (err < min_err) {
22362306a36Sopenharmony_ci			min_err = err;
22462306a36Sopenharmony_ci			*nr = nri;
22562306a36Sopenharmony_ci			*nf = n1;
22662306a36Sopenharmony_ci			*od = CCU_PLL_CLKOD_FACTOR * d1;
22762306a36Sopenharmony_ci		}
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic long ccu_pll_round_rate(struct clk_hw *hw, unsigned long rate,
23262306a36Sopenharmony_ci			       unsigned long *parent_rate)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	unsigned long nr = 1, nf = 1, od = 1;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	ccu_pll_calc_factors(rate, *parent_rate, &nr, &nf, &od);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return ccu_pll_calc_freq(*parent_rate, nr, nf, od);
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci/*
24262306a36Sopenharmony_ci * This method is used for PLLs, which support the on-the-fly dividers
24362306a36Sopenharmony_ci * adjustment. So there is no need in gating such clocks.
24462306a36Sopenharmony_ci */
24562306a36Sopenharmony_cistatic int ccu_pll_set_rate_reset(struct clk_hw *hw, unsigned long rate,
24662306a36Sopenharmony_ci				  unsigned long parent_rate)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	struct ccu_pll *pll = to_ccu_pll(hw);
24962306a36Sopenharmony_ci	unsigned long nr, nf, od;
25062306a36Sopenharmony_ci	unsigned long flags;
25162306a36Sopenharmony_ci	u32 mask, val;
25262306a36Sopenharmony_ci	int ret;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	ccu_pll_calc_factors(rate, parent_rate, &nr, &nf, &od);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	mask = CCU_PLL_CTL_CLKR_MASK | CCU_PLL_CTL_CLKF_MASK |
25762306a36Sopenharmony_ci	       CCU_PLL_CTL_CLKOD_MASK;
25862306a36Sopenharmony_ci	val = FIELD_PREP(CCU_PLL_CTL_CLKR_MASK, nr - 1) |
25962306a36Sopenharmony_ci	      FIELD_PREP(CCU_PLL_CTL_CLKF_MASK, nf - 1) |
26062306a36Sopenharmony_ci	      FIELD_PREP(CCU_PLL_CTL_CLKOD_MASK, od - 1);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	spin_lock_irqsave(&pll->lock, flags);
26362306a36Sopenharmony_ci	regmap_update_bits(pll->sys_regs, pll->reg_ctl, mask, val);
26462306a36Sopenharmony_ci	ret = ccu_pll_reset(pll, parent_rate, nr);
26562306a36Sopenharmony_ci	spin_unlock_irqrestore(&pll->lock, flags);
26662306a36Sopenharmony_ci	if (ret)
26762306a36Sopenharmony_ci		pr_err("PLL '%s' reset timed out\n", clk_hw_get_name(hw));
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	return ret;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci/*
27362306a36Sopenharmony_ci * This method is used for PLLs, which don't support the on-the-fly dividers
27462306a36Sopenharmony_ci * adjustment. So the corresponding clocks are supposed to be gated first.
27562306a36Sopenharmony_ci */
27662306a36Sopenharmony_cistatic int ccu_pll_set_rate_norst(struct clk_hw *hw, unsigned long rate,
27762306a36Sopenharmony_ci				  unsigned long parent_rate)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	struct ccu_pll *pll = to_ccu_pll(hw);
28062306a36Sopenharmony_ci	unsigned long nr, nf, od;
28162306a36Sopenharmony_ci	unsigned long flags;
28262306a36Sopenharmony_ci	u32 mask, val;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	ccu_pll_calc_factors(rate, parent_rate, &nr, &nf, &od);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/*
28762306a36Sopenharmony_ci	 * Disable PLL if it was enabled by default or left enabled by the
28862306a36Sopenharmony_ci	 * system bootloader.
28962306a36Sopenharmony_ci	 */
29062306a36Sopenharmony_ci	mask = CCU_PLL_CTL_CLKR_MASK | CCU_PLL_CTL_CLKF_MASK |
29162306a36Sopenharmony_ci	       CCU_PLL_CTL_CLKOD_MASK | CCU_PLL_CTL_EN;
29262306a36Sopenharmony_ci	val = FIELD_PREP(CCU_PLL_CTL_CLKR_MASK, nr - 1) |
29362306a36Sopenharmony_ci	      FIELD_PREP(CCU_PLL_CTL_CLKF_MASK, nf - 1) |
29462306a36Sopenharmony_ci	      FIELD_PREP(CCU_PLL_CTL_CLKOD_MASK, od - 1);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	spin_lock_irqsave(&pll->lock, flags);
29762306a36Sopenharmony_ci	regmap_update_bits(pll->sys_regs, pll->reg_ctl, mask, val);
29862306a36Sopenharmony_ci	spin_unlock_irqrestore(&pll->lock, flags);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return 0;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistruct ccu_pll_dbgfs_bit {
30662306a36Sopenharmony_ci	struct ccu_pll *pll;
30762306a36Sopenharmony_ci	const char *name;
30862306a36Sopenharmony_ci	unsigned int reg;
30962306a36Sopenharmony_ci	u32 mask;
31062306a36Sopenharmony_ci};
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistruct ccu_pll_dbgfs_fld {
31362306a36Sopenharmony_ci	struct ccu_pll *pll;
31462306a36Sopenharmony_ci	const char *name;
31562306a36Sopenharmony_ci	unsigned int reg;
31662306a36Sopenharmony_ci	unsigned int lsb;
31762306a36Sopenharmony_ci	u32 mask;
31862306a36Sopenharmony_ci	u32 min;
31962306a36Sopenharmony_ci	u32 max;
32062306a36Sopenharmony_ci};
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci#define CCU_PLL_DBGFS_BIT_ATTR(_name, _reg, _mask)	\
32362306a36Sopenharmony_ci	{						\
32462306a36Sopenharmony_ci		.name = _name,				\
32562306a36Sopenharmony_ci		.reg = _reg,				\
32662306a36Sopenharmony_ci		.mask = _mask				\
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci#define CCU_PLL_DBGFS_FLD_ATTR(_name, _reg, _lsb, _mask, _min, _max)	\
33062306a36Sopenharmony_ci	{								\
33162306a36Sopenharmony_ci		.name = _name,						\
33262306a36Sopenharmony_ci		.reg = _reg,						\
33362306a36Sopenharmony_ci		.lsb = _lsb,						\
33462306a36Sopenharmony_ci		.mask = _mask,						\
33562306a36Sopenharmony_ci		.min = _min,						\
33662306a36Sopenharmony_ci		.max = _max						\
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic const struct ccu_pll_dbgfs_bit ccu_pll_bits[] = {
34062306a36Sopenharmony_ci	CCU_PLL_DBGFS_BIT_ATTR("pll_en", CCU_PLL_CTL, CCU_PLL_CTL_EN),
34162306a36Sopenharmony_ci	CCU_PLL_DBGFS_BIT_ATTR("pll_rst", CCU_PLL_CTL, CCU_PLL_CTL_RST),
34262306a36Sopenharmony_ci	CCU_PLL_DBGFS_BIT_ATTR("pll_bypass", CCU_PLL_CTL, CCU_PLL_CTL_BYPASS),
34362306a36Sopenharmony_ci	CCU_PLL_DBGFS_BIT_ATTR("pll_lock", CCU_PLL_CTL, CCU_PLL_CTL_LOCK)
34462306a36Sopenharmony_ci};
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci#define CCU_PLL_DBGFS_BIT_NUM	ARRAY_SIZE(ccu_pll_bits)
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic const struct ccu_pll_dbgfs_fld ccu_pll_flds[] = {
34962306a36Sopenharmony_ci	CCU_PLL_DBGFS_FLD_ATTR("pll_nr", CCU_PLL_CTL, CCU_PLL_CTL_CLKR_FLD,
35062306a36Sopenharmony_ci				CCU_PLL_CTL_CLKR_MASK, 1, CCU_PLL_NR_MAX),
35162306a36Sopenharmony_ci	CCU_PLL_DBGFS_FLD_ATTR("pll_nf", CCU_PLL_CTL, CCU_PLL_CTL_CLKF_FLD,
35262306a36Sopenharmony_ci				CCU_PLL_CTL_CLKF_MASK, 1, CCU_PLL_NF_MAX),
35362306a36Sopenharmony_ci	CCU_PLL_DBGFS_FLD_ATTR("pll_od", CCU_PLL_CTL, CCU_PLL_CTL_CLKOD_FLD,
35462306a36Sopenharmony_ci				CCU_PLL_CTL_CLKOD_MASK, 1, CCU_PLL_OD_MAX),
35562306a36Sopenharmony_ci	CCU_PLL_DBGFS_FLD_ATTR("pll_nb", CCU_PLL_CTL1, CCU_PLL_CTL1_BWADJ_FLD,
35662306a36Sopenharmony_ci				CCU_PLL_CTL1_BWADJ_MASK, 1, CCU_PLL_NB_MAX)
35762306a36Sopenharmony_ci};
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci#define CCU_PLL_DBGFS_FLD_NUM	ARRAY_SIZE(ccu_pll_flds)
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci/*
36262306a36Sopenharmony_ci * It can be dangerous to change the PLL settings behind clock framework back,
36362306a36Sopenharmony_ci * therefore we don't provide any kernel config based compile time option for
36462306a36Sopenharmony_ci * this feature to enable.
36562306a36Sopenharmony_ci */
36662306a36Sopenharmony_ci#undef CCU_PLL_ALLOW_WRITE_DEBUGFS
36762306a36Sopenharmony_ci#ifdef CCU_PLL_ALLOW_WRITE_DEBUGFS
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic int ccu_pll_dbgfs_bit_set(void *priv, u64 val)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	const struct ccu_pll_dbgfs_bit *bit = priv;
37262306a36Sopenharmony_ci	struct ccu_pll *pll = bit->pll;
37362306a36Sopenharmony_ci	unsigned long flags;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	spin_lock_irqsave(&pll->lock, flags);
37662306a36Sopenharmony_ci	regmap_update_bits(pll->sys_regs, pll->reg_ctl + bit->reg,
37762306a36Sopenharmony_ci			   bit->mask, val ? bit->mask : 0);
37862306a36Sopenharmony_ci	spin_unlock_irqrestore(&pll->lock, flags);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return 0;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic int ccu_pll_dbgfs_fld_set(void *priv, u64 val)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	struct ccu_pll_dbgfs_fld *fld = priv;
38662306a36Sopenharmony_ci	struct ccu_pll *pll = fld->pll;
38762306a36Sopenharmony_ci	unsigned long flags;
38862306a36Sopenharmony_ci	u32 data;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	val = clamp_t(u64, val, fld->min, fld->max);
39162306a36Sopenharmony_ci	data = ((val - 1) << fld->lsb) & fld->mask;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	spin_lock_irqsave(&pll->lock, flags);
39462306a36Sopenharmony_ci	regmap_update_bits(pll->sys_regs, pll->reg_ctl + fld->reg, fld->mask,
39562306a36Sopenharmony_ci			   data);
39662306a36Sopenharmony_ci	spin_unlock_irqrestore(&pll->lock, flags);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return 0;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci#define ccu_pll_dbgfs_mode	0644
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci#else /* !CCU_PLL_ALLOW_WRITE_DEBUGFS */
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci#define ccu_pll_dbgfs_bit_set	NULL
40662306a36Sopenharmony_ci#define ccu_pll_dbgfs_fld_set	NULL
40762306a36Sopenharmony_ci#define ccu_pll_dbgfs_mode	0444
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci#endif /* !CCU_PLL_ALLOW_WRITE_DEBUGFS */
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistatic int ccu_pll_dbgfs_bit_get(void *priv, u64 *val)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	struct ccu_pll_dbgfs_bit *bit = priv;
41462306a36Sopenharmony_ci	struct ccu_pll *pll = bit->pll;
41562306a36Sopenharmony_ci	u32 data = 0;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	regmap_read(pll->sys_regs, pll->reg_ctl + bit->reg, &data);
41862306a36Sopenharmony_ci	*val = !!(data & bit->mask);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	return 0;
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(ccu_pll_dbgfs_bit_fops,
42362306a36Sopenharmony_ci	ccu_pll_dbgfs_bit_get, ccu_pll_dbgfs_bit_set, "%llu\n");
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic int ccu_pll_dbgfs_fld_get(void *priv, u64 *val)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	struct ccu_pll_dbgfs_fld *fld = priv;
42862306a36Sopenharmony_ci	struct ccu_pll *pll = fld->pll;
42962306a36Sopenharmony_ci	u32 data = 0;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	regmap_read(pll->sys_regs, pll->reg_ctl + fld->reg, &data);
43262306a36Sopenharmony_ci	*val = ((data & fld->mask) >> fld->lsb) + 1;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	return 0;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(ccu_pll_dbgfs_fld_fops,
43762306a36Sopenharmony_ci	ccu_pll_dbgfs_fld_get, ccu_pll_dbgfs_fld_set, "%llu\n");
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic void ccu_pll_debug_init(struct clk_hw *hw, struct dentry *dentry)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct ccu_pll *pll = to_ccu_pll(hw);
44262306a36Sopenharmony_ci	struct ccu_pll_dbgfs_bit *bits;
44362306a36Sopenharmony_ci	struct ccu_pll_dbgfs_fld *flds;
44462306a36Sopenharmony_ci	int idx;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	bits = kcalloc(CCU_PLL_DBGFS_BIT_NUM, sizeof(*bits), GFP_KERNEL);
44762306a36Sopenharmony_ci	if (!bits)
44862306a36Sopenharmony_ci		return;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	for (idx = 0; idx < CCU_PLL_DBGFS_BIT_NUM; ++idx) {
45162306a36Sopenharmony_ci		bits[idx] = ccu_pll_bits[idx];
45262306a36Sopenharmony_ci		bits[idx].pll = pll;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci		debugfs_create_file_unsafe(bits[idx].name, ccu_pll_dbgfs_mode,
45562306a36Sopenharmony_ci					   dentry, &bits[idx],
45662306a36Sopenharmony_ci					   &ccu_pll_dbgfs_bit_fops);
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	flds = kcalloc(CCU_PLL_DBGFS_FLD_NUM, sizeof(*flds), GFP_KERNEL);
46062306a36Sopenharmony_ci	if (!flds)
46162306a36Sopenharmony_ci		return;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	for (idx = 0; idx < CCU_PLL_DBGFS_FLD_NUM; ++idx) {
46462306a36Sopenharmony_ci		flds[idx] = ccu_pll_flds[idx];
46562306a36Sopenharmony_ci		flds[idx].pll = pll;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci		debugfs_create_file_unsafe(flds[idx].name, ccu_pll_dbgfs_mode,
46862306a36Sopenharmony_ci					   dentry, &flds[idx],
46962306a36Sopenharmony_ci					   &ccu_pll_dbgfs_fld_fops);
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci#else /* !CONFIG_DEBUG_FS */
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci#define ccu_pll_debug_init NULL
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci#endif /* !CONFIG_DEBUG_FS */
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic const struct clk_ops ccu_pll_gate_to_set_ops = {
48062306a36Sopenharmony_ci	.enable = ccu_pll_enable,
48162306a36Sopenharmony_ci	.disable = ccu_pll_disable,
48262306a36Sopenharmony_ci	.is_enabled = ccu_pll_is_enabled,
48362306a36Sopenharmony_ci	.recalc_rate = ccu_pll_recalc_rate,
48462306a36Sopenharmony_ci	.round_rate = ccu_pll_round_rate,
48562306a36Sopenharmony_ci	.set_rate = ccu_pll_set_rate_norst,
48662306a36Sopenharmony_ci	.debug_init = ccu_pll_debug_init
48762306a36Sopenharmony_ci};
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic const struct clk_ops ccu_pll_straight_set_ops = {
49062306a36Sopenharmony_ci	.enable = ccu_pll_enable,
49162306a36Sopenharmony_ci	.disable = ccu_pll_disable,
49262306a36Sopenharmony_ci	.is_enabled = ccu_pll_is_enabled,
49362306a36Sopenharmony_ci	.recalc_rate = ccu_pll_recalc_rate,
49462306a36Sopenharmony_ci	.round_rate = ccu_pll_round_rate,
49562306a36Sopenharmony_ci	.set_rate = ccu_pll_set_rate_reset,
49662306a36Sopenharmony_ci	.debug_init = ccu_pll_debug_init
49762306a36Sopenharmony_ci};
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistruct ccu_pll *ccu_pll_hw_register(const struct ccu_pll_init_data *pll_init)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	struct clk_parent_data parent_data = { };
50262306a36Sopenharmony_ci	struct clk_init_data hw_init = { };
50362306a36Sopenharmony_ci	struct ccu_pll *pll;
50462306a36Sopenharmony_ci	int ret;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	if (!pll_init)
50762306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
51062306a36Sopenharmony_ci	if (!pll)
51162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	/*
51462306a36Sopenharmony_ci	 * Note since Baikal-T1 System Controller registers are MMIO-backed
51562306a36Sopenharmony_ci	 * we won't check the regmap IO operations return status, because it
51662306a36Sopenharmony_ci	 * must be zero anyway.
51762306a36Sopenharmony_ci	 */
51862306a36Sopenharmony_ci	pll->hw.init = &hw_init;
51962306a36Sopenharmony_ci	pll->reg_ctl = pll_init->base + CCU_PLL_CTL;
52062306a36Sopenharmony_ci	pll->reg_ctl1 = pll_init->base + CCU_PLL_CTL1;
52162306a36Sopenharmony_ci	pll->sys_regs = pll_init->sys_regs;
52262306a36Sopenharmony_ci	pll->id = pll_init->id;
52362306a36Sopenharmony_ci	spin_lock_init(&pll->lock);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	hw_init.name = pll_init->name;
52662306a36Sopenharmony_ci	hw_init.flags = pll_init->flags;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	if (hw_init.flags & CLK_SET_RATE_GATE)
52962306a36Sopenharmony_ci		hw_init.ops = &ccu_pll_gate_to_set_ops;
53062306a36Sopenharmony_ci	else
53162306a36Sopenharmony_ci		hw_init.ops = &ccu_pll_straight_set_ops;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (!pll_init->parent_name) {
53462306a36Sopenharmony_ci		ret = -EINVAL;
53562306a36Sopenharmony_ci		goto err_free_pll;
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci	parent_data.fw_name = pll_init->parent_name;
53862306a36Sopenharmony_ci	hw_init.parent_data = &parent_data;
53962306a36Sopenharmony_ci	hw_init.num_parents = 1;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	ret = of_clk_hw_register(pll_init->np, &pll->hw);
54262306a36Sopenharmony_ci	if (ret)
54362306a36Sopenharmony_ci		goto err_free_pll;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	return pll;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cierr_free_pll:
54862306a36Sopenharmony_ci	kfree(pll);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	return ERR_PTR(ret);
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_civoid ccu_pll_hw_unregister(struct ccu_pll *pll)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	clk_hw_unregister(&pll->hw);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	kfree(pll);
55862306a36Sopenharmony_ci}
559