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