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