/* * 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 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);