1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * sunxi RTC ccu driver
4 *
5 * Copyright (c) 2020, DaLv <lvda@allwinnertech.com>
6 */
7
8 #include <linux/clk-provider.h>
9 #include <linux/io.h>
10 #include <linux/of_address.h>
11 #include <linux/platform_device.h>
12 #include <linux/module.h>
13
14 #include "ccu_common.h"
15 #include "ccu_reset.h"
16 #include "ccu_div.h"
17 #include "ccu_gate.h"
18 #include "ccu_mp.h"
19 #include "ccu_mult.h"
20 #include "ccu_nk.h"
21 #include "ccu_nkm.h"
22 #include "ccu_nkmp.h"
23 #include "ccu_nm.h"
24 #include "ccu_phase.h"
25
26 #include "ccu-sun50iw9-rtc.h"
27
28 /*
29 * clock source:
30 * iosc---16M_RC: used by the cpu system directly.
31 * dcxo---24M Crystal: used by the cpu system directly.
32 * osc32k---to make it easier, force it to be a fixed clock source:open auto-switch,
33 * use the 32768 EXT Crystal as default
34 *
35 * the 3 clock source can be treated as the fixed rate clock.
36 * They should be descripted in the dts, being parsed by
37 * clk-fixed-rate.c
38 */
39
40 static CLK_FIXED_FACTOR_FW_NAME(rtc_1k_clk, "rtc-1k", "losc", 32, 1, 0);
41
42 /* pll-periph0-2x-32k, real source is pll-periph0-2x */
43 static CLK_FIXED_FACTOR_FW_NAME(pll_periph0_2x_32k_clk, "pll-periph0-2x-32k", "losc", 1, 1, 0);
44
45 static CLK_FIXED_FACTOR_FW_NAME(dcxo_32k_clk, "dcxo-32k", "dcxo24M", 750, 1, 0);
46
47 static SUNXI_CCU_GATE(dcxo_32k_out_clk, "dcxo-32k-out", "dcxo-32k", 0x60, BIT(16), 0);
48
49 static const char * const osc32k_out_parents[] = { "osc32k", "pll-periph0-2x-32k", "dcxo-32k-out"};
50
51 static SUNXI_CCU_MUX_WITH_GATE(osc32k_out_clk, "osc32k-out", osc32k_out_parents,
52 0x60, 1, 2, BIT(0), 0);
53
54 static struct ccu_common *sun50iw9_rtc_ccu_clks[] = {
55 &osc32k_out_clk.common,
56 &dcxo_32k_out_clk.common,
57 };
58
59 static struct clk_hw_onecell_data sun50iw9_rtc_ccu_hw_clks = {
60 .hws = {
61 [CLK_RTC_1K] = &rtc_1k_clk.hw,
62 [CLK_PLL_PERIPHO_2X_32K] = &pll_periph0_2x_32k_clk.hw,
63 [CLK_DCXO_32K] = &dcxo_32k_clk.hw,
64 [CLK_DCXO_32K_OUT] = &dcxo_32k_out_clk.common.hw,
65 [CLK_OSC32K_OUT] = &osc32k_out_clk.common.hw,
66 },
67 .num = CLK_NUMBER,
68 };
69
70 static const struct sunxi_ccu_desc sun50iw9_rtc_ccu_desc = {
71 .ccu_clks = sun50iw9_rtc_ccu_clks,
72 .num_ccu_clks = ARRAY_SIZE(sun50iw9_rtc_ccu_clks),
73
74 .hw_clks = &sun50iw9_rtc_ccu_hw_clks,
75 };
76
clock_source_init(char __iomem * base)77 static void clock_source_init(char __iomem *base)
78 {
79 /* (1) enable DCXO */
80 /* by default, DCXO_EN = 1. We don't have to do this... */
81 set_reg(base + XO_CTRL_REG, 0x1, 1, 0);
82
83 /* (2) enable calibrated RC-16M, and switch to it */
84 /* set WAKEUP_DCXO_EN is 0 */
85 set_reg(base + CALI_CTRL_REG, 0x0, 1, 31);
86 /*
87 * open Calibrated RC and select it
88 * BIT(0) 0: Normal RC; 1: Calibrated RC
89 * BIT(1) 0: disable; 1: enable
90 */
91 set_reg(base + INTOSC_CLK_AUTO_CALI_REG, 0x3, 2, 0);
92
93 /* (3) enable auto switch function */
94 /*
95 * In some cases, we boot with auto switch function disabled, and try to
96 * enable the auto switch function by rebooting.
97 * But the rtc default value does not change unless vcc-rtc is loss.
98 * So we should not rely on the default value of reg.
99 * BIT(14): LOSC auto switch 32k clk source sel enable. 1: enable
100 * BIT(15): LOSC auto switch function disable. 1: disable
101 */
102 //set_reg(base + LOSC_CTRL_REG, 0x16aa3, 20, 12);
103 set_reg_key(base + LOSC_CTRL_REG,
104 KEY_FIELD_MAGIC, 16, 16,
105 0x1, 2, 14);
106
107 /* (4) set the parent of osc32k-sys to ext-osc32k */
108 set_reg_key(base + LOSC_CTRL_REG,
109 0x16aa, 16, 16,
110 0x1, 1, 0);
111 /*
112 * the 32K fanout has been set in the clk tree
113 * this part can be deleted
114 */
115 /* (5) set the parent of osc32k-out to osc32k-sys*/
116 /* by default, LOSC_OUT_SRC_SEL = 0x0. We don't have to do this... */
117 set_reg(base + LOSC_OUT_GATING_REG,
118 0x0, 2, 1);
119 }
120
sun50iw9_rtc_ccu_probe(struct platform_device * pdev)121 static int sun50iw9_rtc_ccu_probe(struct platform_device *pdev)
122 {
123 struct resource *res;
124 struct device *dev = &pdev->dev;
125 void __iomem *reg;
126
127 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
128 if (!res) {
129 dev_err(dev, "Fail to get IORESOURCE_MEM\n");
130 return -EINVAL;
131 }
132
133 /*
134 * Don't use devm_ioremap_resource() here! Or else the RTC driver will
135 * not able to get the same resource later in rtc-sunxi.c.
136 */
137 reg = devm_ioremap(dev, res->start, resource_size(res));
138 if (IS_ERR(reg)) {
139 dev_err(dev, "Fail to map IO resource\n");
140 return PTR_ERR(reg);
141 }
142
143 clock_source_init(reg);
144
145 return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50iw9_rtc_ccu_desc);
146 }
147
148 static const struct of_device_id sun50iw9_rtc_ccu_ids[] = {
149 { .compatible = "allwinner,sun50iw9-rtc-ccu" },
150 { }
151 };
152
153 static struct platform_driver sun50iw9_rtc_ccu_driver = {
154 .probe = sun50iw9_rtc_ccu_probe,
155 .driver = {
156 .name = "sun50iw9-rtc-ccu",
157 .of_match_table = sun50iw9_rtc_ccu_ids,
158 },
159 };
160 builtin_platform_driver(sun50iw9_rtc_ccu_driver);
161
162 MODULE_DESCRIPTION("sunxi RTC CCU driver");
163 MODULE_AUTHOR("Da Lv <lvda@allwinnertech.com>");
164 MODULE_LICENSE("GPL v2");
165 MODULE_VERSION("1.1.0");
166