• 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-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