1*9c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29e263131SStephen Boyd /*
39e263131SStephen Boyd * Copyright (c) 2013, The Linux Foundation. All rights reserved.
49e263131SStephen Boyd */
59e263131SStephen Boyd
69e263131SStephen Boyd #include <linux/kernel.h>
79e263131SStephen Boyd #include <linux/bitops.h>
89e263131SStephen Boyd #include <linux/err.h>
99e263131SStephen Boyd #include <linux/bug.h>
109e263131SStephen Boyd #include <linux/delay.h>
119e263131SStephen Boyd #include <linux/export.h>
129e263131SStephen Boyd #include <linux/clk-provider.h>
139e263131SStephen Boyd #include <linux/regmap.h>
149e263131SStephen Boyd
159e263131SStephen Boyd #include <asm/div64.h>
169e263131SStephen Boyd
179e263131SStephen Boyd #include "clk-pll.h"
18400d9fdaSRajendra Nayak #include "common.h"
199e263131SStephen Boyd
209e263131SStephen Boyd #define PLL_OUTCTRL BIT(0)
219e263131SStephen Boyd #define PLL_BYPASSNL BIT(1)
229e263131SStephen Boyd #define PLL_RESET_N BIT(2)
239e263131SStephen Boyd
clk_pll_enable(struct clk_hw * hw)249e263131SStephen Boyd static int clk_pll_enable(struct clk_hw *hw)
259e263131SStephen Boyd {
269e263131SStephen Boyd struct clk_pll *pll = to_clk_pll(hw);
279e263131SStephen Boyd int ret;
289e263131SStephen Boyd u32 mask, val;
299e263131SStephen Boyd
309e263131SStephen Boyd mask = PLL_OUTCTRL | PLL_RESET_N | PLL_BYPASSNL;
319e263131SStephen Boyd ret = regmap_read(pll->clkr.regmap, pll->mode_reg, &val);
329e263131SStephen Boyd if (ret)
339e263131SStephen Boyd return ret;
349e263131SStephen Boyd
359e263131SStephen Boyd /* Skip if already enabled or in FSM mode */
369e263131SStephen Boyd if ((val & mask) == mask || val & PLL_VOTE_FSM_ENA)
379e263131SStephen Boyd return 0;
389e263131SStephen Boyd
399e263131SStephen Boyd /* Disable PLL bypass mode. */
409e263131SStephen Boyd ret = regmap_update_bits(pll->clkr.regmap, pll->mode_reg, PLL_BYPASSNL,
419e263131SStephen Boyd PLL_BYPASSNL);
429e263131SStephen Boyd if (ret)
439e263131SStephen Boyd return ret;
449e263131SStephen Boyd
459e263131SStephen Boyd /*
469e263131SStephen Boyd * H/W requires a 5us delay between disabling the bypass and
479e263131SStephen Boyd * de-asserting the reset. Delay 10us just to be safe.
489e263131SStephen Boyd */
499e263131SStephen Boyd udelay(10);
509e263131SStephen Boyd
519e263131SStephen Boyd /* De-assert active-low PLL reset. */
529e263131SStephen Boyd ret = regmap_update_bits(pll->clkr.regmap, pll->mode_reg, PLL_RESET_N,
539e263131SStephen Boyd PLL_RESET_N);
549e263131SStephen Boyd if (ret)
559e263131SStephen Boyd return ret;
569e263131SStephen Boyd
579e263131SStephen Boyd /* Wait until PLL is locked. */
589e263131SStephen Boyd udelay(50);
599e263131SStephen Boyd
609e263131SStephen Boyd /* Enable PLL output. */
61d41bd923SFengguang Wu return regmap_update_bits(pll->clkr.regmap, pll->mode_reg, PLL_OUTCTRL,
629e263131SStephen Boyd PLL_OUTCTRL);
639e263131SStephen Boyd }
649e263131SStephen Boyd
clk_pll_disable(struct clk_hw * hw)659e263131SStephen Boyd static void clk_pll_disable(struct clk_hw *hw)
669e263131SStephen Boyd {
679e263131SStephen Boyd struct clk_pll *pll = to_clk_pll(hw);
689e263131SStephen Boyd u32 mask;
699e263131SStephen Boyd u32 val;
709e263131SStephen Boyd
719e263131SStephen Boyd regmap_read(pll->clkr.regmap, pll->mode_reg, &val);
729e263131SStephen Boyd /* Skip if in FSM mode */
739e263131SStephen Boyd if (val & PLL_VOTE_FSM_ENA)
749e263131SStephen Boyd return;
759e263131SStephen Boyd mask = PLL_OUTCTRL | PLL_RESET_N | PLL_BYPASSNL;
769e263131SStephen Boyd regmap_update_bits(pll->clkr.regmap, pll->mode_reg, mask, 0);
779e263131SStephen Boyd }
789e263131SStephen Boyd
799e263131SStephen Boyd static unsigned long
clk_pll_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)809e263131SStephen Boyd clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
819e263131SStephen Boyd {
829e263131SStephen Boyd struct clk_pll *pll = to_clk_pll(hw);
83ae3669acSStephen Boyd u32 l, m, n, config;
849e263131SStephen Boyd unsigned long rate;
859e263131SStephen Boyd u64 tmp;
869e263131SStephen Boyd
879e263131SStephen Boyd regmap_read(pll->clkr.regmap, pll->l_reg, &l);
889e263131SStephen Boyd regmap_read(pll->clkr.regmap, pll->m_reg, &m);
899e263131SStephen Boyd regmap_read(pll->clkr.regmap, pll->n_reg, &n);
909e263131SStephen Boyd
919e263131SStephen Boyd l &= 0x3ff;
929e263131SStephen Boyd m &= 0x7ffff;
939e263131SStephen Boyd n &= 0x7ffff;
949e263131SStephen Boyd
959e263131SStephen Boyd rate = parent_rate * l;
969e263131SStephen Boyd if (n) {
979e263131SStephen Boyd tmp = parent_rate;
989e263131SStephen Boyd tmp *= m;
999e263131SStephen Boyd do_div(tmp, n);
1009e263131SStephen Boyd rate += tmp;
1019e263131SStephen Boyd }
102ae3669acSStephen Boyd if (pll->post_div_width) {
103ae3669acSStephen Boyd regmap_read(pll->clkr.regmap, pll->config_reg, &config);
104ae3669acSStephen Boyd config >>= pll->post_div_shift;
105ae3669acSStephen Boyd config &= BIT(pll->post_div_width) - 1;
106ae3669acSStephen Boyd rate /= config + 1;
107ae3669acSStephen Boyd }
108ae3669acSStephen Boyd
1099e263131SStephen Boyd return rate;
1109e263131SStephen Boyd }
1119e263131SStephen Boyd
112ae3669acSStephen Boyd static const
find_freq(const struct pll_freq_tbl * f,unsigned long rate)113ae3669acSStephen Boyd struct pll_freq_tbl *find_freq(const struct pll_freq_tbl *f, unsigned long rate)
114ae3669acSStephen Boyd {
115ae3669acSStephen Boyd if (!f)
116ae3669acSStephen Boyd return NULL;
117ae3669acSStephen Boyd
118ae3669acSStephen Boyd for (; f->freq; f++)
119ae3669acSStephen Boyd if (rate <= f->freq)
120ae3669acSStephen Boyd return f;
121ae3669acSStephen Boyd
122ae3669acSStephen Boyd return NULL;
123ae3669acSStephen Boyd }
124ae3669acSStephen Boyd
1250817b62cSBoris Brezillon static int
clk_pll_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)1260817b62cSBoris Brezillon clk_pll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
127ae3669acSStephen Boyd {
128ae3669acSStephen Boyd struct clk_pll *pll = to_clk_pll(hw);
129ae3669acSStephen Boyd const struct pll_freq_tbl *f;
130ae3669acSStephen Boyd
1310817b62cSBoris Brezillon f = find_freq(pll->freq_tbl, req->rate);
1320817b62cSBoris Brezillon if (!f)
1330817b62cSBoris Brezillon req->rate = clk_pll_recalc_rate(hw, req->best_parent_rate);
1340817b62cSBoris Brezillon else
1350817b62cSBoris Brezillon req->rate = f->freq;
1360817b62cSBoris Brezillon
1370817b62cSBoris Brezillon return 0;
138ae3669acSStephen Boyd }
139ae3669acSStephen Boyd
140ae3669acSStephen Boyd static int
clk_pll_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long p_rate)141ae3669acSStephen Boyd clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long p_rate)
142ae3669acSStephen Boyd {
143ae3669acSStephen Boyd struct clk_pll *pll = to_clk_pll(hw);
144ae3669acSStephen Boyd const struct pll_freq_tbl *f;
145ae3669acSStephen Boyd bool enabled;
146ae3669acSStephen Boyd u32 mode;
147ae3669acSStephen Boyd u32 enable_mask = PLL_OUTCTRL | PLL_BYPASSNL | PLL_RESET_N;
148ae3669acSStephen Boyd
149ae3669acSStephen Boyd f = find_freq(pll->freq_tbl, rate);
150ae3669acSStephen Boyd if (!f)
151ae3669acSStephen Boyd return -EINVAL;
152ae3669acSStephen Boyd
153ae3669acSStephen Boyd regmap_read(pll->clkr.regmap, pll->mode_reg, &mode);
154ae3669acSStephen Boyd enabled = (mode & enable_mask) == enable_mask;
155ae3669acSStephen Boyd
156ae3669acSStephen Boyd if (enabled)
157ae3669acSStephen Boyd clk_pll_disable(hw);
158ae3669acSStephen Boyd
159ae3669acSStephen Boyd regmap_update_bits(pll->clkr.regmap, pll->l_reg, 0x3ff, f->l);
160ae3669acSStephen Boyd regmap_update_bits(pll->clkr.regmap, pll->m_reg, 0x7ffff, f->m);
161ae3669acSStephen Boyd regmap_update_bits(pll->clkr.regmap, pll->n_reg, 0x7ffff, f->n);
162ae3669acSStephen Boyd regmap_write(pll->clkr.regmap, pll->config_reg, f->ibits);
163ae3669acSStephen Boyd
164ae3669acSStephen Boyd if (enabled)
165ae3669acSStephen Boyd clk_pll_enable(hw);
166ae3669acSStephen Boyd
167ae3669acSStephen Boyd return 0;
168ae3669acSStephen Boyd }
169ae3669acSStephen Boyd
1709e263131SStephen Boyd const struct clk_ops clk_pll_ops = {
1719e263131SStephen Boyd .enable = clk_pll_enable,
1729e263131SStephen Boyd .disable = clk_pll_disable,
1739e263131SStephen Boyd .recalc_rate = clk_pll_recalc_rate,
174ae3669acSStephen Boyd .determine_rate = clk_pll_determine_rate,
175ae3669acSStephen Boyd .set_rate = clk_pll_set_rate,
1769e263131SStephen Boyd };
1779e263131SStephen Boyd EXPORT_SYMBOL_GPL(clk_pll_ops);
1789e263131SStephen Boyd
wait_for_pll(struct clk_pll * pll)1799e263131SStephen Boyd static int wait_for_pll(struct clk_pll *pll)
1809e263131SStephen Boyd {
1819e263131SStephen Boyd u32 val;
1829e263131SStephen Boyd int count;
1839e263131SStephen Boyd int ret;
184ac269395SStephen Boyd const char *name = clk_hw_get_name(&pll->clkr.hw);
1859e263131SStephen Boyd
1869e263131SStephen Boyd /* Wait for pll to enable. */
1879e263131SStephen Boyd for (count = 200; count > 0; count--) {
1889e263131SStephen Boyd ret = regmap_read(pll->clkr.regmap, pll->status_reg, &val);
1899e263131SStephen Boyd if (ret)
1909e263131SStephen Boyd return ret;
1919e263131SStephen Boyd if (val & BIT(pll->status_bit))
1929e263131SStephen Boyd return 0;
1939e263131SStephen Boyd udelay(1);
1949e263131SStephen Boyd }
1959e263131SStephen Boyd
1969e263131SStephen Boyd WARN(1, "%s didn't enable after voting for it!\n", name);
1979e263131SStephen Boyd return -ETIMEDOUT;
1989e263131SStephen Boyd }
1999e263131SStephen Boyd
clk_pll_vote_enable(struct clk_hw * hw)2009e263131SStephen Boyd static int clk_pll_vote_enable(struct clk_hw *hw)
2019e263131SStephen Boyd {
2029e263131SStephen Boyd int ret;
203ac269395SStephen Boyd struct clk_pll *p = to_clk_pll(clk_hw_get_parent(hw));
2049e263131SStephen Boyd
2059e263131SStephen Boyd ret = clk_enable_regmap(hw);
2069e263131SStephen Boyd if (ret)
2079e263131SStephen Boyd return ret;
2089e263131SStephen Boyd
2099e263131SStephen Boyd return wait_for_pll(p);
2109e263131SStephen Boyd }
2119e263131SStephen Boyd
2129e263131SStephen Boyd const struct clk_ops clk_pll_vote_ops = {
2139e263131SStephen Boyd .enable = clk_pll_vote_enable,
2149e263131SStephen Boyd .disable = clk_disable_regmap,
2159e263131SStephen Boyd };
2169e263131SStephen Boyd EXPORT_SYMBOL_GPL(clk_pll_vote_ops);
2179e263131SStephen Boyd
clk_pll_configure(struct clk_pll * pll,struct regmap * regmap,const struct pll_config * config)2189e263131SStephen Boyd static void clk_pll_configure(struct clk_pll *pll, struct regmap *regmap,
2199e263131SStephen Boyd const struct pll_config *config)
2209e263131SStephen Boyd {
2219e263131SStephen Boyd u32 val;
2229e263131SStephen Boyd u32 mask;
2239e263131SStephen Boyd
2249e263131SStephen Boyd regmap_write(regmap, pll->l_reg, config->l);
2259e263131SStephen Boyd regmap_write(regmap, pll->m_reg, config->m);
2269e263131SStephen Boyd regmap_write(regmap, pll->n_reg, config->n);
2279e263131SStephen Boyd
2289e263131SStephen Boyd val = config->vco_val;
2299e263131SStephen Boyd val |= config->pre_div_val;
2309e263131SStephen Boyd val |= config->post_div_val;
2319e263131SStephen Boyd val |= config->mn_ena_mask;
2329e263131SStephen Boyd val |= config->main_output_mask;
2339e263131SStephen Boyd val |= config->aux_output_mask;
2349e263131SStephen Boyd
2359e263131SStephen Boyd mask = config->vco_mask;
2369e263131SStephen Boyd mask |= config->pre_div_mask;
2379e263131SStephen Boyd mask |= config->post_div_mask;
2389e263131SStephen Boyd mask |= config->mn_ena_mask;
2399e263131SStephen Boyd mask |= config->main_output_mask;
2409e263131SStephen Boyd mask |= config->aux_output_mask;
2419e263131SStephen Boyd
2429e263131SStephen Boyd regmap_update_bits(regmap, pll->config_reg, mask, val);
2439e263131SStephen Boyd }
2449e263131SStephen Boyd
clk_pll_configure_sr(struct clk_pll * pll,struct regmap * regmap,const struct pll_config * config,bool fsm_mode)245d8c25d3aSStephen Boyd void clk_pll_configure_sr(struct clk_pll *pll, struct regmap *regmap,
246d8c25d3aSStephen Boyd const struct pll_config *config, bool fsm_mode)
247d8c25d3aSStephen Boyd {
248d8c25d3aSStephen Boyd clk_pll_configure(pll, regmap, config);
249d8c25d3aSStephen Boyd if (fsm_mode)
250400d9fdaSRajendra Nayak qcom_pll_set_fsm_mode(regmap, pll->mode_reg, 1, 8);
251d8c25d3aSStephen Boyd }
252d8c25d3aSStephen Boyd EXPORT_SYMBOL_GPL(clk_pll_configure_sr);
253d8c25d3aSStephen Boyd
clk_pll_configure_sr_hpm_lp(struct clk_pll * pll,struct regmap * regmap,const struct pll_config * config,bool fsm_mode)2549e263131SStephen Boyd void clk_pll_configure_sr_hpm_lp(struct clk_pll *pll, struct regmap *regmap,
2559e263131SStephen Boyd const struct pll_config *config, bool fsm_mode)
2569e263131SStephen Boyd {
2579e263131SStephen Boyd clk_pll_configure(pll, regmap, config);
2589e263131SStephen Boyd if (fsm_mode)
259400d9fdaSRajendra Nayak qcom_pll_set_fsm_mode(regmap, pll->mode_reg, 1, 0);
2609e263131SStephen Boyd }
2619e263131SStephen Boyd EXPORT_SYMBOL_GPL(clk_pll_configure_sr_hpm_lp);
262d4f76de3SGeorgi Djakov
clk_pll_sr2_enable(struct clk_hw * hw)263d4f76de3SGeorgi Djakov static int clk_pll_sr2_enable(struct clk_hw *hw)
264d4f76de3SGeorgi Djakov {
265d4f76de3SGeorgi Djakov struct clk_pll *pll = to_clk_pll(hw);
266d4f76de3SGeorgi Djakov int ret;
267d4f76de3SGeorgi Djakov u32 mode;
268d4f76de3SGeorgi Djakov
269d4f76de3SGeorgi Djakov ret = regmap_read(pll->clkr.regmap, pll->mode_reg, &mode);
270d4f76de3SGeorgi Djakov if (ret)
271d4f76de3SGeorgi Djakov return ret;
272d4f76de3SGeorgi Djakov
273d4f76de3SGeorgi Djakov /* Disable PLL bypass mode. */
274d4f76de3SGeorgi Djakov ret = regmap_update_bits(pll->clkr.regmap, pll->mode_reg, PLL_BYPASSNL,
275d4f76de3SGeorgi Djakov PLL_BYPASSNL);
276d4f76de3SGeorgi Djakov if (ret)
277d4f76de3SGeorgi Djakov return ret;
278d4f76de3SGeorgi Djakov
279d4f76de3SGeorgi Djakov /*
280d4f76de3SGeorgi Djakov * H/W requires a 5us delay between disabling the bypass and
281d4f76de3SGeorgi Djakov * de-asserting the reset. Delay 10us just to be safe.
282d4f76de3SGeorgi Djakov */
283d4f76de3SGeorgi Djakov udelay(10);
284d4f76de3SGeorgi Djakov
285d4f76de3SGeorgi Djakov /* De-assert active-low PLL reset. */
286d4f76de3SGeorgi Djakov ret = regmap_update_bits(pll->clkr.regmap, pll->mode_reg, PLL_RESET_N,
287d4f76de3SGeorgi Djakov PLL_RESET_N);
288d4f76de3SGeorgi Djakov if (ret)
289d4f76de3SGeorgi Djakov return ret;
290d4f76de3SGeorgi Djakov
291d4f76de3SGeorgi Djakov ret = wait_for_pll(pll);
292d4f76de3SGeorgi Djakov if (ret)
293d4f76de3SGeorgi Djakov return ret;
294d4f76de3SGeorgi Djakov
295d4f76de3SGeorgi Djakov /* Enable PLL output. */
296d4f76de3SGeorgi Djakov return regmap_update_bits(pll->clkr.regmap, pll->mode_reg, PLL_OUTCTRL,
297d4f76de3SGeorgi Djakov PLL_OUTCTRL);
298d4f76de3SGeorgi Djakov }
299d4f76de3SGeorgi Djakov
300d4f76de3SGeorgi Djakov static int
clk_pll_sr2_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long prate)301d4f76de3SGeorgi Djakov clk_pll_sr2_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long prate)
302d4f76de3SGeorgi Djakov {
303d4f76de3SGeorgi Djakov struct clk_pll *pll = to_clk_pll(hw);
304d4f76de3SGeorgi Djakov const struct pll_freq_tbl *f;
305d4f76de3SGeorgi Djakov bool enabled;
306d4f76de3SGeorgi Djakov u32 mode;
307d4f76de3SGeorgi Djakov u32 enable_mask = PLL_OUTCTRL | PLL_BYPASSNL | PLL_RESET_N;
308d4f76de3SGeorgi Djakov
309d4f76de3SGeorgi Djakov f = find_freq(pll->freq_tbl, rate);
310d4f76de3SGeorgi Djakov if (!f)
311d4f76de3SGeorgi Djakov return -EINVAL;
312d4f76de3SGeorgi Djakov
313d4f76de3SGeorgi Djakov regmap_read(pll->clkr.regmap, pll->mode_reg, &mode);
314d4f76de3SGeorgi Djakov enabled = (mode & enable_mask) == enable_mask;
315d4f76de3SGeorgi Djakov
316d4f76de3SGeorgi Djakov if (enabled)
317d4f76de3SGeorgi Djakov clk_pll_disable(hw);
318d4f76de3SGeorgi Djakov
319d4f76de3SGeorgi Djakov regmap_update_bits(pll->clkr.regmap, pll->l_reg, 0x3ff, f->l);
320d4f76de3SGeorgi Djakov regmap_update_bits(pll->clkr.regmap, pll->m_reg, 0x7ffff, f->m);
321d4f76de3SGeorgi Djakov regmap_update_bits(pll->clkr.regmap, pll->n_reg, 0x7ffff, f->n);
322d4f76de3SGeorgi Djakov
323d4f76de3SGeorgi Djakov if (enabled)
324d4f76de3SGeorgi Djakov clk_pll_sr2_enable(hw);
325d4f76de3SGeorgi Djakov
326d4f76de3SGeorgi Djakov return 0;
327d4f76de3SGeorgi Djakov }
328d4f76de3SGeorgi Djakov
329d4f76de3SGeorgi Djakov const struct clk_ops clk_pll_sr2_ops = {
330d4f76de3SGeorgi Djakov .enable = clk_pll_sr2_enable,
331d4f76de3SGeorgi Djakov .disable = clk_pll_disable,
332d4f76de3SGeorgi Djakov .set_rate = clk_pll_sr2_set_rate,
333d4f76de3SGeorgi Djakov .recalc_rate = clk_pll_recalc_rate,
334d4f76de3SGeorgi Djakov .determine_rate = clk_pll_determine_rate,
335d4f76de3SGeorgi Djakov };
336d4f76de3SGeorgi Djakov EXPORT_SYMBOL_GPL(clk_pll_sr2_ops);
337