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, LOG_LEVEL_ERR);
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 	default:
109 		*rate = 0U;
110 		/* Invalid parameters */
111 		return -EINVAL;
112 	}
113 
114 	return 0;
115 }
116 
117 /* Platform specific clock controller functions */
118 #if defined(CONFIG_PM)
npcx_clock_control_turn_on_system_sleep(bool is_deep,bool is_instant)119 void npcx_clock_control_turn_on_system_sleep(bool is_deep, bool is_instant)
120 {
121 	const struct device *const clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE);
122 	struct pmc_reg *const inst_pmc = HAL_PMC_INST(clk_dev);
123 	/* Configure that ec enters system sleep mode if receiving 'wfi' */
124 	uint8_t pm_flags = BIT(NPCX_PMCSR_IDLE);
125 
126 	/* Add 'Disable High-Frequency' flag (ie. 'deep sleep' mode) */
127 	if (is_deep) {
128 		pm_flags |= BIT(NPCX_PMCSR_DHF);
129 		/* Add 'Instant Wake-up' flag if sleep time is within 200 ms */
130 		if (is_instant)
131 			pm_flags |= BIT(NPCX_PMCSR_DI_INSTW);
132 	}
133 
134 	inst_pmc->PMCSR = pm_flags;
135 }
136 
npcx_clock_control_turn_off_system_sleep(void)137 void npcx_clock_control_turn_off_system_sleep(void)
138 {
139 	const struct device *const clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE);
140 	struct pmc_reg *const inst_pmc = HAL_PMC_INST(clk_dev);
141 
142 	inst_pmc->PMCSR = 0;
143 }
144 #endif /* CONFIG_PM */
145 
146 /* Clock controller driver registration */
147 static struct clock_control_driver_api npcx_clock_control_api = {
148 	.on = npcx_clock_control_on,
149 	.off = npcx_clock_control_off,
150 	.get_rate = npcx_clock_control_get_subsys_rate,
151 };
152 
153 /* valid clock frequency check */
154 BUILD_ASSERT(OFMCLK <= MAX_OFMCLK, "Exceed maximum OFMCLK setting");
155 BUILD_ASSERT(CORE_CLK <= MAX_OFMCLK && CORE_CLK >= MHZ(4) &&
156 	     OFMCLK % CORE_CLK == 0 &&
157 	     OFMCLK / CORE_CLK <= 10,
158 	     "Invalid CORE_CLK setting");
159 BUILD_ASSERT(CORE_CLK / (FIUDIV_VAL + 1) <= (MAX_OFMCLK / 2) &&
160 	     CORE_CLK / (FIUDIV_VAL + 1) >= MHZ(4),
161 	     "Invalid FIUCLK setting");
162 #if defined(FIU1DIV_VAL)
163 BUILD_ASSERT(CORE_CLK / (FIU1DIV_VAL + 1) <= (MAX_OFMCLK / 2) &&
164 	     CORE_CLK / (FIU1DIV_VAL + 1) >= MHZ(4),
165 	     "Invalid FIU1CLK setting");
166 #endif
167 BUILD_ASSERT(CORE_CLK / (AHB6DIV_VAL + 1) <= (MAX_OFMCLK / 2) &&
168 	     CORE_CLK / (AHB6DIV_VAL + 1) >= MHZ(4),
169 	     "Invalid AHB6_CLK setting");
170 BUILD_ASSERT(APBSRC_CLK / (APB1DIV_VAL + 1) <= (MAX_OFMCLK / 2) &&
171 	     APBSRC_CLK / (APB1DIV_VAL + 1) >= MHZ(4) &&
172 	     (APB1DIV_VAL + 1) % (FPRED_VAL + 1) == 0,
173 	     "Invalid APB1_CLK setting");
174 BUILD_ASSERT(APBSRC_CLK / (APB2DIV_VAL + 1) <= (MAX_OFMCLK / 2) &&
175 	     APBSRC_CLK / (APB2DIV_VAL + 1) >= MHZ(8) &&
176 	     (APB2DIV_VAL + 1) % (FPRED_VAL + 1) == 0,
177 	     "Invalid APB2_CLK setting");
178 BUILD_ASSERT(APBSRC_CLK / (APB3DIV_VAL + 1) <= (MAX_OFMCLK / 2) &&
179 	     APBSRC_CLK / (APB3DIV_VAL + 1) >= KHZ(12500) &&
180 	     (APB3DIV_VAL + 1) % (FPRED_VAL + 1) == 0,
181 	     "Invalid APB3_CLK setting");
182 #if defined(APB4DIV_VAL)
183 BUILD_ASSERT(APBSRC_CLK / (APB4DIV_VAL + 1) <= MAX_OFMCLK &&
184 	     APBSRC_CLK / (APB4DIV_VAL + 1) >= MHZ(8) &&
185 	     (APB4DIV_VAL + 1) % (FPRED_VAL + 1) == 0,
186 	     "Invalid APB4_CLK setting");
187 #endif
188 
npcx_clock_control_init(const struct device * dev)189 static int npcx_clock_control_init(const struct device *dev)
190 {
191 	struct cdcg_reg *const inst_cdcg = HAL_CDCG_INST(dev);
192 	const uint32_t pmc_base = ((const struct npcx_pcc_config *)dev->config)->base_pmc;
193 
194 	if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NPCX_EXTERNAL_SRC)) {
195 		inst_cdcg->LFCGCTL2 |= BIT(NPCX_LFCGCTL2_XT_OSC_SL_EN);
196 	}
197 
198 	/*
199 	 * Resetting the OFMCLK (even to the same value) will make the clock
200 	 * unstable for a little which can affect peripheral communication like
201 	 * eSPI. Skip this if not needed.
202 	 */
203 	if (inst_cdcg->HFCGN != HFCGN_VAL || inst_cdcg->HFCGML != HFCGML_VAL
204 			|| inst_cdcg->HFCGMH != HFCGMH_VAL) {
205 		/*
206 		 * Configure frequency multiplier M/N values according to
207 		 * the requested OFMCLK (Unit:Hz).
208 		 */
209 		inst_cdcg->HFCGN  = HFCGN_VAL;
210 		inst_cdcg->HFCGML = HFCGML_VAL;
211 		inst_cdcg->HFCGMH = HFCGMH_VAL;
212 
213 		/* Load M and N values into the frequency multiplier */
214 		inst_cdcg->HFCGCTRL |= BIT(NPCX_HFCGCTRL_LOAD);
215 		/* Wait for stable */
216 		while (IS_BIT_SET(inst_cdcg->HFCGCTRL, NPCX_HFCGCTRL_CLK_CHNG))
217 			;
218 	}
219 
220 	/* Set all clock prescalers of core and peripherals. */
221 	inst_cdcg->HFCGP   = VAL_HFCGP;
222 	inst_cdcg->HFCBCD  = VAL_HFCBCD;
223 	inst_cdcg->HFCBCD1 = VAL_HFCBCD1;
224 	inst_cdcg->HFCBCD2 = VAL_HFCBCD2;
225 
226 	/*
227 	 * Power-down (turn off clock) the modules initially for better
228 	 * power consumption.
229 	 */
230 	for (int i = 0; i < ARRAY_SIZE(pddwn_ctl_val); i++) {
231 		NPCX_PWDWN_CTL(pmc_base, i) = pddwn_ctl_val[i];
232 	}
233 
234 	/* Turn off the clock of the eSPI module only if eSPI isn't required */
235 	if (!IS_ENABLED(CONFIG_ESPI)) {
236 		NPCX_PWDWN_CTL(pmc_base, NPCX_PWDWN_CTL6) |= BIT(7);
237 	}
238 
239 	return 0;
240 }
241 
242 const struct npcx_pcc_config pcc_config = {
243 	.base_cdcg = DT_INST_REG_ADDR_BY_NAME(0, cdcg),
244 	.base_pmc  = DT_INST_REG_ADDR_BY_NAME(0, pmc),
245 };
246 
247 DEVICE_DT_INST_DEFINE(0,
248 		    &npcx_clock_control_init,
249 		    NULL,
250 		    NULL, &pcc_config,
251 		    PRE_KERNEL_1,
252 		    CONFIG_CLOCK_CONTROL_INIT_PRIORITY,
253 		    &npcx_clock_control_api);
254