1 /*
2  * Copyright (c) 2023 Nuvoton Technology Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nuvoton_numaker_scc
8 
9 #include <zephyr/drivers/clock_control.h>
10 #include <zephyr/drivers/clock_control/clock_control_numaker.h>
11 #include <zephyr/logging/log.h>
12 #include <NuMicro.h>
13 
14 LOG_MODULE_REGISTER(clock_control_numaker_scc, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
15 
16 struct numaker_scc_config {
17 	uint32_t clk_base;
18 	int hxt;
19 	int lxt;
20 	int hirc48;
21 	uint32_t clk_pclkdiv;
22 	uint32_t core_clock;
23 };
24 
25 #if defined(CONFIG_SOC_SERIES_M55M1X)
26 static const uint64_t numaker_clkmodidx_tab[] = {
27 	0x0000000000000000, 0x0000000000000400, 0x0000800000000000, 0x0001008000800000,
28 	0x0001008000800480, 0x0001810203FF8000, 0x0001810203FF8488, 0x0002018003800000,
29 	0x0002800000000000, 0x0003000000000000, 0x0003800000000000, 0x0004028283FF8000,
30 	0x0004028001800080, 0x0004830301FF8000, 0x0005000000000000, 0x0005800000000000,
31 	0x0005800000000400, 0x0005800000000800, 0x0005800000000C00, 0x0006000000000000,
32 	0x0006838000800000, 0x0006838000800480, 0x0007000000000000, 0x0007000000000400,
33 	0x0007000000000800, 0x0007000000000C00, 0x0007840000800000, 0x0007800000004000,
34 	0x0008000000000000, 0x0008800000000000, 0x0008800000000400, 0x0008800000000800,
35 	0x0008800000000C00, 0x0008800000001000, 0x0008800000001400, 0x0008800000001800,
36 	0x0008800000001C00, 0x0008800000002000, 0x0008800000002400, 0x0009000000000000,
37 	0x0009800000000000, 0x000A000000000000, 0x000A800000000000, 0x000A800000000400,
38 	0x000A800000000800, 0x000A800000000C00, 0x000B048383FF8000, 0x000B048383FF8488,
39 	0x000B850000800000, 0x000C000000000000, 0x000C858401FF8000, 0x000D000000000000,
40 	0x000D860481FF8000, 0x000E000000000000, 0x000E800000000000, 0x000F000000000000,
41 	0x000F868001800000, 0x0010000000000000, 0x0010870003800000, 0x0010870003800480,
42 	0x0011078503878000, 0x0011800000000000, 0x0012800000000000, 0x0013000000000000,
43 	0x0013800000000000, 0x0013800000000400, 0x0014080583FF8000, 0x0014888003800000,
44 	0x0014888003800480, 0x0015000000000000, 0x0015890603FF8000, 0x0015890603FF8488,
45 	0x0015890603FF8910, 0x0016000000000000, 0x0016898683FF8000, 0x0016898683FF8488,
46 	0x00170A0003800000, 0x00170A0003800480, 0x00170A0003800900, 0x00170A0003800D80,
47 	0x0017800000000000, 0x0018000000000000, 0x0018000000000400, 0x0018000000000800,
48 	0x0018000000000C00, 0x0019800000000000, 0x001A8B0003800000, 0x001A8B0003800480,
49 	0x001A8B0003800900, 0x001A8B0003800D80, 0x001B000000000000, 0x001B8B8003800000,
50 	0x001B8B8003800480, 0x001C0C0783878000, 0x001C0C0783878484, 0x001C0C0783878908,
51 	0x001C0C0783878D8C, 0x001C0C0783879210, 0x001C0C0783879694, 0x001C0C0783879B18,
52 	0x001C0C0783879F9C, 0x001C0C880387A000, 0x001C0C880387A484, 0x001C8D0880878000,
53 	0x001D0D0880878000, 0x001D800000000000, 0x001E000000000000, 0x001E8D8000800000,
54 	0x001E8D8000800480, 0x001F0F8000800000, 0x001F0F8000800480,
55 };
56 #endif
57 
numaker_scc_on(const struct device * dev,clock_control_subsys_t subsys)58 static inline int numaker_scc_on(const struct device *dev, clock_control_subsys_t subsys)
59 {
60 	ARG_UNUSED(dev);
61 
62 	struct numaker_scc_subsys *scc_subsys = (struct numaker_scc_subsys *)subsys;
63 
64 	if (scc_subsys->subsys_id == NUMAKER_SCC_SUBSYS_ID_PCC) {
65 		SYS_UnlockReg();
66 #if defined(CONFIG_SOC_SERIES_M55M1X)
67 		__ASSERT_NO_MSG(scc_subsys->pcc.clk_modidx < ARRAY_SIZE(numaker_clkmodidx_tab));
68 		CLK_EnableModuleClock(numaker_clkmodidx_tab[scc_subsys->pcc.clk_modidx]);
69 #else
70 		CLK_EnableModuleClock(scc_subsys->pcc.clk_modidx);
71 #endif
72 		SYS_LockReg();
73 	} else {
74 		return -EINVAL;
75 	}
76 
77 	return 0;
78 }
79 
numaker_scc_off(const struct device * dev,clock_control_subsys_t subsys)80 static inline int numaker_scc_off(const struct device *dev, clock_control_subsys_t subsys)
81 {
82 	ARG_UNUSED(dev);
83 
84 	struct numaker_scc_subsys *scc_subsys = (struct numaker_scc_subsys *)subsys;
85 
86 	if (scc_subsys->subsys_id == NUMAKER_SCC_SUBSYS_ID_PCC) {
87 		SYS_UnlockReg();
88 #if defined(CONFIG_SOC_SERIES_M55M1X)
89 		__ASSERT_NO_MSG(scc_subsys->pcc.clk_modidx < ARRAY_SIZE(numaker_clkmodidx_tab));
90 		CLK_DisableModuleClock(numaker_clkmodidx_tab[scc_subsys->pcc.clk_modidx]);
91 #else
92 		CLK_DisableModuleClock(scc_subsys->pcc.clk_modidx);
93 #endif
94 		SYS_LockReg();
95 	} else {
96 		return -EINVAL;
97 	}
98 
99 	return 0;
100 }
101 
numaker_scc_get_rate(const struct device * dev,clock_control_subsys_t subsys,uint32_t * rate)102 static inline int numaker_scc_get_rate(const struct device *dev, clock_control_subsys_t subsys,
103 				       uint32_t *rate)
104 {
105 	ARG_UNUSED(dev);
106 	ARG_UNUSED(subsys);
107 	ARG_UNUSED(rate);
108 	return -ENOTSUP;
109 }
110 
numaker_scc_set_rate(const struct device * dev,clock_control_subsys_t subsys,clock_control_subsys_rate_t rate)111 static inline int numaker_scc_set_rate(const struct device *dev, clock_control_subsys_t subsys,
112 				       clock_control_subsys_rate_t rate)
113 {
114 	ARG_UNUSED(dev);
115 	ARG_UNUSED(subsys);
116 	ARG_UNUSED(rate);
117 	return -ENOTSUP;
118 }
119 
numaker_scc_configure(const struct device * dev,clock_control_subsys_t subsys,void * data)120 static inline int numaker_scc_configure(const struct device *dev, clock_control_subsys_t subsys,
121 					void *data)
122 {
123 	ARG_UNUSED(dev);
124 	ARG_UNUSED(data);
125 
126 	struct numaker_scc_subsys *scc_subsys = (struct numaker_scc_subsys *)subsys;
127 
128 	if (scc_subsys->subsys_id == NUMAKER_SCC_SUBSYS_ID_PCC) {
129 		SYS_UnlockReg();
130 #if defined(CONFIG_SOC_SERIES_M55M1X)
131 		__ASSERT_NO_MSG(scc_subsys->pcc.clk_modidx < ARRAY_SIZE(numaker_clkmodidx_tab));
132 		CLK_SetModuleClock(numaker_clkmodidx_tab[scc_subsys->pcc.clk_modidx],
133 				   scc_subsys->pcc.clk_src,
134 				   scc_subsys->pcc.clk_div);
135 #else
136 		CLK_SetModuleClock(scc_subsys->pcc.clk_modidx, scc_subsys->pcc.clk_src,
137 				   scc_subsys->pcc.clk_div);
138 #endif
139 		SYS_LockReg();
140 	} else {
141 		return -EINVAL;
142 	}
143 
144 	return 0;
145 }
146 
147 /* System clock controller driver registration */
148 static DEVICE_API(clock_control, numaker_scc_api) = {
149 	.on = numaker_scc_on,
150 	.off = numaker_scc_off,
151 	.get_rate = numaker_scc_get_rate,
152 	.set_rate = numaker_scc_set_rate,
153 	.configure = numaker_scc_configure,
154 };
155 
156 /* At most one compatible with status "okay" */
157 BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) <= 1,
158 	     "Requires at most one compatible with status \"okay\"");
159 
160 #define LOG_OSC_SW(osc, sw)                                                                        \
161 	if (sw == NUMAKER_SCC_CLKSW_ENABLE) {                                                      \
162 		LOG_DBG("Enable " #osc);                                                           \
163 	} else if (sw == NUMAKER_SCC_CLKSW_DISABLE) {                                              \
164 		LOG_DBG("Disable " #osc);                                                          \
165 	}
166 
numaker_scc_init(const struct device * dev)167 static int numaker_scc_init(const struct device *dev)
168 {
169 	const struct numaker_scc_config *cfg = dev->config;
170 
171 	LOG_DBG("CLK base: 0x%08x", cfg->clk_base);
172 #if DT_NODE_HAS_PROP(DT_NODELABEL(scc), hxt)
173 	LOG_OSC_SW(HXT, cfg->hxt);
174 #endif
175 #if DT_NODE_HAS_PROP(DT_NODELABEL(scc), lxt)
176 	LOG_OSC_SW(LXT, cfg->lxt);
177 #endif
178 #if DT_NODE_HAS_PROP(DT_NODELABEL(scc), hirc48)
179 	LOG_OSC_SW(HIRC48, cfg->hirc48);
180 #endif
181 #if DT_NODE_HAS_PROP(DT_NODELABEL(scc), clk_pclkdiv)
182 	LOG_DBG("CLK_PCLKDIV: 0x%08x", cfg->clk_pclkdiv);
183 #endif
184 #if DT_NODE_HAS_PROP(DT_NODELABEL(scc), core_clock)
185 	LOG_DBG("Core clock: %d (Hz)", cfg->core_clock);
186 #endif
187 
188 	/*
189 	 * soc_reset_hook() will respect above configurations and
190 	 * actually take charge of system clock control initialization.
191 	 */
192 
193 	SystemCoreClockUpdate();
194 	LOG_DBG("SystemCoreClock: %d (Hz)", SystemCoreClock);
195 
196 	return 0;
197 }
198 
199 #define NUMICRO_SCC_INIT(inst)                                                                     \
200 	static const struct numaker_scc_config numaker_scc_config_##inst = {                       \
201 		.clk_base = DT_INST_REG_ADDR(inst),                                                \
202 		.hxt = DT_INST_ENUM_IDX_OR(inst, hxt, NUMAKER_SCC_CLKSW_UNTOUCHED),                \
203 		.lxt = DT_INST_ENUM_IDX_OR(inst, lxt, NUMAKER_SCC_CLKSW_UNTOUCHED),                \
204 		.hirc48 = DT_INST_ENUM_IDX_OR(inst, hirc48, NUMAKER_SCC_CLKSW_UNTOUCHED),          \
205 		.clk_pclkdiv = DT_INST_PROP_OR(inst, clk_pclkdiv, 0),                              \
206 		.core_clock = DT_INST_PROP_OR(inst, core_clock, 0),                                \
207 	};                                                                                         \
208                                                                                                    \
209 	DEVICE_DT_INST_DEFINE(inst, numaker_scc_init, NULL, NULL, &numaker_scc_config_##inst,     \
210 			      PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &numaker_scc_api);
211 
212 DT_INST_FOREACH_STATUS_OKAY(NUMICRO_SCC_INIT);
213