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