• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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