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