xref: /linux/drivers/clk/meson/clk-pll.c (revision 45fcbec70c084631dc430810dad14a7ece5000b8)
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>
377a29a869SCarlo Caione #include <linux/module.h>
387a29a869SCarlo Caione #include <linux/of_address.h>
397a29a869SCarlo Caione #include <linux/slab.h>
407a29a869SCarlo Caione #include <linux/string.h>
417a29a869SCarlo Caione 
427a29a869SCarlo Caione #include "clkc.h"
437a29a869SCarlo Caione 
447a29a869SCarlo Caione #define MESON_PLL_RESET				BIT(29)
457a29a869SCarlo Caione #define MESON_PLL_LOCK				BIT(31)
467a29a869SCarlo Caione 
477a29a869SCarlo Caione #define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw)
487a29a869SCarlo Caione 
497a29a869SCarlo Caione static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
507a29a869SCarlo Caione 						unsigned long parent_rate)
517a29a869SCarlo Caione {
527a29a869SCarlo Caione 	struct meson_clk_pll *pll = to_meson_clk_pll(hw);
537a29a869SCarlo Caione 	struct parm *p;
547a29a869SCarlo Caione 	unsigned long parent_rate_mhz = parent_rate / 1000000;
557a29a869SCarlo Caione 	unsigned long rate_mhz;
564a472951SMichael Turquette 	u16 n, m, frac = 0, od, od2 = 0;
577a29a869SCarlo Caione 	u32 reg;
587a29a869SCarlo Caione 
59ec623f2aSMichael Turquette 	p = &pll->n;
607a29a869SCarlo Caione 	reg = readl(pll->base + p->reg_off);
617a29a869SCarlo Caione 	n = PARM_GET(p->width, p->shift, reg);
627a29a869SCarlo Caione 
63ec623f2aSMichael Turquette 	p = &pll->m;
647a29a869SCarlo Caione 	reg = readl(pll->base + p->reg_off);
657a29a869SCarlo Caione 	m = PARM_GET(p->width, p->shift, reg);
667a29a869SCarlo Caione 
67ec623f2aSMichael Turquette 	p = &pll->od;
687a29a869SCarlo Caione 	reg = readl(pll->base + p->reg_off);
697a29a869SCarlo Caione 	od = PARM_GET(p->width, p->shift, reg);
707a29a869SCarlo Caione 
714a472951SMichael Turquette 	p = &pll->od2;
724a472951SMichael Turquette 	if (p->width) {
734a472951SMichael Turquette 		reg = readl(pll->base + p->reg_off);
744a472951SMichael Turquette 		od2 = PARM_GET(p->width, p->shift, reg);
754a472951SMichael Turquette 	}
764a472951SMichael Turquette 
774a472951SMichael Turquette 	p = &pll->frac;
784a472951SMichael Turquette 	if (p->width) {
794a472951SMichael Turquette 		reg = readl(pll->base + p->reg_off);
804a472951SMichael Turquette 		frac = PARM_GET(p->width, p->shift, reg);
814a472951SMichael Turquette 		rate_mhz = (parent_rate_mhz * m + \
824a472951SMichael Turquette 				(parent_rate_mhz * frac >> 12)) * 2 / n;
834a472951SMichael Turquette 		rate_mhz = rate_mhz >> od >> od2;
844a472951SMichael Turquette 	} else
854a472951SMichael Turquette 		rate_mhz = (parent_rate_mhz * m / n) >> od >> od2;
867a29a869SCarlo Caione 
877a29a869SCarlo Caione 	return rate_mhz * 1000000;
887a29a869SCarlo Caione }
897a29a869SCarlo Caione 
907a29a869SCarlo Caione static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
917a29a869SCarlo Caione 				     unsigned long *parent_rate)
927a29a869SCarlo Caione {
937a29a869SCarlo Caione 	struct meson_clk_pll *pll = to_meson_clk_pll(hw);
94ec623f2aSMichael Turquette 	const struct pll_rate_table *rate_table = pll->rate_table;
957a29a869SCarlo Caione 	int i;
967a29a869SCarlo Caione 
977a29a869SCarlo Caione 	for (i = 0; i < pll->rate_count; i++) {
987a29a869SCarlo Caione 		if (rate <= rate_table[i].rate)
997a29a869SCarlo Caione 			return rate_table[i].rate;
1007a29a869SCarlo Caione 	}
1017a29a869SCarlo Caione 
1027a29a869SCarlo Caione 	/* else return the smallest value */
1037a29a869SCarlo Caione 	return rate_table[0].rate;
1047a29a869SCarlo Caione }
1057a29a869SCarlo Caione 
1067a29a869SCarlo Caione static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll,
1077a29a869SCarlo Caione 							       unsigned long rate)
1087a29a869SCarlo Caione {
109ec623f2aSMichael Turquette 	const struct pll_rate_table *rate_table = pll->rate_table;
1107a29a869SCarlo Caione 	int i;
1117a29a869SCarlo Caione 
1127a29a869SCarlo Caione 	for (i = 0; i < pll->rate_count; i++) {
1137a29a869SCarlo Caione 		if (rate == rate_table[i].rate)
1147a29a869SCarlo Caione 			return &rate_table[i];
1157a29a869SCarlo Caione 	}
1167a29a869SCarlo Caione 	return NULL;
1177a29a869SCarlo Caione }
1187a29a869SCarlo Caione 
119*45fcbec7SNeil Armstrong /* Specific wait loop for GXL/GXM GP0 PLL */
120*45fcbec7SNeil Armstrong static int meson_clk_pll_wait_lock_reset(struct meson_clk_pll *pll,
121*45fcbec7SNeil Armstrong 					 struct parm *p_n)
122*45fcbec7SNeil Armstrong {
123*45fcbec7SNeil Armstrong 	int delay = 100;
124*45fcbec7SNeil Armstrong 	u32 reg;
125*45fcbec7SNeil Armstrong 
126*45fcbec7SNeil Armstrong 	while (delay > 0) {
127*45fcbec7SNeil Armstrong 		reg = readl(pll->base + p_n->reg_off);
128*45fcbec7SNeil Armstrong 		writel(reg | MESON_PLL_RESET, pll->base + p_n->reg_off);
129*45fcbec7SNeil Armstrong 		udelay(10);
130*45fcbec7SNeil Armstrong 		writel(reg & ~MESON_PLL_RESET, pll->base + p_n->reg_off);
131*45fcbec7SNeil Armstrong 
132*45fcbec7SNeil Armstrong 		/* This delay comes from AMLogic tree clk-gp0-gxl driver */
133*45fcbec7SNeil Armstrong 		mdelay(1);
134*45fcbec7SNeil Armstrong 
135*45fcbec7SNeil Armstrong 		reg = readl(pll->base + p_n->reg_off);
136*45fcbec7SNeil Armstrong 		if (reg & MESON_PLL_LOCK)
137*45fcbec7SNeil Armstrong 			return 0;
138*45fcbec7SNeil Armstrong 		delay--;
139*45fcbec7SNeil Armstrong 	}
140*45fcbec7SNeil Armstrong 	return -ETIMEDOUT;
141*45fcbec7SNeil Armstrong }
142*45fcbec7SNeil Armstrong 
1437a29a869SCarlo Caione static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll,
1447a29a869SCarlo Caione 				   struct parm *p_n)
1457a29a869SCarlo Caione {
1467a29a869SCarlo Caione 	int delay = 24000000;
1477a29a869SCarlo Caione 	u32 reg;
1487a29a869SCarlo Caione 
1497a29a869SCarlo Caione 	while (delay > 0) {
1507a29a869SCarlo Caione 		reg = readl(pll->base + p_n->reg_off);
1517a29a869SCarlo Caione 
1527a29a869SCarlo Caione 		if (reg & MESON_PLL_LOCK)
1537a29a869SCarlo Caione 			return 0;
1547a29a869SCarlo Caione 		delay--;
1557a29a869SCarlo Caione 	}
1567a29a869SCarlo Caione 	return -ETIMEDOUT;
1577a29a869SCarlo Caione }
1587a29a869SCarlo Caione 
159*45fcbec7SNeil Armstrong static void meson_clk_pll_init_params(struct meson_clk_pll *pll)
160*45fcbec7SNeil Armstrong {
161*45fcbec7SNeil Armstrong 	int i;
162*45fcbec7SNeil Armstrong 
163*45fcbec7SNeil Armstrong 	for (i = 0 ; i < pll->params.params_count ; ++i)
164*45fcbec7SNeil Armstrong 		writel(pll->params.params_table[i].value,
165*45fcbec7SNeil Armstrong 		       pll->base + pll->params.params_table[i].reg_off);
166*45fcbec7SNeil Armstrong }
167*45fcbec7SNeil Armstrong 
1687a29a869SCarlo Caione static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
1697a29a869SCarlo Caione 				  unsigned long parent_rate)
1707a29a869SCarlo Caione {
1717a29a869SCarlo Caione 	struct meson_clk_pll *pll = to_meson_clk_pll(hw);
1727a29a869SCarlo Caione 	struct parm *p;
1737a29a869SCarlo Caione 	const struct pll_rate_table *rate_set;
1747a29a869SCarlo Caione 	unsigned long old_rate;
1757a29a869SCarlo Caione 	int ret = 0;
1767a29a869SCarlo Caione 	u32 reg;
1777a29a869SCarlo Caione 
1787a29a869SCarlo Caione 	if (parent_rate == 0 || rate == 0)
1797a29a869SCarlo Caione 		return -EINVAL;
1807a29a869SCarlo Caione 
1817a29a869SCarlo Caione 	old_rate = rate;
1827a29a869SCarlo Caione 
1837a29a869SCarlo Caione 	rate_set = meson_clk_get_pll_settings(pll, rate);
1847a29a869SCarlo Caione 	if (!rate_set)
1857a29a869SCarlo Caione 		return -EINVAL;
1867a29a869SCarlo Caione 
187*45fcbec7SNeil Armstrong 	/* Initialize the PLL in a clean state if specified */
188*45fcbec7SNeil Armstrong 	if (pll->params.params_count)
189*45fcbec7SNeil Armstrong 		meson_clk_pll_init_params(pll);
190*45fcbec7SNeil Armstrong 
1917a29a869SCarlo Caione 	/* PLL reset */
192ec623f2aSMichael Turquette 	p = &pll->n;
1937a29a869SCarlo Caione 	reg = readl(pll->base + p->reg_off);
194*45fcbec7SNeil Armstrong 	/* If no_init_reset is provided, avoid resetting at this point */
195*45fcbec7SNeil Armstrong 	if (!pll->params.no_init_reset)
1967a29a869SCarlo Caione 		writel(reg | MESON_PLL_RESET, pll->base + p->reg_off);
1977a29a869SCarlo Caione 
1987a29a869SCarlo Caione 	reg = PARM_SET(p->width, p->shift, reg, rate_set->n);
1997a29a869SCarlo Caione 	writel(reg, pll->base + p->reg_off);
2007a29a869SCarlo Caione 
201ec623f2aSMichael Turquette 	p = &pll->m;
2027a29a869SCarlo Caione 	reg = readl(pll->base + p->reg_off);
2037a29a869SCarlo Caione 	reg = PARM_SET(p->width, p->shift, reg, rate_set->m);
2047a29a869SCarlo Caione 	writel(reg, pll->base + p->reg_off);
2057a29a869SCarlo Caione 
206ec623f2aSMichael Turquette 	p = &pll->od;
2077a29a869SCarlo Caione 	reg = readl(pll->base + p->reg_off);
2087a29a869SCarlo Caione 	reg = PARM_SET(p->width, p->shift, reg, rate_set->od);
2097a29a869SCarlo Caione 	writel(reg, pll->base + p->reg_off);
2107a29a869SCarlo Caione 
2114a472951SMichael Turquette 	p = &pll->od2;
2124a472951SMichael Turquette 	if (p->width) {
2134a472951SMichael Turquette 		reg = readl(pll->base + p->reg_off);
2144a472951SMichael Turquette 		reg = PARM_SET(p->width, p->shift, reg, rate_set->od2);
2154a472951SMichael Turquette 		writel(reg, pll->base + p->reg_off);
2164a472951SMichael Turquette 	}
2174a472951SMichael Turquette 
2184a472951SMichael Turquette 	p = &pll->frac;
2194a472951SMichael Turquette 	if (p->width) {
2204a472951SMichael Turquette 		reg = readl(pll->base + p->reg_off);
2214a472951SMichael Turquette 		reg = PARM_SET(p->width, p->shift, reg, rate_set->frac);
2224a472951SMichael Turquette 		writel(reg, pll->base + p->reg_off);
2234a472951SMichael Turquette 	}
2244a472951SMichael Turquette 
225ec623f2aSMichael Turquette 	p = &pll->n;
226*45fcbec7SNeil Armstrong 	/* If clear_reset_for_lock is provided, remove the reset bit here */
227*45fcbec7SNeil Armstrong 	if (pll->params.clear_reset_for_lock) {
228*45fcbec7SNeil Armstrong 		reg = readl(pll->base + p->reg_off);
229*45fcbec7SNeil Armstrong 		writel(reg & ~MESON_PLL_RESET, pll->base + p->reg_off);
230*45fcbec7SNeil Armstrong 	}
231*45fcbec7SNeil Armstrong 
232*45fcbec7SNeil Armstrong 	/* If reset_lock_loop, use a special loop including resetting */
233*45fcbec7SNeil Armstrong 	if (pll->params.reset_lock_loop)
234*45fcbec7SNeil Armstrong 		ret = meson_clk_pll_wait_lock_reset(pll, p);
235*45fcbec7SNeil Armstrong 	else
2367a29a869SCarlo Caione 		ret = meson_clk_pll_wait_lock(pll, p);
2377a29a869SCarlo Caione 	if (ret) {
2387a29a869SCarlo Caione 		pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
2397a29a869SCarlo Caione 			__func__, old_rate);
2407a29a869SCarlo Caione 		meson_clk_pll_set_rate(hw, old_rate, parent_rate);
2417a29a869SCarlo Caione 	}
2427a29a869SCarlo Caione 
2437a29a869SCarlo Caione 	return ret;
2447a29a869SCarlo Caione }
2457a29a869SCarlo Caione 
246ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ops = {
2477a29a869SCarlo Caione 	.recalc_rate	= meson_clk_pll_recalc_rate,
2487a29a869SCarlo Caione 	.round_rate	= meson_clk_pll_round_rate,
2497a29a869SCarlo Caione 	.set_rate	= meson_clk_pll_set_rate,
2507a29a869SCarlo Caione };
2517a29a869SCarlo Caione 
252ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ro_ops = {
2537a29a869SCarlo Caione 	.recalc_rate	= meson_clk_pll_recalc_rate,
2547a29a869SCarlo Caione };
255