1 /*
2 * Copyright (c) 2020 Nuvoton Technology Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT nuvoton_npcx_pcc
8
9 #include <soc.h>
10 #include <zephyr/drivers/clock_control.h>
11 #include <zephyr/dt-bindings/clock/npcx_clock.h>
12
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(clock_control_npcx, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
15
16 #if defined(CONFIG_NPCX_SOC_VARIANT_NPCXN)
17 #define NPCX_PWDWN_CTL_START_OFFSET NPCX_PWDWN_CTL1
18 #elif defined(CONFIG_NPCX_SOC_VARIANT_NPCKN)
19 #define NPCX_PWDWN_CTL_START_OFFSET NPCX_PWDWN_CTL0
20 #endif
21
22 /* Driver config */
23 struct npcx_pcc_config {
24 /* cdcg device base address */
25 uintptr_t base_cdcg;
26 /* pmc device base address */
27 uintptr_t base_pmc;
28 };
29
30 /* Driver convenience defines */
31 #define HAL_CDCG_INST(dev) \
32 ((struct cdcg_reg *)((const struct npcx_pcc_config *)(dev)->config)->base_cdcg)
33
34 #define HAL_PMC_INST(dev) \
35 ((struct pmc_reg *)((const struct npcx_pcc_config *)(dev)->config)->base_pmc)
36
37 static uint8_t pddwn_ctl_val[] = {NPCX_PWDWN_CTL_INIT};
38
39 /* Clock controller local functions */
npcx_clock_control_on(const struct device * dev,clock_control_subsys_t sub_system)40 static inline int npcx_clock_control_on(const struct device *dev,
41 clock_control_subsys_t sub_system)
42 {
43 ARG_UNUSED(dev);
44 struct npcx_clk_cfg *clk_cfg = (struct npcx_clk_cfg *)(sub_system);
45 const uint32_t pmc_base = ((const struct npcx_pcc_config *)dev->config)->base_pmc;
46
47 if (clk_cfg->ctrl >= NPCX_PWDWN_CTL_COUNT) {
48 return -EINVAL;
49 }
50
51 /* Clear related PD (Power-Down) bit of module to turn on clock */
52 NPCX_PWDWN_CTL(pmc_base, clk_cfg->ctrl) &= ~(BIT(clk_cfg->bit));
53 return 0;
54 }
55
npcx_clock_control_off(const struct device * dev,clock_control_subsys_t sub_system)56 static inline int npcx_clock_control_off(const struct device *dev,
57 clock_control_subsys_t sub_system)
58 {
59 ARG_UNUSED(dev);
60 struct npcx_clk_cfg *clk_cfg = (struct npcx_clk_cfg *)(sub_system);
61 const uint32_t pmc_base = ((const struct npcx_pcc_config *)dev->config)->base_pmc;
62
63 if (clk_cfg->ctrl >= NPCX_PWDWN_CTL_COUNT) {
64 return -EINVAL;
65 }
66
67 /* Set related PD (Power-Down) bit of module to turn off clock */
68 NPCX_PWDWN_CTL(pmc_base, clk_cfg->ctrl) |= BIT(clk_cfg->bit);
69 return 0;
70 }
71
npcx_clock_control_get_subsys_rate(const struct device * dev,clock_control_subsys_t sub_system,uint32_t * rate)72 static int npcx_clock_control_get_subsys_rate(const struct device *dev,
73 clock_control_subsys_t sub_system,
74 uint32_t *rate)
75 {
76 ARG_UNUSED(dev);
77 struct npcx_clk_cfg *clk_cfg = (struct npcx_clk_cfg *)(sub_system);
78
79 switch (clk_cfg->bus) {
80 case NPCX_CLOCK_BUS_APB1:
81 *rate = NPCX_APB_CLOCK(1);
82 break;
83 case NPCX_CLOCK_BUS_APB2:
84 *rate = NPCX_APB_CLOCK(2);
85 break;
86 case NPCX_CLOCK_BUS_APB3:
87 *rate = NPCX_APB_CLOCK(3);
88 break;
89 #if defined(APB4DIV_VAL)
90 case NPCX_CLOCK_BUS_APB4:
91 *rate = NPCX_APB_CLOCK(4);
92 break;
93 #endif
94 case NPCX_CLOCK_BUS_AHB6:
95 *rate = CORE_CLK/(AHB6DIV_VAL + 1);
96 break;
97 case NPCX_CLOCK_BUS_FIU:
98 *rate = CORE_CLK/(FIUDIV_VAL + 1);
99 break;
100 #if defined(FIU1DIV_VAL)
101 case NPCX_CLOCK_BUS_FIU1:
102 *rate = CORE_CLK/(FIU1DIV_VAL + 1);
103 break;
104 #endif
105 case NPCX_CLOCK_BUS_CORE:
106 *rate = CORE_CLK;
107 break;
108 case NPCX_CLOCK_BUS_LFCLK:
109 *rate = LFCLK;
110 break;
111 case NPCX_CLOCK_BUS_FMCLK:
112 *rate = FMCLK;
113 break;
114 case NPCX_CLOCK_BUS_MCLKD:
115 *rate = OFMCLK/(MCLKD_SL + 1);
116 break;
117 default:
118 *rate = 0U;
119 /* Invalid parameters */
120 return -EINVAL;
121 }
122
123 return 0;
124 }
125
126 /* Platform specific clock controller functions */
127 #if defined(CONFIG_PM)
npcx_clock_control_turn_on_system_sleep(bool is_deep,bool is_instant)128 void npcx_clock_control_turn_on_system_sleep(bool is_deep, bool is_instant)
129 {
130 const struct device *const clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE);
131 struct pmc_reg *const inst_pmc = HAL_PMC_INST(clk_dev);
132 /* Configure that ec enters system sleep mode if receiving 'wfi' */
133 uint8_t pm_flags = BIT(NPCX_PMCSR_IDLE);
134
135 /* Add 'Disable High-Frequency' flag (ie. 'deep sleep' mode) */
136 if (is_deep) {
137 pm_flags |= BIT(NPCX_PMCSR_DHF);
138 /* Add 'Instant Wake-up' flag if sleep time is within 200 ms */
139 if (is_instant) {
140 pm_flags |= BIT(NPCX_PMCSR_DI_INSTW);
141 }
142 }
143
144 inst_pmc->PMCSR = pm_flags;
145 }
146
npcx_clock_control_turn_off_system_sleep(void)147 void npcx_clock_control_turn_off_system_sleep(void)
148 {
149 const struct device *const clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE);
150 struct pmc_reg *const inst_pmc = HAL_PMC_INST(clk_dev);
151
152 inst_pmc->PMCSR = 0;
153 }
154 #endif /* CONFIG_PM */
155
156 /* Clock controller driver registration */
157 static DEVICE_API(clock_control, npcx_clock_control_api) = {
158 .on = npcx_clock_control_on,
159 .off = npcx_clock_control_off,
160 .get_rate = npcx_clock_control_get_subsys_rate,
161 };
162
163 /* valid clock frequency check */
164 BUILD_ASSERT(CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC == OFMCLK / (APB2DIV_VAL + 1),
165 "SYS_CLOCK_HW_CYCLES_PER_SEC must equal to OFMCLK/APB2DIV_VAL");
166 BUILD_ASSERT(OFMCLK <= MAX_OFMCLK, "Exceed maximum OFMCLK setting");
167 BUILD_ASSERT(CORE_CLK <= MAX_OFMCLK && CORE_CLK >= MHZ(4) &&
168 OFMCLK % CORE_CLK == 0 &&
169 OFMCLK / CORE_CLK <= 10,
170 "Invalid CORE_CLK setting");
171 BUILD_ASSERT(CORE_CLK / (FIUDIV_VAL + 1) <= (MAX_OFMCLK / 2) &&
172 CORE_CLK / (FIUDIV_VAL + 1) >= MHZ(4),
173 "Invalid FIUCLK setting");
174 #if defined(FIU1DIV_VAL)
175 BUILD_ASSERT(CORE_CLK / (FIU1DIV_VAL + 1) <= (MAX_OFMCLK / 2) &&
176 CORE_CLK / (FIU1DIV_VAL + 1) >= MHZ(4),
177 "Invalid FIU1CLK setting");
178 #endif
179 BUILD_ASSERT(CORE_CLK / (AHB6DIV_VAL + 1) <= (MAX_OFMCLK / 2) &&
180 CORE_CLK / (AHB6DIV_VAL + 1) >= MHZ(4),
181 "Invalid AHB6_CLK setting");
182 BUILD_ASSERT(APBSRC_CLK / (APB1DIV_VAL + 1) <= (MAX_OFMCLK / 2) &&
183 APBSRC_CLK / (APB1DIV_VAL + 1) >= MHZ(4) &&
184 (APB1DIV_VAL + 1) % (FPRED_VAL + 1) == 0,
185 "Invalid APB1_CLK setting");
186 BUILD_ASSERT(APBSRC_CLK / (APB2DIV_VAL + 1) <= (MAX_OFMCLK / 2) &&
187 APBSRC_CLK / (APB2DIV_VAL + 1) >= MHZ(8) &&
188 (APB2DIV_VAL + 1) % (FPRED_VAL + 1) == 0,
189 "Invalid APB2_CLK setting");
190 BUILD_ASSERT(APBSRC_CLK / (APB3DIV_VAL + 1) <= (MAX_OFMCLK / 2) &&
191 APBSRC_CLK / (APB3DIV_VAL + 1) >= KHZ(12500) &&
192 (APB3DIV_VAL + 1) % (FPRED_VAL + 1) == 0,
193 "Invalid APB3_CLK setting");
194 #if defined(APB4DIV_VAL)
195 BUILD_ASSERT(APBSRC_CLK / (APB4DIV_VAL + 1) <= MAX_OFMCLK &&
196 APBSRC_CLK / (APB4DIV_VAL + 1) >= MHZ(8) &&
197 (APB4DIV_VAL + 1) % (FPRED_VAL + 1) == 0,
198 "Invalid APB4_CLK setting");
199 #endif
200 #if defined(CONFIG_I3C_NPCX)
201 BUILD_ASSERT(OFMCLK / (MCLKD_SL + 1) <= MHZ(50) &&
202 OFMCLK / (MCLKD_SL + 1) >= MHZ(40),
203 "Invalid MCLKD_SL setting");
204 BUILD_ASSERT(APBSRC_CLK / (APB4DIV_VAL + 1) >= MHZ(20),
205 "Invalid PDMA CLK setting");
206 #endif
207
npcx_clock_control_init(const struct device * dev)208 static int npcx_clock_control_init(const struct device *dev)
209 {
210 struct cdcg_reg *const inst_cdcg = HAL_CDCG_INST(dev);
211 const uint32_t pmc_base = ((const struct npcx_pcc_config *)dev->config)->base_pmc;
212
213 #if defined(CONFIG_NPCX_SOC_VARIANT_NPCXN)
214 if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NPCX_EXTERNAL_SRC)) {
215 inst_cdcg->LFCGCTL2 |= BIT(NPCX_LFCGCTL2_XT_OSC_SL_EN);
216 }
217 #endif
218
219 /*
220 * Resetting the OFMCLK (even to the same value) will make the clock
221 * unstable for a little which can affect peripheral communication like
222 * eSPI. Skip this if not needed.
223 */
224 if (inst_cdcg->HFCGN != HFCGN_VAL || inst_cdcg->HFCGML != HFCGML_VAL
225 || inst_cdcg->HFCGMH != HFCGMH_VAL) {
226 /*
227 * Configure frequency multiplier M/N values according to
228 * the requested OFMCLK (Unit:Hz).
229 */
230 inst_cdcg->HFCGN = HFCGN_VAL;
231 inst_cdcg->HFCGML = HFCGML_VAL;
232 inst_cdcg->HFCGMH = HFCGMH_VAL;
233
234 /* Load M and N values into the frequency multiplier */
235 inst_cdcg->HFCGCTRL |= BIT(NPCX_HFCGCTRL_LOAD);
236 /* Wait for stable */
237 while (IS_BIT_SET(inst_cdcg->HFCGCTRL, NPCX_HFCGCTRL_CLK_CHNG)) {
238 ;
239 }
240 }
241
242 /* Set all clock prescalers of core and peripherals. */
243 inst_cdcg->HFCGP = VAL_HFCGP;
244 inst_cdcg->HFCBCD = VAL_HFCBCD;
245 inst_cdcg->HFCBCD1 = VAL_HFCBCD1;
246 inst_cdcg->HFCBCD2 = VAL_HFCBCD2;
247 #if defined(CONFIG_SOC_SERIES_NPCX4)
248 inst_cdcg->HFCBCD3 = VAL_HFCBCD3;
249 #endif
250
251 /*
252 * Power-down (turn off clock) the modules initially for better
253 * power consumption.
254 */
255 for (int i = 0; i < ARRAY_SIZE(pddwn_ctl_val); i++) {
256 NPCX_PWDWN_CTL(pmc_base, i + NPCX_PWDWN_CTL_START_OFFSET) = pddwn_ctl_val[i];
257 }
258
259 /* Turn off the clock of the eSPI module only if eSPI isn't required */
260 if (!IS_ENABLED(CONFIG_ESPI)) {
261 NPCX_PWDWN_CTL(pmc_base, NPCX_PWDWN_CTL6) |= BIT(7);
262 }
263
264 return 0;
265 }
266
267 const struct npcx_pcc_config pcc_config = {
268 .base_cdcg = DT_INST_REG_ADDR_BY_NAME(0, cdcg),
269 .base_pmc = DT_INST_REG_ADDR_BY_NAME(0, pmc),
270 };
271
272 DEVICE_DT_INST_DEFINE(0,
273 npcx_clock_control_init,
274 NULL,
275 NULL, &pcc_config,
276 PRE_KERNEL_1,
277 CONFIG_CLOCK_CONTROL_INIT_PRIORITY,
278 &npcx_clock_control_api);
279