1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2016-2017 Socionext Inc.
4 * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
8 #include <clk-uclass.h>
10 #include <linux/bitops.h>
12 #include <linux/sizes.h>
14 #include "clk-uniphier.h"
17 * struct uniphier_clk_priv - private data for UniPhier clock driver
19 * @base: base address of the clock provider
20 * @data: SoC specific data
22 struct uniphier_clk_priv {
25 const struct uniphier_clk_data *data;
28 static void uniphier_clk_gate_enable(struct uniphier_clk_priv *priv,
29 const struct uniphier_clk_gate_data *gate)
33 val = readl(priv->base + gate->reg);
34 val |= BIT(gate->bit);
35 writel(val, priv->base + gate->reg);
38 static void uniphier_clk_mux_set_parent(struct uniphier_clk_priv *priv,
39 const struct uniphier_clk_mux_data *mux,
45 for (i = 0; i < mux->num_parents; i++) {
46 if (mux->parent_ids[i] != id)
49 val = readl(priv->base + mux->reg);
50 val &= ~mux->masks[i];
52 writel(val, priv->base + mux->reg);
59 static u8 uniphier_clk_mux_get_parent(struct uniphier_clk_priv *priv,
60 const struct uniphier_clk_mux_data *mux)
65 val = readl(priv->base + mux->reg);
67 for (i = 0; i < mux->num_parents; i++)
68 if ((mux->masks[i] & val) == mux->vals[i])
69 return mux->parent_ids[i];
71 dev_err(priv->dev, "invalid mux setting\n");
73 return UNIPHIER_CLK_ID_INVALID;
76 static const struct uniphier_clk_data *uniphier_clk_get_data(
77 struct uniphier_clk_priv *priv, u8 id)
79 const struct uniphier_clk_data *data;
81 for (data = priv->data; data->type != UNIPHIER_CLK_TYPE_END; data++)
85 dev_err(priv->dev, "id=%u not found\n", id);
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)
94 const struct uniphier_clk_data *parent_data;
95 u8 parent_id = UNIPHIER_CLK_ID_INVALID;
98 case UNIPHIER_CLK_TYPE_GATE:
99 parent_id = data->data.gate.parent_id;
101 case UNIPHIER_CLK_TYPE_MUX:
102 parent_id = uniphier_clk_mux_get_parent(priv, &data->data.mux);
108 if (parent_id == UNIPHIER_CLK_ID_INVALID)
111 parent_data = uniphier_clk_get_data(priv, parent_id);
113 WARN_ON(!parent_data);
118 static void __uniphier_clk_enable(struct uniphier_clk_priv *priv,
119 const struct uniphier_clk_data *data)
121 const struct uniphier_clk_data *parent_data;
123 if (data->type == UNIPHIER_CLK_TYPE_GATE)
124 uniphier_clk_gate_enable(priv, &data->data.gate);
126 parent_data = uniphier_clk_get_parent_data(priv, data);
130 return __uniphier_clk_enable(priv, parent_data);
133 static int uniphier_clk_enable(struct clk *clk)
135 struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
136 const struct uniphier_clk_data *data;
138 data = uniphier_clk_get_data(priv, clk->id);
142 __uniphier_clk_enable(priv, data);
147 static unsigned long __uniphier_clk_get_rate(
148 struct uniphier_clk_priv *priv,
149 const struct uniphier_clk_data *data)
151 const struct uniphier_clk_data *parent_data;
153 if (data->type == UNIPHIER_CLK_TYPE_FIXED_RATE)
154 return data->data.rate.fixed_rate;
156 parent_data = uniphier_clk_get_parent_data(priv, data);
160 return __uniphier_clk_get_rate(priv, parent_data);
163 static unsigned long uniphier_clk_get_rate(struct clk *clk)
165 struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
166 const struct uniphier_clk_data *data;
168 data = uniphier_clk_get_data(priv, clk->id);
172 return __uniphier_clk_get_rate(priv, data);
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)
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;
187 if (data->type == UNIPHIER_CLK_TYPE_FIXED_RATE)
188 return data->data.rate.fixed_rate;
190 if (data->type == UNIPHIER_CLK_TYPE_GATE) {
191 parent_data = uniphier_clk_get_parent_data(priv, data);
195 return __uniphier_clk_set_rate(priv, parent_data, rate, set);
198 if (WARN_ON(data->type != UNIPHIER_CLK_TYPE_MUX))
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))
207 parent_rate = __uniphier_clk_set_rate(priv, parent_data, rate,
210 if (parent_rate <= rate && best_rate < parent_rate) {
211 best_rate = parent_rate;
212 best_parent_data = parent_data;
216 dev_dbg(priv->dev, "id=%u, best_rate=%lu\n", data->id, best_rate);
218 if (!best_parent_data)
224 uniphier_clk_mux_set_parent(priv, &data->data.mux,
225 best_parent_data->id);
227 return best_rate = __uniphier_clk_set_rate(priv, best_parent_data,
231 static unsigned long uniphier_clk_set_rate(struct clk *clk, ulong rate)
233 struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
234 const struct uniphier_clk_data *data;
236 data = uniphier_clk_get_data(priv, clk->id);
240 return __uniphier_clk_set_rate(priv, data, rate, true);
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,
249 static int uniphier_clk_probe(struct udevice *dev)
251 struct uniphier_clk_priv *priv = dev_get_priv(dev);
254 addr = devfdt_get_addr(dev->parent);
255 if (addr == FDT_ADDR_T_NONE)
258 priv->base = devm_ioremap(dev, addr, SZ_4K);
263 priv->data = (void *)dev_get_driver_data(dev);
268 static const struct udevice_id uniphier_clk_match[] = {
271 .compatible = "socionext,uniphier-ld4-clock",
272 .data = (ulong)uniphier_pxs2_sys_clk_data,
275 .compatible = "socionext,uniphier-pro4-clock",
276 .data = (ulong)uniphier_pxs2_sys_clk_data,
279 .compatible = "socionext,uniphier-sld8-clock",
280 .data = (ulong)uniphier_pxs2_sys_clk_data,
283 .compatible = "socionext,uniphier-pro5-clock",
284 .data = (ulong)uniphier_pxs2_sys_clk_data,
287 .compatible = "socionext,uniphier-pxs2-clock",
288 .data = (ulong)uniphier_pxs2_sys_clk_data,
291 .compatible = "socionext,uniphier-ld11-clock",
292 .data = (ulong)uniphier_ld20_sys_clk_data,
295 .compatible = "socionext,uniphier-ld20-clock",
296 .data = (ulong)uniphier_ld20_sys_clk_data,
299 .compatible = "socionext,uniphier-pxs3-clock",
300 .data = (ulong)uniphier_pxs3_sys_clk_data,
302 /* Media I/O clock */
304 .compatible = "socionext,uniphier-ld4-mio-clock",
305 .data = (ulong)uniphier_mio_clk_data,
308 .compatible = "socionext,uniphier-pro4-mio-clock",
309 .data = (ulong)uniphier_mio_clk_data,
312 .compatible = "socionext,uniphier-sld8-mio-clock",
313 .data = (ulong)uniphier_mio_clk_data,
316 .compatible = "socionext,uniphier-pro5-sd-clock",
317 .data = (ulong)uniphier_mio_clk_data,
320 .compatible = "socionext,uniphier-pxs2-sd-clock",
321 .data = (ulong)uniphier_mio_clk_data,
324 .compatible = "socionext,uniphier-ld11-mio-clock",
325 .data = (ulong)uniphier_mio_clk_data,
328 .compatible = "socionext,uniphier-ld20-sd-clock",
329 .data = (ulong)uniphier_mio_clk_data,
332 .compatible = "socionext,uniphier-pxs3-sd-clock",
333 .data = (ulong)uniphier_mio_clk_data,
338 U_BOOT_DRIVER(uniphier_clk) = {
339 .name = "uniphier-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,