diff --git a/drivers/clock_control/clock_stm32_ll_common.c b/drivers/clock_control/clock_stm32_ll_common.c index f1f8b7436fca2e..a815488329d93b 100644 --- a/drivers/clock_control/clock_stm32_ll_common.c +++ b/drivers/clock_control/clock_stm32_ll_common.c @@ -215,6 +215,13 @@ int enabled_clock(uint32_t src_clk) } break; #endif /* STM32_SRC_PLL_R */ +#if defined(STM32_SRC_PLLI2S_Q) + case STM32_SRC_PLLI2S_Q: + if (!IS_ENABLED(STM32_PLLI2S_Q_ENABLED)) { + r = -ENOTSUP; + } + break; +#endif /* STM32_SRC_PLLI2S_Q */ #if defined(STM32_SRC_PLLI2S_R) case STM32_SRC_PLLI2S_R: if (!IS_ENABLED(STM32_PLLI2S_R_ENABLED)) { @@ -426,6 +433,14 @@ static int stm32_clock_control_get_subsys_rate(const struct device *clock, STM32_PLL_R_DIVISOR); break; #endif +#if defined(STM32_SRC_PLLI2S_Q) & STM32_PLLI2S_ENABLED + case STM32_SRC_PLLI2S_Q: + *rate = get_pll_div_frequency(get_pllsrc_frequency(), + STM32_PLLI2S_M_DIVISOR, + STM32_PLLI2S_N_MULTIPLIER, + STM32_PLLI2S_Q_DIVISOR); + break; +#endif /* STM32_SRC_PLLI2S_Q */ #if defined(STM32_SRC_PLLI2S_R) & STM32_PLLI2S_ENABLED case STM32_SRC_PLLI2S_R: *rate = get_pll_div_frequency(get_pllsrc_frequency(), @@ -434,6 +449,7 @@ static int stm32_clock_control_get_subsys_rate(const struct device *clock, STM32_PLLI2S_R_DIVISOR); break; #endif /* STM32_SRC_PLLI2S_R */ + /* PLLSAI1x not supported yet */ /* PLLSAI2x not supported yet */ #if defined(STM32_SRC_LSE) @@ -466,6 +482,12 @@ static int stm32_clock_control_get_subsys_rate(const struct device *clock, *rate = STM32_HSI48_FREQ; break; #endif /* STM32_HSI48_ENABLED */ +#if defined(STM32_CK48_ENABLED) + case STM32_SRC_CK48: + *rate = get_ck48_frequency(); + break; +#endif /* STM32_CK48_ENABLED */ + default: return -ENOTSUP; } diff --git a/drivers/clock_control/clock_stm32_ll_common.h b/drivers/clock_control/clock_stm32_ll_common.h index e13fb34c2d3e1f..aff2532c57c0f4 100644 --- a/drivers/clock_control/clock_stm32_ll_common.h +++ b/drivers/clock_control/clock_stm32_ll_common.h @@ -30,6 +30,9 @@ #define z_plli2s_m(v) LL_RCC_PLLI2SM_DIV_ ## v #define plli2sm(v) z_plli2s_m(v) +#define z_plli2s_q(v) LL_RCC_PLLI2SQ_DIV_ ## v +#define plli2sq(v) z_plli2s_q(v) + #define z_plli2s_r(v) LL_RCC_PLLI2SR_DIV_ ## v #define plli2sr(v) z_plli2s_r(v) @@ -52,6 +55,10 @@ void config_enable_default_clocks(void); void config_regulator_voltage(uint32_t hclk_freq); int enabled_clock(uint32_t src_clk); +#if defined(STM32_CK48_ENABLED) +uint32_t get_ck48_frequency(void); +#endif + /* functions exported to the soc power.c */ int stm32_clock_control_init(const struct device *dev); void stm32_clock_control_standby_exit(void); diff --git a/drivers/clock_control/clock_stm32f2_f4_f7.c b/drivers/clock_control/clock_stm32f2_f4_f7.c index a10fede688704b..e916991a5abcb6 100644 --- a/drivers/clock_control/clock_stm32f2_f4_f7.c +++ b/drivers/clock_control/clock_stm32f2_f4_f7.c @@ -50,6 +50,46 @@ uint32_t get_pllsrc_frequency(void) return 0; } +#if defined(STM32_CK48_ENABLED) +/** + * @brief calculate the CK48 frequency depending on its clock source + */ +__unused +uint32_t get_ck48_frequency(void) +{ + uint32_t source; + + if (LL_RCC_GetCK48MClockSource(LL_RCC_CK48M_CLKSOURCE) == + LL_RCC_CK48M_CLKSOURCE_PLL) { + /* Get the PLL48CK source : HSE or HSI */ + source = (LL_RCC_PLL_GetMainSource() == LL_RCC_PLLSOURCE_HSE) + ? HSE_VALUE + : HSI_VALUE; + /* Get the PLL48CK Q freq. No HAL macro for that */ + return __LL_RCC_CALC_PLLCLK_48M_FREQ(source, + LL_RCC_PLL_GetDivider(), + LL_RCC_PLL_GetN(), + LL_RCC_PLL_GetQ() + ); + } else if (LL_RCC_GetCK48MClockSource(LL_RCC_CK48M_CLKSOURCE) == + LL_RCC_CK48M_CLKSOURCE_PLLI2S) { + /* Get the PLL I2S source : HSE or HSI */ + source = (LL_RCC_PLLI2S_GetMainSource() == LL_RCC_PLLSOURCE_HSE) + ? HSE_VALUE + : HSI_VALUE; + /* Get the PLL I2S Q freq. No HAL macro for that */ + return __LL_RCC_CALC_PLLI2S_48M_FREQ(source, + LL_RCC_PLLI2S_GetDivider(), + LL_RCC_PLLI2S_GetN(), + LL_RCC_PLLI2S_GetQ() + ); + } + + __ASSERT(0, "Invalid source"); + return 0; +} +#endif + /** * @brief Set up pll configuration */ @@ -114,7 +154,16 @@ void config_plli2s(void) plli2sm(STM32_PLLI2S_M_DIVISOR), STM32_PLLI2S_N_MULTIPLIER, plli2sr(STM32_PLLI2S_R_DIVISOR)); +#if STM32_PLLI2S_Q_ENABLED + /* There is a Q divider on the PLLI2S to configure the PLL48CK */ + LL_RCC_PLLI2S_ConfigDomain_48M(get_pll_source(), + plli2sm(STM32_PLLI2S_M_DIVISOR), + STM32_PLLI2S_N_MULTIPLIER, + plli2sq(STM32_PLLI2S_Q_DIVISOR)); +#endif /* STM32_PLLI2S_Q_ENABLED */ #endif + + } #endif /* STM32_PLLI2S_ENABLED */ diff --git a/dts/arm/st/f4/stm32f412.dtsi b/dts/arm/st/f4/stm32f412.dtsi index d250d167259d8d..6ec9381a429000 100644 --- a/dts/arm/st/f4/stm32f412.dtsi +++ b/dts/arm/st/f4/stm32f412.dtsi @@ -16,6 +16,12 @@ compatible = "st,stm32f412-plli2s-clock"; status = "disabled"; }; + + clk48: clk48 { + #clock-cells = <0>; + compatible = "st,stm32-clock-mux"; + status = "disabled"; + }; }; soc { @@ -201,8 +207,7 @@ }; sdmmc1: sdmmc@40012c00 { - clocks = <&rcc STM32_CLOCK(APB2, 11U)>, - <&rcc STM32_SRC_SYSCLK SDIO_SEL(1)>; + clocks = <&rcc STM32_CLOCK(APB2, 11U)>; }; quadspi: quadspi@a0001000 { diff --git a/dts/bindings/clock/st,stm32f412-plli2s-clock.yaml b/dts/bindings/clock/st,stm32f412-plli2s-clock.yaml index fbe6c78682c274..dab2740f932959 100644 --- a/dts/bindings/clock/st,stm32f412-plli2s-clock.yaml +++ b/dts/bindings/clock/st,stm32f412-plli2s-clock.yaml @@ -24,3 +24,23 @@ properties: description: | Division factor for the PLL input clock Valid range: 2 - 63 + + div-q: + type: int + description: | + PLLI2S division factor for I2S Clocks to supply USB/SDIO/RNG + enum: + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - 12 + - 13 + - 14 + - 15 diff --git a/include/zephyr/drivers/clock_control/stm32_clock_control.h b/include/zephyr/drivers/clock_control/stm32_clock_control.h index b03fa790027099..12f80835a11677 100644 --- a/include/zephyr/drivers/clock_control/stm32_clock_control.h +++ b/include/zephyr/drivers/clock_control/stm32_clock_control.h @@ -27,6 +27,7 @@ #elif defined(CONFIG_SOC_SERIES_STM32F2X) || \ defined(CONFIG_SOC_SERIES_STM32F4X) #include +#include #elif defined(CONFIG_SOC_SERIES_STM32F7X) #include #elif defined(CONFIG_SOC_SERIES_STM32G0X) @@ -183,6 +184,8 @@ #define STM32_PLLI2S_ENABLED 1 #define STM32_PLLI2S_M_DIVISOR DT_PROP(DT_NODELABEL(plli2s), div_m) #define STM32_PLLI2S_N_MULTIPLIER DT_PROP(DT_NODELABEL(plli2s), mul_n) +#define STM32_PLLI2S_Q_ENABLED DT_NODE_HAS_PROP(DT_NODELABEL(plli2s), div_q) +#define STM32_PLLI2S_Q_DIVISOR DT_PROP_OR(DT_NODELABEL(plli2s), div_q, 1) #define STM32_PLLI2S_R_ENABLED DT_NODE_HAS_PROP(DT_NODELABEL(plli2s), div_r) #define STM32_PLLI2S_R_DIVISOR DT_PROP_OR(DT_NODELABEL(plli2s), div_r, 1) #endif @@ -428,6 +431,12 @@ #define STM32_CKPER_ENABLED 1 #endif +#if DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(clk48), st_stm32_clock_mux, okay) +/* Assuming the 48MHz clock is 48MHz */ +#define STM32_CK48_ENABLED 1 +#define STM32_CK48_FREQ 48000000 +#endif + /** Driver structure definition */ struct stm32_pclken { diff --git a/include/zephyr/dt-bindings/clock/stm32f4_clock.h b/include/zephyr/dt-bindings/clock/stm32f4_clock.h index 18356856ef2426..e248237b725ab8 100644 --- a/include/zephyr/dt-bindings/clock/stm32f4_clock.h +++ b/include/zephyr/dt-bindings/clock/stm32f4_clock.h @@ -28,14 +28,18 @@ /* defined in stm32_common_clocks.h */ /** Fixed clocks */ /* Low speed clocks defined in stm32_common_clocks.h */ -#define STM32_SRC_HSI (STM32_SRC_LSI + 1) -#define STM32_SRC_HSE (STM32_SRC_HSI + 1) +#define STM32_SRC_HSI (STM32_SRC_LSI + 1) +#define STM32_SRC_HSE (STM32_SRC_HSI + 1) /** PLL clock outputs */ -#define STM32_SRC_PLL_P (STM32_SRC_HSE + 1) -#define STM32_SRC_PLL_Q (STM32_SRC_PLL_P + 1) -#define STM32_SRC_PLL_R (STM32_SRC_PLL_Q + 1) +#define STM32_SRC_PLL_P (STM32_SRC_HSE + 1) +#define STM32_SRC_PLL_Q (STM32_SRC_PLL_P + 1) +#define STM32_SRC_PLL_R (STM32_SRC_PLL_Q + 1) /** I2S sources */ -#define STM32_SRC_PLLI2S_R (STM32_SRC_PLL_R + 1) +#define STM32_SRC_PLLI2S_Q (STM32_SRC_PLL_R + 1) +#define STM32_SRC_PLLI2S_R (STM32_SRC_PLLI2S_Q + 1) +/* CLK48MHz sources */ +#define STM32_SRC_CK48 (STM32_SRC_PLLI2S_R + 1) + /* I2S_CKIN not supported yet */ /* #define STM32_SRC_I2S_CKIN TBD */ diff --git a/tests/drivers/clock_control/stm32_clock_configuration/stm32_common_devices/boards/f4_sdmmc48_pll.overlay b/tests/drivers/clock_control/stm32_clock_configuration/stm32_common_devices/boards/f4_sdmmc48_pll.overlay new file mode 100644 index 00000000000000..f0f40392185194 --- /dev/null +++ b/tests/drivers/clock_control/stm32_clock_configuration/stm32_common_devices/boards/f4_sdmmc48_pll.overlay @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Node is disabled by default unless the PLL_I2S is enabled */ +&clk48 { + /* select one source for the clk48MHz domain clock */ +/* clocks = <&rcc STM32_SRC_PLL_Q CK48M_SEL(0)>;*/ + clocks = <&rcc STM32_SRC_PLLI2S_Q CK48M_SEL(1)>; + status = "okay"; +}; + +&plli2s { + div-m = <4>; + mul-n = <96>; + div-q = <4>; + div-r = <2>; + clocks = <&clk_hse>; + status = "okay"; +}; + +&sdmmc1 { + clocks = <&rcc STM32_CLOCK(APB2, 11U)>, + /* select one source for the sdmmc domain clock */ +/* <&rcc STM32_SRC_SYSCLK SDIO_SEL(1)>; */ + <&rcc STM32_SRC_CK48 SDIO_SEL(0)>; + pinctrl-0 = <&sdio_cmd_pa6 &sdio_ck_pc12 + &sdio_d0_pc8 &sdio_d1_pc9 + &sdio_d2_pc10 &sdio_d3_pc11>; + pinctrl-names = "default"; + status = "okay"; +}; diff --git a/tests/drivers/clock_control/stm32_clock_configuration/stm32_common_devices/src/test_stm32_clock_configuration_sdmmc.c b/tests/drivers/clock_control/stm32_clock_configuration/stm32_common_devices/src/test_stm32_clock_configuration_sdmmc.c new file mode 100644 index 00000000000000..937191ee5e4297 --- /dev/null +++ b/tests/drivers/clock_control/stm32_clock_configuration/stm32_common_devices/src/test_stm32_clock_configuration_sdmmc.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(sdmmc1)) + +#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_sdmmc) +#define DT_DRV_COMPAT st_stm32_sdmmc1 +#endif + +#if !DT_HAS_COMPAT_STATUS_OKAY(st_stm32_clock_mux) +#warning "Missing clock 48MHz" +#endif + +#if !DT_HAS_COMPAT_STATUS_OKAY(st_stm32f412_plli2s_clock) +#warning "Missing clock I2S PLL clock" +#endif + +#include "stm32_ll_rcc.h" + +ZTEST(stm32_common_devices_clocks, test_sdmmc_clk_config) +{ + static const struct stm32_pclken pclken[] = STM32_DT_CLOCKS(DT_NODELABEL(sdmmc1)); + + uint32_t dev_dt_clk_freq, dev_actual_clk_freq; + uint32_t dev_actual_clk_src; + int r; + + /* Test clock_on(gating clock) */ + r = clock_control_on(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), + (clock_control_subsys_t) &pclken[0]); + zassert_true((r == 0), "Could not enable SDMMC gating clock"); + + zassert_true(__HAL_RCC_SDIO_IS_CLK_ENABLED(), "SDMMC gating clock should be on"); + TC_PRINT("SDMMC gating clock on\n"); + + zassert_true((DT_NUM_CLOCKS(DT_NODELABEL(sdmmc1)) > 1), "No domain clock defined in dts"); + + if (pclken[1].bus == STM32_SRC_CK48) { + /* CLK 48 is enabled through the clock-mux */ + zassert_true(DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(clk48)), "No clock 48MHz"); + r = 0; + } else if (pclken[1].bus == STM32_SRC_SYSCLK) { + /* Test clock_on(domain_clk) STM32_SRC_SYSCLK */ + r = clock_control_configure(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), + (clock_control_subsys_t) &pclken[1], + NULL); + } else { + r = -127; + } + + zassert_true((r == 0), "Could not enable SDMMC domain clock"); + TC_PRINT("SDMMC domain clock configured\n"); + + /* Test clock source */ + dev_actual_clk_src = __HAL_RCC_GET_SDIO_SOURCE(); + + if (pclken[1].bus == STM32_SRC_CK48) { + zassert_equal(dev_actual_clk_src, RCC_SDIOCLKSOURCE_CLK48, + "Expected SDMMC src: CLK 48 (0x%lx). Actual src: 0x%x", + RCC_SDIOCLKSOURCE_CLK48, dev_actual_clk_src); + } else if (pclken[1].bus == STM32_SRC_SYSCLK) { + zassert_equal(dev_actual_clk_src, RCC_SDIOCLKSOURCE_SYSCLK, + "Expected SDMMC src: SYSCLK (0x%lx). Actual src: 0x%x", + RCC_SDIOCLKSOURCE_SYSCLK, dev_actual_clk_src); + } else { + zassert_true(0, "Unexpected domain clk (0x%x)", dev_actual_clk_src); + } + + /* Test get_rate(srce clk) */ + if (pclken[1].bus == STM32_SRC_CK48) { + /* Get the CK48M source : PLL Q or PLLI2S Q */ + if (LL_RCC_GetCK48MClockSource(LL_RCC_CK48M_CLKSOURCE) == + LL_RCC_CK48M_CLKSOURCE_PLL) { + /* Get the PLL Q freq. No HAL macro for that */ + TC_PRINT("SDMMC sourced by PLLQ at "); + dev_actual_clk_freq = __LL_RCC_CALC_PLLCLK_48M_FREQ(HSE_VALUE, + LL_RCC_PLLI2S_GetDivider(), + LL_RCC_PLLI2S_GetN(), + LL_RCC_PLLI2S_GetQ() + ); + } else { + /* Get the I2S PLL Q freq. No HAL macro for that */ + dev_actual_clk_freq = __LL_RCC_CALC_PLLI2S_48M_FREQ(HSE_VALUE, + LL_RCC_PLLI2S_GetDivider(), + LL_RCC_PLLI2S_GetN(), + LL_RCC_PLLI2S_GetQ() + ); + TC_PRINT("SDMMC sourced by PLLI2SQ at "); + } + + TC_PRINT("%d Hz\n", dev_actual_clk_freq); + r = 0; + + } else if (pclken[1].bus == STM32_SRC_SYSCLK) { + dev_actual_clk_freq = HAL_RCC_GetSysClockFreq(); + TC_PRINT(" STM32_SRC_SYSCLK at %d\n", dev_actual_clk_freq); + } else { + r = -127; + } + + zassert_true((r == 0), "Could not get SDMMC clk srce freq"); + + r = clock_control_get_rate(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), + (clock_control_subsys_t) &pclken[1], + &dev_dt_clk_freq); + + zassert_equal(dev_dt_clk_freq, dev_actual_clk_freq, + "Expected freq: %d Hz. Actual clk: %d Hz", + dev_dt_clk_freq, dev_actual_clk_freq); + + TC_PRINT("SDMMC clock rate: %d Hz\n", dev_dt_clk_freq); + + /* Test clock_off(gating clk) */ + r = clock_control_off(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), + (clock_control_subsys_t) &pclken[0]); + zassert_true((r == 0), "Could not disable SDMMC gating clk"); + + zassert_true(!__HAL_RCC_SDIO_IS_CLK_ENABLED(), "SDMMC gating clk should be off"); + TC_PRINT("SDMMC gating clk off\n"); +} +#endif diff --git a/tests/drivers/clock_control/stm32_clock_configuration/stm32_common_devices/testcase.yaml b/tests/drivers/clock_control/stm32_clock_configuration/stm32_common_devices/testcase.yaml index 535a16a4ed9708..6c5714240f592d 100644 --- a/tests/drivers/clock_control/stm32_clock_configuration/stm32_common_devices/testcase.yaml +++ b/tests/drivers/clock_control/stm32_clock_configuration/stm32_common_devices/testcase.yaml @@ -34,3 +34,6 @@ tests: drivers.clock.stm32_clock_configuration.common_device.f3.i2c1_hsi: extra_args: DTC_OVERLAY_FILE="boards/f3_i2c1_hsi.overlay" platform_allow: stm32f3_disco + drivers.clock.stm32_clock_configuration.common_device.f4.sdmmc_48: + extra_args: DTC_OVERLAY_FILE="boards/f4_sdmmc48_pll.overlay" + platform_allow: stm32f412g_disco nucleo_f412zg