]>
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> | |
48264d9b MY |
9 | #include <linux/bitops.h> |
10 | #include <linux/io.h> | |
45a3b1fd | 11 | #include <linux/sizes.h> |
135aa950 | 12 | #include <clk-uclass.h> |
48264d9b MY |
13 | #include <dm/device.h> |
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 | |
21 | * @socdata: SoC specific data | |
22 | */ | |
23 | struct uniphier_clk_priv { | |
24 | void __iomem *base; | |
25 | const struct uniphier_clk_soc_data *socdata; | |
26 | }; | |
27 | ||
28 | int uniphier_clk_probe(struct udevice *dev) | |
29 | { | |
30 | struct uniphier_clk_priv *priv = dev_get_priv(dev); | |
31 | fdt_addr_t addr; | |
32 | ||
6dc5b6b1 | 33 | addr = dev_get_addr(dev->parent); |
102e3187 MY |
34 | if (addr == FDT_ADDR_T_NONE) |
35 | return -EINVAL; | |
36 | ||
37 | priv->base = devm_ioremap(dev, addr, SZ_4K); | |
38 | if (!priv->base) | |
39 | return -ENOMEM; | |
40 | ||
41 | priv->socdata = (void *)dev_get_driver_data(dev); | |
42 | ||
43 | return 0; | |
44 | } | |
45 | ||
135aa950 | 46 | static int uniphier_clk_enable(struct clk *clk) |
48264d9b | 47 | { |
135aa950 | 48 | struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); |
3524d47c | 49 | const struct uniphier_clk_gate_data *gate = priv->socdata->gate; |
48264d9b MY |
50 | unsigned int nr_gate = priv->socdata->nr_gate; |
51 | void __iomem *reg; | |
52 | u32 mask, data, tmp; | |
53 | int i; | |
54 | ||
55 | for (i = 0; i < nr_gate; i++) { | |
135aa950 | 56 | if (gate[i].index != clk->id) |
48264d9b MY |
57 | continue; |
58 | ||
59 | reg = priv->base + gate[i].reg; | |
60 | mask = gate[i].mask; | |
61 | data = gate[i].data & mask; | |
62 | ||
63 | tmp = readl(reg); | |
64 | tmp &= ~mask; | |
65 | tmp |= data & mask; | |
66 | debug("%s: %p: %08x\n", __func__, reg, tmp); | |
67 | writel(tmp, reg); | |
68 | } | |
69 | ||
70 | return 0; | |
71 | } | |
72 | ||
135aa950 | 73 | static ulong uniphier_clk_get_rate(struct clk *clk) |
48264d9b | 74 | { |
135aa950 | 75 | struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); |
3524d47c | 76 | const struct uniphier_clk_rate_data *rdata = priv->socdata->rate; |
48264d9b MY |
77 | unsigned int nr_rdata = priv->socdata->nr_rate; |
78 | void __iomem *reg; | |
79 | u32 mask, data; | |
80 | ulong matched_rate = 0; | |
81 | int i; | |
82 | ||
83 | for (i = 0; i < nr_rdata; i++) { | |
135aa950 | 84 | if (rdata[i].index != clk->id) |
48264d9b MY |
85 | continue; |
86 | ||
87 | if (rdata[i].reg == UNIPHIER_CLK_RATE_IS_FIXED) | |
88 | return rdata[i].rate; | |
89 | ||
90 | reg = priv->base + rdata[i].reg; | |
91 | mask = rdata[i].mask; | |
92 | data = rdata[i].data & mask; | |
93 | if ((readl(reg) & mask) == data) { | |
94 | if (matched_rate && rdata[i].rate != matched_rate) { | |
95 | printf("failed to get clk rate for insane register values\n"); | |
96 | return -EINVAL; | |
97 | } | |
98 | matched_rate = rdata[i].rate; | |
99 | } | |
100 | } | |
101 | ||
102 | debug("%s: rate = %lu\n", __func__, matched_rate); | |
103 | ||
104 | return matched_rate; | |
105 | } | |
106 | ||
135aa950 | 107 | static ulong uniphier_clk_set_rate(struct clk *clk, ulong rate) |
48264d9b | 108 | { |
135aa950 | 109 | struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); |
3524d47c | 110 | const struct uniphier_clk_rate_data *rdata = priv->socdata->rate; |
48264d9b MY |
111 | unsigned int nr_rdata = priv->socdata->nr_rate; |
112 | void __iomem *reg; | |
113 | u32 mask, data, tmp; | |
114 | ulong best_rate = 0; | |
115 | int i; | |
116 | ||
117 | /* first, decide the best match rate */ | |
118 | for (i = 0; i < nr_rdata; i++) { | |
135aa950 | 119 | if (rdata[i].index != clk->id) |
48264d9b MY |
120 | continue; |
121 | ||
122 | if (rdata[i].reg == UNIPHIER_CLK_RATE_IS_FIXED) | |
123 | return 0; | |
124 | ||
125 | if (rdata[i].rate > best_rate && rdata[i].rate <= rate) | |
126 | best_rate = rdata[i].rate; | |
127 | } | |
128 | ||
129 | if (!best_rate) | |
130 | return -ENODEV; | |
131 | ||
132 | debug("%s: requested rate = %lu, set rate = %lu\n", __func__, | |
133 | rate, best_rate); | |
134 | ||
135 | /* second, really set registers */ | |
136 | for (i = 0; i < nr_rdata; i++) { | |
135aa950 | 137 | if (rdata[i].index != clk->id || rdata[i].rate != best_rate) |
48264d9b MY |
138 | continue; |
139 | ||
140 | reg = priv->base + rdata[i].reg; | |
141 | mask = rdata[i].mask; | |
142 | data = rdata[i].data & mask; | |
143 | ||
144 | tmp = readl(reg); | |
145 | tmp &= ~mask; | |
146 | tmp |= data; | |
147 | debug("%s: %p: %08x\n", __func__, reg, tmp); | |
148 | writel(tmp, reg); | |
149 | } | |
150 | ||
151 | return best_rate; | |
152 | } | |
153 | ||
154 | const struct clk_ops uniphier_clk_ops = { | |
155 | .enable = uniphier_clk_enable, | |
135aa950 SW |
156 | .get_rate = uniphier_clk_get_rate, |
157 | .set_rate = uniphier_clk_set_rate, | |
48264d9b MY |
158 | }; |
159 | ||
102e3187 MY |
160 | static const struct udevice_id uniphier_clk_match[] = { |
161 | { | |
6dc5b6b1 | 162 | .compatible = "socionext,uniphier-sld3-mio-clock", |
102e3187 MY |
163 | .data = (ulong)&uniphier_mio_clk_data, |
164 | }, | |
165 | { | |
6dc5b6b1 | 166 | .compatible = "socionext,uniphier-ld4-mio-clock", |
102e3187 MY |
167 | .data = (ulong)&uniphier_mio_clk_data, |
168 | }, | |
169 | { | |
6dc5b6b1 | 170 | .compatible = "socionext,uniphier-pro4-mio-clock", |
102e3187 MY |
171 | .data = (ulong)&uniphier_mio_clk_data, |
172 | }, | |
173 | { | |
6dc5b6b1 | 174 | .compatible = "socionext,uniphier-sld8-mio-clock", |
102e3187 MY |
175 | .data = (ulong)&uniphier_mio_clk_data, |
176 | }, | |
177 | { | |
6dc5b6b1 | 178 | .compatible = "socionext,uniphier-pro5-mio-clock", |
102e3187 MY |
179 | .data = (ulong)&uniphier_mio_clk_data, |
180 | }, | |
181 | { | |
6dc5b6b1 | 182 | .compatible = "socionext,uniphier-pxs2-mio-clock", |
102e3187 MY |
183 | .data = (ulong)&uniphier_mio_clk_data, |
184 | }, | |
185 | { | |
6dc5b6b1 | 186 | .compatible = "socionext,uniphier-ld11-mio-clock", |
102e3187 MY |
187 | .data = (ulong)&uniphier_mio_clk_data, |
188 | }, | |
189 | { | |
6dc5b6b1 | 190 | .compatible = "socionext,uniphier-ld20-mio-clock", |
102e3187 MY |
191 | .data = (ulong)&uniphier_mio_clk_data, |
192 | }, | |
193 | { /* sentinel */ } | |
194 | }; | |
48264d9b | 195 | |
102e3187 MY |
196 | U_BOOT_DRIVER(uniphier_clk) = { |
197 | .name = "uniphier-clk", | |
198 | .id = UCLASS_CLK, | |
199 | .of_match = uniphier_clk_match, | |
200 | .probe = uniphier_clk_probe, | |
201 | .priv_auto_alloc_size = sizeof(struct uniphier_clk_priv), | |
202 | .ops = &uniphier_clk_ops, | |
203 | }; |