Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

248 lines
7.1 KiB

/*
* 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 DEVICE_API(clock_control, 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);