GNU Linux-libre 6.7.9-gnu
[releases.git] / drivers / clk / sunxi-ng / ccu-sun6i-rtc.c
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
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
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
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
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
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
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
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
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
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
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");