162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci// Copyright (c) 2018, The Linux Foundation. All rights reserved.
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/kernel.h>
562306a36Sopenharmony_ci#include <linux/export.h>
662306a36Sopenharmony_ci#include <linux/regmap.h>
762306a36Sopenharmony_ci#include <linux/delay.h>
862306a36Sopenharmony_ci#include <linux/err.h>
962306a36Sopenharmony_ci#include <linux/clk-provider.h>
1062306a36Sopenharmony_ci#include <linux/spinlock.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "clk-regmap.h"
1362306a36Sopenharmony_ci#include "clk-hfpll.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define PLL_OUTCTRL	BIT(0)
1662306a36Sopenharmony_ci#define PLL_BYPASSNL	BIT(1)
1762306a36Sopenharmony_ci#define PLL_RESET_N	BIT(2)
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* Initialize a HFPLL at a given rate and enable it. */
2062306a36Sopenharmony_cistatic void __clk_hfpll_init_once(struct clk_hw *hw)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	struct clk_hfpll *h = to_clk_hfpll(hw);
2362306a36Sopenharmony_ci	struct hfpll_data const *hd = h->d;
2462306a36Sopenharmony_ci	struct regmap *regmap = h->clkr.regmap;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	if (likely(h->init_done))
2762306a36Sopenharmony_ci		return;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	/* Configure PLL parameters for integer mode. */
3062306a36Sopenharmony_ci	if (hd->config_val)
3162306a36Sopenharmony_ci		regmap_write(regmap, hd->config_reg, hd->config_val);
3262306a36Sopenharmony_ci	regmap_write(regmap, hd->m_reg, 0);
3362306a36Sopenharmony_ci	regmap_write(regmap, hd->n_reg, 1);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	if (hd->user_reg) {
3662306a36Sopenharmony_ci		u32 regval = hd->user_val;
3762306a36Sopenharmony_ci		unsigned long rate;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci		rate = clk_hw_get_rate(hw);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci		/* Pick the right VCO. */
4262306a36Sopenharmony_ci		if (hd->user_vco_mask && rate > hd->low_vco_max_rate)
4362306a36Sopenharmony_ci			regval |= hd->user_vco_mask;
4462306a36Sopenharmony_ci		regmap_write(regmap, hd->user_reg, regval);
4562306a36Sopenharmony_ci	}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	if (hd->droop_reg)
4862306a36Sopenharmony_ci		regmap_write(regmap, hd->droop_reg, hd->droop_val);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	h->init_done = true;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic void __clk_hfpll_enable(struct clk_hw *hw)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	struct clk_hfpll *h = to_clk_hfpll(hw);
5662306a36Sopenharmony_ci	struct hfpll_data const *hd = h->d;
5762306a36Sopenharmony_ci	struct regmap *regmap = h->clkr.regmap;
5862306a36Sopenharmony_ci	u32 val;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	__clk_hfpll_init_once(hw);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/* Disable PLL bypass mode. */
6362306a36Sopenharmony_ci	regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	/*
6662306a36Sopenharmony_ci	 * H/W requires a 5us delay between disabling the bypass and
6762306a36Sopenharmony_ci	 * de-asserting the reset. Delay 10us just to be safe.
6862306a36Sopenharmony_ci	 */
6962306a36Sopenharmony_ci	udelay(10);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/* De-assert active-low PLL reset. */
7262306a36Sopenharmony_ci	regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* Wait for PLL to lock. */
7562306a36Sopenharmony_ci	if (hd->status_reg)
7662306a36Sopenharmony_ci		/*
7762306a36Sopenharmony_ci		 * Busy wait. Should never timeout, we add a timeout to
7862306a36Sopenharmony_ci		 * prevent any sort of stall.
7962306a36Sopenharmony_ci		 */
8062306a36Sopenharmony_ci		regmap_read_poll_timeout(regmap, hd->status_reg, val,
8162306a36Sopenharmony_ci					 !(val & BIT(hd->lock_bit)), 0,
8262306a36Sopenharmony_ci					 100 * USEC_PER_MSEC);
8362306a36Sopenharmony_ci	else
8462306a36Sopenharmony_ci		udelay(60);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/* Enable PLL output. */
8762306a36Sopenharmony_ci	regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/* Enable an already-configured HFPLL. */
9162306a36Sopenharmony_cistatic int clk_hfpll_enable(struct clk_hw *hw)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	unsigned long flags;
9462306a36Sopenharmony_ci	struct clk_hfpll *h = to_clk_hfpll(hw);
9562306a36Sopenharmony_ci	struct hfpll_data const *hd = h->d;
9662306a36Sopenharmony_ci	struct regmap *regmap = h->clkr.regmap;
9762306a36Sopenharmony_ci	u32 mode;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	spin_lock_irqsave(&h->lock, flags);
10062306a36Sopenharmony_ci	regmap_read(regmap, hd->mode_reg, &mode);
10162306a36Sopenharmony_ci	if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)))
10262306a36Sopenharmony_ci		__clk_hfpll_enable(hw);
10362306a36Sopenharmony_ci	spin_unlock_irqrestore(&h->lock, flags);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return 0;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic void __clk_hfpll_disable(struct clk_hfpll *h)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct hfpll_data const *hd = h->d;
11162306a36Sopenharmony_ci	struct regmap *regmap = h->clkr.regmap;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	/*
11462306a36Sopenharmony_ci	 * Disable the PLL output, disable test mode, enable the bypass mode,
11562306a36Sopenharmony_ci	 * and assert the reset.
11662306a36Sopenharmony_ci	 */
11762306a36Sopenharmony_ci	regmap_update_bits(regmap, hd->mode_reg,
11862306a36Sopenharmony_ci			   PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic void clk_hfpll_disable(struct clk_hw *hw)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct clk_hfpll *h = to_clk_hfpll(hw);
12462306a36Sopenharmony_ci	unsigned long flags;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	spin_lock_irqsave(&h->lock, flags);
12762306a36Sopenharmony_ci	__clk_hfpll_disable(h);
12862306a36Sopenharmony_ci	spin_unlock_irqrestore(&h->lock, flags);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic int clk_hfpll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	struct clk_hfpll *h = to_clk_hfpll(hw);
13462306a36Sopenharmony_ci	struct hfpll_data const *hd = h->d;
13562306a36Sopenharmony_ci	unsigned long rrate;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	req->rate = clamp(req->rate, hd->min_rate, hd->max_rate);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	rrate = DIV_ROUND_UP(req->rate, req->best_parent_rate) * req->best_parent_rate;
14062306a36Sopenharmony_ci	if (rrate > hd->max_rate)
14162306a36Sopenharmony_ci		rrate -= req->best_parent_rate;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	req->rate = rrate;
14462306a36Sopenharmony_ci	return 0;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/*
14862306a36Sopenharmony_ci * For optimization reasons, assumes no downstream clocks are actively using
14962306a36Sopenharmony_ci * it.
15062306a36Sopenharmony_ci */
15162306a36Sopenharmony_cistatic int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate,
15262306a36Sopenharmony_ci			      unsigned long parent_rate)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	struct clk_hfpll *h = to_clk_hfpll(hw);
15562306a36Sopenharmony_ci	struct hfpll_data const *hd = h->d;
15662306a36Sopenharmony_ci	struct regmap *regmap = h->clkr.regmap;
15762306a36Sopenharmony_ci	unsigned long flags;
15862306a36Sopenharmony_ci	u32 l_val, val;
15962306a36Sopenharmony_ci	bool enabled;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	l_val = rate / parent_rate;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	spin_lock_irqsave(&h->lock, flags);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	enabled = __clk_is_enabled(hw->clk);
16662306a36Sopenharmony_ci	if (enabled)
16762306a36Sopenharmony_ci		__clk_hfpll_disable(h);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	/* Pick the right VCO. */
17062306a36Sopenharmony_ci	if (hd->user_reg && hd->user_vco_mask) {
17162306a36Sopenharmony_ci		regmap_read(regmap, hd->user_reg, &val);
17262306a36Sopenharmony_ci		if (rate <= hd->low_vco_max_rate)
17362306a36Sopenharmony_ci			val &= ~hd->user_vco_mask;
17462306a36Sopenharmony_ci		else
17562306a36Sopenharmony_ci			val |= hd->user_vco_mask;
17662306a36Sopenharmony_ci		regmap_write(regmap, hd->user_reg, val);
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	regmap_write(regmap, hd->l_reg, l_val);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if (enabled)
18262306a36Sopenharmony_ci		__clk_hfpll_enable(hw);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	spin_unlock_irqrestore(&h->lock, flags);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return 0;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw,
19062306a36Sopenharmony_ci					   unsigned long parent_rate)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	struct clk_hfpll *h = to_clk_hfpll(hw);
19362306a36Sopenharmony_ci	struct hfpll_data const *hd = h->d;
19462306a36Sopenharmony_ci	struct regmap *regmap = h->clkr.regmap;
19562306a36Sopenharmony_ci	u32 l_val;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	regmap_read(regmap, hd->l_reg, &l_val);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	return l_val * parent_rate;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic int clk_hfpll_init(struct clk_hw *hw)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct clk_hfpll *h = to_clk_hfpll(hw);
20562306a36Sopenharmony_ci	struct hfpll_data const *hd = h->d;
20662306a36Sopenharmony_ci	struct regmap *regmap = h->clkr.regmap;
20762306a36Sopenharmony_ci	u32 mode, status;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	regmap_read(regmap, hd->mode_reg, &mode);
21062306a36Sopenharmony_ci	if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) {
21162306a36Sopenharmony_ci		__clk_hfpll_init_once(hw);
21262306a36Sopenharmony_ci		return 0;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (hd->status_reg) {
21662306a36Sopenharmony_ci		regmap_read(regmap, hd->status_reg, &status);
21762306a36Sopenharmony_ci		if (!(status & BIT(hd->lock_bit))) {
21862306a36Sopenharmony_ci			WARN(1, "HFPLL %s is ON, but not locked!\n",
21962306a36Sopenharmony_ci			     __clk_get_name(hw->clk));
22062306a36Sopenharmony_ci			clk_hfpll_disable(hw);
22162306a36Sopenharmony_ci			__clk_hfpll_init_once(hw);
22262306a36Sopenharmony_ci		}
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	return 0;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic int hfpll_is_enabled(struct clk_hw *hw)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct clk_hfpll *h = to_clk_hfpll(hw);
23162306a36Sopenharmony_ci	struct hfpll_data const *hd = h->d;
23262306a36Sopenharmony_ci	struct regmap *regmap = h->clkr.regmap;
23362306a36Sopenharmony_ci	u32 mode;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	regmap_read(regmap, hd->mode_reg, &mode);
23662306a36Sopenharmony_ci	mode &= 0x7;
23762306a36Sopenharmony_ci	return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL);
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ciconst struct clk_ops clk_ops_hfpll = {
24162306a36Sopenharmony_ci	.enable = clk_hfpll_enable,
24262306a36Sopenharmony_ci	.disable = clk_hfpll_disable,
24362306a36Sopenharmony_ci	.is_enabled = hfpll_is_enabled,
24462306a36Sopenharmony_ci	.determine_rate = clk_hfpll_determine_rate,
24562306a36Sopenharmony_ci	.set_rate = clk_hfpll_set_rate,
24662306a36Sopenharmony_ci	.recalc_rate = clk_hfpll_recalc_rate,
24762306a36Sopenharmony_ci	.init = clk_hfpll_init,
24862306a36Sopenharmony_ci};
24962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_ops_hfpll);
250