xref: /linux/drivers/clk/meson/clk-pll.c (revision ec623f2a43ebe482abc925f8785f462c0fe3c08a)
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;
567a29a869SCarlo Caione 	u16 n, m, od;
577a29a869SCarlo Caione 	u32 reg;
587a29a869SCarlo Caione 
59*ec623f2aSMichael 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 
63*ec623f2aSMichael 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 
67*ec623f2aSMichael 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 
717a29a869SCarlo Caione 	rate_mhz = (parent_rate_mhz * m / n) >> od;
727a29a869SCarlo Caione 
737a29a869SCarlo Caione 	return rate_mhz * 1000000;
747a29a869SCarlo Caione }
757a29a869SCarlo Caione 
767a29a869SCarlo Caione static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
777a29a869SCarlo Caione 				     unsigned long *parent_rate)
787a29a869SCarlo Caione {
797a29a869SCarlo Caione 	struct meson_clk_pll *pll = to_meson_clk_pll(hw);
80*ec623f2aSMichael Turquette 	const struct pll_rate_table *rate_table = pll->rate_table;
817a29a869SCarlo Caione 	int i;
827a29a869SCarlo Caione 
837a29a869SCarlo Caione 	for (i = 0; i < pll->rate_count; i++) {
847a29a869SCarlo Caione 		if (rate <= rate_table[i].rate)
857a29a869SCarlo Caione 			return rate_table[i].rate;
867a29a869SCarlo Caione 	}
877a29a869SCarlo Caione 
887a29a869SCarlo Caione 	/* else return the smallest value */
897a29a869SCarlo Caione 	return rate_table[0].rate;
907a29a869SCarlo Caione }
917a29a869SCarlo Caione 
927a29a869SCarlo Caione static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll,
937a29a869SCarlo Caione 							       unsigned long rate)
947a29a869SCarlo Caione {
95*ec623f2aSMichael Turquette 	const struct pll_rate_table *rate_table = pll->rate_table;
967a29a869SCarlo Caione 	int i;
977a29a869SCarlo Caione 
987a29a869SCarlo Caione 	for (i = 0; i < pll->rate_count; i++) {
997a29a869SCarlo Caione 		if (rate == rate_table[i].rate)
1007a29a869SCarlo Caione 			return &rate_table[i];
1017a29a869SCarlo Caione 	}
1027a29a869SCarlo Caione 	return NULL;
1037a29a869SCarlo Caione }
1047a29a869SCarlo Caione 
1057a29a869SCarlo Caione static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll,
1067a29a869SCarlo Caione 				   struct parm *p_n)
1077a29a869SCarlo Caione {
1087a29a869SCarlo Caione 	int delay = 24000000;
1097a29a869SCarlo Caione 	u32 reg;
1107a29a869SCarlo Caione 
1117a29a869SCarlo Caione 	while (delay > 0) {
1127a29a869SCarlo Caione 		reg = readl(pll->base + p_n->reg_off);
1137a29a869SCarlo Caione 
1147a29a869SCarlo Caione 		if (reg & MESON_PLL_LOCK)
1157a29a869SCarlo Caione 			return 0;
1167a29a869SCarlo Caione 		delay--;
1177a29a869SCarlo Caione 	}
1187a29a869SCarlo Caione 	return -ETIMEDOUT;
1197a29a869SCarlo Caione }
1207a29a869SCarlo Caione 
1217a29a869SCarlo Caione static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
1227a29a869SCarlo Caione 				  unsigned long parent_rate)
1237a29a869SCarlo Caione {
1247a29a869SCarlo Caione 	struct meson_clk_pll *pll = to_meson_clk_pll(hw);
1257a29a869SCarlo Caione 	struct parm *p;
1267a29a869SCarlo Caione 	const struct pll_rate_table *rate_set;
1277a29a869SCarlo Caione 	unsigned long old_rate;
1287a29a869SCarlo Caione 	int ret = 0;
1297a29a869SCarlo Caione 	u32 reg;
1307a29a869SCarlo Caione 
1317a29a869SCarlo Caione 	if (parent_rate == 0 || rate == 0)
1327a29a869SCarlo Caione 		return -EINVAL;
1337a29a869SCarlo Caione 
1347a29a869SCarlo Caione 	old_rate = rate;
1357a29a869SCarlo Caione 
1367a29a869SCarlo Caione 	rate_set = meson_clk_get_pll_settings(pll, rate);
1377a29a869SCarlo Caione 	if (!rate_set)
1387a29a869SCarlo Caione 		return -EINVAL;
1397a29a869SCarlo Caione 
1407a29a869SCarlo Caione 	/* PLL reset */
141*ec623f2aSMichael Turquette 	p = &pll->n;
1427a29a869SCarlo Caione 	reg = readl(pll->base + p->reg_off);
1437a29a869SCarlo Caione 	writel(reg | MESON_PLL_RESET, pll->base + p->reg_off);
1447a29a869SCarlo Caione 
1457a29a869SCarlo Caione 	reg = PARM_SET(p->width, p->shift, reg, rate_set->n);
1467a29a869SCarlo Caione 	writel(reg, pll->base + p->reg_off);
1477a29a869SCarlo Caione 
148*ec623f2aSMichael Turquette 	p = &pll->m;
1497a29a869SCarlo Caione 	reg = readl(pll->base + p->reg_off);
1507a29a869SCarlo Caione 	reg = PARM_SET(p->width, p->shift, reg, rate_set->m);
1517a29a869SCarlo Caione 	writel(reg, pll->base + p->reg_off);
1527a29a869SCarlo Caione 
153*ec623f2aSMichael Turquette 	p = &pll->od;
1547a29a869SCarlo Caione 	reg = readl(pll->base + p->reg_off);
1557a29a869SCarlo Caione 	reg = PARM_SET(p->width, p->shift, reg, rate_set->od);
1567a29a869SCarlo Caione 	writel(reg, pll->base + p->reg_off);
1577a29a869SCarlo Caione 
158*ec623f2aSMichael Turquette 	p = &pll->n;
1597a29a869SCarlo Caione 	ret = meson_clk_pll_wait_lock(pll, p);
1607a29a869SCarlo Caione 	if (ret) {
1617a29a869SCarlo Caione 		pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
1627a29a869SCarlo Caione 			__func__, old_rate);
1637a29a869SCarlo Caione 		meson_clk_pll_set_rate(hw, old_rate, parent_rate);
1647a29a869SCarlo Caione 	}
1657a29a869SCarlo Caione 
1667a29a869SCarlo Caione 	return ret;
1677a29a869SCarlo Caione }
1687a29a869SCarlo Caione 
169*ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ops = {
1707a29a869SCarlo Caione 	.recalc_rate	= meson_clk_pll_recalc_rate,
1717a29a869SCarlo Caione 	.round_rate	= meson_clk_pll_round_rate,
1727a29a869SCarlo Caione 	.set_rate	= meson_clk_pll_set_rate,
1737a29a869SCarlo Caione };
1747a29a869SCarlo Caione 
175*ec623f2aSMichael Turquette const struct clk_ops meson_clk_pll_ro_ops = {
1767a29a869SCarlo Caione 	.recalc_rate	= meson_clk_pll_recalc_rate,
1777a29a869SCarlo Caione };
178