xref: /openbmc/linux/drivers/clk/meson/clk-pll.c (revision ab55c050)
1 /*
2  * Copyright (c) 2015 Endless Mobile, Inc.
3  * Author: Carlo Caione <carlo@endlessm.com>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU General Public License,
7  * version 2, as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /*
19  * In the most basic form, a Meson PLL is composed as follows:
20  *
21  *                     PLL
22  *      +------------------------------+
23  *      |                              |
24  * in -----[ /N ]---[ *M ]---[ >>OD ]----->> out
25  *      |         ^        ^           |
26  *      +------------------------------+
27  *                |        |
28  *               FREF     VCO
29  *
30  * out = (in * M / N) >> OD
31  */
32 
33 #include <linux/clk-provider.h>
34 #include <linux/delay.h>
35 #include <linux/err.h>
36 #include <linux/io.h>
37 #include <linux/module.h>
38 #include <linux/of_address.h>
39 #include <linux/slab.h>
40 #include <linux/string.h>
41 
42 #include "clkc.h"
43 
44 #define MESON_PLL_RESET				BIT(29)
45 #define MESON_PLL_LOCK				BIT(31)
46 
47 struct meson_clk_pll {
48 	struct clk_hw	hw;
49 	void __iomem	*base;
50 	struct pll_conf	*conf;
51 	unsigned int	rate_count;
52 	spinlock_t	*lock;
53 };
54 #define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw)
55 
56 static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
57 						unsigned long parent_rate)
58 {
59 	struct meson_clk_pll *pll = to_meson_clk_pll(hw);
60 	struct parm *p;
61 	unsigned long parent_rate_mhz = parent_rate / 1000000;
62 	unsigned long rate_mhz;
63 	u16 n, m, od;
64 	u32 reg;
65 
66 	p = &pll->conf->n;
67 	reg = readl(pll->base + p->reg_off);
68 	n = PARM_GET(p->width, p->shift, reg);
69 
70 	p = &pll->conf->m;
71 	reg = readl(pll->base + p->reg_off);
72 	m = PARM_GET(p->width, p->shift, reg);
73 
74 	p = &pll->conf->od;
75 	reg = readl(pll->base + p->reg_off);
76 	od = PARM_GET(p->width, p->shift, reg);
77 
78 	rate_mhz = (parent_rate_mhz * m / n) >> od;
79 
80 	return rate_mhz * 1000000;
81 }
82 
83 static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
84 				     unsigned long *parent_rate)
85 {
86 	struct meson_clk_pll *pll = to_meson_clk_pll(hw);
87 	const struct pll_rate_table *rate_table = pll->conf->rate_table;
88 	int i;
89 
90 	for (i = 0; i < pll->rate_count; i++) {
91 		if (rate <= rate_table[i].rate)
92 			return rate_table[i].rate;
93 	}
94 
95 	/* else return the smallest value */
96 	return rate_table[0].rate;
97 }
98 
99 static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll,
100 							       unsigned long rate)
101 {
102 	const struct pll_rate_table *rate_table = pll->conf->rate_table;
103 	int i;
104 
105 	for (i = 0; i < pll->rate_count; i++) {
106 		if (rate == rate_table[i].rate)
107 			return &rate_table[i];
108 	}
109 	return NULL;
110 }
111 
112 static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll,
113 				   struct parm *p_n)
114 {
115 	int delay = 24000000;
116 	u32 reg;
117 
118 	while (delay > 0) {
119 		reg = readl(pll->base + p_n->reg_off);
120 
121 		if (reg & MESON_PLL_LOCK)
122 			return 0;
123 		delay--;
124 	}
125 	return -ETIMEDOUT;
126 }
127 
128 static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
129 				  unsigned long parent_rate)
130 {
131 	struct meson_clk_pll *pll = to_meson_clk_pll(hw);
132 	struct parm *p;
133 	const struct pll_rate_table *rate_set;
134 	unsigned long old_rate;
135 	int ret = 0;
136 	u32 reg;
137 
138 	if (parent_rate == 0 || rate == 0)
139 		return -EINVAL;
140 
141 	old_rate = rate;
142 
143 	rate_set = meson_clk_get_pll_settings(pll, rate);
144 	if (!rate_set)
145 		return -EINVAL;
146 
147 	/* PLL reset */
148 	p = &pll->conf->n;
149 	reg = readl(pll->base + p->reg_off);
150 	writel(reg | MESON_PLL_RESET, pll->base + p->reg_off);
151 
152 	reg = PARM_SET(p->width, p->shift, reg, rate_set->n);
153 	writel(reg, pll->base + p->reg_off);
154 
155 	p = &pll->conf->m;
156 	reg = readl(pll->base + p->reg_off);
157 	reg = PARM_SET(p->width, p->shift, reg, rate_set->m);
158 	writel(reg, pll->base + p->reg_off);
159 
160 	p = &pll->conf->od;
161 	reg = readl(pll->base + p->reg_off);
162 	reg = PARM_SET(p->width, p->shift, reg, rate_set->od);
163 	writel(reg, pll->base + p->reg_off);
164 
165 	p = &pll->conf->n;
166 	ret = meson_clk_pll_wait_lock(pll, p);
167 	if (ret) {
168 		pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
169 			__func__, old_rate);
170 		meson_clk_pll_set_rate(hw, old_rate, parent_rate);
171 	}
172 
173 	return ret;
174 }
175 
176 static const struct clk_ops meson_clk_pll_ops = {
177 	.recalc_rate	= meson_clk_pll_recalc_rate,
178 	.round_rate	= meson_clk_pll_round_rate,
179 	.set_rate	= meson_clk_pll_set_rate,
180 };
181 
182 static const struct clk_ops meson_clk_pll_ro_ops = {
183 	.recalc_rate	= meson_clk_pll_recalc_rate,
184 };
185 
186 struct clk *meson_clk_register_pll(const struct clk_conf *clk_conf,
187 				   void __iomem *reg_base,
188 				   spinlock_t *lock)
189 {
190 	struct clk *clk;
191 	struct meson_clk_pll *clk_pll;
192 	struct clk_init_data init;
193 
194 	clk_pll = kzalloc(sizeof(*clk_pll), GFP_KERNEL);
195 	if (!clk_pll)
196 		return ERR_PTR(-ENOMEM);
197 
198 	clk_pll->base = reg_base + clk_conf->reg_off;
199 	clk_pll->lock = lock;
200 	clk_pll->conf = clk_conf->conf.pll;
201 
202 	init.name = clk_conf->clk_name;
203 	init.flags = clk_conf->flags | CLK_GET_RATE_NOCACHE;
204 
205 	init.parent_names = &clk_conf->clks_parent[0];
206 	init.num_parents = 1;
207 	init.ops = &meson_clk_pll_ro_ops;
208 
209 	/* If no rate_table is specified we assume the PLL is read-only */
210 	if (clk_pll->conf->rate_table) {
211 		int len;
212 
213 		for (len = 0; clk_pll->conf->rate_table[len].rate != 0; )
214 			len++;
215 
216 		 clk_pll->rate_count = len;
217 		 init.ops = &meson_clk_pll_ops;
218 	}
219 
220 	clk_pll->hw.init = &init;
221 
222 	clk = clk_register(NULL, &clk_pll->hw);
223 	if (IS_ERR(clk))
224 		kfree(clk_pll);
225 
226 	return clk;
227 }
228