Browse Source

drivers: clock_control: Add initial SiM3U1xx support

This serves two main purposes:
- change the CPU clock via devicetree nodes
- provide the APB frequency to device drivers via the clock driver
  interface

Theoretically this could also support choosing between the available
clock sources, but right now we only support LPOSC0 going into PLL0,
going into AHB.

Turning the PLL back off is also not supported since the only current
use case is to set the PLL frequency, turn it on, and switch the AHB
over to it.

Signed-off-by: Michael Zimmermann <michael.zimmermann@grandcentrix.net>
pull/77583/head
Michael Zimmermann 2 years ago committed by Carles Cufí
parent
commit
d49cc8a56f
  1. 3
      drivers/clock_control/CMakeLists.txt
  2. 2
      drivers/clock_control/Kconfig
  3. 18
      drivers/clock_control/Kconfig.si32
  4. 103
      drivers/clock_control/clock_control_si32_ahb.c
  5. 80
      drivers/clock_control/clock_control_si32_apb.c
  6. 139
      drivers/clock_control/clock_control_si32_pll.c
  7. 26
      dts/arm/silabs/sim3u.dtsi
  8. 1
      soc/silabs/silabs_sim3/sim3u/Kconfig

3
drivers/clock_control/CMakeLists.txt

@ -24,6 +24,9 @@ zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_DRIVER_CALIBRATION nrf_clo @@ -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)

2
drivers/clock_control/Kconfig

@ -72,6 +72,8 @@ source "drivers/clock_control/Kconfig.gd32" @@ -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"

18
drivers/clock_control/Kconfig.si32

@ -0,0 +1,18 @@ @@ -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

103
drivers/clock_control/clock_control_si32_ahb.c

@ -0,0 +1,103 @@ @@ -0,0 +1,103 @@
/*
* Copyright (c) 2024 GARDENA GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT silabs_si32_ahb
#include <stdint.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/clock_control.h>
#include <SI32_CLKCTRL_A_Type.h>
#include <SI32_FLASHCTRL_A_Type.h>
#include <si32_device.h>
#define LOG_LEVEL LOG_LEVEL_DBG
#include <zephyr/logging/log.h>
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);

80
drivers/clock_control/clock_control_si32_apb.c

@ -0,0 +1,80 @@ @@ -0,0 +1,80 @@
/*
* Copyright (c) 2024 GARDENA GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT silabs_si32_apb
#include <stdint.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/clock_control.h>
#include <SI32_CLKCTRL_A_Type.h>
#include <si32_device.h>
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);

139
drivers/clock_control/clock_control_si32_pll.c

@ -0,0 +1,139 @@ @@ -0,0 +1,139 @@
/*
* Copyright (c) 2024 GARDENA GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT silabs_si32_pll
#include <stdint.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/clock_control.h>
#include <SI32_CLKCTRL_A_Type.h>
#include <SI32_PLL_A_Type.h>
#include <si32_device.h>
#define LOG_LEVEL LOG_LEVEL_DBG
#include <zephyr/logging/log.h>
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);

26
dts/arm/silabs/sim3u.dtsi

@ -34,6 +34,32 @@ @@ -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";

1
soc/silabs/silabs_sim3/sim3u/Kconfig

@ -4,6 +4,7 @@ @@ -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

Loading…
Cancel
Save