xref: /linux/drivers/clk/meson/clk-pll.c (revision c178b003bfcfde5a973c6ba6a45ca60fb1470fc6)
17a29a869SCarlo Caione /*
27a29a869SCarlo Caione  * Copyright (c) 2015 Endless Mobile, Inc.
37a29a869SCarlo Caione  * Author: Carlo Caione <carlo@endlessm.com>
47a29a869SCarlo Caione  *
57a29a869SCarlo Caione  * This program is free software; you can redistribute it and/or modify it
67a29a869SCarlo Caione  * under the terms and conditions of the GNU General Public License,
77a29a869SCarlo Caione  * version 2, as published by the Free Software Foundation.
87a29a869SCarlo Caione  *
97a29a869SCarlo Caione  * This program is distributed in the hope it will be useful, but WITHOUT
107a29a869SCarlo Caione  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
117a29a869SCarlo Caione  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
127a29a869SCarlo Caione  * more details.
137a29a869SCarlo Caione  *
147a29a869SCarlo Caione  * You should have received a copy of the GNU General Public License along with
157a29a869SCarlo Caione  * this program.  If not, see <http://www.gnu.org/licenses/>.
167a29a869SCarlo Caione  */
177a29a869SCarlo Caione 
187a29a869SCarlo Caione /*
197a29a869SCarlo Caione  * In the most basic form, a Meson PLL is composed as follows:
207a29a869SCarlo Caione  *
217a29a869SCarlo Caione  *                     PLL
227a29a869SCarlo Caione  *      +------------------------------+
237a29a869SCarlo Caione  *      |                              |
247a29a869SCarlo Caione  * in -----[ /N ]---[ *M ]---[ >>OD ]----->> out
257a29a869SCarlo Caione  *      |         ^        ^           |
267a29a869SCarlo Caione  *      +------------------------------+
277a29a869SCarlo Caione  *                |        |
287a29a869SCarlo Caione  *               FREF     VCO
297a29a869SCarlo Caione  *
307a29a869SCarlo Caione  * out = (in * M / N) >> OD
317a29a869SCarlo Caione  */
327a29a869SCarlo Caione 
337a29a869SCarlo Caione #include <linux/clk-provider.h>
347a29a869SCarlo Caione #include <linux/delay.h>
357a29a869SCarlo Caione #include <linux/err.h>
367a29a869SCarlo Caione #include <linux/io.h>
3794aa8a41SJerome Brunet #include <linux/math64.h>
387a29a869SCarlo Caione #include <linux/module.h>
397a29a869SCarlo Caione #include <linux/of_address.h>
407a29a869SCarlo Caione #include <linux/slab.h>
417a29a869SCarlo Caione #include <linux/string.h>
427a29a869SCarlo Caione 
437a29a869SCarlo Caione #include "clkc.h"
447a29a869SCarlo Caione 
45722825dcSJerome Brunet static inline struct meson_clk_pll_data *
46722825dcSJerome Brunet meson_clk_pll_data(struct clk_regmap *clk)
47722825dcSJerome Brunet {
48722825dcSJerome Brunet 	return (struct meson_clk_pll_data *)clk->data;
49722825dcSJerome Brunet }
507a29a869SCarlo Caione 
517a29a869SCarlo Caione static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
527a29a869SCarlo Caione 						unsigned long parent_rate)
537a29a869SCarlo Caione {
54722825dcSJerome Brunet 	struct clk_regmap *clk = to_clk_regmap(hw);
55722825dcSJerome Brunet 	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
5694aa8a41SJerome Brunet 	u64 rate;
577d3142e5SJerome Brunet 	u16 n, m, frac = 0, od, od2 = 0, od3 = 0;
587a29a869SCarlo Caione 
59722825dcSJerome Brunet 	n = meson_parm_read(clk->map, &pll->n);
60722825dcSJerome Brunet 	m = meson_parm_read(clk->map, &pll->m);
61722825dcSJerome Brunet 	od = meson_parm_read(clk->map, &pll->od);
627a29a869SCarlo Caione 
63722825dcSJerome Brunet 	if (MESON_PARM_APPLICABLE(&pll->od2))
64722825dcSJerome Brunet 		od2 = meson_parm_read(clk->map, &pll->od2);
657a29a869SCarlo Caione 
66722825dcSJerome Brunet 	if (MESON_PARM_APPLICABLE(&pll->od3))
67722825dcSJerome Brunet 		od3 = meson_parm_read(clk->map, &pll->od3);
687d3142e5SJerome Brunet 
697d3142e5SJerome Brunet 	rate = (u64)m * parent_rate;
7094aa8a41SJerome Brunet 
71722825dcSJerome Brunet 	if (MESON_PARM_APPLICABLE(&pll->frac)) {
72722825dcSJerome Brunet 		frac = meson_parm_read(clk->map, &pll->frac);
737a29a869SCarlo Caione 
74722825dcSJerome Brunet 		rate += mul_u64_u32_shr(parent_rate, frac, pll->frac.width);
7594aa8a41SJerome Brunet 	}
7694aa8a41SJerome Brunet 
777d3142e5SJerome Brunet 	return div_u64(rate, n) >> od >> od2 >> od3;
787a29a869SCarlo Caione }
797a29a869SCarlo Caione 
807a29a869SCarlo Caione static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
817a29a869SCarlo Caione 				     unsigned long *parent_rate)
827a29a869SCarlo Caione {
83722825dcSJerome Brunet 	struct clk_regmap *clk = to_clk_regmap(hw);
84722825dcSJerome Brunet 	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
85722825dcSJerome Brunet 	const struct pll_rate_table *pllt;
867a29a869SCarlo Caione 
87840e1a73SJerome Brunet 	/*
88840e1a73SJerome Brunet 	 * if the table is missing, just return the current rate
89840e1a73SJerome Brunet 	 * since we don't have the other available frequencies
90840e1a73SJerome Brunet 	 */
91722825dcSJerome Brunet 	if (!pll->table)
92840e1a73SJerome Brunet 		return meson_clk_pll_recalc_rate(hw, *parent_rate);
93840e1a73SJerome Brunet 
94722825dcSJerome Brunet 	for (pllt = pll->table; pllt->rate; pllt++) {
95722825dcSJerome Brunet 		if (rate <= pllt->rate)
96722825dcSJerome Brunet 			return pllt->rate;
977a29a869SCarlo Caione 	}
987a29a869SCarlo Caione 
997a29a869SCarlo Caione 	/* else return the smallest value */
100722825dcSJerome Brunet 	return pll->table[0].rate;
1017a29a869SCarlo Caione }
1027a29a869SCarlo Caione 
103722825dcSJerome Brunet static const struct pll_rate_table *
104722825dcSJerome Brunet meson_clk_get_pll_settings(const struct pll_rate_table *table,
1057a29a869SCarlo Caione 			   unsigned long rate)
1067a29a869SCarlo Caione {
107722825dcSJerome Brunet 	const struct pll_rate_table *pllt;
1087a29a869SCarlo Caione 
109722825dcSJerome Brunet 	if (!table)
110840e1a73SJerome Brunet 		return NULL;
111840e1a73SJerome Brunet 
112722825dcSJerome Brunet 	for (pllt = table; pllt->rate; pllt++) {
113722825dcSJerome Brunet 		if (rate == pllt->rate)
114722825dcSJerome Brunet 			return pllt;
1157a29a869SCarlo Caione 	}
116722825dcSJerome Brunet 
1177a29a869SCarlo Caione 	return NULL;
1187a29a869SCarlo Caione }
1197a29a869SCarlo Caione 
120722825dcSJerome Brunet static int meson_clk_pll_wait_lock(struct clk_hw *hw)
121722825dcSJerome Brunet {
122722825dcSJerome Brunet 	struct clk_regmap *clk = to_clk_regmap(hw);
123722825dcSJerome Brunet 	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
124*c178b003SJerome Brunet 	int delay = 24000000;
125722825dcSJerome Brunet 
126722825dcSJerome Brunet 	do {
127722825dcSJerome Brunet 		/* Is the clock locked now ? */
128722825dcSJerome Brunet 		if (meson_parm_read(clk->map, &pll->l))
129722825dcSJerome Brunet 			return 0;
130722825dcSJerome Brunet 
131722825dcSJerome Brunet 		delay--;
132722825dcSJerome Brunet 	} while (delay > 0);
133722825dcSJerome Brunet 
13445fcbec7SNeil Armstrong 	return -ETIMEDOUT;
13545fcbec7SNeil Armstrong }
13645fcbec7SNeil Armstrong 
137722825dcSJerome Brunet static void meson_clk_pll_init(struct clk_hw *hw)
1387a29a869SCarlo Caione {
139722825dcSJerome Brunet 	struct clk_regmap *clk = to_clk_regmap(hw);
140722825dcSJerome Brunet 	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
1417a29a869SCarlo Caione 
142722825dcSJerome Brunet 	if (pll->init_count) {
143722825dcSJerome Brunet 		meson_parm_write(clk->map, &pll->rst, 1);
144722825dcSJerome Brunet 		regmap_multi_reg_write(clk->map, pll->init_regs,
145722825dcSJerome Brunet 				       pll->init_count);
146722825dcSJerome Brunet 		meson_parm_write(clk->map, &pll->rst, 0);
1477a29a869SCarlo Caione 	}
14845fcbec7SNeil Armstrong }
14945fcbec7SNeil Armstrong 
1507a29a869SCarlo Caione static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
1517a29a869SCarlo Caione 				  unsigned long parent_rate)
1527a29a869SCarlo Caione {
153722825dcSJerome Brunet 	struct clk_regmap *clk = to_clk_regmap(hw);
154722825dcSJerome Brunet 	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
155722825dcSJerome Brunet 	const struct pll_rate_table *pllt;
1567a29a869SCarlo Caione 	unsigned long old_rate;
1577a29a869SCarlo Caione 
1587a29a869SCarlo Caione 	if (parent_rate == 0 || rate == 0)
1597a29a869SCarlo Caione 		return -EINVAL;
1607a29a869SCarlo Caione 
1617a29a869SCarlo Caione 	old_rate = rate;
1627a29a869SCarlo Caione 
163722825dcSJerome Brunet 	pllt = meson_clk_get_pll_settings(pll->table, rate);
164722825dcSJerome Brunet 	if (!pllt)
1657a29a869SCarlo Caione 		return -EINVAL;
1667a29a869SCarlo Caione 
167722825dcSJerome Brunet 	/* Put the pll in reset to write the params */
168722825dcSJerome Brunet 	meson_parm_write(clk->map, &pll->rst, 1);
16945fcbec7SNeil Armstrong 
170722825dcSJerome Brunet 	meson_parm_write(clk->map, &pll->n, pllt->n);
171722825dcSJerome Brunet 	meson_parm_write(clk->map, &pll->m, pllt->m);
172722825dcSJerome Brunet 	meson_parm_write(clk->map, &pll->od, pllt->od);
1737a29a869SCarlo Caione 
174722825dcSJerome Brunet 	if (MESON_PARM_APPLICABLE(&pll->od2))
175722825dcSJerome Brunet 		meson_parm_write(clk->map, &pll->od2, pllt->od2);
1767a29a869SCarlo Caione 
177722825dcSJerome Brunet 	if (MESON_PARM_APPLICABLE(&pll->od3))
178722825dcSJerome Brunet 		meson_parm_write(clk->map, &pll->od3, pllt->od3);
1797a29a869SCarlo Caione 
180722825dcSJerome Brunet 	if (MESON_PARM_APPLICABLE(&pll->frac))
181722825dcSJerome Brunet 		meson_parm_write(clk->map, &pll->frac, pllt->frac);
1827a29a869SCarlo Caione 
183722825dcSJerome Brunet 	/* make sure the reset is cleared at this point */
184722825dcSJerome Brunet 	meson_parm_write(clk->map, &pll->rst, 0);
1854a472951SMichael Turquette 
186722825dcSJerome Brunet 	if (meson_clk_pll_wait_lock(hw)) {
1877a29a869SCarlo Caione 		pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
1887a29a869SCarlo Caione 			__func__, old_rate);
189722825dcSJerome Brunet 		/*
190722825dcSJerome Brunet 		 * FIXME: Do we really need/want this HACK ?
191722825dcSJerome Brunet 		 * It looks unsafe. what happens if the clock gets into a
192722825dcSJerome Brunet 		 * broken state and we can't lock back on the old_rate ? Looks
193722825dcSJerome Brunet 		 * like an infinite recursion is possible
194722825dcSJerome Brunet 		 */
1957a29a869SCarlo Caione 		meson_clk_pll_set_rate(hw, old_rate, parent_rate);
1967a29a869SCarlo Caione 	}
1977a29a869SCarlo Caione 
198722825dcSJerome Brunet 	return 0;
1997a29a869SCarlo Caione }
2007a29a869SCarlo Caione 
201ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ops = {
202722825dcSJerome Brunet 	.init		= meson_clk_pll_init,
2037a29a869SCarlo Caione 	.recalc_rate	= meson_clk_pll_recalc_rate,
2047a29a869SCarlo Caione 	.round_rate	= meson_clk_pll_round_rate,
2057a29a869SCarlo Caione 	.set_rate	= meson_clk_pll_set_rate,
2067a29a869SCarlo Caione };
2077a29a869SCarlo Caione 
208ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ro_ops = {
2097a29a869SCarlo Caione 	.recalc_rate	= meson_clk_pll_recalc_rate,
2107a29a869SCarlo Caione };
211