• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2016-2017 Socionext Inc.
4  *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5  */
6 
7 #include <common.h>
8 #include <clk-uclass.h>
9 #include <dm.h>
10 #include <linux/bitops.h>
11 #include <linux/io.h>
12 #include <linux/sizes.h>
13 
14 #include "clk-uniphier.h"
15 
16 /**
17  * struct uniphier_clk_priv - private data for UniPhier clock driver
18  *
19  * @base: base address of the clock provider
20  * @data: SoC specific data
21  */
22 struct uniphier_clk_priv {
23 	struct udevice *dev;
24 	void __iomem *base;
25 	const struct uniphier_clk_data *data;
26 };
27 
uniphier_clk_gate_enable(struct uniphier_clk_priv * priv,const struct uniphier_clk_gate_data * gate)28 static void uniphier_clk_gate_enable(struct uniphier_clk_priv *priv,
29 				     const struct uniphier_clk_gate_data *gate)
30 {
31 	u32 val;
32 
33 	val = readl(priv->base + gate->reg);
34 	val |= BIT(gate->bit);
35 	writel(val, priv->base + gate->reg);
36 }
37 
uniphier_clk_mux_set_parent(struct uniphier_clk_priv * priv,const struct uniphier_clk_mux_data * mux,u8 id)38 static void uniphier_clk_mux_set_parent(struct uniphier_clk_priv *priv,
39 					const struct uniphier_clk_mux_data *mux,
40 					u8 id)
41 {
42 	u32 val;
43 	int i;
44 
45 	for (i = 0; i < mux->num_parents; i++) {
46 		if (mux->parent_ids[i] != id)
47 			continue;
48 
49 		val = readl(priv->base + mux->reg);
50 		val &= ~mux->masks[i];
51 		val |= mux->vals[i];
52 		writel(val, priv->base + mux->reg);
53 		return;
54 	}
55 
56 	WARN_ON(1);
57 }
58 
uniphier_clk_mux_get_parent(struct uniphier_clk_priv * priv,const struct uniphier_clk_mux_data * mux)59 static u8 uniphier_clk_mux_get_parent(struct uniphier_clk_priv *priv,
60 				      const struct uniphier_clk_mux_data *mux)
61 {
62 	u32 val;
63 	int i;
64 
65 	val = readl(priv->base + mux->reg);
66 
67 	for (i = 0; i < mux->num_parents; i++)
68 		if ((mux->masks[i] & val) == mux->vals[i])
69 			return mux->parent_ids[i];
70 
71 	dev_err(priv->dev, "invalid mux setting\n");
72 
73 	return UNIPHIER_CLK_ID_INVALID;
74 }
75 
uniphier_clk_get_data(struct uniphier_clk_priv * priv,u8 id)76 static const struct uniphier_clk_data *uniphier_clk_get_data(
77 					struct uniphier_clk_priv *priv, u8 id)
78 {
79 	const struct uniphier_clk_data *data;
80 
81 	for (data = priv->data; data->type != UNIPHIER_CLK_TYPE_END; data++)
82 		if (data->id == id)
83 			return data;
84 
85 	dev_err(priv->dev, "id=%u not found\n", id);
86 
87 	return NULL;
88 }
89 
uniphier_clk_get_parent_data(struct uniphier_clk_priv * priv,const struct uniphier_clk_data * data)90 static const struct uniphier_clk_data *uniphier_clk_get_parent_data(
91 					struct uniphier_clk_priv *priv,
92 					const struct uniphier_clk_data *data)
93 {
94 	const struct uniphier_clk_data *parent_data;
95 	u8 parent_id = UNIPHIER_CLK_ID_INVALID;
96 
97 	switch (data->type) {
98 	case UNIPHIER_CLK_TYPE_GATE:
99 		parent_id = data->data.gate.parent_id;
100 		break;
101 	case UNIPHIER_CLK_TYPE_MUX:
102 		parent_id = uniphier_clk_mux_get_parent(priv, &data->data.mux);
103 		break;
104 	default:
105 		break;
106 	}
107 
108 	if (parent_id == UNIPHIER_CLK_ID_INVALID)
109 		return NULL;
110 
111 	parent_data = uniphier_clk_get_data(priv, parent_id);
112 
113 	WARN_ON(!parent_data);
114 
115 	return parent_data;
116 }
117 
__uniphier_clk_enable(struct uniphier_clk_priv * priv,const struct uniphier_clk_data * data)118 static void __uniphier_clk_enable(struct uniphier_clk_priv *priv,
119 				  const struct uniphier_clk_data *data)
120 {
121 	const struct uniphier_clk_data *parent_data;
122 
123 	if (data->type == UNIPHIER_CLK_TYPE_GATE)
124 		uniphier_clk_gate_enable(priv, &data->data.gate);
125 
126 	parent_data = uniphier_clk_get_parent_data(priv, data);
127 	if (!parent_data)
128 		return;
129 
130 	return __uniphier_clk_enable(priv, parent_data);
131 }
132 
uniphier_clk_enable(struct clk * clk)133 static int uniphier_clk_enable(struct clk *clk)
134 {
135 	struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
136 	const struct uniphier_clk_data *data;
137 
138 	data = uniphier_clk_get_data(priv, clk->id);
139 	if (!data)
140 		return -ENODEV;
141 
142 	__uniphier_clk_enable(priv, data);
143 
144 	return 0;
145 }
146 
__uniphier_clk_get_rate(struct uniphier_clk_priv * priv,const struct uniphier_clk_data * data)147 static unsigned long __uniphier_clk_get_rate(
148 					struct uniphier_clk_priv *priv,
149 					const struct uniphier_clk_data *data)
150 {
151 	const struct uniphier_clk_data *parent_data;
152 
153 	if (data->type == UNIPHIER_CLK_TYPE_FIXED_RATE)
154 		return data->data.rate.fixed_rate;
155 
156 	parent_data = uniphier_clk_get_parent_data(priv, data);
157 	if (!parent_data)
158 		return 0;
159 
160 	return __uniphier_clk_get_rate(priv, parent_data);
161 }
162 
uniphier_clk_get_rate(struct clk * clk)163 static unsigned long uniphier_clk_get_rate(struct clk *clk)
164 {
165 	struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
166 	const struct uniphier_clk_data *data;
167 
168 	data = uniphier_clk_get_data(priv, clk->id);
169 	if (!data)
170 		return -ENODEV;
171 
172 	return __uniphier_clk_get_rate(priv, data);
173 }
174 
__uniphier_clk_set_rate(struct uniphier_clk_priv * priv,const struct uniphier_clk_data * data,unsigned long rate,bool set)175 static unsigned long __uniphier_clk_set_rate(
176 					struct uniphier_clk_priv *priv,
177 					const struct uniphier_clk_data *data,
178 					unsigned long rate, bool set)
179 {
180 	const struct uniphier_clk_data *best_parent_data = NULL;
181 	const struct uniphier_clk_data *parent_data;
182 	unsigned long best_rate = 0;
183 	unsigned long parent_rate;
184 	u8 parent_id;
185 	int i;
186 
187 	if (data->type == UNIPHIER_CLK_TYPE_FIXED_RATE)
188 		return data->data.rate.fixed_rate;
189 
190 	if (data->type == UNIPHIER_CLK_TYPE_GATE) {
191 		parent_data = uniphier_clk_get_parent_data(priv, data);
192 		if (!parent_data)
193 			return 0;
194 
195 		return __uniphier_clk_set_rate(priv, parent_data, rate, set);
196 	}
197 
198 	if (WARN_ON(data->type != UNIPHIER_CLK_TYPE_MUX))
199 		return -EINVAL;
200 
201 	for (i = 0; i < data->data.mux.num_parents; i++) {
202 		parent_id = data->data.mux.parent_ids[i];
203 		parent_data = uniphier_clk_get_data(priv, parent_id);
204 		if (WARN_ON(!parent_data))
205 			return -EINVAL;
206 
207 		parent_rate = __uniphier_clk_set_rate(priv, parent_data, rate,
208 						      false);
209 
210 		if (parent_rate <= rate && best_rate < parent_rate) {
211 			best_rate = parent_rate;
212 			best_parent_data = parent_data;
213 		}
214 	}
215 
216 	dev_dbg(priv->dev, "id=%u, best_rate=%lu\n", data->id, best_rate);
217 
218 	if (!best_parent_data)
219 		return -EINVAL;
220 
221 	if (!set)
222 		return best_rate;
223 
224 	uniphier_clk_mux_set_parent(priv, &data->data.mux,
225 				    best_parent_data->id);
226 
227 	return best_rate = __uniphier_clk_set_rate(priv, best_parent_data,
228 						   rate, true);
229 }
230 
uniphier_clk_set_rate(struct clk * clk,ulong rate)231 static unsigned long uniphier_clk_set_rate(struct clk *clk, ulong rate)
232 {
233 	struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
234 	const struct uniphier_clk_data *data;
235 
236 	data = uniphier_clk_get_data(priv, clk->id);
237 	if (!data)
238 		return -ENODEV;
239 
240 	return __uniphier_clk_set_rate(priv, data, rate, true);
241 }
242 
243 static const struct clk_ops uniphier_clk_ops = {
244 	.enable = uniphier_clk_enable,
245 	.get_rate = uniphier_clk_get_rate,
246 	.set_rate = uniphier_clk_set_rate,
247 };
248 
uniphier_clk_probe(struct udevice * dev)249 static int uniphier_clk_probe(struct udevice *dev)
250 {
251 	struct uniphier_clk_priv *priv = dev_get_priv(dev);
252 	fdt_addr_t addr;
253 
254 	addr = devfdt_get_addr(dev->parent);
255 	if (addr == FDT_ADDR_T_NONE)
256 		return -EINVAL;
257 
258 	priv->base = devm_ioremap(dev, addr, SZ_4K);
259 	if (!priv->base)
260 		return -ENOMEM;
261 
262 	priv->dev = dev;
263 	priv->data = (void *)dev_get_driver_data(dev);
264 
265 	return 0;
266 }
267 
268 static const struct udevice_id uniphier_clk_match[] = {
269 	/* System clock */
270 	{
271 		.compatible = "socionext,uniphier-ld4-clock",
272 		.data = (ulong)uniphier_pxs2_sys_clk_data,
273 	},
274 	{
275 		.compatible = "socionext,uniphier-pro4-clock",
276 		.data = (ulong)uniphier_pxs2_sys_clk_data,
277 	},
278 	{
279 		.compatible = "socionext,uniphier-sld8-clock",
280 		.data = (ulong)uniphier_pxs2_sys_clk_data,
281 	},
282 	{
283 		.compatible = "socionext,uniphier-pro5-clock",
284 		.data = (ulong)uniphier_pxs2_sys_clk_data,
285 	},
286 	{
287 		.compatible = "socionext,uniphier-pxs2-clock",
288 		.data = (ulong)uniphier_pxs2_sys_clk_data,
289 	},
290 	{
291 		.compatible = "socionext,uniphier-ld11-clock",
292 		.data = (ulong)uniphier_ld20_sys_clk_data,
293 	},
294 	{
295 		.compatible = "socionext,uniphier-ld20-clock",
296 		.data = (ulong)uniphier_ld20_sys_clk_data,
297 	},
298 	{
299 		.compatible = "socionext,uniphier-pxs3-clock",
300 		.data = (ulong)uniphier_pxs3_sys_clk_data,
301 	},
302 	/* Media I/O clock */
303 	{
304 		.compatible = "socionext,uniphier-ld4-mio-clock",
305 		.data = (ulong)uniphier_mio_clk_data,
306 	},
307 	{
308 		.compatible = "socionext,uniphier-pro4-mio-clock",
309 		.data = (ulong)uniphier_mio_clk_data,
310 	},
311 	{
312 		.compatible = "socionext,uniphier-sld8-mio-clock",
313 		.data = (ulong)uniphier_mio_clk_data,
314 	},
315 	{
316 		.compatible = "socionext,uniphier-pro5-sd-clock",
317 		.data = (ulong)uniphier_mio_clk_data,
318 	},
319 	{
320 		.compatible = "socionext,uniphier-pxs2-sd-clock",
321 		.data = (ulong)uniphier_mio_clk_data,
322 	},
323 	{
324 		.compatible = "socionext,uniphier-ld11-mio-clock",
325 		.data = (ulong)uniphier_mio_clk_data,
326 	},
327 	{
328 		.compatible = "socionext,uniphier-ld20-sd-clock",
329 		.data = (ulong)uniphier_mio_clk_data,
330 	},
331 	{
332 		.compatible = "socionext,uniphier-pxs3-sd-clock",
333 		.data = (ulong)uniphier_mio_clk_data,
334 	},
335 	{ /* sentinel */ }
336 };
337 
338 U_BOOT_DRIVER(uniphier_clk) = {
339 	.name = "uniphier-clk",
340 	.id = UCLASS_CLK,
341 	.of_match = uniphier_clk_match,
342 	.probe = uniphier_clk_probe,
343 	.priv_auto_alloc_size = sizeof(struct uniphier_clk_priv),
344 	.ops = &uniphier_clk_ops,
345 };
346