2 * Copyright (C) 2016-2017 Socionext Inc.
3 * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5 * SPDX-License-Identifier: GPL-2.0+
9 #include <clk-uclass.h>
11 #include <linux/bitops.h>
13 #include <linux/sizes.h>
15 #include "clk-uniphier.h"
18 * struct uniphier_clk_priv - private data for UniPhier clock driver
20 * @base: base address of the clock provider
21 * @data: SoC specific data
23 struct uniphier_clk_priv {
26 const struct uniphier_clk_data *data;
29 static void uniphier_clk_gate_enable(struct uniphier_clk_priv *priv,
30 const struct uniphier_clk_gate_data *gate)
34 val = readl(priv->base + gate->reg);
35 val |= BIT(gate->bit);
36 writel(val, priv->base + gate->reg);
39 static void uniphier_clk_mux_set_parent(struct uniphier_clk_priv *priv,
40 const struct uniphier_clk_mux_data *mux,
46 for (i = 0; i < mux->num_parents; i++) {
47 if (mux->parent_ids[i] != id)
50 val = readl(priv->base + mux->reg);
51 val &= ~mux->masks[i];
53 writel(val, priv->base + mux->reg);
60 static u8 uniphier_clk_mux_get_parent(struct uniphier_clk_priv *priv,
61 const struct uniphier_clk_mux_data *mux)
66 val = readl(priv->base + mux->reg);
68 for (i = 0; i < mux->num_parents; i++)
69 if ((mux->masks[i] & val) == mux->vals[i])
70 return mux->parent_ids[i];
72 dev_err(priv->dev, "invalid mux setting\n");
74 return UNIPHIER_CLK_ID_INVALID;
77 static const struct uniphier_clk_data *uniphier_clk_get_data(
78 struct uniphier_clk_priv *priv, u8 id)
80 const struct uniphier_clk_data *data;
82 for (data = priv->data; data->type != UNIPHIER_CLK_TYPE_END; data++)
86 dev_err(priv->dev, "id=%u not found\n", id);
91 static const struct uniphier_clk_data *uniphier_clk_get_parent_data(
92 struct uniphier_clk_priv *priv,
93 const struct uniphier_clk_data *data)
95 const struct uniphier_clk_data *parent_data;
96 u8 parent_id = UNIPHIER_CLK_ID_INVALID;
99 case UNIPHIER_CLK_TYPE_GATE:
100 parent_id = data->data.gate.parent_id;
102 case UNIPHIER_CLK_TYPE_MUX:
103 parent_id = uniphier_clk_mux_get_parent(priv, &data->data.mux);
109 if (parent_id == UNIPHIER_CLK_ID_INVALID)
112 parent_data = uniphier_clk_get_data(priv, parent_id);
114 WARN_ON(!parent_data);
119 static void __uniphier_clk_enable(struct uniphier_clk_priv *priv,
120 const struct uniphier_clk_data *data)
122 const struct uniphier_clk_data *parent_data;
124 if (data->type == UNIPHIER_CLK_TYPE_GATE)
125 uniphier_clk_gate_enable(priv, &data->data.gate);
127 parent_data = uniphier_clk_get_parent_data(priv, data);
131 return __uniphier_clk_enable(priv, parent_data);
134 static int uniphier_clk_enable(struct clk *clk)
136 struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
137 const struct uniphier_clk_data *data;
139 data = uniphier_clk_get_data(priv, clk->id);
143 __uniphier_clk_enable(priv, data);
148 static unsigned long __uniphier_clk_get_rate(
149 struct uniphier_clk_priv *priv,
150 const struct uniphier_clk_data *data)
152 const struct uniphier_clk_data *parent_data;
154 if (data->type == UNIPHIER_CLK_TYPE_FIXED_RATE)
155 return data->data.rate.fixed_rate;
157 parent_data = uniphier_clk_get_parent_data(priv, data);
161 return __uniphier_clk_get_rate(priv, parent_data);
164 static unsigned long uniphier_clk_get_rate(struct clk *clk)
166 struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
167 const struct uniphier_clk_data *data;
169 data = uniphier_clk_get_data(priv, clk->id);
173 return __uniphier_clk_get_rate(priv, data);
176 static unsigned long __uniphier_clk_set_rate(
177 struct uniphier_clk_priv *priv,
178 const struct uniphier_clk_data *data,
179 unsigned long rate, bool set)
181 const struct uniphier_clk_data *best_parent_data = NULL;
182 const struct uniphier_clk_data *parent_data;
183 unsigned long best_rate = 0;
184 unsigned long parent_rate;
188 if (data->type == UNIPHIER_CLK_TYPE_FIXED_RATE)
189 return data->data.rate.fixed_rate;
191 if (data->type == UNIPHIER_CLK_TYPE_GATE) {
192 parent_data = uniphier_clk_get_parent_data(priv, data);
196 return __uniphier_clk_set_rate(priv, parent_data, rate, set);
199 if (WARN_ON(data->type != UNIPHIER_CLK_TYPE_MUX))
202 for (i = 0; i < data->data.mux.num_parents; i++) {
203 parent_id = data->data.mux.parent_ids[i];
204 parent_data = uniphier_clk_get_data(priv, parent_id);
205 if (WARN_ON(!parent_data))
208 parent_rate = __uniphier_clk_set_rate(priv, parent_data, rate,
211 if (parent_rate <= rate && best_rate < parent_rate) {
212 best_rate = parent_rate;
213 best_parent_data = parent_data;
217 dev_dbg(priv->dev, "id=%u, best_rate=%lu\n", data->id, best_rate);
219 if (!best_parent_data)
225 uniphier_clk_mux_set_parent(priv, &data->data.mux,
226 best_parent_data->id);
228 return best_rate = __uniphier_clk_set_rate(priv, best_parent_data,
232 static unsigned long uniphier_clk_set_rate(struct clk *clk, ulong rate)
234 struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
235 const struct uniphier_clk_data *data;
237 data = uniphier_clk_get_data(priv, clk->id);
241 return __uniphier_clk_set_rate(priv, data, rate, true);
244 static const struct clk_ops uniphier_clk_ops = {
245 .enable = uniphier_clk_enable,
246 .get_rate = uniphier_clk_get_rate,
247 .set_rate = uniphier_clk_set_rate,
250 static int uniphier_clk_probe(struct udevice *dev)
252 struct uniphier_clk_priv *priv = dev_get_priv(dev);
255 addr = devfdt_get_addr(dev->parent);
256 if (addr == FDT_ADDR_T_NONE)
259 priv->base = devm_ioremap(dev, addr, SZ_4K);
264 priv->data = (void *)dev_get_driver_data(dev);
269 static const struct udevice_id uniphier_clk_match[] = {
272 .compatible = "socionext,uniphier-ld4-clock",
273 .data = (ulong)uniphier_pxs2_sys_clk_data,
276 .compatible = "socionext,uniphier-pro4-clock",
277 .data = (ulong)uniphier_pxs2_sys_clk_data,
280 .compatible = "socionext,uniphier-sld8-clock",
281 .data = (ulong)uniphier_pxs2_sys_clk_data,
284 .compatible = "socionext,uniphier-pro5-clock",
285 .data = (ulong)uniphier_pxs2_sys_clk_data,
288 .compatible = "socionext,uniphier-pxs2-clock",
289 .data = (ulong)uniphier_pxs2_sys_clk_data,
292 .compatible = "socionext,uniphier-ld11-clock",
293 .data = (ulong)uniphier_ld20_sys_clk_data,
296 .compatible = "socionext,uniphier-ld20-clock",
297 .data = (ulong)uniphier_ld20_sys_clk_data,
299 /* Media I/O clock */
301 .compatible = "socionext,uniphier-ld4-mio-clock",
302 .data = (ulong)uniphier_mio_clk_data,
305 .compatible = "socionext,uniphier-pro4-mio-clock",
306 .data = (ulong)uniphier_mio_clk_data,
309 .compatible = "socionext,uniphier-sld8-mio-clock",
310 .data = (ulong)uniphier_mio_clk_data,
313 .compatible = "socionext,uniphier-pro5-sd-clock",
314 .data = (ulong)uniphier_mio_clk_data,
317 .compatible = "socionext,uniphier-pxs2-sd-clock",
318 .data = (ulong)uniphier_mio_clk_data,
321 .compatible = "socionext,uniphier-ld11-mio-clock",
322 .data = (ulong)uniphier_mio_clk_data,
325 .compatible = "socionext,uniphier-ld20-sd-clock",
326 .data = (ulong)uniphier_mio_clk_data,
331 U_BOOT_DRIVER(uniphier_clk) = {
332 .name = "uniphier-clk",
334 .of_match = uniphier_clk_match,
335 .probe = uniphier_clk_probe,
336 .priv_auto_alloc_size = sizeof(struct uniphier_clk_priv),
337 .ops = &uniphier_clk_ops,