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-sun8iw20-rtc.h"
27
28 /*
29 * iosc clk:
30 */
31 static SUNXI_CCU_GATE(iosc_clk, "iosc", "rc-16m", 0x160, BIT(0), 0);
32
33 static SUNXI_CCU_GATE_WITH_KEY(ext32k_gate_clk, "ext32k-gate",
34 "ext-32k", 0x0,
35 KEY_FIELD_MAGIC_NUM_RTC,
36 BIT(4), 0);
37
38 static CLK_FIXED_FACTOR(iosc_div32k_clk, "iosc-div32k", "iosc", 500, 1, 0);
39
40 /*
41 * osc32k clk(losc)
42 */
43 static const char * const osc32k_parents[] = { "iosc-div32k", "ext32k-gate" };
44 static SUNXI_CCU_MUX_WITH_GATE_KEY(osc32k_clk, "osc32k", osc32k_parents,
45 0x0, 0, 1,
46 KEY_FIELD_MAGIC_NUM_RTC, 0, 0);
47
48 static SUNXI_CCU_GATE_WITH_FIXED_RATE(dcxo24M_div32k_clk, "dcxo24M-div32k",
49 "dcxo24M", 0x60,
50 32768, BIT(16));
51 /*
52 * rtc-1k clock
53 */
54 static const char * const rtc32k_clk_parents[] = { "osc32k", "dcxo24M-div32k"};
55 static SUNXI_CCU_MUX_WITH_GATE_KEY(rtc32k_clk, "rtc32k", rtc32k_clk_parents,
56 0x0, 1, 1,
57 KEY_FIELD_MAGIC_NUM_RTC, 0, 0);
58 static CLK_FIXED_FACTOR(rtc_1k_clk, "rtc-1k", "rtc32k", 32, 1, 0);
59
60 /* rtc-32k-fanout: only for debug */
61 static const char * const rtc_32k_fanout_clk_parents[] = { "osc32k", "ext32k-gate",
62 "dcxo24M-div32k"};
63 static SUNXI_CCU_MUX_WITH_GATE(rtc_32k_fanout_clk, "rtc-32k-fanout",
64 rtc_32k_fanout_clk_parents, 0x60, 1,
65 2, BIT(0), 0);
66
67 /* TODO: should add the div func */
68 static SUNXI_CCU_GATE(rtc_spi_clk, "rtc-spi", "r-ahb", 0x310, BIT(31), 0);
69
70 static struct ccu_common *sun8iw20_rtc_ccu_clks[] = {
71 &iosc_clk.common,
72 &ext32k_gate_clk.common,
73 &osc32k_clk.common,
74 &dcxo24M_div32k_clk.common,
75 &rtc32k_clk.common,
76 &rtc_32k_fanout_clk.common,
77 &rtc_spi_clk.common,
78 };
79
80 static struct clk_hw_onecell_data sun8iw20_rtc_ccu_hw_clks = {
81 .hws = {
82 [CLK_IOSC] = &iosc_clk.common.hw,
83 [CLK_EXT32K_GATE] = &ext32k_gate_clk.common.hw,
84 [CLK_IOSC_DIV32K] = &iosc_div32k_clk.hw,
85 [CLK_OSC32K] = &osc32k_clk.common.hw,
86 [CLK_DCXO24M_DIV32K] = &dcxo24M_div32k_clk.common.hw,
87 [CLK_RTC32K] = &rtc32k_clk.common.hw,
88 [CLK_RTC_1K] = &rtc_1k_clk.hw,
89 [CLK_RTC_32K_FANOUT] = &rtc_32k_fanout_clk.common.hw,
90 [CLK_RTC_SPI] = &rtc_spi_clk.common.hw,
91 },
92 .num = CLK_NUMBER,
93 };
94
95 static const struct sunxi_ccu_desc sun8iw20_rtc_ccu_desc = {
96 .ccu_clks = sun8iw20_rtc_ccu_clks,
97 .num_ccu_clks = ARRAY_SIZE(sun8iw20_rtc_ccu_clks),
98
99 .hw_clks = &sun8iw20_rtc_ccu_hw_clks,
100 };
101
clock_source_init(char __iomem * base)102 static void clock_source_init(char __iomem *base)
103 {
104 /* (1) enable DCXO */
105 /* by default, DCXO_EN = 1. We don't have to do this... */
106 set_reg(base + XO_CTRL_REG, 0x1, 1, 1);
107
108 /* (2) enable auto switch function */
109 /*
110 * In some cases, we boot with auto switch function disabled, and try to
111 * enable the auto switch function by rebooting.
112 * But the rtc default value does not change unless vcc-rtc is loss.
113 * So we should not rely on the default value of reg.
114 * BIT(14): LOSC auto switch 32k clk source sel enable. 1: enable
115 * BIT(15): LOSC auto switch function disable. 1: disable
116 */
117 set_reg_key(base + LOSC_CTRL_REG,
118 KEY_FIELD_MAGIC_NUM_RTC >> 16, 16, 16,
119 0x1, 2, 14);
120
121 /* (3) set the parent of osc32k-sys to ext-osc32k */
122 set_reg_key(base + LOSC_CTRL_REG,
123 KEY_FIELD_MAGIC_NUM_RTC >> 16, 16, 16,
124 0x1, 1, 0);
125
126 /* (4) set the parent of osc32k-out to osc32k-sys */
127 /* by default, LOSC_OUT_SRC_SEL = 0x0. We don't have to do this... */
128 set_reg(base + LOSC_OUT_GATING_REG,
129 0x0, 2, 1);
130 }
131
sun8iw20_rtc_ccu_probe(struct platform_device * pdev)132 static int sun8iw20_rtc_ccu_probe(struct platform_device *pdev)
133 {
134 struct resource *res;
135 struct device *dev = &pdev->dev;
136 void __iomem *reg;
137
138 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
139 if (!res) {
140 dev_err(dev, "Fail to get IORESOURCE_MEM\n");
141 return -EINVAL;
142 }
143
144 /*
145 * Don't use devm_ioremap_resource() here! Or else the RTC driver will
146 * not able to get the same resource later in rtc-sunxi.c.
147 */
148 reg = devm_ioremap(dev, res->start, resource_size(res));
149 if (IS_ERR(reg)) {
150 dev_err(dev, "Fail to map IO resource\n");
151 return PTR_ERR(reg);
152 }
153
154 clock_source_init(reg);
155
156 return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun8iw20_rtc_ccu_desc);
157 }
158
159 static const struct of_device_id sun8iw20_rtc_ccu_ids[] = {
160 { .compatible = "allwinner,sun8iw20-rtc-ccu" },
161 { .compatible = "allwinner,sun20iw1-rtc-ccu" },
162 { }
163 };
164
165 static struct platform_driver sun8iw20_rtc_ccu_driver = {
166 .probe = sun8iw20_rtc_ccu_probe,
167 .driver = {
168 .name = "sun8iw20-rtc-ccu",
169 .of_match_table = sun8iw20_rtc_ccu_ids,
170 },
171 };
172
sun8iw20_rtc_ccu_init(void)173 static int __init sun8iw20_rtc_ccu_init(void)
174 {
175 int err;
176
177 err = platform_driver_register(&sun8iw20_rtc_ccu_driver);
178 if (err)
179 pr_err("Fail to register sunxi_rtc_ccu as platform device\n");
180
181 return err;
182 }
183 core_initcall(sun8iw20_rtc_ccu_init);
184
sun8iw20_rtc_ccu_exit(void)185 static void __exit sun8iw20_rtc_ccu_exit(void)
186 {
187 platform_driver_unregister(&sun8iw20_rtc_ccu_driver);
188 }
189 module_exit(sun8iw20_rtc_ccu_exit);
190
191 MODULE_DESCRIPTION("sunxi RTC CCU driver");
192 MODULE_AUTHOR("Da Lv <lvda@allwinnertech.com>");
193 MODULE_LICENSE("GPL v2");
194 MODULE_VERSION("1.1.0");
195