xref: /openbmc/linux/drivers/clk/qcom/clk-pll.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
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