1 // SPDX-License-Identifier: GPL-2.0-only
2 //
3 // Copyright (c) 2021 Samuel Holland <samuel@sholland.org>
4 //
5
6 #include <linux/clk.h>
7 #include <linux/clk-provider.h>
8 #include <linux/device.h>
9 #include <linux/io.h>
10 #include <linux/module.h>
11 #include <linux/of.h>
12 #include <linux/of_device.h>
13
14 #include <linux/clk/sunxi-ng.h>
15
16 #include "ccu_common.h"
17
18 #include "ccu_div.h"
19 #include "ccu_gate.h"
20 #include "ccu_mux.h"
21
22 #include "ccu-sun6i-rtc.h"
23
24 #define IOSC_ACCURACY 300000000 /* 30% */
25 #define IOSC_RATE 16000000
26
27 #define LOSC_RATE 32768
28 #define LOSC_RATE_SHIFT 15
29
30 #define LOSC_CTRL_REG 0x0
31 #define LOSC_CTRL_KEY 0x16aa0000
32
33 #define IOSC_32K_CLK_DIV_REG 0x8
34 #define IOSC_32K_CLK_DIV GENMASK(4, 0)
35 #define IOSC_32K_PRE_DIV 32
36
37 #define IOSC_CLK_CALI_REG 0xc
38 #define IOSC_CLK_CALI_DIV_ONES 22
39 #define IOSC_CLK_CALI_EN BIT(1)
40 #define IOSC_CLK_CALI_SRC_SEL BIT(0)
41
42 #define LOSC_OUT_GATING_REG 0x60
43
44 #define DCXO_CTRL_REG 0x160
45 #define DCXO_CTRL_CLK16M_RC_EN BIT(0)
46
47 struct sun6i_rtc_match_data {
48 bool have_ext_osc32k : 1;
49 bool have_iosc_calibration : 1;
50 bool rtc_32k_single_parent : 1;
51 const struct clk_parent_data *osc32k_fanout_parents;
52 u8 osc32k_fanout_nparents;
53 };
54
55 static bool have_iosc_calibration;
56
ccu_iosc_enable(struct clk_hw * hw)57 static int ccu_iosc_enable(struct clk_hw *hw)
58 {
59 struct ccu_common *cm = hw_to_ccu_common(hw);
60
61 return ccu_gate_helper_enable(cm, DCXO_CTRL_CLK16M_RC_EN);
62 }
63
ccu_iosc_disable(struct clk_hw * hw)64 static void ccu_iosc_disable(struct clk_hw *hw)
65 {
66 struct ccu_common *cm = hw_to_ccu_common(hw);
67
68 return ccu_gate_helper_disable(cm, DCXO_CTRL_CLK16M_RC_EN);
69 }
70
ccu_iosc_is_enabled(struct clk_hw * hw)71 static int ccu_iosc_is_enabled(struct clk_hw *hw)
72 {
73 struct ccu_common *cm = hw_to_ccu_common(hw);
74
75 return ccu_gate_helper_is_enabled(cm, DCXO_CTRL_CLK16M_RC_EN);
76 }
77
ccu_iosc_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)78 static unsigned long ccu_iosc_recalc_rate(struct clk_hw *hw,
79 unsigned long parent_rate)
80 {
81 struct ccu_common *cm = hw_to_ccu_common(hw);
82
83 if (have_iosc_calibration) {
84 u32 reg = readl(cm->base + IOSC_CLK_CALI_REG);
85
86 /*
87 * Recover the IOSC frequency by shifting the ones place of
88 * (fixed-point divider * 32768) into bit zero.
89 */
90 if (reg & IOSC_CLK_CALI_EN)
91 return reg >> (IOSC_CLK_CALI_DIV_ONES - LOSC_RATE_SHIFT);
92 }
93
94 return IOSC_RATE;
95 }
96
ccu_iosc_recalc_accuracy(struct clk_hw * hw,unsigned long parent_accuracy)97 static unsigned long ccu_iosc_recalc_accuracy(struct clk_hw *hw,
98 unsigned long parent_accuracy)
99 {
100 return IOSC_ACCURACY;
101 }
102
103 static const struct clk_ops ccu_iosc_ops = {
104 .enable = ccu_iosc_enable,
105 .disable = ccu_iosc_disable,
106 .is_enabled = ccu_iosc_is_enabled,
107 .recalc_rate = ccu_iosc_recalc_rate,
108 .recalc_accuracy = ccu_iosc_recalc_accuracy,
109 };
110
111 static struct ccu_common iosc_clk = {
112 .reg = DCXO_CTRL_REG,
113 .hw.init = CLK_HW_INIT_NO_PARENT("iosc", &ccu_iosc_ops,
114 CLK_GET_RATE_NOCACHE),
115 };
116
ccu_iosc_32k_prepare(struct clk_hw * hw)117 static int ccu_iosc_32k_prepare(struct clk_hw *hw)
118 {
119 struct ccu_common *cm = hw_to_ccu_common(hw);
120 u32 val;
121
122 if (!have_iosc_calibration)
123 return 0;
124
125 val = readl(cm->base + IOSC_CLK_CALI_REG);
126 writel(val | IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL,
127 cm->base + IOSC_CLK_CALI_REG);
128
129 return 0;
130 }
131
ccu_iosc_32k_unprepare(struct clk_hw * hw)132 static void ccu_iosc_32k_unprepare(struct clk_hw *hw)
133 {
134 struct ccu_common *cm = hw_to_ccu_common(hw);
135 u32 val;
136
137 if (!have_iosc_calibration)
138 return;
139
140 val = readl(cm->base + IOSC_CLK_CALI_REG);
141 writel(val & ~(IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL),
142 cm->base + IOSC_CLK_CALI_REG);
143 }
144
ccu_iosc_32k_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)145 static unsigned long ccu_iosc_32k_recalc_rate(struct clk_hw *hw,
146 unsigned long parent_rate)
147 {
148 struct ccu_common *cm = hw_to_ccu_common(hw);
149 u32 val;
150
151 if (have_iosc_calibration) {
152 val = readl(cm->base + IOSC_CLK_CALI_REG);
153
154 /* Assume the calibrated 32k clock is accurate. */
155 if (val & IOSC_CLK_CALI_SRC_SEL)
156 return LOSC_RATE;
157 }
158
159 val = readl(cm->base + IOSC_32K_CLK_DIV_REG) & IOSC_32K_CLK_DIV;
160
161 return parent_rate / IOSC_32K_PRE_DIV / (val + 1);
162 }
163
ccu_iosc_32k_recalc_accuracy(struct clk_hw * hw,unsigned long parent_accuracy)164 static unsigned long ccu_iosc_32k_recalc_accuracy(struct clk_hw *hw,
165 unsigned long parent_accuracy)
166 {
167 struct ccu_common *cm = hw_to_ccu_common(hw);
168 u32 val;
169
170 if (have_iosc_calibration) {
171 val = readl(cm->base + IOSC_CLK_CALI_REG);
172
173 /* Assume the calibrated 32k clock is accurate. */
174 if (val & IOSC_CLK_CALI_SRC_SEL)
175 return 0;
176 }
177
178 return parent_accuracy;
179 }
180
181 static const struct clk_ops ccu_iosc_32k_ops = {
182 .prepare = ccu_iosc_32k_prepare,
183 .unprepare = ccu_iosc_32k_unprepare,
184 .recalc_rate = ccu_iosc_32k_recalc_rate,
185 .recalc_accuracy = ccu_iosc_32k_recalc_accuracy,
186 };
187
188 static struct ccu_common iosc_32k_clk = {
189 .hw.init = CLK_HW_INIT_HW("iosc-32k", &iosc_clk.hw,
190 &ccu_iosc_32k_ops,
191 CLK_GET_RATE_NOCACHE),
192 };
193
194 static const struct clk_hw *ext_osc32k[] = { NULL }; /* updated during probe */
195
196 static SUNXI_CCU_GATE_HWS(ext_osc32k_gate_clk, "ext-osc32k-gate",
197 ext_osc32k, 0x0, BIT(4), 0);
198
199 static const struct clk_hw *osc32k_parents[] = {
200 &iosc_32k_clk.hw,
201 &ext_osc32k_gate_clk.common.hw
202 };
203
204 static struct clk_init_data osc32k_init_data = {
205 .name = "osc32k",
206 .ops = &ccu_mux_ops,
207 .parent_hws = osc32k_parents,
208 .num_parents = ARRAY_SIZE(osc32k_parents), /* updated during probe */
209 };
210
211 static struct ccu_mux osc32k_clk = {
212 .mux = _SUNXI_CCU_MUX(0, 1),
213 .common = {
214 .reg = LOSC_CTRL_REG,
215 .features = CCU_FEATURE_KEY_FIELD,
216 .hw.init = &osc32k_init_data,
217 },
218 };
219
220 /* This falls back to the global name for fwnodes without a named reference. */
221 static const struct clk_parent_data osc24M[] = {
222 { .fw_name = "hosc", .name = "osc24M" }
223 };
224
225 static struct ccu_gate osc24M_32k_clk = {
226 .enable = BIT(16),
227 .common = {
228 .reg = LOSC_OUT_GATING_REG,
229 .prediv = 750,
230 .features = CCU_FEATURE_ALL_PREDIV,
231 .hw.init = CLK_HW_INIT_PARENTS_DATA("osc24M-32k", osc24M,
232 &ccu_gate_ops, 0),
233 },
234 };
235
236 static const struct clk_hw *rtc_32k_parents[] = {
237 &osc32k_clk.common.hw,
238 &osc24M_32k_clk.common.hw
239 };
240
241 static struct clk_init_data rtc_32k_init_data = {
242 .name = "rtc-32k",
243 .ops = &ccu_mux_ops,
244 .parent_hws = rtc_32k_parents,
245 .num_parents = ARRAY_SIZE(rtc_32k_parents), /* updated during probe */
246 .flags = CLK_IS_CRITICAL,
247 };
248
249 static struct ccu_mux rtc_32k_clk = {
250 .mux = _SUNXI_CCU_MUX(1, 1),
251 .common = {
252 .reg = LOSC_CTRL_REG,
253 .features = CCU_FEATURE_KEY_FIELD,
254 .hw.init = &rtc_32k_init_data,
255 },
256 };
257
258 static struct clk_init_data osc32k_fanout_init_data = {
259 .name = "osc32k-fanout",
260 .ops = &ccu_mux_ops,
261 /* parents are set during probe */
262 };
263
264 static struct ccu_mux osc32k_fanout_clk = {
265 .enable = BIT(0),
266 .mux = _SUNXI_CCU_MUX(1, 2),
267 .common = {
268 .reg = LOSC_OUT_GATING_REG,
269 .hw.init = &osc32k_fanout_init_data,
270 },
271 };
272
273 static struct ccu_common *sun6i_rtc_ccu_clks[] = {
274 &iosc_clk,
275 &iosc_32k_clk,
276 &ext_osc32k_gate_clk.common,
277 &osc32k_clk.common,
278 &osc24M_32k_clk.common,
279 &rtc_32k_clk.common,
280 &osc32k_fanout_clk.common,
281 };
282
283 static struct clk_hw_onecell_data sun6i_rtc_ccu_hw_clks = {
284 .num = CLK_NUMBER,
285 .hws = {
286 [CLK_OSC32K] = &osc32k_clk.common.hw,
287 [CLK_OSC32K_FANOUT] = &osc32k_fanout_clk.common.hw,
288 [CLK_IOSC] = &iosc_clk.hw,
289 [CLK_IOSC_32K] = &iosc_32k_clk.hw,
290 [CLK_EXT_OSC32K_GATE] = &ext_osc32k_gate_clk.common.hw,
291 [CLK_OSC24M_32K] = &osc24M_32k_clk.common.hw,
292 [CLK_RTC_32K] = &rtc_32k_clk.common.hw,
293 },
294 };
295
296 static const struct sunxi_ccu_desc sun6i_rtc_ccu_desc = {
297 .ccu_clks = sun6i_rtc_ccu_clks,
298 .num_ccu_clks = ARRAY_SIZE(sun6i_rtc_ccu_clks),
299
300 .hw_clks = &sun6i_rtc_ccu_hw_clks,
301 };
302
303 static const struct clk_parent_data sun50i_h616_osc32k_fanout_parents[] = {
304 { .hw = &osc32k_clk.common.hw },
305 { .fw_name = "pll-32k" },
306 { .hw = &osc24M_32k_clk.common.hw }
307 };
308
309 static const struct clk_parent_data sun50i_r329_osc32k_fanout_parents[] = {
310 { .hw = &osc32k_clk.common.hw },
311 { .hw = &ext_osc32k_gate_clk.common.hw },
312 { .hw = &osc24M_32k_clk.common.hw }
313 };
314
315 static const struct sun6i_rtc_match_data sun50i_h616_rtc_ccu_data = {
316 .have_iosc_calibration = true,
317 .rtc_32k_single_parent = true,
318 .osc32k_fanout_parents = sun50i_h616_osc32k_fanout_parents,
319 .osc32k_fanout_nparents = ARRAY_SIZE(sun50i_h616_osc32k_fanout_parents),
320 };
321
322 static const struct sun6i_rtc_match_data sun50i_r329_rtc_ccu_data = {
323 .have_ext_osc32k = true,
324 .osc32k_fanout_parents = sun50i_r329_osc32k_fanout_parents,
325 .osc32k_fanout_nparents = ARRAY_SIZE(sun50i_r329_osc32k_fanout_parents),
326 };
327
328 static const struct of_device_id sun6i_rtc_ccu_match[] = {
329 {
330 .compatible = "allwinner,sun50i-h616-rtc",
331 .data = &sun50i_h616_rtc_ccu_data,
332 },
333 {
334 .compatible = "allwinner,sun50i-r329-rtc",
335 .data = &sun50i_r329_rtc_ccu_data,
336 },
337 {},
338 };
339
sun6i_rtc_ccu_probe(struct device * dev,void __iomem * reg)340 int sun6i_rtc_ccu_probe(struct device *dev, void __iomem *reg)
341 {
342 const struct sun6i_rtc_match_data *data;
343 struct clk *ext_osc32k_clk = NULL;
344 const struct of_device_id *match;
345
346 /* This driver is only used for newer variants of the hardware. */
347 match = of_match_device(sun6i_rtc_ccu_match, dev);
348 if (!match)
349 return 0;
350
351 data = match->data;
352 have_iosc_calibration = data->have_iosc_calibration;
353
354 if (data->have_ext_osc32k) {
355 const char *fw_name;
356
357 /* ext-osc32k was the only input clock in the old binding. */
358 fw_name = of_property_read_bool(dev->of_node, "clock-names")
359 ? "ext-osc32k" : NULL;
360 ext_osc32k_clk = devm_clk_get_optional(dev, fw_name);
361 if (IS_ERR(ext_osc32k_clk))
362 return PTR_ERR(ext_osc32k_clk);
363 }
364
365 if (ext_osc32k_clk) {
366 /* Link ext-osc32k-gate to its parent. */
367 *ext_osc32k = __clk_get_hw(ext_osc32k_clk);
368 } else {
369 /* ext-osc32k-gate is an orphan, so do not register it. */
370 sun6i_rtc_ccu_hw_clks.hws[CLK_EXT_OSC32K_GATE] = NULL;
371 osc32k_init_data.num_parents = 1;
372 }
373
374 if (data->rtc_32k_single_parent)
375 rtc_32k_init_data.num_parents = 1;
376
377 osc32k_fanout_init_data.parent_data = data->osc32k_fanout_parents;
378 osc32k_fanout_init_data.num_parents = data->osc32k_fanout_nparents;
379
380 return devm_sunxi_ccu_probe(dev, reg, &sun6i_rtc_ccu_desc);
381 }
382
383 MODULE_IMPORT_NS(SUNXI_CCU);
384 MODULE_LICENSE("GPL");
385