@@ -205,15 +205,18 @@ static void rtc_clk_cpu_freq_to_8m(void)
205205 */
206206static void rtc_clk_cpu_freq_to_cpll_mhz (int cpu_freq_mhz , hal_utils_clk_div_t * div )
207207{
208- // CPLL -> CPU_CLK -> MEM_CLK -> SYS_CLK -> APB_CLK
209- // Constraint: MEM_CLK <= 200MHz, APB_CLK <= 100MHz
210- // This implies that when clock source is CPLL,
211- // If cpu_divider < 2, mem_divider must be larger or equal to 2
212- // If cpu_divider < 2, mem_divider = 2, sys_divider < 2, apb_divider must be larger or equal to 2
213- // Current available configurations:
214- // 360 - 360 - 180 - 180 - 90
215- // 360 - 180 - 180 - 180 - 90
216- // 360 - 90 - 90 - 90 - 90
208+ /**
209+ * Constraint: MEM_CLK <= 200MHz, APB_CLK <= 100MHz
210+ * This implies that when clock source is CPLL,
211+ * If cpu_divider < 2, mem_divider must be larger or equal to 2
212+ * If cpu_divider < 2, mem_divider = 2, sys_divider < 2, apb_divider must be larger or equal to 2
213+ *
214+ * Current available configurations:
215+ * CPLL -> CPU_CLK -> MEM_CLK -> SYS_CLK -> APB_CLK
216+ * 360 div1 360 div2 180 div1 180 div2 90
217+ * 360 div2 180 div1 180 div1 180 div2 90
218+ * 360 div4 90 div1 90 div1 90 div1 90
219+ */
217220 uint32_t mem_divider = 1 ;
218221 uint32_t sys_divider = 1 ; // We are not going to change this
219222 uint32_t apb_divider = 1 ;
@@ -237,16 +240,38 @@ static void rtc_clk_cpu_freq_to_cpll_mhz(int cpu_freq_mhz, hal_utils_clk_div_t *
237240 // To avoid such case, we will strictly do abort here.
238241 abort ();
239242 }
240- // Update bit does not control CPU clock sel mux. Therefore, there may be a middle state during the switch (CPU rises)
241- // Since this is upscaling, we need to configure the frequency division coefficient before switching the clock source.
242- // Otherwise, an intermediate state will occur, in the intermediate state, the frequency of APB/MEM does not meet the
243- // timing requirements. If there are periperals/CPU access that depend on these two clocks at this moment, some exception
244- // might occur.
245- clk_ll_cpu_set_divider (div -> integer , div -> numerator , div -> denominator );
246- clk_ll_mem_set_divider (mem_divider );
247- clk_ll_sys_set_divider (sys_divider );
248- clk_ll_apb_set_divider (apb_divider );
249- clk_ll_bus_update ();
243+
244+ // If it's upscaling, the divider of MEM/SYS/APB needs to be increased, to avoid illegal intermediate states,
245+ // the clock divider should be updated in the order from the APB_CLK to CPU_CLK.
246+ // And if it's downscaling, the divider of MEM/SYS/APB needs to be decreased, the clock divider should be updated
247+ // in the order from the CPU_CLK to APB_CLK.
248+ // Otherwise, an intermediate state will occur, in the intermediate state, the frequency of APB/MEM does not meet
249+ // the timing requirements. If there are periperals/CPU access that depend on these two clocks at this moment, some
250+ // exception might occur.
251+ if (cpu_freq_mhz >= esp_rom_get_cpu_ticks_per_us ()) {
252+ // Frequency Upscaling
253+ clk_ll_apb_set_divider (apb_divider );
254+ clk_ll_bus_update ();
255+ clk_ll_sys_set_divider (sys_divider );
256+ clk_ll_bus_update ();
257+ clk_ll_mem_set_divider (mem_divider );
258+ clk_ll_bus_update ();
259+ clk_ll_cpu_set_divider (div -> integer , div -> numerator , div -> denominator );
260+ clk_ll_bus_update ();
261+ } else {
262+ // Frequency Downscaling
263+ clk_ll_cpu_set_divider (div -> integer , div -> numerator , div -> denominator );
264+ clk_ll_bus_update ();
265+ clk_ll_mem_set_divider (mem_divider );
266+ clk_ll_bus_update ();
267+ clk_ll_sys_set_divider (sys_divider );
268+ clk_ll_bus_update ();
269+ clk_ll_apb_set_divider (apb_divider );
270+ clk_ll_bus_update ();
271+ }
272+
273+ // Update bit does not control CPU clock sel mux, the clock source needs to be switched at
274+ // last to avoid intermediate states.
250275 clk_ll_cpu_set_src (SOC_CPU_CLK_SRC_PLL );
251276 esp_rom_set_cpu_ticks_per_us (cpu_freq_mhz );
252277}
0 commit comments