Skip to content

Commit 51bb5dd

Browse files
ssekar15kartben
authored andcommitted
drivers: clock: ti: Add initial support TI MSPM0 clock module
Add initial support TI MSPM0 clock module Signed-off-by: Saravanan Sekar <[email protected]> Signed-off-by: Jackson Farley <[email protected]>
1 parent 234d289 commit 51bb5dd

File tree

10 files changed

+422
-0
lines changed

10 files changed

+422
-0
lines changed

drivers/clock_control/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_MCUX_SCG clock_cont
1919
zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_MCUX_SCG_K4 clock_control_mcux_scg_k4.c)
2020
zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_MCUX_SIM clock_control_mcux_sim.c)
2121
zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_MCUX_SYSCON clock_control_mcux_syscon.c)
22+
zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_MSPM0 clock_control_mspm0.c)
2223
zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NPCM clock_control_npcm.c)
2324
zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NPCX clock_control_npcx.c)
2425
zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF clock_control_nrf.c)

drivers/clock_control/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ source "drivers/clock_control/Kconfig.mcux_sim"
5050

5151
source "drivers/clock_control/Kconfig.mcux_syscon"
5252

53+
source "drivers/clock_control/Kconfig.mspm0"
54+
5355
source "drivers/clock_control/Kconfig.npcm"
5456

5557
source "drivers/clock_control/Kconfig.npcx"

drivers/clock_control/Kconfig.mspm0

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# TI MSPM0 Family
2+
3+
# Copyright (c) 2025, Texas Instruments Inc.
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
config CLOCK_CONTROL_MSPM0
7+
bool "TI MSPM0 clock"
8+
default y
9+
depends on SOC_FAMILY_TI_MSPM0
10+
help
11+
This option enables the TI MSPM0 Clock Control Enabler
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
/*
2+
* Copyright (c) 2025 Texas Instruments
3+
* Copyright (c) 2025 Linumiz
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
#include <zephyr/drivers/clock_control.h>
9+
#include <zephyr/drivers/clock_control/mspm0_clock_control.h>
10+
11+
#include <ti/driverlib/driverlib.h>
12+
#include <string.h>
13+
14+
#define MSPM0_ULPCLK_DIV COND_CODE_1( \
15+
DT_NODE_HAS_PROP(DT_NODELABEL(ulpclk), clk_div), \
16+
(CONCAT(DL_SYSCTL_ULPCLK_DIV_, \
17+
DT_PROP(DT_NODELABEL(ulpclk), clk_div))), \
18+
(0))
19+
20+
#define MSPM0_MCLK_DIV COND_CODE_1( \
21+
DT_NODE_HAS_PROP(DT_NODELABEL(mclk), clk_div), \
22+
(CONCAT(DL_SYSCTL_MCLK_DIVIDER_, \
23+
DT_PROP(DT_NODELABEL(mclk), clk_div))), \
24+
(0))
25+
26+
#define MSPM0_MFPCLK_DIV COND_CODE_1( \
27+
DT_NODE_HAS_PROP(DT_NODELABEL(mfpclk), clk_div), \
28+
(CONCAT(DL_SYSCTL_HFCLK_MFPCLK_DIVIDER_, \
29+
DT_PROP(DT_NODELABEL(mfpclk), clk_div))), \
30+
(0))
31+
32+
#if DT_NODE_HAS_STATUS(DT_NODELABEL(mfpclk), okay)
33+
#define MSPM0_MFPCLK_ENABLED 1
34+
#endif
35+
36+
#if DT_NODE_HAS_STATUS(DT_NODELABEL(pll), okay)
37+
#define MSPM0_PLL_ENABLED 1
38+
#endif
39+
40+
#define DT_MCLK_CLOCKS_CTRL DT_CLOCKS_CTLR(DT_NODELABEL(mclk))
41+
#define DT_LFCLK_CLOCKS_CTRL DT_CLOCKS_CTLR(DT_NODELABEL(lfclk))
42+
#define DT_HSCLK_CLOCKS_CTRL DT_CLOCKS_CTLR(DT_NODELABEL(hsclk))
43+
#define DT_HFCLK_CLOCKS_CTRL DT_CLOCKS_CTLR(DT_NODELABEL(hfclk))
44+
#define DT_MFPCLK_CLOCKS_CTRL DT_CLOCKS_CTLR(DT_NODELABEL(mfpclk))
45+
#define DT_PLL_CLOCKS_CTRL DT_CLOCKS_CTLR(DT_NODELABEL(pll))
46+
47+
struct mspm0_clk_cfg {
48+
uint32_t clk_div;
49+
uint32_t clk_freq;
50+
};
51+
52+
static struct mspm0_clk_cfg mspm0_lfclk_cfg = {
53+
.clk_freq = DT_PROP(DT_NODELABEL(lfclk), clock_frequency),
54+
};
55+
56+
static struct mspm0_clk_cfg mspm0_ulpclk_cfg = {
57+
.clk_freq = DT_PROP(DT_NODELABEL(ulpclk), clock_frequency),
58+
.clk_div = MSPM0_ULPCLK_DIV,
59+
};
60+
61+
static struct mspm0_clk_cfg mspm0_mclk_cfg = {
62+
.clk_freq = DT_PROP(DT_NODELABEL(mclk), clock_frequency),
63+
.clk_div = MSPM0_MCLK_DIV,
64+
};
65+
66+
#if MSPM0_MFPCLK_ENABLED
67+
static struct mspm0_clk_cfg mspm0_mfpclk_cfg = {
68+
.clk_freq = DT_PROP(DT_NODELABEL(mfpclk), clock_frequency),
69+
.clk_div = MSPM0_MFPCLK_DIV,
70+
};
71+
#endif
72+
73+
#if MSPM0_PLL_ENABLED
74+
/* basic checks of the devicetree to follow */
75+
#if (DT_NODE_HAS_PROP(DT_NODELABEL(pll), clk2x_div) && \
76+
DT_NODE_HAS_PROP(DT_NODELABEL(pll), clk0_div))
77+
#error "Only CLK2X or CLK0 can be enabled at a time on the PLL"
78+
#endif
79+
80+
static DL_SYSCTL_SYSPLLConfig clock_mspm0_cfg_syspll = {
81+
.inputFreq = DL_SYSCTL_SYSPLL_INPUT_FREQ_32_48_MHZ,
82+
.sysPLLMCLK = DL_SYSCTL_SYSPLL_MCLK_CLK2X,
83+
.sysPLLRef = DL_SYSCTL_SYSPLL_REF_SYSOSC,
84+
.rDivClk2x = (DT_PROP_OR(DT_NODELABEL(pll), clk2x_div, 1) - 1),
85+
.rDivClk1 = (DT_PROP_OR(DT_NODELABEL(pll), clk1_div, 1) - 1),
86+
.rDivClk0 = (DT_PROP_OR(DT_NODELABEL(pll), clk0_div, 1) - 1),
87+
.qDiv = (DT_PROP(DT_NODELABEL(pll), q_div) - 1),
88+
.pDiv = CONCAT(DL_SYSCTL_SYSPLL_PDIV_,
89+
DT_PROP(DT_NODELABEL(pll), p_div)),
90+
.enableCLK2x = COND_CODE_1(
91+
DT_NODE_HAS_PROP(DT_NODELABEL(pll), clk2x_div),
92+
(DL_SYSCTL_SYSPLL_CLK2X_ENABLE),
93+
(DL_SYSCTL_SYSPLL_CLK2X_DISABLE)),
94+
.enableCLK1 = COND_CODE_1(
95+
DT_NODE_HAS_PROP(DT_NODELABEL(pll), clk1_div),
96+
(DL_SYSCTL_SYSPLL_CLK1_ENABLE),
97+
(DL_SYSCTL_SYSPLL_CLK1_DISABLE)),
98+
.enableCLK0 = COND_CODE_1(
99+
DT_NODE_HAS_PROP(DT_NODELABEL(pll), clk0_div),
100+
(DL_SYSCTL_SYSPLL_CLK0_ENABLE),
101+
(DL_SYSCTL_SYSPLL_CLK0_DISABLE)),
102+
};
103+
#endif
104+
105+
static int clock_mspm0_on(const struct device *dev, clock_control_subsys_t sys)
106+
{
107+
return 0;
108+
}
109+
110+
static int clock_mspm0_off(const struct device *dev, clock_control_subsys_t sys)
111+
{
112+
return 0;
113+
}
114+
115+
static int clock_mspm0_get_rate(const struct device *dev,
116+
clock_control_subsys_t sys,
117+
uint32_t *rate)
118+
{
119+
struct mspm0_sys_clock *sys_clock = (struct mspm0_sys_clock *)sys;
120+
121+
switch (sys_clock->clk) {
122+
case MSPM0_CLOCK_LFCLK:
123+
*rate = mspm0_lfclk_cfg.clk_freq;
124+
break;
125+
126+
case MSPM0_CLOCK_ULPCLK:
127+
*rate = mspm0_ulpclk_cfg.clk_freq;
128+
break;
129+
130+
case MSPM0_CLOCK_MCLK:
131+
*rate = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC;
132+
break;
133+
134+
#if MSPM0_MFPCLK_ENABLED
135+
case MSPM0_CLOCK_MFPCLK:
136+
*rate = mspm0_mfpclk_cfg.clk_freq;
137+
break;
138+
#endif
139+
140+
case MSPM0_CLOCK_MFCLK:
141+
case MSPM0_CLOCK_CANCLK:
142+
default:
143+
return -ENOTSUP;
144+
}
145+
146+
return 0;
147+
}
148+
149+
static int clock_mspm0_init(const struct device *dev)
150+
{
151+
/* setup clocks based on specific rates */
152+
DL_SYSCTL_setSYSOSCFreq(DL_SYSCTL_SYSOSC_FREQ_BASE);
153+
154+
DL_SYSCTL_setMCLKDivider(mspm0_mclk_cfg.clk_div);
155+
DL_SYSCTL_setULPCLKDivider(mspm0_ulpclk_cfg.clk_div);
156+
157+
#if MSPM0_PLL_ENABLED
158+
#if DT_SAME_NODE(DT_HSCLK_CLOCKS_CTRL, DT_NODELABEL(syspll0))
159+
clock_mspm0_cfg_syspll.sysPLLMCLK = DL_SYSCTL_SYSPLL_MCLK_CLK0;
160+
#endif
161+
#if DT_SAME_NODE(DT_PLL_CLOCKS_CTRL, DT_NODELABEL(hfclk))
162+
clock_mspm0_cfg_syspll.sysPLLRef = DL_SYSCTL_SYSPLL_REF_HFCLK;
163+
#endif
164+
DL_SYSCTL_configSYSPLL(
165+
(DL_SYSCTL_SYSPLLConfig *)&clock_mspm0_cfg_syspll);
166+
#endif
167+
168+
#if DT_SAME_NODE(DT_HFCLK_CLOCKS_CTRL, DT_NODELABEL(hfxt))
169+
uint32_t hf_range;
170+
uint32_t hfxt_freq = DT_PROP(DT_NODELABEL(hfxt),
171+
clock_frequency) / MHZ(1);
172+
uint32_t xtal_startup_delay = DT_PROP_OR(DT_NODELABEL(hfxt),
173+
ti_xtal_startup_delay_us, 0);
174+
175+
if (hfxt_freq >= 4 &&
176+
hfxt_freq <= 8) {
177+
hf_range = DL_SYSCTL_HFXT_RANGE_4_8_MHZ;
178+
} else if (hfxt_freq > 8 &&
179+
hfxt_freq <= 16) {
180+
hf_range = DL_SYSCTL_HFXT_RANGE_8_16_MHZ;
181+
} else if (hfxt_freq > 16 &&
182+
hfxt_freq <= 32) {
183+
hf_range = DL_SYSCTL_HFXT_RANGE_16_32_MHZ;
184+
} else if (hfxt_freq > 32 &&
185+
hfxt_freq <= 48) {
186+
hf_range = DL_SYSCTL_HFXT_RANGE_32_48_MHZ;
187+
} else {
188+
return -EINVAL;
189+
}
190+
191+
/* startup time in 64us resolution */
192+
DL_SYSCTL_setHFCLKSourceHFXTParams(hf_range,
193+
mspm0_hfclk_cfg.xtal_startup_delay / 64,
194+
true);
195+
#else
196+
DL_SYSCTL_setHFCLKSourceHFCLKIN();
197+
#endif
198+
199+
#if MSPM0_LFCLK_ENABLED
200+
#if DT_SAME_NODE(DT_LFCLK_CLOCKS_CTRL, DT_NODELABEL(lfxt))
201+
DL_SYSCTL_LFCLKConfig config = {0};
202+
203+
DL_SYSCTL_setLFCLKSourceLFXT(&config);
204+
#elif DT_SAME_NODE(DT_LFCLK_CLOCKS_CTRL, DT_NODELABEL(lfdig_in))
205+
DL_SYSCTL_setLFCLKSourceEXLF();
206+
#endif
207+
#endif /* MSPM0_LFCLK_ENABLED */
208+
209+
#if DT_SAME_NODE(DT_MCLK_CLOCKS_CTRL, DT_NODELABEL(hsclk))
210+
#if DT_SAME_NODE(DT_HSCLK_CLOCKS_CTRL, DT_NODELABEL(hfclk))
211+
DL_SYSCTL_setMCLKSource(SYSOSC, HSCLK,
212+
DL_SYSCTL_HSCLK_SOURCE_HFCLK);
213+
#endif
214+
215+
#if MSPM0_PLL_ENABLED
216+
#if (DT_SAME_NODE(DT_HSCLK_CLOCKS_CTRL, DT_NODELABEL(syspll0)) || \
217+
DT_SAME_NODE(DT_HSCLK_CLOCKS_CTRL, DT_NODELABEL(syspll2x)))
218+
DL_SYSCTL_setMCLKSource(SYSOSC, HSCLK,
219+
DL_SYSCTL_HSCLK_SOURCE_SYSPLL);
220+
#endif
221+
#endif /* MSPM0_PLL_ENABLED */
222+
223+
#elif DT_SAME_NODE(DT_MCLK_CLOCKS_CTRL, DT_NODELABEL(lfclk))
224+
DL_SYSCTL_setMCLKSource(SYSOSC, LFCLK, false);
225+
#endif /* DT_SAME_NODE(DT_MCLK_CLOCKS_CTRL, DT_NODELABEL(hsclk)) */
226+
227+
#if MSPM0_MFPCLK_ENABLED
228+
#if DT_SAME_NODE(DT_MFPCLK_CLOCKS_CTRL, DT_NODELABEL(hfclk))
229+
DL_SYSCTL_setHFCLKDividerForMFPCLK(mspm0_mfpclk_cfg.clk_div);
230+
DL_SYSCTL_setMFPCLKSource(DL_SYSCTL_MFPCLK_SOURCE_HFCLK);
231+
#else
232+
DL_SYSCTL_setMFPCLKSource(DL_SYSCTL_MFPCLK_SOURCE_SYSOSC);
233+
#endif
234+
DL_SYSCTL_enableMFPCLK();
235+
#endif /* MSPM0_MFPCLK_ENABLED */
236+
237+
return 0;
238+
}
239+
240+
static const struct clock_control_driver_api clock_mspm0_driver_api = {
241+
.on = clock_mspm0_on,
242+
.off = clock_mspm0_off,
243+
.get_rate = clock_mspm0_get_rate,
244+
};
245+
246+
DEVICE_DT_DEFINE(DT_NODELABEL(ckm), &clock_mspm0_init, NULL, NULL, NULL,
247+
PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY,
248+
&clock_mspm0_driver_api);

dts/bindings/clock/ti,mspm0-clk.yaml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Copyright (c) 2025 Texas Instruments Inc.
2+
# Copyright (c) 2025 Linumiz
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
description: TI MSPM0 Clock
6+
7+
compatible: "ti,mspm0-clk"
8+
9+
include: [clock-controller.yaml, base.yaml]
10+
11+
properties:
12+
"#clock-cells":
13+
const: 1
14+
15+
clocks:
16+
description: |
17+
Clock reference source
18+
19+
clock-frequency:
20+
type: int
21+
description: |
22+
Output clock frequency in Hz.
23+
24+
clk-div:
25+
type: int
26+
description: |
27+
Clock divider selction value. Valid range [2 ... 16].
28+
29+
clock-cells:
30+
- clk

dts/bindings/clock/ti,mspm0-osc.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright (c) 2025 Texas Instruments Inc.
2+
# Copyright (c) 2025 Linumiz
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
description: TI MSPM0 oscillator
6+
7+
compatible: "ti,mspm0-osc"
8+
9+
include: [fixed-clock.yaml, base.yaml]
10+
11+
properties:
12+
"#clock-cells":
13+
const: 0
14+
15+
ti,xtal-startup-delay-us:
16+
type: int
17+
description: |
18+
Crystal Oscillator startup delay in micro seconds.
19+
20+
ti,low-cap:
21+
type: boolean
22+
description: |
23+
Specifies if capacitance is less than 3pF to reduce power consumption.

dts/bindings/clock/ti,mspm0-pll.yaml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Copyright (c) 2024 Texas Instruments Inc.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: TI MSPM0 Phase Locked Loop
5+
6+
compatible: "ti,mspm0-pll"
7+
8+
include: [clock-controller.yaml, base.yaml]
9+
10+
properties:
11+
clocks:
12+
required: true
13+
description: |
14+
Clock reference source
15+
16+
"#clock-cells":
17+
const: 0
18+
19+
p-div:
20+
type: int
21+
required: true
22+
enum:
23+
- 1
24+
- 2
25+
- 4
26+
- 8
27+
description: |
28+
pdiv is the pre-divider of the output. ref_in / pdiv * qdiv = VCO
29+
30+
q-div:
31+
type: int
32+
required: true
33+
description: |
34+
qdiv functions as a multiplier value for the ref_in / pdiv * qdiv = VCO
35+
Valid Range: 2 - 128
36+
37+
clk0-div:
38+
type: int
39+
description: |
40+
CLK0 PLL output is only enabled if the divider is present. Use CLK0 on
41+
the MSPM0 to output to the MCLK, UCLK, and CPUCLK
42+
Valid Range: 1 - 16
43+
44+
clk1-div:
45+
type: int
46+
description: |
47+
CLK1 PLL output is only enabled if the divider is present. Use CLK1 on
48+
the MSPM0 to output to the CANCLK, FCC, or output via EXCLK
49+
Valid Range: 1 - 16
50+
51+
clk2x-div:
52+
type: int
53+
description: |
54+
CLK2X PLL output is only enabled if the divider is present. Use CLK2X on
55+
the MSPM0 to output to the MCLK, UCLK, and CPUCLK instead of CLK0
56+
Valid Range: 1 - 16

0 commit comments

Comments
 (0)