diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index c081fa19b23..fc2d33efd19 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -24,6 +24,9 @@ zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_DRIVER_CALIBRATION nrf_clo zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_RV32M1_PCC clock_control_rv32m1_pcc.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_INFINEON_CAT1 clock_control_ifx_cat1.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_SAM clock_control_sam_pmc.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_SI32_PLL clock_control_si32_pll.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_SI32_AHB clock_control_si32_ahb.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_SI32_APB clock_control_si32_apb.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_SMARTBOND clock_control_smartbond.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NUMAKER_SCC clock_control_numaker_scc.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NXP_S32 clock_control_nxp_s32.c) diff --git a/drivers/clock_control/Kconfig b/drivers/clock_control/Kconfig index 7d661a1cffe..42a5f23ad1f 100644 --- a/drivers/clock_control/Kconfig +++ b/drivers/clock_control/Kconfig @@ -72,6 +72,8 @@ source "drivers/clock_control/Kconfig.gd32" source "drivers/clock_control/Kconfig.sam" +source "drivers/clock_control/Kconfig.si32" + source "drivers/clock_control/Kconfig.smartbond" source "drivers/clock_control/Kconfig.numaker" diff --git a/drivers/clock_control/Kconfig.si32 b/drivers/clock_control/Kconfig.si32 new file mode 100644 index 00000000000..82940931859 --- /dev/null +++ b/drivers/clock_control/Kconfig.si32 @@ -0,0 +1,18 @@ +# Copyright (c) 2024 GARDENA GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +config CLOCK_CONTROL_SI32_PLL + bool "SI32 PLL clock control" + default y + depends on DT_HAS_SILABS_SI32_PLL_ENABLED + +config CLOCK_CONTROL_SI32_AHB + bool "SI32 AHB clock control" + default y + depends on DT_HAS_SILABS_SI32_AHB_ENABLED + +config CLOCK_CONTROL_SI32_APB + bool "SI32 APB clock control" + default y + depends on DT_HAS_SILABS_SI32_APB_ENABLED diff --git a/drivers/clock_control/clock_control_si32_ahb.c b/drivers/clock_control/clock_control_si32_ahb.c new file mode 100644 index 00000000000..aa1f90aef74 --- /dev/null +++ b/drivers/clock_control/clock_control_si32_ahb.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT silabs_si32_ahb + +#include + +#include +#include +#include + +#include +#include +#include + +#define LOG_LEVEL LOG_LEVEL_DBG +#include +LOG_MODULE_REGISTER(ahb); + +struct clock_control_si32_ahb_config { + const struct device *clock_dev; + uint32_t freq; +}; + +static int clock_control_si32_ahb_on(const struct device *dev, clock_control_subsys_t sys) +{ + return -ENOTSUP; +} + +static int clock_control_si32_ahb_off(const struct device *dev, clock_control_subsys_t sys) +{ + + return -ENOTSUP; +} + +static int clock_control_si32_ahb_get_rate(const struct device *dev, clock_control_subsys_t sys, + uint32_t *rate) +{ + const struct clock_control_si32_ahb_config *config = dev->config; + *rate = config->freq; + return 0; +} + +static struct clock_control_driver_api clock_control_si32_ahb_api = { + .on = clock_control_si32_ahb_on, + .off = clock_control_si32_ahb_off, + .get_rate = clock_control_si32_ahb_get_rate, +}; + +static int clock_control_si32_ahb_init(const struct device *dev) +{ + const struct clock_control_si32_ahb_config *config = dev->config; + int ret; + + if (!device_is_ready(config->clock_dev)) { + return -ENODEV; + } + + if (config->freq != 20000000) { + uint32_t freq = config->freq; + + ret = clock_control_set_rate(config->clock_dev, NULL, &freq); + if (ret) { + LOG_ERR("failed to set parent clock rate: %d", ret); + return ret; + } + + ret = clock_control_on(config->clock_dev, NULL); + if (ret) { + LOG_ERR("failed to enable parent clock: %d", ret); + return ret; + } + + uint32_t spmd; + + if (config->freq > 80000000) { + spmd = 3; + } else if (config->freq > 53000000) { + spmd = 2; + } else if (config->freq > 26000000) { + spmd = 1; + } else { + spmd = 0; + } + SI32_FLASHCTRL_A_select_flash_speed_mode(SI32_FLASHCTRL_0, spmd); + + /* TODO: support other clock sources */ + SI32_CLKCTRL_A_select_ahb_source_pll(SI32_CLKCTRL_0); + } + + return 0; +} + +static const struct clock_control_si32_ahb_config config = { + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(0)), + .freq = DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency), +}; + +DEVICE_DT_INST_DEFINE(0, clock_control_si32_ahb_init, NULL, NULL, &config, PRE_KERNEL_1, + CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &clock_control_si32_ahb_api); diff --git a/drivers/clock_control/clock_control_si32_apb.c b/drivers/clock_control/clock_control_si32_apb.c new file mode 100644 index 00000000000..d66adfed4ec --- /dev/null +++ b/drivers/clock_control/clock_control_si32_apb.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT silabs_si32_apb + +#include + +#include +#include +#include + +#include +#include + +struct clock_control_si32_apb_config { + const struct device *clock_dev; + uint32_t divider; +}; + +static int clock_control_si32_apb_on(const struct device *dev, clock_control_subsys_t sys) +{ + return -ENOTSUP; +} + +static int clock_control_si32_apb_off(const struct device *dev, clock_control_subsys_t sys) +{ + + return -ENOTSUP; +} + +static int clock_control_si32_apb_get_rate(const struct device *dev, clock_control_subsys_t sys, + uint32_t *rate) +{ + const struct clock_control_si32_apb_config *config = dev->config; + const int ret = clock_control_get_rate(config->clock_dev, NULL, rate); + + if (ret) { + return ret; + } + + *rate /= config->divider; + + return 0; +} + +static struct clock_control_driver_api clock_control_si32_apb_api = { + .on = clock_control_si32_apb_on, + .off = clock_control_si32_apb_off, + .get_rate = clock_control_si32_apb_get_rate, +}; + +static int clock_control_si32_apb_init(const struct device *dev) +{ + const struct clock_control_si32_apb_config *config = dev->config; + + if (!device_is_ready(config->clock_dev)) { + return -ENODEV; + } + + if (config->divider == 1) { + SI32_CLKCTRL_A_select_apb_divider_1(SI32_CLKCTRL_0); + } else if (config->divider == 2) { + SI32_CLKCTRL_A_select_apb_divider_2(SI32_CLKCTRL_0); + } else { + return -ENOTSUP; + } + + return 0; +} + +static const struct clock_control_si32_apb_config config = { + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(0)), + .divider = DT_PROP(DT_NODELABEL(clk_apb), divider), +}; + +DEVICE_DT_INST_DEFINE(0, clock_control_si32_apb_init, NULL, NULL, &config, PRE_KERNEL_1, + CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &clock_control_si32_apb_api); diff --git a/drivers/clock_control/clock_control_si32_pll.c b/drivers/clock_control/clock_control_si32_pll.c new file mode 100644 index 00000000000..913420d3266 --- /dev/null +++ b/drivers/clock_control/clock_control_si32_pll.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2024 GARDENA GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT silabs_si32_pll + +#include + +#include +#include +#include + +#include +#include +#include + +#define LOG_LEVEL LOG_LEVEL_DBG +#include +LOG_MODULE_REGISTER(pll); + +struct clock_control_si32_pll_config { + SI32_PLL_A_Type *pll; +}; + +struct clock_control_si32_pll_data { + uint32_t freq; +}; + +static int clock_control_si32_pll_on(const struct device *dev, clock_control_subsys_t sys) +{ + const struct clock_control_si32_pll_config *config = dev->config; + struct clock_control_si32_pll_data *data = dev->data; + + if (data->freq == 0) { + return -ENOTSUP; + } + + uint32_t dco_range; + + if (data->freq > 80000000) { + dco_range = 4; + } else if (data->freq > 76500000) { + dco_range = 4; + } else if (data->freq > 62000000) { + dco_range = 3; + } else if (data->freq > 49500000) { + dco_range = 2; + } else if (data->freq > 35000000) { + dco_range = 1; + } else if (data->freq > 23000000) { + dco_range = 0; + } else { + return -ENOTSUP; + } + + /* lposc0div */ + const uint32_t source_clock_freq = 2500000; + const uint32_t div_m = 100; + const uint32_t div_n = (data->freq / source_clock_freq) * (div_m + 1) - 1; + + if (div_n < 32 || div_n > 4095) { + return -ENOTSUP; + } + + /* Setup PLL to lock to requested frequency */ + SI32_PLL_A_initialize(config->pll, 0x00, 0x00, 0x00, 0x000FFF0); + SI32_PLL_A_set_numerator(config->pll, div_n); + SI32_PLL_A_set_denominator(config->pll, div_m); + /* TODO: support other clock sources */ + SI32_PLL_A_select_reference_clock_source_lp0oscdiv(config->pll); + + /* Wait for lock */ + SI32_PLL_A_select_disable_dco_output(config->pll); + SI32_PLL_A_set_frequency_adjuster_value(config->pll, 0xFFF); + SI32_PLL_A_set_output_frequency_range(config->pll, dco_range); + + /* Lock and block for result */ + SI32_PLL_A_select_dco_frequency_lock_mode(config->pll); + while (!(SI32_PLL_A_is_locked(config->pll) || + SI32_PLL_A_is_saturation_low_interrupt_pending(config->pll) || + SI32_PLL_A_is_saturation_high_interrupt_pending(config->pll))) + ; + + return 0; +} + +static int clock_control_si32_pll_off(const struct device *dev, clock_control_subsys_t sys) +{ + + return -ENOTSUP; +} + +static int clock_control_si32_pll_get_rate(const struct device *dev, clock_control_subsys_t sys, + uint32_t *rate) +{ + struct clock_control_si32_pll_data *data = dev->data; + + *rate = data->freq; + + return 0; +} + +static int clock_control_si32_pll_set_rate(const struct device *dev, clock_control_subsys_t sys, + clock_control_subsys_rate_t rate_) +{ + struct clock_control_si32_pll_data *data = dev->data; + const uint32_t *rate = rate_; + + data->freq = *rate; + + return 0; +} + +static struct clock_control_driver_api clock_control_si32_pll_api = { + .on = clock_control_si32_pll_on, + .off = clock_control_si32_pll_off, + .get_rate = clock_control_si32_pll_get_rate, + .set_rate = clock_control_si32_pll_set_rate, +}; + +static int clock_control_si32_pll_init(const struct device *dev) +{ + SI32_CLKCTRL_A_enable_apb_to_modules_0(SI32_CLKCTRL_0, SI32_CLKCTRL_A_APBCLKG0_PLL0); + + return 0; +} + +static const struct clock_control_si32_pll_config config = { + .pll = (SI32_PLL_A_Type *)DT_REG_ADDR(DT_NODELABEL(pll0)), +}; + +static struct clock_control_si32_pll_data data = { + .freq = 0, +}; + +DEVICE_DT_INST_DEFINE(0, clock_control_si32_pll_init, NULL, &data, &config, PRE_KERNEL_1, + CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &clock_control_si32_pll_api); diff --git a/dts/arm/silabs/sim3u.dtsi b/dts/arm/silabs/sim3u.dtsi index 77320768280..6cede39f99f 100644 --- a/dts/arm/silabs/sim3u.dtsi +++ b/dts/arm/silabs/sim3u.dtsi @@ -34,6 +34,32 @@ status = "okay"; }; + clocks { + #address-cells = <1>; + #size-cells = <0>; + + pll0: pll0@4003b000 { + compatible = "silabs,si32-pll"; + #clock-cells = <0>; + reg = <0x4003b000>; + status = "disabled"; + }; + + clk_ahb: clk-ahb { + compatible = "silabs,si32-ahb"; + #clock-cells = <0>; + status = "disabled"; + }; + + clk_apb: clk-apb { + compatible = "silabs,si32-apb"; + #clock-cells = <0>; + divider = <1>; + clocks = <&clk_ahb>; + status = "disabled"; + }; + }; + soc { flash: flash-controller@4002e000 { compatible = "silabs,si32-flash-controller"; diff --git a/soc/silabs/silabs_sim3/sim3u/Kconfig b/soc/silabs/silabs_sim3/sim3u/Kconfig index 7b52312ee94..39ef1525460 100644 --- a/soc/silabs/silabs_sim3/sim3u/Kconfig +++ b/soc/silabs/silabs_sim3/sim3u/Kconfig @@ -4,6 +4,7 @@ config SOC_SERIES_SIM3U select ARM + select CLOCK_CONTROL select CPU_CORTEX_M3 select CPU_CORTEX_M_HAS_SYSTICK select HAS_SILABS_SI32