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