drivers: clock: ti: Add initial support TI MSPM0 clock module

Add initial support TI MSPM0 clock module

Signed-off-by: Saravanan Sekar <saravanan@linumiz.com>
Signed-off-by: Jackson Farley <j-farley@ti.com>
This commit is contained in:
Saravanan Sekar 2025-04-13 10:29:18 +05:30 committed by Benjamin Cabé
commit 51bb5ddde4
10 changed files with 422 additions and 0 deletions

View file

@ -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)

View file

@ -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"

View file

@ -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

View file

@ -0,0 +1,248 @@
/*
* Copyright (c) 2025 Texas Instruments
* Copyright (c) 2025 Linumiz
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/mspm0_clock_control.h>
#include <ti/driverlib/driverlib.h>
#include <string.h>
#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);

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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 <zephyr/dt-bindings/clock/mspm0_clock.h>
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 */

View file

@ -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

View file

@ -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