]>
Commit | Line | Data |
---|---|---|
48264d9b | 1 | /* |
4e3d8406 MY |
2 | * Copyright (C) 2016 Socionext Inc. |
3 | * Author: Masahiro Yamada <yamada.masahiro@socionext.com> | |
48264d9b MY |
4 | * |
5 | * SPDX-License-Identifier: GPL-2.0+ | |
6 | */ | |
7 | ||
8 | #include <common.h> | |
805dc44c | 9 | #include <clk-uclass.h> |
9d922450 | 10 | #include <dm.h> |
48264d9b MY |
11 | #include <linux/bitops.h> |
12 | #include <linux/io.h> | |
45a3b1fd | 13 | #include <linux/sizes.h> |
48264d9b MY |
14 | |
15 | #include "clk-uniphier.h" | |
16 | ||
102e3187 MY |
17 | /** |
18 | * struct uniphier_clk_priv - private data for UniPhier clock driver | |
19 | * | |
20 | * @base: base address of the clock provider | |
805dc44c | 21 | * @data: SoC specific data |
102e3187 MY |
22 | */ |
23 | struct uniphier_clk_priv { | |
24 | void __iomem *base; | |
805dc44c | 25 | const struct uniphier_clk_data *data; |
102e3187 MY |
26 | }; |
27 | ||
805dc44c | 28 | static int uniphier_clk_enable(struct clk *clk) |
102e3187 | 29 | { |
805dc44c MY |
30 | struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); |
31 | unsigned long id = clk->id; | |
32 | const struct uniphier_clk_gate_data *p; | |
102e3187 | 33 | |
805dc44c MY |
34 | for (p = priv->data->gate; p->id != UNIPHIER_CLK_ID_END; p++) { |
35 | u32 val; | |
102e3187 | 36 | |
805dc44c MY |
37 | if (p->id != id) |
38 | continue; | |
102e3187 | 39 | |
805dc44c MY |
40 | val = readl(priv->base + p->reg); |
41 | val |= BIT(p->bit); | |
42 | writel(val, priv->base + p->reg); | |
102e3187 | 43 | |
805dc44c MY |
44 | return 0; |
45 | } | |
46 | ||
47 | dev_err(priv->dev, "clk_id=%lu was not handled\n", id); | |
48 | return -EINVAL; | |
102e3187 MY |
49 | } |
50 | ||
805dc44c MY |
51 | static const struct uniphier_clk_mux_data * |
52 | uniphier_clk_get_mux_data(struct uniphier_clk_priv *priv, unsigned long id) | |
48264d9b | 53 | { |
805dc44c | 54 | const struct uniphier_clk_mux_data *p; |
48264d9b | 55 | |
805dc44c MY |
56 | for (p = priv->data->mux; p->id != UNIPHIER_CLK_ID_END; p++) { |
57 | if (p->id == id) | |
58 | return p; | |
48264d9b MY |
59 | } |
60 | ||
805dc44c | 61 | return NULL; |
48264d9b MY |
62 | } |
63 | ||
135aa950 | 64 | static ulong uniphier_clk_get_rate(struct clk *clk) |
48264d9b | 65 | { |
135aa950 | 66 | struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); |
805dc44c MY |
67 | const struct uniphier_clk_mux_data *mux; |
68 | u32 val; | |
48264d9b MY |
69 | int i; |
70 | ||
805dc44c MY |
71 | mux = uniphier_clk_get_mux_data(priv, clk->id); |
72 | if (!mux) | |
73 | return 0; | |
48264d9b | 74 | |
805dc44c MY |
75 | if (!mux->nr_muxs) /* fixed-rate */ |
76 | return mux->rates[0]; | |
77 | ||
78 | val = readl(priv->base + mux->reg); | |
48264d9b | 79 | |
805dc44c MY |
80 | for (i = 0; i < mux->nr_muxs; i++) |
81 | if ((mux->masks[i] & val) == mux->vals[i]) | |
82 | return mux->rates[i]; | |
48264d9b | 83 | |
805dc44c | 84 | return -EINVAL; |
48264d9b MY |
85 | } |
86 | ||
135aa950 | 87 | static ulong uniphier_clk_set_rate(struct clk *clk, ulong rate) |
48264d9b | 88 | { |
135aa950 | 89 | struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); |
805dc44c MY |
90 | const struct uniphier_clk_mux_data *mux; |
91 | u32 val; | |
92 | int i, best_rate_id = -1; | |
48264d9b | 93 | ulong best_rate = 0; |
48264d9b | 94 | |
805dc44c MY |
95 | mux = uniphier_clk_get_mux_data(priv, clk->id); |
96 | if (!mux) | |
97 | return 0; | |
48264d9b | 98 | |
805dc44c MY |
99 | if (!mux->nr_muxs) /* fixed-rate */ |
100 | return mux->rates[0]; | |
48264d9b | 101 | |
805dc44c MY |
102 | /* first, decide the best match rate */ |
103 | for (i = 0; i < mux->nr_muxs; i++) { | |
104 | if (mux->rates[i] > best_rate && mux->rates[i] <= rate) { | |
105 | best_rate = mux->rates[i]; | |
106 | best_rate_id = i; | |
107 | } | |
48264d9b MY |
108 | } |
109 | ||
805dc44c MY |
110 | if (best_rate_id < 0) |
111 | return -EINVAL; | |
112 | ||
113 | val = readl(priv->base + mux->reg); | |
114 | val &= ~mux->masks[best_rate_id]; | |
115 | val |= mux->vals[best_rate_id]; | |
116 | writel(val, priv->base + mux->reg); | |
48264d9b MY |
117 | |
118 | debug("%s: requested rate = %lu, set rate = %lu\n", __func__, | |
119 | rate, best_rate); | |
120 | ||
48264d9b MY |
121 | return best_rate; |
122 | } | |
123 | ||
124 | const struct clk_ops uniphier_clk_ops = { | |
125 | .enable = uniphier_clk_enable, | |
135aa950 SW |
126 | .get_rate = uniphier_clk_get_rate, |
127 | .set_rate = uniphier_clk_set_rate, | |
48264d9b MY |
128 | }; |
129 | ||
805dc44c MY |
130 | static int uniphier_clk_probe(struct udevice *dev) |
131 | { | |
132 | struct uniphier_clk_priv *priv = dev_get_priv(dev); | |
133 | fdt_addr_t addr; | |
134 | ||
a821c4af | 135 | addr = devfdt_get_addr(dev->parent); |
805dc44c MY |
136 | if (addr == FDT_ADDR_T_NONE) |
137 | return -EINVAL; | |
138 | ||
139 | priv->base = devm_ioremap(dev, addr, SZ_4K); | |
140 | if (!priv->base) | |
141 | return -ENOMEM; | |
142 | ||
143 | priv->data = (void *)dev_get_driver_data(dev); | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
102e3187 MY |
148 | static const struct udevice_id uniphier_clk_match[] = { |
149 | { | |
6dc5b6b1 | 150 | .compatible = "socionext,uniphier-sld3-mio-clock", |
102e3187 MY |
151 | .data = (ulong)&uniphier_mio_clk_data, |
152 | }, | |
153 | { | |
6dc5b6b1 | 154 | .compatible = "socionext,uniphier-ld4-mio-clock", |
102e3187 MY |
155 | .data = (ulong)&uniphier_mio_clk_data, |
156 | }, | |
157 | { | |
6dc5b6b1 | 158 | .compatible = "socionext,uniphier-pro4-mio-clock", |
102e3187 MY |
159 | .data = (ulong)&uniphier_mio_clk_data, |
160 | }, | |
161 | { | |
6dc5b6b1 | 162 | .compatible = "socionext,uniphier-sld8-mio-clock", |
102e3187 MY |
163 | .data = (ulong)&uniphier_mio_clk_data, |
164 | }, | |
165 | { | |
4c642e68 | 166 | .compatible = "socionext,uniphier-pro5-sd-clock", |
102e3187 MY |
167 | .data = (ulong)&uniphier_mio_clk_data, |
168 | }, | |
169 | { | |
4c642e68 | 170 | .compatible = "socionext,uniphier-pxs2-sd-clock", |
102e3187 MY |
171 | .data = (ulong)&uniphier_mio_clk_data, |
172 | }, | |
173 | { | |
6dc5b6b1 | 174 | .compatible = "socionext,uniphier-ld11-mio-clock", |
102e3187 MY |
175 | .data = (ulong)&uniphier_mio_clk_data, |
176 | }, | |
177 | { | |
4c642e68 | 178 | .compatible = "socionext,uniphier-ld20-sd-clock", |
102e3187 MY |
179 | .data = (ulong)&uniphier_mio_clk_data, |
180 | }, | |
181 | { /* sentinel */ } | |
182 | }; | |
48264d9b | 183 | |
102e3187 MY |
184 | U_BOOT_DRIVER(uniphier_clk) = { |
185 | .name = "uniphier-clk", | |
186 | .id = UCLASS_CLK, | |
187 | .of_match = uniphier_clk_match, | |
188 | .probe = uniphier_clk_probe, | |
189 | .priv_auto_alloc_size = sizeof(struct uniphier_clk_priv), | |
190 | .ops = &uniphier_clk_ops, | |
191 | }; |