xref: /openbmc/linux/drivers/clk/qcom/clk-hfpll.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1b3f2f106SStephen Boyd // SPDX-License-Identifier: GPL-2.0
2b3f2f106SStephen Boyd // Copyright (c) 2018, The Linux Foundation. All rights reserved.
3b3f2f106SStephen Boyd 
4b3f2f106SStephen Boyd #include <linux/kernel.h>
5b3f2f106SStephen Boyd #include <linux/export.h>
6b3f2f106SStephen Boyd #include <linux/regmap.h>
7b3f2f106SStephen Boyd #include <linux/delay.h>
8b3f2f106SStephen Boyd #include <linux/err.h>
9b3f2f106SStephen Boyd #include <linux/clk-provider.h>
10b3f2f106SStephen Boyd #include <linux/spinlock.h>
11b3f2f106SStephen Boyd 
12b3f2f106SStephen Boyd #include "clk-regmap.h"
13b3f2f106SStephen Boyd #include "clk-hfpll.h"
14b3f2f106SStephen Boyd 
15b3f2f106SStephen Boyd #define PLL_OUTCTRL	BIT(0)
16b3f2f106SStephen Boyd #define PLL_BYPASSNL	BIT(1)
17b3f2f106SStephen Boyd #define PLL_RESET_N	BIT(2)
18b3f2f106SStephen Boyd 
19b3f2f106SStephen Boyd /* Initialize a HFPLL at a given rate and enable it. */
__clk_hfpll_init_once(struct clk_hw * hw)20b3f2f106SStephen Boyd static void __clk_hfpll_init_once(struct clk_hw *hw)
21b3f2f106SStephen Boyd {
22b3f2f106SStephen Boyd 	struct clk_hfpll *h = to_clk_hfpll(hw);
23b3f2f106SStephen Boyd 	struct hfpll_data const *hd = h->d;
24b3f2f106SStephen Boyd 	struct regmap *regmap = h->clkr.regmap;
25b3f2f106SStephen Boyd 
26b3f2f106SStephen Boyd 	if (likely(h->init_done))
27b3f2f106SStephen Boyd 		return;
28b3f2f106SStephen Boyd 
29b3f2f106SStephen Boyd 	/* Configure PLL parameters for integer mode. */
30b3f2f106SStephen Boyd 	if (hd->config_val)
31b3f2f106SStephen Boyd 		regmap_write(regmap, hd->config_reg, hd->config_val);
32b3f2f106SStephen Boyd 	regmap_write(regmap, hd->m_reg, 0);
33b3f2f106SStephen Boyd 	regmap_write(regmap, hd->n_reg, 1);
34b3f2f106SStephen Boyd 
35b3f2f106SStephen Boyd 	if (hd->user_reg) {
36b3f2f106SStephen Boyd 		u32 regval = hd->user_val;
37b3f2f106SStephen Boyd 		unsigned long rate;
38b3f2f106SStephen Boyd 
39b3f2f106SStephen Boyd 		rate = clk_hw_get_rate(hw);
40b3f2f106SStephen Boyd 
41b3f2f106SStephen Boyd 		/* Pick the right VCO. */
42b3f2f106SStephen Boyd 		if (hd->user_vco_mask && rate > hd->low_vco_max_rate)
43b3f2f106SStephen Boyd 			regval |= hd->user_vco_mask;
44b3f2f106SStephen Boyd 		regmap_write(regmap, hd->user_reg, regval);
45b3f2f106SStephen Boyd 	}
46b3f2f106SStephen Boyd 
47b3f2f106SStephen Boyd 	if (hd->droop_reg)
48b3f2f106SStephen Boyd 		regmap_write(regmap, hd->droop_reg, hd->droop_val);
49b3f2f106SStephen Boyd 
50b3f2f106SStephen Boyd 	h->init_done = true;
51b3f2f106SStephen Boyd }
52b3f2f106SStephen Boyd 
__clk_hfpll_enable(struct clk_hw * hw)53b3f2f106SStephen Boyd static void __clk_hfpll_enable(struct clk_hw *hw)
54b3f2f106SStephen Boyd {
55b3f2f106SStephen Boyd 	struct clk_hfpll *h = to_clk_hfpll(hw);
56b3f2f106SStephen Boyd 	struct hfpll_data const *hd = h->d;
57b3f2f106SStephen Boyd 	struct regmap *regmap = h->clkr.regmap;
58b3f2f106SStephen Boyd 	u32 val;
59b3f2f106SStephen Boyd 
60b3f2f106SStephen Boyd 	__clk_hfpll_init_once(hw);
61b3f2f106SStephen Boyd 
62b3f2f106SStephen Boyd 	/* Disable PLL bypass mode. */
63b3f2f106SStephen Boyd 	regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL);
64b3f2f106SStephen Boyd 
65b3f2f106SStephen Boyd 	/*
66b3f2f106SStephen Boyd 	 * H/W requires a 5us delay between disabling the bypass and
67b3f2f106SStephen Boyd 	 * de-asserting the reset. Delay 10us just to be safe.
68b3f2f106SStephen Boyd 	 */
69b3f2f106SStephen Boyd 	udelay(10);
70b3f2f106SStephen Boyd 
71b3f2f106SStephen Boyd 	/* De-assert active-low PLL reset. */
72b3f2f106SStephen Boyd 	regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N);
73b3f2f106SStephen Boyd 
74b3f2f106SStephen Boyd 	/* Wait for PLL to lock. */
75fcfbfe37SAnsuel Smith 	if (hd->status_reg)
76fcfbfe37SAnsuel Smith 		/*
77fcfbfe37SAnsuel Smith 		 * Busy wait. Should never timeout, we add a timeout to
78fcfbfe37SAnsuel Smith 		 * prevent any sort of stall.
79fcfbfe37SAnsuel Smith 		 */
80fcfbfe37SAnsuel Smith 		regmap_read_poll_timeout(regmap, hd->status_reg, val,
81fcfbfe37SAnsuel Smith 					 !(val & BIT(hd->lock_bit)), 0,
82fcfbfe37SAnsuel Smith 					 100 * USEC_PER_MSEC);
83fcfbfe37SAnsuel Smith 	else
84b3f2f106SStephen Boyd 		udelay(60);
85b3f2f106SStephen Boyd 
86b3f2f106SStephen Boyd 	/* Enable PLL output. */
87b3f2f106SStephen Boyd 	regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL);
88b3f2f106SStephen Boyd }
89b3f2f106SStephen Boyd 
90b3f2f106SStephen Boyd /* Enable an already-configured HFPLL. */
clk_hfpll_enable(struct clk_hw * hw)91b3f2f106SStephen Boyd static int clk_hfpll_enable(struct clk_hw *hw)
92b3f2f106SStephen Boyd {
93b3f2f106SStephen Boyd 	unsigned long flags;
94b3f2f106SStephen Boyd 	struct clk_hfpll *h = to_clk_hfpll(hw);
95b3f2f106SStephen Boyd 	struct hfpll_data const *hd = h->d;
96b3f2f106SStephen Boyd 	struct regmap *regmap = h->clkr.regmap;
97b3f2f106SStephen Boyd 	u32 mode;
98b3f2f106SStephen Boyd 
99b3f2f106SStephen Boyd 	spin_lock_irqsave(&h->lock, flags);
100b3f2f106SStephen Boyd 	regmap_read(regmap, hd->mode_reg, &mode);
101b3f2f106SStephen Boyd 	if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)))
102b3f2f106SStephen Boyd 		__clk_hfpll_enable(hw);
103b3f2f106SStephen Boyd 	spin_unlock_irqrestore(&h->lock, flags);
104b3f2f106SStephen Boyd 
105b3f2f106SStephen Boyd 	return 0;
106b3f2f106SStephen Boyd }
107b3f2f106SStephen Boyd 
__clk_hfpll_disable(struct clk_hfpll * h)108b3f2f106SStephen Boyd static void __clk_hfpll_disable(struct clk_hfpll *h)
109b3f2f106SStephen Boyd {
110b3f2f106SStephen Boyd 	struct hfpll_data const *hd = h->d;
111b3f2f106SStephen Boyd 	struct regmap *regmap = h->clkr.regmap;
112b3f2f106SStephen Boyd 
113b3f2f106SStephen Boyd 	/*
114b3f2f106SStephen Boyd 	 * Disable the PLL output, disable test mode, enable the bypass mode,
115b3f2f106SStephen Boyd 	 * and assert the reset.
116b3f2f106SStephen Boyd 	 */
117b3f2f106SStephen Boyd 	regmap_update_bits(regmap, hd->mode_reg,
118b3f2f106SStephen Boyd 			   PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0);
119b3f2f106SStephen Boyd }
120b3f2f106SStephen Boyd 
clk_hfpll_disable(struct clk_hw * hw)121b3f2f106SStephen Boyd static void clk_hfpll_disable(struct clk_hw *hw)
122b3f2f106SStephen Boyd {
123b3f2f106SStephen Boyd 	struct clk_hfpll *h = to_clk_hfpll(hw);
124b3f2f106SStephen Boyd 	unsigned long flags;
125b3f2f106SStephen Boyd 
126b3f2f106SStephen Boyd 	spin_lock_irqsave(&h->lock, flags);
127b3f2f106SStephen Boyd 	__clk_hfpll_disable(h);
128b3f2f106SStephen Boyd 	spin_unlock_irqrestore(&h->lock, flags);
129b3f2f106SStephen Boyd }
130b3f2f106SStephen Boyd 
clk_hfpll_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)131*04648b8fSLuca Weiss static int clk_hfpll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
132b3f2f106SStephen Boyd {
133b3f2f106SStephen Boyd 	struct clk_hfpll *h = to_clk_hfpll(hw);
134b3f2f106SStephen Boyd 	struct hfpll_data const *hd = h->d;
135b3f2f106SStephen Boyd 	unsigned long rrate;
136b3f2f106SStephen Boyd 
137*04648b8fSLuca Weiss 	req->rate = clamp(req->rate, hd->min_rate, hd->max_rate);
138b3f2f106SStephen Boyd 
139*04648b8fSLuca Weiss 	rrate = DIV_ROUND_UP(req->rate, req->best_parent_rate) * req->best_parent_rate;
140b3f2f106SStephen Boyd 	if (rrate > hd->max_rate)
141*04648b8fSLuca Weiss 		rrate -= req->best_parent_rate;
142b3f2f106SStephen Boyd 
143*04648b8fSLuca Weiss 	req->rate = rrate;
144*04648b8fSLuca Weiss 	return 0;
145b3f2f106SStephen Boyd }
146b3f2f106SStephen Boyd 
147b3f2f106SStephen Boyd /*
148b3f2f106SStephen Boyd  * For optimization reasons, assumes no downstream clocks are actively using
149b3f2f106SStephen Boyd  * it.
150b3f2f106SStephen Boyd  */
clk_hfpll_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)151b3f2f106SStephen Boyd static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate,
152b3f2f106SStephen Boyd 			      unsigned long parent_rate)
153b3f2f106SStephen Boyd {
154b3f2f106SStephen Boyd 	struct clk_hfpll *h = to_clk_hfpll(hw);
155b3f2f106SStephen Boyd 	struct hfpll_data const *hd = h->d;
156b3f2f106SStephen Boyd 	struct regmap *regmap = h->clkr.regmap;
157b3f2f106SStephen Boyd 	unsigned long flags;
158b3f2f106SStephen Boyd 	u32 l_val, val;
159b3f2f106SStephen Boyd 	bool enabled;
160b3f2f106SStephen Boyd 
161b3f2f106SStephen Boyd 	l_val = rate / parent_rate;
162b3f2f106SStephen Boyd 
163b3f2f106SStephen Boyd 	spin_lock_irqsave(&h->lock, flags);
164b3f2f106SStephen Boyd 
165b3f2f106SStephen Boyd 	enabled = __clk_is_enabled(hw->clk);
166b3f2f106SStephen Boyd 	if (enabled)
167b3f2f106SStephen Boyd 		__clk_hfpll_disable(h);
168b3f2f106SStephen Boyd 
169b3f2f106SStephen Boyd 	/* Pick the right VCO. */
170b3f2f106SStephen Boyd 	if (hd->user_reg && hd->user_vco_mask) {
171b3f2f106SStephen Boyd 		regmap_read(regmap, hd->user_reg, &val);
172b3f2f106SStephen Boyd 		if (rate <= hd->low_vco_max_rate)
173b3f2f106SStephen Boyd 			val &= ~hd->user_vco_mask;
174b3f2f106SStephen Boyd 		else
175b3f2f106SStephen Boyd 			val |= hd->user_vco_mask;
176b3f2f106SStephen Boyd 		regmap_write(regmap, hd->user_reg, val);
177b3f2f106SStephen Boyd 	}
178b3f2f106SStephen Boyd 
179b3f2f106SStephen Boyd 	regmap_write(regmap, hd->l_reg, l_val);
180b3f2f106SStephen Boyd 
181b3f2f106SStephen Boyd 	if (enabled)
182b3f2f106SStephen Boyd 		__clk_hfpll_enable(hw);
183b3f2f106SStephen Boyd 
184b3f2f106SStephen Boyd 	spin_unlock_irqrestore(&h->lock, flags);
185b3f2f106SStephen Boyd 
186b3f2f106SStephen Boyd 	return 0;
187b3f2f106SStephen Boyd }
188b3f2f106SStephen Boyd 
clk_hfpll_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)189b3f2f106SStephen Boyd static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw,
190b3f2f106SStephen Boyd 					   unsigned long parent_rate)
191b3f2f106SStephen Boyd {
192b3f2f106SStephen Boyd 	struct clk_hfpll *h = to_clk_hfpll(hw);
193b3f2f106SStephen Boyd 	struct hfpll_data const *hd = h->d;
194b3f2f106SStephen Boyd 	struct regmap *regmap = h->clkr.regmap;
195b3f2f106SStephen Boyd 	u32 l_val;
196b3f2f106SStephen Boyd 
197b3f2f106SStephen Boyd 	regmap_read(regmap, hd->l_reg, &l_val);
198b3f2f106SStephen Boyd 
199b3f2f106SStephen Boyd 	return l_val * parent_rate;
200b3f2f106SStephen Boyd }
201b3f2f106SStephen Boyd 
clk_hfpll_init(struct clk_hw * hw)20289d079dcSJerome Brunet static int clk_hfpll_init(struct clk_hw *hw)
203b3f2f106SStephen Boyd {
204b3f2f106SStephen Boyd 	struct clk_hfpll *h = to_clk_hfpll(hw);
205b3f2f106SStephen Boyd 	struct hfpll_data const *hd = h->d;
206b3f2f106SStephen Boyd 	struct regmap *regmap = h->clkr.regmap;
207b3f2f106SStephen Boyd 	u32 mode, status;
208b3f2f106SStephen Boyd 
209b3f2f106SStephen Boyd 	regmap_read(regmap, hd->mode_reg, &mode);
210b3f2f106SStephen Boyd 	if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) {
211b3f2f106SStephen Boyd 		__clk_hfpll_init_once(hw);
21289d079dcSJerome Brunet 		return 0;
213b3f2f106SStephen Boyd 	}
214b3f2f106SStephen Boyd 
215b3f2f106SStephen Boyd 	if (hd->status_reg) {
216b3f2f106SStephen Boyd 		regmap_read(regmap, hd->status_reg, &status);
217b3f2f106SStephen Boyd 		if (!(status & BIT(hd->lock_bit))) {
218b3f2f106SStephen Boyd 			WARN(1, "HFPLL %s is ON, but not locked!\n",
219b3f2f106SStephen Boyd 			     __clk_get_name(hw->clk));
220b3f2f106SStephen Boyd 			clk_hfpll_disable(hw);
221b3f2f106SStephen Boyd 			__clk_hfpll_init_once(hw);
222b3f2f106SStephen Boyd 		}
223b3f2f106SStephen Boyd 	}
22489d079dcSJerome Brunet 
22589d079dcSJerome Brunet 	return 0;
226b3f2f106SStephen Boyd }
227b3f2f106SStephen Boyd 
hfpll_is_enabled(struct clk_hw * hw)228b3f2f106SStephen Boyd static int hfpll_is_enabled(struct clk_hw *hw)
229b3f2f106SStephen Boyd {
230b3f2f106SStephen Boyd 	struct clk_hfpll *h = to_clk_hfpll(hw);
231b3f2f106SStephen Boyd 	struct hfpll_data const *hd = h->d;
232b3f2f106SStephen Boyd 	struct regmap *regmap = h->clkr.regmap;
233b3f2f106SStephen Boyd 	u32 mode;
234b3f2f106SStephen Boyd 
235b3f2f106SStephen Boyd 	regmap_read(regmap, hd->mode_reg, &mode);
236b3f2f106SStephen Boyd 	mode &= 0x7;
237b3f2f106SStephen Boyd 	return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL);
238b3f2f106SStephen Boyd }
239b3f2f106SStephen Boyd 
240b3f2f106SStephen Boyd const struct clk_ops clk_ops_hfpll = {
241b3f2f106SStephen Boyd 	.enable = clk_hfpll_enable,
242b3f2f106SStephen Boyd 	.disable = clk_hfpll_disable,
243b3f2f106SStephen Boyd 	.is_enabled = hfpll_is_enabled,
244*04648b8fSLuca Weiss 	.determine_rate = clk_hfpll_determine_rate,
245b3f2f106SStephen Boyd 	.set_rate = clk_hfpll_set_rate,
246b3f2f106SStephen Boyd 	.recalc_rate = clk_hfpll_recalc_rate,
247b3f2f106SStephen Boyd 	.init = clk_hfpll_init,
248b3f2f106SStephen Boyd };
249b3f2f106SStephen Boyd EXPORT_SYMBOL_GPL(clk_ops_hfpll);
250