diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index 9d7a87e03e6..3a7892ef9ac 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -26,6 +26,7 @@ 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_SILABS_SIWX91X clock_control_silabs_siwx91x.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_SILABS_SERIES clock_control_silabs_series.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) diff --git a/drivers/clock_control/Kconfig b/drivers/clock_control/Kconfig index eec9b11c188..a746628d86d 100644 --- a/drivers/clock_control/Kconfig +++ b/drivers/clock_control/Kconfig @@ -102,6 +102,8 @@ source "drivers/clock_control/Kconfig.arm_scmi" source "drivers/clock_control/Kconfig.silabs" +source "drivers/clock_control/Kconfig.siwx91x" + source "drivers/clock_control/Kconfig.wch_rcc" endif # CLOCK_CONTROL diff --git a/drivers/clock_control/Kconfig.siwx91x b/drivers/clock_control/Kconfig.siwx91x new file mode 100644 index 00000000000..0aa0fc240b8 --- /dev/null +++ b/drivers/clock_control/Kconfig.siwx91x @@ -0,0 +1,15 @@ +# Copyright (c) 2024 Silicon Laboratories Inc. +# SPDX-License-Identifier: Apache-2.0 + +config CLOCK_CONTROL_SILABS_SIWX91X + bool "SiWx91x clock control driver" + default y + depends on DT_HAS_SILABS_SIWX91X_CLOCK_ENABLED + help + Enable clock management on Silicon Labs SiWx91x chips. This driver + includes support for HP (High Performace), ULP (Ultra Low Power), and + ULP VBAT clocks. + + The original hardware allow to customize the various clocks offered for + every devices. This driver does not provide such customizations. It + just hardcodes sane default parameters for every devices. diff --git a/drivers/clock_control/clock_control_silabs_siwx91x.c b/drivers/clock_control/clock_control_silabs_siwx91x.c new file mode 100644 index 00000000000..44f4b3909e6 --- /dev/null +++ b/drivers/clock_control/clock_control_silabs_siwx91x.c @@ -0,0 +1,186 @@ +/* Copyright (c) 2024 Silicon Laboratories Inc. + * SPDX-License-Identifier: Apache-2.0 + * + * Poor man driver for 917 clocks. 917 includes High Performace (HP) clock + * (@46000000), Ultra Lower Power (ULP) clock (@24041400) and ULP VBAT (@24048000) + * + */ +#include +#include +#include + +#include "rsi_power_save.h" +#include "rsi_rom_ulpss_clk.h" +#include "rsi_rom_clks.h" +#include "clock_update.h" +#include "sl_si91x_clock_manager.h" + +#define DT_DRV_COMPAT silabs_siwx91x_clock + +LOG_MODULE_REGISTER(siwx91x_clock, CONFIG_CLOCK_CONTROL_LOG_LEVEL); + +struct siwx91x_clock_data { + uint32_t enable; +}; + +static int siwx91x_clock_on(const struct device *dev, clock_control_subsys_t sys) +{ + struct siwx91x_clock_data *data = dev->data; + uintptr_t clockid = (uintptr_t)sys; + + switch (clockid) { + case SIWX91X_CLK_ULP_UART: + RSI_PS_UlpssPeriPowerUp(ULPSS_PWRGATE_ULP_UART); + RSI_ULPSS_UlpUartClkConfig(ULPCLK, ENABLE_STATIC_CLK, + false, ULP_UART_ULP_MHZ_RC_CLK, 1); + break; + case SIWX91X_CLK_ULP_I2C: + RSI_PS_UlpssPeriPowerUp(ULPSS_PWRGATE_ULP_I2C); + RSI_ULPSS_PeripheralEnable(ULPCLK, ULP_I2C_CLK, ENABLE_STATIC_CLK); + break; + case SIWX91X_CLK_ULP_DMA: + RSI_PS_UlpssPeriPowerUp(ULPSS_PWRGATE_ULP_UDMA); + RSI_ULPSS_PeripheralEnable(ULPCLK, ULP_UDMA_CLK, ENABLE_STATIC_CLK); + break; + case SIWX91X_CLK_UART1: + RSI_PS_M4ssPeriPowerUp(M4SS_PWRGATE_ULP_EFUSE_PERI); + /* RSI_CLK_UsartClkConfig() calls RSI_CLK_PeripheralClkEnable(); */ + RSI_CLK_UsartClkConfig(M4CLK, ENABLE_STATIC_CLK, 0, USART1, 0, 1); + break; + case SIWX91X_CLK_UART2: + RSI_PS_M4ssPeriPowerUp(M4SS_PWRGATE_ULP_EFUSE_PERI); + /* RSI_CLK_UsartClkConfig() calls RSI_CLK_PeripheralClkEnable(); */ + RSI_CLK_UsartClkConfig(M4CLK, ENABLE_STATIC_CLK, 0, USART2, 0, 1); + break; + case SIWX91X_CLK_I2C0: + RSI_PS_M4ssPeriPowerUp(M4SS_PWRGATE_ULP_EFUSE_PERI); + RSI_CLK_I2CClkConfig(M4CLK, true, 0); + break; + case SIWX91X_CLK_I2C1: + RSI_PS_M4ssPeriPowerUp(M4SS_PWRGATE_ULP_EFUSE_PERI); + RSI_CLK_I2CClkConfig(M4CLK, true, 1); + break; + case SIWX91X_CLK_DMA0: + RSI_PS_M4ssPeriPowerUp(M4SS_PWRGATE_ULP_EFUSE_PERI); + RSI_CLK_PeripheralClkEnable(M4CLK, UDMA_CLK, ENABLE_STATIC_CLK); + break; + default: + return -EINVAL; + } + data->enable |= BIT(clockid); + + return 0; +} + +static int siwx91x_clock_off(const struct device *dev, clock_control_subsys_t sys) +{ + struct siwx91x_clock_data *data = dev->data; + uintptr_t clockid = (uintptr_t)sys; + + switch (clockid) { + case SIWX91X_CLK_ULP_I2C: + RSI_ULPSS_PeripheralDisable(ULPCLK, ULP_I2C_CLK); + break; + case SIWX91X_CLK_ULP_DMA: + RSI_ULPSS_PeripheralDisable(ULPCLK, ULP_UDMA_CLK); + break; + case SIWX91X_CLK_UART1: + RSI_CLK_PeripheralClkDisable(M4CLK, USART1_CLK); + break; + case SIWX91X_CLK_UART2: + RSI_CLK_PeripheralClkDisable(M4CLK, USART2_CLK); + break; + case SIWX91X_CLK_DMA0: + RSI_CLK_PeripheralClkDisable(M4CLK, UDMA_CLK); + break; + case SIWX91X_CLK_ULP_UART: + case SIWX91X_CLK_I2C0: + case SIWX91X_CLK_I2C1: + /* Not supported */ + return 0; + default: + return -EINVAL; + } + + data->enable &= ~BIT(clockid); + return 0; +} + +static int siwx91x_clock_get_rate(const struct device *dev, clock_control_subsys_t sys, + uint32_t *rate) +{ + uintptr_t clockid = (uintptr_t)sys; + + switch (clockid) { + case SIWX91X_CLK_ULP_UART: + *rate = RSI_CLK_GetBaseClock(ULPSS_UART); + return 0; + case SIWX91X_CLK_UART1: + *rate = RSI_CLK_GetBaseClock(M4_USART0); + return 0; + case SIWX91X_CLK_UART2: + *rate = RSI_CLK_GetBaseClock(M4_UART1); + return 0; + default: + /* For now, no other driver need clock rate */ + return -EINVAL; + } +} + +static enum clock_control_status siwx91x_clock_get_status(const struct device *dev, + clock_control_subsys_t sys) +{ + struct siwx91x_clock_data *data = dev->data; + uintptr_t clockid = (uintptr_t)sys; + + if (data->enable & BIT(clockid)) { + return CLOCK_CONTROL_STATUS_ON; + } else { + return CLOCK_CONTROL_STATUS_OFF; + } +} + +static int siwx91x_clock_init(const struct device *dev) +{ + SystemCoreClockUpdate(); + + /* Use SoC PLL at configured frequency as core clock */ + sl_si91x_clock_manager_m4_set_core_clk(M4_SOCPLLCLK, CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC); + + /* Use interface PLL at configured frequency as peripheral clock */ + sl_si91x_clock_manager_set_pll_freq(INFT_PLL, CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC, + PLL_REF_CLK_VAL_XTAL); + + /* FIXME: Currently the clock consumer use clocks without power on them. + * This should be fixed in drivers. Meanwhile, get the list of required + * clocks using DT labels. + */ +#if DT_NODE_HAS_STATUS(DT_NODELABEL(ulpi2c), okay) + siwx91x_clock_on(dev, (clock_control_subsys_t)SIWX91X_CLK_ULP_I2C); +#endif + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(i2c0), okay) + siwx91x_clock_on(dev, (clock_control_subsys_t)SIWX91X_CLK_I2C0); +#endif + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(i2c1), okay) + siwx91x_clock_on(dev, (clock_control_subsys_t)SIWX91X_CLK_I2C1); +#endif + + return 0; +} + +static DEVICE_API(clock_control, siwx91x_clock_api) = { + .on = siwx91x_clock_on, + .off = siwx91x_clock_off, + .get_rate = siwx91x_clock_get_rate, + .get_status = siwx91x_clock_get_status, +}; + +#define SIWX91X_CLOCK_INIT(p) \ + static struct siwx91x_clock_data siwx91x_clock_data_##p; \ + DEVICE_DT_INST_DEFINE(p, siwx91x_clock_init, NULL, &siwx91x_clock_data_##p, NULL, \ + PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, \ + &siwx91x_clock_api); + +DT_INST_FOREACH_STATUS_OKAY(SIWX91X_CLOCK_INIT) diff --git a/dts/bindings/clock/silabs,siwx91x-clock.yaml b/dts/bindings/clock/silabs,siwx91x-clock.yaml new file mode 100644 index 00000000000..80bf83aadb0 --- /dev/null +++ b/dts/bindings/clock/silabs,siwx91x-clock.yaml @@ -0,0 +1,18 @@ +# Copyright (c) 2024 Silicon Laboratories Inc. +# SPDX-License-Identifier: Apache-2.0 + +description: Clocks embedded on Silabs SiWx91x chips + +compatible: "silabs,siwx91x-clock" + +include: [clock-controller.yaml, base.yaml] + +properties: + reg: + required: true + + "#clock-cells": + const: 1 + +clock-cells: + - clkid diff --git a/include/zephyr/dt-bindings/clock/silabs/siwx91x-clock.h b/include/zephyr/dt-bindings/clock/silabs/siwx91x-clock.h new file mode 100644 index 00000000000..06a007e82eb --- /dev/null +++ b/include/zephyr/dt-bindings/clock/silabs/siwx91x-clock.h @@ -0,0 +1,16 @@ +/* Copyright (c) 2024 Silicon Laboratories Inc. + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_SILABS_SIWX91X_CLOCK_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_SILABS_SIWX91X_CLOCK_H_ + +#define SIWX91X_CLK_ULP_UART 0 +#define SIWX91X_CLK_ULP_I2C 1 +#define SIWX91X_CLK_ULP_DMA 2 +#define SIWX91X_CLK_UART1 3 +#define SIWX91X_CLK_UART2 4 +#define SIWX91X_CLK_I2C0 5 +#define SIWX91X_CLK_I2C1 6 +#define SIWX91X_CLK_DMA0 7 + +#endif