]>
Commit | Line | Data |
---|---|---|
87e460c3 LM |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright (C) 2019 | |
4 | * Lukasz Majewski, DENX Software Engineering, [email protected] | |
5 | * | |
6 | * Common Clock Framework [CCF] driver for Sandbox | |
7 | */ | |
8 | ||
87e460c3 LM |
9 | #include <dm.h> |
10 | #include <clk.h> | |
336d4615 | 11 | #include <malloc.h> |
87e460c3 LM |
12 | #include <asm/clk.h> |
13 | #include <clk-uclass.h> | |
61b29b82 | 14 | #include <dm/devres.h> |
cd93d625 | 15 | #include <linux/bitops.h> |
87e460c3 LM |
16 | #include <linux/clk-provider.h> |
17 | #include <sandbox-clk.h> | |
61b29b82 | 18 | #include <linux/err.h> |
87e460c3 LM |
19 | |
20 | /* | |
21 | * Sandbox implementation of CCF primitives necessary for clk-uclass testing | |
22 | * | |
23 | * --- Sandbox PLLv3 --- | |
24 | */ | |
25 | struct clk_pllv3 { | |
26 | struct clk clk; | |
27 | u32 div_mask; | |
28 | u32 div_shift; | |
29 | }; | |
30 | ||
c66f4f5e PF |
31 | int sandbox_clk_enable_count(struct clk *clk) |
32 | { | |
33 | struct clk *clkp = NULL; | |
34 | int ret; | |
35 | ||
36 | ret = clk_get_by_id(clk->id, &clkp); | |
37 | if (ret) | |
38 | return 0; | |
39 | ||
40 | return clkp->enable_count; | |
41 | } | |
42 | ||
87e460c3 LM |
43 | static ulong clk_pllv3_get_rate(struct clk *clk) |
44 | { | |
45 | unsigned long parent_rate = clk_get_parent_rate(clk); | |
46 | ||
47 | return parent_rate * 24; | |
48 | } | |
49 | ||
50 | static const struct clk_ops clk_pllv3_generic_ops = { | |
51 | .get_rate = clk_pllv3_get_rate, | |
52 | }; | |
53 | ||
54 | struct clk *sandbox_clk_pllv3(enum sandbox_pllv3_type type, const char *name, | |
55 | const char *parent_name, void __iomem *base, | |
56 | u32 div_mask) | |
57 | { | |
58 | struct clk_pllv3 *pll; | |
59 | struct clk *clk; | |
60 | char *drv_name = "sandbox_clk_pllv3"; | |
61 | int ret; | |
62 | ||
63 | pll = kzalloc(sizeof(*pll), GFP_KERNEL); | |
64 | if (!pll) | |
65 | return ERR_PTR(-ENOMEM); | |
66 | ||
67 | pll->div_mask = div_mask; | |
68 | clk = &pll->clk; | |
69 | ||
70 | ret = clk_register(clk, drv_name, name, parent_name); | |
71 | if (ret) { | |
72 | kfree(pll); | |
73 | return ERR_PTR(ret); | |
74 | } | |
75 | ||
76 | return clk; | |
77 | } | |
78 | ||
79 | U_BOOT_DRIVER(sandbox_clk_pll_generic) = { | |
80 | .name = "sandbox_clk_pllv3", | |
81 | .id = UCLASS_CLK, | |
82 | .ops = &clk_pllv3_generic_ops, | |
83 | }; | |
84 | ||
85 | /* --- Sandbox PLLv3 --- */ | |
86 | /* --- Sandbox Gate --- */ | |
87 | struct clk_gate2 { | |
88 | struct clk clk; | |
89 | bool state; | |
90 | }; | |
91 | ||
92 | #define to_clk_gate2(_clk) container_of(_clk, struct clk_gate2, clk) | |
93 | ||
94 | static int clk_gate2_enable(struct clk *clk) | |
95 | { | |
96 | struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev)); | |
97 | ||
98 | gate->state = 1; | |
99 | return 0; | |
100 | } | |
101 | ||
102 | static int clk_gate2_disable(struct clk *clk) | |
103 | { | |
104 | struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev)); | |
105 | ||
106 | gate->state = 0; | |
107 | return 0; | |
108 | } | |
109 | ||
110 | static const struct clk_ops clk_gate2_ops = { | |
111 | .enable = clk_gate2_enable, | |
112 | .disable = clk_gate2_disable, | |
113 | .get_rate = clk_generic_get_rate, | |
114 | }; | |
115 | ||
116 | struct clk *sandbox_clk_register_gate2(struct device *dev, const char *name, | |
117 | const char *parent_name, | |
118 | unsigned long flags, void __iomem *reg, | |
119 | u8 bit_idx, u8 cgr_val, | |
120 | u8 clk_gate2_flags) | |
121 | { | |
122 | struct clk_gate2 *gate; | |
123 | struct clk *clk; | |
124 | int ret; | |
125 | ||
126 | gate = kzalloc(sizeof(*gate), GFP_KERNEL); | |
127 | if (!gate) | |
128 | return ERR_PTR(-ENOMEM); | |
129 | ||
130 | gate->state = 0; | |
131 | clk = &gate->clk; | |
16bdc85b | 132 | clk->flags = flags; |
87e460c3 LM |
133 | |
134 | ret = clk_register(clk, "sandbox_clk_gate2", name, parent_name); | |
135 | if (ret) { | |
136 | kfree(gate); | |
137 | return ERR_PTR(ret); | |
138 | } | |
139 | ||
140 | return clk; | |
141 | } | |
142 | ||
143 | U_BOOT_DRIVER(sandbox_clk_gate2) = { | |
144 | .name = "sandbox_clk_gate2", | |
145 | .id = UCLASS_CLK, | |
146 | .ops = &clk_gate2_ops, | |
147 | }; | |
148 | ||
8f611dc7 PF |
149 | static unsigned long sandbox_clk_composite_divider_recalc_rate(struct clk *clk) |
150 | { | |
151 | struct clk_divider *divider = (struct clk_divider *)to_clk_divider(clk); | |
152 | struct clk_composite *composite = (struct clk_composite *)clk->data; | |
153 | ulong parent_rate = clk_get_parent_rate(&composite->clk); | |
154 | unsigned int val; | |
155 | ||
156 | val = divider->io_divider_val; | |
157 | val >>= divider->shift; | |
158 | val &= clk_div_mask(divider->width); | |
159 | ||
160 | return divider_recalc_rate(clk, parent_rate, val, divider->table, | |
161 | divider->flags, divider->width); | |
162 | } | |
163 | ||
164 | static const struct clk_ops sandbox_clk_composite_divider_ops = { | |
165 | .get_rate = sandbox_clk_composite_divider_recalc_rate, | |
166 | }; | |
167 | ||
168 | struct clk *sandbox_clk_composite(const char *name, | |
169 | const char * const *parent_names, | |
170 | int num_parents, void __iomem *reg, | |
171 | unsigned long flags) | |
172 | { | |
173 | struct clk *clk = ERR_PTR(-ENOMEM); | |
174 | struct clk_divider *div = NULL; | |
175 | struct clk_gate *gate = NULL; | |
176 | struct clk_mux *mux = NULL; | |
177 | ||
178 | mux = kzalloc(sizeof(*mux), GFP_KERNEL); | |
179 | if (!mux) | |
180 | goto fail; | |
181 | ||
182 | mux->reg = reg; | |
183 | mux->shift = 24; | |
184 | mux->mask = 0x7; | |
185 | mux->num_parents = num_parents; | |
186 | mux->flags = flags; | |
187 | mux->parent_names = parent_names; | |
188 | ||
189 | div = kzalloc(sizeof(*div), GFP_KERNEL); | |
190 | if (!div) | |
191 | goto fail; | |
192 | ||
193 | div->reg = reg; | |
194 | div->shift = 16; | |
195 | div->width = 3; | |
196 | div->flags = CLK_DIVIDER_ROUND_CLOSEST | flags; | |
197 | ||
198 | gate = kzalloc(sizeof(*gate), GFP_KERNEL); | |
199 | if (!gate) | |
200 | goto fail; | |
201 | ||
202 | gate->reg = reg; | |
203 | gate->bit_idx = 28; | |
204 | gate->flags = flags; | |
205 | ||
206 | clk = clk_register_composite(NULL, name, | |
207 | parent_names, num_parents, | |
208 | &mux->clk, &clk_mux_ops, &div->clk, | |
209 | &sandbox_clk_composite_divider_ops, | |
210 | &gate->clk, &clk_gate_ops, flags); | |
211 | if (IS_ERR(clk)) | |
212 | goto fail; | |
213 | ||
214 | return clk; | |
215 | ||
216 | fail: | |
217 | kfree(gate); | |
218 | kfree(div); | |
219 | kfree(mux); | |
220 | return ERR_CAST(clk); | |
221 | } | |
222 | ||
87e460c3 LM |
223 | /* --- Sandbox Gate --- */ |
224 | /* The CCF core driver itself */ | |
225 | static const struct udevice_id sandbox_clk_ccf_test_ids[] = { | |
226 | { .compatible = "sandbox,clk-ccf" }, | |
227 | { } | |
228 | }; | |
229 | ||
230 | static const char *const usdhc_sels[] = { "pll3_60m", "pll3_80m", }; | |
8f611dc7 | 231 | static const char *const i2c_sels[] = { "pll3_60m", "pll3_80m", }; |
87e460c3 LM |
232 | |
233 | static int sandbox_clk_ccf_probe(struct udevice *dev) | |
234 | { | |
235 | void *base = NULL; | |
236 | u32 reg; | |
237 | ||
238 | clk_dm(SANDBOX_CLK_PLL3, | |
239 | sandbox_clk_pllv3(SANDBOX_PLLV3_USB, "pll3_usb_otg", "osc", | |
240 | base + 0x10, 0x3)); | |
241 | ||
242 | clk_dm(SANDBOX_CLK_PLL3_60M, | |
243 | sandbox_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8)); | |
244 | ||
245 | clk_dm(SANDBOX_CLK_PLL3_80M, | |
246 | sandbox_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6)); | |
247 | ||
248 | /* The HW adds +1 to the divider value (2+1) is the divider */ | |
249 | reg = (2 << 19); | |
250 | clk_dm(SANDBOX_CLK_ECSPI_ROOT, | |
251 | sandbox_clk_divider("ecspi_root", "pll3_60m", ®, 19, 6)); | |
252 | ||
cd16c57b DB |
253 | reg = 0; |
254 | clk_dm(SANDBOX_CLK_ECSPI0, | |
255 | sandbox_clk_gate("ecspi0", "ecspi_root", ®, 0, 0)); | |
256 | ||
87e460c3 LM |
257 | clk_dm(SANDBOX_CLK_ECSPI1, |
258 | sandbox_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0)); | |
259 | ||
260 | /* Select 'pll3_60m' */ | |
261 | reg = 0; | |
262 | clk_dm(SANDBOX_CLK_USDHC1_SEL, | |
263 | sandbox_clk_mux("usdhc1_sel", ®, 16, 1, usdhc_sels, | |
264 | ARRAY_SIZE(usdhc_sels))); | |
265 | ||
266 | /* Select 'pll3_80m' */ | |
267 | reg = BIT(17); | |
268 | clk_dm(SANDBOX_CLK_USDHC2_SEL, | |
269 | sandbox_clk_mux("usdhc2_sel", ®, 17, 1, usdhc_sels, | |
270 | ARRAY_SIZE(usdhc_sels))); | |
271 | ||
8f611dc7 PF |
272 | reg = BIT(28) | BIT(24) | BIT(16); |
273 | clk_dm(SANDBOX_CLK_I2C, | |
274 | sandbox_clk_composite("i2c", i2c_sels, ARRAY_SIZE(i2c_sels), | |
16bdc85b | 275 | ®, CLK_SET_RATE_UNGATE)); |
8f611dc7 | 276 | |
c66f4f5e PF |
277 | clk_dm(SANDBOX_CLK_I2C_ROOT, |
278 | sandbox_clk_gate2("i2c_root", "i2c", base + 0x7c, 0)); | |
279 | ||
87e460c3 LM |
280 | return 0; |
281 | } | |
282 | ||
283 | U_BOOT_DRIVER(sandbox_clk_ccf) = { | |
284 | .name = "sandbox_clk_ccf", | |
285 | .id = UCLASS_CLK, | |
d3061824 | 286 | .ops = &ccf_clk_ops, |
87e460c3 LM |
287 | .probe = sandbox_clk_ccf_probe, |
288 | .of_match = sandbox_clk_ccf_test_ids, | |
289 | }; |