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 "ccu.h"
9 #include "ccu_common.h"
10 #include "ccu_reset.h"
11 #include "ccu_div.h"
12 #include "ccu_gate.h"
13 #include "ccu_mp.h"
14 #include "ccu_mult.h"
15 #include "ccu_nk.h"
16 #include "ccu_nkm.h"
17 #include "ccu_nkmp.h"
18 #include "ccu_nm.h"
19 #include "ccu_phase.h"
20
21 #include "ccu-sun8iw20-rtc.h"
22
23 /*
24 * iosc clk:
25 */
26 static SUNXI_CCU_GATE(iosc_clk, "iosc", "rc-16m", 0x160, BIT(0), 0);
27
28 static SUNXI_CCU_GATE_WITH_KEY(ext32k_gate_clk, "ext32k-gate",
29 "ext-32k", 0x0,
30 KEY_FIELD_MAGIC_NUM_RTC,
31 BIT(4), 0);
32
33 static CLK_FIXED_FACTOR(iosc_div32k_clk, "iosc-div32k", "iosc", 500, 1, 0);
34
35 /*
36 * osc32k clk(losc)
37 */
38 static const char *const osc32k_parents[] = { "iosc-div32k", "ext32k-gate" };
39 static SUNXI_CCU_MUX_WITH_GATE_KEY(osc32k_clk, "osc32k", osc32k_parents,
40 0x0, 0, 1,
41 KEY_FIELD_MAGIC_NUM_RTC, 0, 0);
42
43 static SUNXI_CCU_GATE_WITH_FIXED_RATE(dcxo24M_div32k_clk, "dcxo24M-div32k",
44 "dcxo24M", 0x60,
45 32768, BIT(16));
46 /*
47 * rtc-1k clock
48 */
49 static const char *const rtc32k_clk_parents[] = { "osc32k", "dcxo24M-div32k"};
50 static SUNXI_CCU_MUX_WITH_GATE_KEY(rtc32k_clk, "rtc32k", rtc32k_clk_parents,
51 0x0, 1, 1,
52 KEY_FIELD_MAGIC_NUM_RTC, 0, 0);
53 static CLK_FIXED_FACTOR(rtc_1k_clk, "rtc-1k", "rtc32k", 32, 1, 0);
54
55 /* rtc-32k-fanout: only for debug */
56 static const char *const rtc_32k_fanout_clk_parents[] = { "osc32k", "ext32k-gate",
57 "dcxo24M-div32k"
58 };
59 static SUNXI_CCU_MUX_WITH_GATE(rtc_32k_fanout_clk, "rtc-32k-fanout",
60 rtc_32k_fanout_clk_parents, 0x60, 1,
61 2, BIT(0), 0);
62
63 /* TODO: should add the div func */
64 static SUNXI_CCU_GATE(rtc_spi_clk, "rtc-spi", "r-ahb", 0x310, BIT(31), 0);
65
66 static struct ccu_common *sun8iw20_rtc_ccu_clks[] =
67 {
68 &iosc_clk.common,
69 &ext32k_gate_clk.common,
70 &osc32k_clk.common,
71 &dcxo24M_div32k_clk.common,
72 &rtc32k_clk.common,
73 &rtc_32k_fanout_clk.common,
74 &rtc_spi_clk.common,
75 };
76
77 static struct clk_hw_onecell_data sun8iw20_rtc_ccu_hw_clks =
78 {
79 .hws = {
80 [CLK_IOSC] = &iosc_clk.common.hw,
81 [CLK_EXT32K_GATE] = &ext32k_gate_clk.common.hw,
82 [CLK_IOSC_DIV32K] = &iosc_div32k_clk.hw,
83 [CLK_OSC32K] = &osc32k_clk.common.hw,
84 [CLK_DCXO24M_DIV32K] = &dcxo24M_div32k_clk.common.hw,
85 [CLK_RTC32K] = &rtc32k_clk.common.hw,
86 [CLK_RTC_1K] = &rtc_1k_clk.hw,
87 [CLK_RTC_32K_FANOUT] = &rtc_32k_fanout_clk.common.hw,
88 [CLK_RTC_SPI] = &rtc_spi_clk.common.hw,
89 },
90 .num = CLK_RTC_NUMBER,
91 };
92
93 static const struct sunxi_ccu_desc sun8iw20_rtc_ccu_desc =
94 {
95 .ccu_clks = sun8iw20_rtc_ccu_clks,
96 .num_ccu_clks = ARRAY_SIZE(sun8iw20_rtc_ccu_clks),
97
98 .hw_clks = &sun8iw20_rtc_ccu_hw_clks,
99 .clk_type = HAL_SUNXI_RTC_CCU,
100 };
101
clock_source_init(unsigned long base)102 static void clock_source_init(unsigned long 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
sunxi_rtc_ccu_init(void)132 int sunxi_rtc_ccu_init(void)
133 {
134 unsigned long reg = (unsigned long)SUNXI_RTC_CCU_REG;
135
136 clock_source_init(reg);
137
138 return ccu_common_init(reg, &sun8iw20_rtc_ccu_desc);
139 }
140