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