From 51bb5ddde418a5c82db5147d7d7ac66499dfb75b Mon Sep 17 00:00:00 2001 From: Saravanan Sekar Date: Sun, 13 Apr 2025 10:29:18 +0530 Subject: [PATCH] drivers: clock: ti: Add initial support TI MSPM0 clock module Add initial support TI MSPM0 clock module Signed-off-by: Saravanan Sekar Signed-off-by: Jackson Farley --- drivers/clock_control/CMakeLists.txt | 1 + drivers/clock_control/Kconfig | 2 + drivers/clock_control/Kconfig.mspm0 | 11 + drivers/clock_control/clock_control_mspm0.c | 248 ++++++++++++++++++ dts/bindings/clock/ti,mspm0-clk.yaml | 30 +++ dts/bindings/clock/ti,mspm0-osc.yaml | 23 ++ dts/bindings/clock/ti,mspm0-pll.yaml | 56 ++++ .../clock_control/mspm0_clock_control.h | 18 ++ .../zephyr/dt-bindings/clock/mspm0_clock.h | 27 ++ soc/ti/mspm0/Kconfig.defconfig | 6 + 10 files changed, 422 insertions(+) create mode 100644 drivers/clock_control/Kconfig.mspm0 create mode 100644 drivers/clock_control/clock_control_mspm0.c create mode 100644 dts/bindings/clock/ti,mspm0-clk.yaml create mode 100644 dts/bindings/clock/ti,mspm0-osc.yaml create mode 100644 dts/bindings/clock/ti,mspm0-pll.yaml create mode 100644 include/zephyr/drivers/clock_control/mspm0_clock_control.h create mode 100644 include/zephyr/dt-bindings/clock/mspm0_clock.h diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index e4a9609936c..325ed9d2815 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -19,6 +19,7 @@ zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_MCUX_SCG clock_cont zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_MCUX_SCG_K4 clock_control_mcux_scg_k4.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_MCUX_SIM clock_control_mcux_sim.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_MCUX_SYSCON clock_control_mcux_syscon.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_MSPM0 clock_control_mspm0.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NPCM clock_control_npcm.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NPCX clock_control_npcx.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF clock_control_nrf.c) diff --git a/drivers/clock_control/Kconfig b/drivers/clock_control/Kconfig index 16db5c77a15..f528aa2dad3 100644 --- a/drivers/clock_control/Kconfig +++ b/drivers/clock_control/Kconfig @@ -50,6 +50,8 @@ source "drivers/clock_control/Kconfig.mcux_sim" source "drivers/clock_control/Kconfig.mcux_syscon" +source "drivers/clock_control/Kconfig.mspm0" + source "drivers/clock_control/Kconfig.npcm" source "drivers/clock_control/Kconfig.npcx" diff --git a/drivers/clock_control/Kconfig.mspm0 b/drivers/clock_control/Kconfig.mspm0 new file mode 100644 index 00000000000..e21e3b305a5 --- /dev/null +++ b/drivers/clock_control/Kconfig.mspm0 @@ -0,0 +1,11 @@ +# TI MSPM0 Family + +# Copyright (c) 2025, Texas Instruments Inc. +# SPDX-License-Identifier: Apache-2.0 + +config CLOCK_CONTROL_MSPM0 + bool "TI MSPM0 clock" + default y + depends on SOC_FAMILY_TI_MSPM0 + help + This option enables the TI MSPM0 Clock Control Enabler diff --git a/drivers/clock_control/clock_control_mspm0.c b/drivers/clock_control/clock_control_mspm0.c new file mode 100644 index 00000000000..9caa2be386f --- /dev/null +++ b/drivers/clock_control/clock_control_mspm0.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2025 Texas Instruments + * Copyright (c) 2025 Linumiz + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include + +#define MSPM0_ULPCLK_DIV COND_CODE_1( \ + DT_NODE_HAS_PROP(DT_NODELABEL(ulpclk), clk_div), \ + (CONCAT(DL_SYSCTL_ULPCLK_DIV_, \ + DT_PROP(DT_NODELABEL(ulpclk), clk_div))), \ + (0)) + +#define MSPM0_MCLK_DIV COND_CODE_1( \ + DT_NODE_HAS_PROP(DT_NODELABEL(mclk), clk_div), \ + (CONCAT(DL_SYSCTL_MCLK_DIVIDER_, \ + DT_PROP(DT_NODELABEL(mclk), clk_div))), \ + (0)) + +#define MSPM0_MFPCLK_DIV COND_CODE_1( \ + DT_NODE_HAS_PROP(DT_NODELABEL(mfpclk), clk_div), \ + (CONCAT(DL_SYSCTL_HFCLK_MFPCLK_DIVIDER_, \ + DT_PROP(DT_NODELABEL(mfpclk), clk_div))), \ + (0)) + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(mfpclk), okay) +#define MSPM0_MFPCLK_ENABLED 1 +#endif + +#if DT_NODE_HAS_STATUS(DT_NODELABEL(pll), okay) +#define MSPM0_PLL_ENABLED 1 +#endif + +#define DT_MCLK_CLOCKS_CTRL DT_CLOCKS_CTLR(DT_NODELABEL(mclk)) +#define DT_LFCLK_CLOCKS_CTRL DT_CLOCKS_CTLR(DT_NODELABEL(lfclk)) +#define DT_HSCLK_CLOCKS_CTRL DT_CLOCKS_CTLR(DT_NODELABEL(hsclk)) +#define DT_HFCLK_CLOCKS_CTRL DT_CLOCKS_CTLR(DT_NODELABEL(hfclk)) +#define DT_MFPCLK_CLOCKS_CTRL DT_CLOCKS_CTLR(DT_NODELABEL(mfpclk)) +#define DT_PLL_CLOCKS_CTRL DT_CLOCKS_CTLR(DT_NODELABEL(pll)) + +struct mspm0_clk_cfg { + uint32_t clk_div; + uint32_t clk_freq; +}; + +static struct mspm0_clk_cfg mspm0_lfclk_cfg = { + .clk_freq = DT_PROP(DT_NODELABEL(lfclk), clock_frequency), +}; + +static struct mspm0_clk_cfg mspm0_ulpclk_cfg = { + .clk_freq = DT_PROP(DT_NODELABEL(ulpclk), clock_frequency), + .clk_div = MSPM0_ULPCLK_DIV, +}; + +static struct mspm0_clk_cfg mspm0_mclk_cfg = { + .clk_freq = DT_PROP(DT_NODELABEL(mclk), clock_frequency), + .clk_div = MSPM0_MCLK_DIV, +}; + +#if MSPM0_MFPCLK_ENABLED +static struct mspm0_clk_cfg mspm0_mfpclk_cfg = { + .clk_freq = DT_PROP(DT_NODELABEL(mfpclk), clock_frequency), + .clk_div = MSPM0_MFPCLK_DIV, +}; +#endif + +#if MSPM0_PLL_ENABLED +/* basic checks of the devicetree to follow */ +#if (DT_NODE_HAS_PROP(DT_NODELABEL(pll), clk2x_div) && \ + DT_NODE_HAS_PROP(DT_NODELABEL(pll), clk0_div)) +#error "Only CLK2X or CLK0 can be enabled at a time on the PLL" +#endif + +static DL_SYSCTL_SYSPLLConfig clock_mspm0_cfg_syspll = { + .inputFreq = DL_SYSCTL_SYSPLL_INPUT_FREQ_32_48_MHZ, + .sysPLLMCLK = DL_SYSCTL_SYSPLL_MCLK_CLK2X, + .sysPLLRef = DL_SYSCTL_SYSPLL_REF_SYSOSC, + .rDivClk2x = (DT_PROP_OR(DT_NODELABEL(pll), clk2x_div, 1) - 1), + .rDivClk1 = (DT_PROP_OR(DT_NODELABEL(pll), clk1_div, 1) - 1), + .rDivClk0 = (DT_PROP_OR(DT_NODELABEL(pll), clk0_div, 1) - 1), + .qDiv = (DT_PROP(DT_NODELABEL(pll), q_div) - 1), + .pDiv = CONCAT(DL_SYSCTL_SYSPLL_PDIV_, + DT_PROP(DT_NODELABEL(pll), p_div)), + .enableCLK2x = COND_CODE_1( + DT_NODE_HAS_PROP(DT_NODELABEL(pll), clk2x_div), + (DL_SYSCTL_SYSPLL_CLK2X_ENABLE), + (DL_SYSCTL_SYSPLL_CLK2X_DISABLE)), + .enableCLK1 = COND_CODE_1( + DT_NODE_HAS_PROP(DT_NODELABEL(pll), clk1_div), + (DL_SYSCTL_SYSPLL_CLK1_ENABLE), + (DL_SYSCTL_SYSPLL_CLK1_DISABLE)), + .enableCLK0 = COND_CODE_1( + DT_NODE_HAS_PROP(DT_NODELABEL(pll), clk0_div), + (DL_SYSCTL_SYSPLL_CLK0_ENABLE), + (DL_SYSCTL_SYSPLL_CLK0_DISABLE)), +}; +#endif + +static int clock_mspm0_on(const struct device *dev, clock_control_subsys_t sys) +{ + return 0; +} + +static int clock_mspm0_off(const struct device *dev, clock_control_subsys_t sys) +{ + return 0; +} + +static int clock_mspm0_get_rate(const struct device *dev, + clock_control_subsys_t sys, + uint32_t *rate) +{ + struct mspm0_sys_clock *sys_clock = (struct mspm0_sys_clock *)sys; + + switch (sys_clock->clk) { + case MSPM0_CLOCK_LFCLK: + *rate = mspm0_lfclk_cfg.clk_freq; + break; + + case MSPM0_CLOCK_ULPCLK: + *rate = mspm0_ulpclk_cfg.clk_freq; + break; + + case MSPM0_CLOCK_MCLK: + *rate = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC; + break; + +#if MSPM0_MFPCLK_ENABLED + case MSPM0_CLOCK_MFPCLK: + *rate = mspm0_mfpclk_cfg.clk_freq; + break; +#endif + + case MSPM0_CLOCK_MFCLK: + case MSPM0_CLOCK_CANCLK: + default: + return -ENOTSUP; + } + + return 0; +} + +static int clock_mspm0_init(const struct device *dev) +{ + /* setup clocks based on specific rates */ + DL_SYSCTL_setSYSOSCFreq(DL_SYSCTL_SYSOSC_FREQ_BASE); + + DL_SYSCTL_setMCLKDivider(mspm0_mclk_cfg.clk_div); + DL_SYSCTL_setULPCLKDivider(mspm0_ulpclk_cfg.clk_div); + +#if MSPM0_PLL_ENABLED +#if DT_SAME_NODE(DT_HSCLK_CLOCKS_CTRL, DT_NODELABEL(syspll0)) + clock_mspm0_cfg_syspll.sysPLLMCLK = DL_SYSCTL_SYSPLL_MCLK_CLK0; +#endif +#if DT_SAME_NODE(DT_PLL_CLOCKS_CTRL, DT_NODELABEL(hfclk)) + clock_mspm0_cfg_syspll.sysPLLRef = DL_SYSCTL_SYSPLL_REF_HFCLK; +#endif + DL_SYSCTL_configSYSPLL( + (DL_SYSCTL_SYSPLLConfig *)&clock_mspm0_cfg_syspll); +#endif + +#if DT_SAME_NODE(DT_HFCLK_CLOCKS_CTRL, DT_NODELABEL(hfxt)) + uint32_t hf_range; + uint32_t hfxt_freq = DT_PROP(DT_NODELABEL(hfxt), + clock_frequency) / MHZ(1); + uint32_t xtal_startup_delay = DT_PROP_OR(DT_NODELABEL(hfxt), + ti_xtal_startup_delay_us, 0); + + if (hfxt_freq >= 4 && + hfxt_freq <= 8) { + hf_range = DL_SYSCTL_HFXT_RANGE_4_8_MHZ; + } else if (hfxt_freq > 8 && + hfxt_freq <= 16) { + hf_range = DL_SYSCTL_HFXT_RANGE_8_16_MHZ; + } else if (hfxt_freq > 16 && + hfxt_freq <= 32) { + hf_range = DL_SYSCTL_HFXT_RANGE_16_32_MHZ; + } else if (hfxt_freq > 32 && + hfxt_freq <= 48) { + hf_range = DL_SYSCTL_HFXT_RANGE_32_48_MHZ; + } else { + return -EINVAL; + } + + /* startup time in 64us resolution */ + DL_SYSCTL_setHFCLKSourceHFXTParams(hf_range, + mspm0_hfclk_cfg.xtal_startup_delay / 64, + true); +#else + DL_SYSCTL_setHFCLKSourceHFCLKIN(); +#endif + +#if MSPM0_LFCLK_ENABLED +#if DT_SAME_NODE(DT_LFCLK_CLOCKS_CTRL, DT_NODELABEL(lfxt)) + DL_SYSCTL_LFCLKConfig config = {0}; + + DL_SYSCTL_setLFCLKSourceLFXT(&config); +#elif DT_SAME_NODE(DT_LFCLK_CLOCKS_CTRL, DT_NODELABEL(lfdig_in)) + DL_SYSCTL_setLFCLKSourceEXLF(); +#endif +#endif /* MSPM0_LFCLK_ENABLED */ + +#if DT_SAME_NODE(DT_MCLK_CLOCKS_CTRL, DT_NODELABEL(hsclk)) +#if DT_SAME_NODE(DT_HSCLK_CLOCKS_CTRL, DT_NODELABEL(hfclk)) + DL_SYSCTL_setMCLKSource(SYSOSC, HSCLK, + DL_SYSCTL_HSCLK_SOURCE_HFCLK); +#endif + +#if MSPM0_PLL_ENABLED +#if (DT_SAME_NODE(DT_HSCLK_CLOCKS_CTRL, DT_NODELABEL(syspll0)) || \ + DT_SAME_NODE(DT_HSCLK_CLOCKS_CTRL, DT_NODELABEL(syspll2x))) + DL_SYSCTL_setMCLKSource(SYSOSC, HSCLK, + DL_SYSCTL_HSCLK_SOURCE_SYSPLL); +#endif +#endif /* MSPM0_PLL_ENABLED */ + +#elif DT_SAME_NODE(DT_MCLK_CLOCKS_CTRL, DT_NODELABEL(lfclk)) + DL_SYSCTL_setMCLKSource(SYSOSC, LFCLK, false); +#endif /* DT_SAME_NODE(DT_MCLK_CLOCKS_CTRL, DT_NODELABEL(hsclk)) */ + +#if MSPM0_MFPCLK_ENABLED +#if DT_SAME_NODE(DT_MFPCLK_CLOCKS_CTRL, DT_NODELABEL(hfclk)) + DL_SYSCTL_setHFCLKDividerForMFPCLK(mspm0_mfpclk_cfg.clk_div); + DL_SYSCTL_setMFPCLKSource(DL_SYSCTL_MFPCLK_SOURCE_HFCLK); +#else + DL_SYSCTL_setMFPCLKSource(DL_SYSCTL_MFPCLK_SOURCE_SYSOSC); +#endif + DL_SYSCTL_enableMFPCLK(); +#endif /* MSPM0_MFPCLK_ENABLED */ + + return 0; +} + +static const struct clock_control_driver_api clock_mspm0_driver_api = { + .on = clock_mspm0_on, + .off = clock_mspm0_off, + .get_rate = clock_mspm0_get_rate, +}; + +DEVICE_DT_DEFINE(DT_NODELABEL(ckm), &clock_mspm0_init, NULL, NULL, NULL, + PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, + &clock_mspm0_driver_api); diff --git a/dts/bindings/clock/ti,mspm0-clk.yaml b/dts/bindings/clock/ti,mspm0-clk.yaml new file mode 100644 index 00000000000..c450398ac3c --- /dev/null +++ b/dts/bindings/clock/ti,mspm0-clk.yaml @@ -0,0 +1,30 @@ +# Copyright (c) 2025 Texas Instruments Inc. +# Copyright (c) 2025 Linumiz +# SPDX-License-Identifier: Apache-2.0 + +description: TI MSPM0 Clock + +compatible: "ti,mspm0-clk" + +include: [clock-controller.yaml, base.yaml] + +properties: + "#clock-cells": + const: 1 + + clocks: + description: | + Clock reference source + + clock-frequency: + type: int + description: | + Output clock frequency in Hz. + + clk-div: + type: int + description: | + Clock divider selction value. Valid range [2 ... 16]. + +clock-cells: + - clk diff --git a/dts/bindings/clock/ti,mspm0-osc.yaml b/dts/bindings/clock/ti,mspm0-osc.yaml new file mode 100644 index 00000000000..c5a68981f34 --- /dev/null +++ b/dts/bindings/clock/ti,mspm0-osc.yaml @@ -0,0 +1,23 @@ +# Copyright (c) 2025 Texas Instruments Inc. +# Copyright (c) 2025 Linumiz +# SPDX-License-Identifier: Apache-2.0 + +description: TI MSPM0 oscillator + +compatible: "ti,mspm0-osc" + +include: [fixed-clock.yaml, base.yaml] + +properties: + "#clock-cells": + const: 0 + + ti,xtal-startup-delay-us: + type: int + description: | + Crystal Oscillator startup delay in micro seconds. + + ti,low-cap: + type: boolean + description: | + Specifies if capacitance is less than 3pF to reduce power consumption. diff --git a/dts/bindings/clock/ti,mspm0-pll.yaml b/dts/bindings/clock/ti,mspm0-pll.yaml new file mode 100644 index 00000000000..e31768a8f8a --- /dev/null +++ b/dts/bindings/clock/ti,mspm0-pll.yaml @@ -0,0 +1,56 @@ +# Copyright (c) 2024 Texas Instruments Inc. +# SPDX-License-Identifier: Apache-2.0 + +description: TI MSPM0 Phase Locked Loop + +compatible: "ti,mspm0-pll" + +include: [clock-controller.yaml, base.yaml] + +properties: + clocks: + required: true + description: | + Clock reference source + + "#clock-cells": + const: 0 + + p-div: + type: int + required: true + enum: + - 1 + - 2 + - 4 + - 8 + description: | + pdiv is the pre-divider of the output. ref_in / pdiv * qdiv = VCO + + q-div: + type: int + required: true + description: | + qdiv functions as a multiplier value for the ref_in / pdiv * qdiv = VCO + Valid Range: 2 - 128 + + clk0-div: + type: int + description: | + CLK0 PLL output is only enabled if the divider is present. Use CLK0 on + the MSPM0 to output to the MCLK, UCLK, and CPUCLK + Valid Range: 1 - 16 + + clk1-div: + type: int + description: | + CLK1 PLL output is only enabled if the divider is present. Use CLK1 on + the MSPM0 to output to the CANCLK, FCC, or output via EXCLK + Valid Range: 1 - 16 + + clk2x-div: + type: int + description: | + CLK2X PLL output is only enabled if the divider is present. Use CLK2X on + the MSPM0 to output to the MCLK, UCLK, and CPUCLK instead of CLK0 + Valid Range: 1 - 16 diff --git a/include/zephyr/drivers/clock_control/mspm0_clock_control.h b/include/zephyr/drivers/clock_control/mspm0_clock_control.h new file mode 100644 index 00000000000..e0028e23547 --- /dev/null +++ b/include/zephyr/drivers/clock_control/mspm0_clock_control.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2025 Texas Instruments Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_CLOCK_CONTROL_MSPM0_CLOCK_CONTROL +#define ZEPHYR_INCLUDE_DRIVERS_CLOCK_CONTROL_MSPM0_CLOCK_CONTROL + +#include + +struct mspm0_sys_clock { + uint32_t clk; +}; + +#define MSPM0_CLOCK_SUBSYS_FN(index) {.clk = DT_INST_CLOCKS_CELL(index, clk)} + +#endif /* ZEPHYR_INCLUDE_DRIVERS_CLOCK_CONTROL_MSPM0_CLOCK_CONTROL */ diff --git a/include/zephyr/dt-bindings/clock/mspm0_clock.h b/include/zephyr/dt-bindings/clock/mspm0_clock.h new file mode 100644 index 00000000000..be3cd42ac3d --- /dev/null +++ b/include/zephyr/dt-bindings/clock/mspm0_clock.h @@ -0,0 +1,27 @@ +/* + * Copyright 2025 Texas Instruments Inc. + * Copyright 2025 Linumiz + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_MSPM0_CLOCK_H +#define ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_MSPM0_CLOCK_H + +#define MSPM0_CLOCK(clk, bit) ((clk << 8) | bit) + +/* Peripheral clock source selection register mask */ +#define MSPM0_CLOCK_PERIPH_REG_MASK(X) (X & 0xFF) + +/* Clock references */ +#define MSPM0_CLOCK_SYSOSC MSPM0_CLOCK(0x0, 0x0) +#define MSPM0_CLOCK_LFCLK MSPM0_CLOCK(0x1, 0x2) +#define MSPM0_CLOCK_MFCLK MSPM0_CLOCK(0x2, 0x4) +#define MSPM0_CLOCK_BUSCLK MSPM0_CLOCK(0x3, 0x8) +#define MSPM0_CLOCK_ULPCLK MSPM0_CLOCK(0x4, 0x8) +#define MSPM0_CLOCK_MCLK MSPM0_CLOCK(0x5, 0x8) +#define MSPM0_CLOCK_MFPCLK MSPM0_CLOCK(0x6, 0x0) +#define MSPM0_CLOCK_CANCLK MSPM0_CLOCK(0x7, 0x0) +#define MSPM0_CLOCK_CLK_OUT MSPM0_CLOCK(0x8, 0x0) + +#endif diff --git a/soc/ti/mspm0/Kconfig.defconfig b/soc/ti/mspm0/Kconfig.defconfig index 950579024a8..335a9596b1a 100644 --- a/soc/ti/mspm0/Kconfig.defconfig +++ b/soc/ti/mspm0/Kconfig.defconfig @@ -6,4 +6,10 @@ if SOC_FAMILY_TI_MSPM0 rsource "*/Kconfig.defconfig" +DT_MSPM0_MCLK_PATH := $(dt_nodelabel_path,mclk) +DT_MSPM0_MCLK_CPU_FREQ := $(dt_node_int_prop_int,$(DT_MSPM0_MCLK_PATH),clock-frequency) + +config SYS_CLOCK_HW_CYCLES_PER_SEC + default "$(DT_MSPM0_MCLK_CPU_FREQ)" if "$(dt_nodelabel_enabled,mclk)" + endif # SOC_FAMILY_TI_MSPM0