1 /*
2  * Copyright (c) 2020, Seagate Technology LLC
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nxp_lpc11u6x_syscon
8 
9 #include <zephyr/devicetree.h>
10 #include <zephyr/device.h>
11 #include <zephyr/kernel.h>
12 
13 #include <zephyr/drivers/clock_control/lpc11u6x_clock_control.h>
14 
15 #include "clock_control_lpc11u6x.h"
16 
syscon_power_up(struct lpc11u6x_syscon_regs * syscon,uint32_t bit,bool enable)17 static void syscon_power_up(struct lpc11u6x_syscon_regs *syscon,
18 			    uint32_t bit, bool enable)
19 {
20 	if (enable) {
21 		syscon->pd_run_cfg = (syscon->pd_run_cfg & ~bit)
22 			| LPC11U6X_PDRUNCFG_MASK;
23 	} else {
24 		syscon->pd_run_cfg = syscon->pd_run_cfg | bit
25 			| LPC11U6X_PDRUNCFG_MASK;
26 	}
27 }
28 
syscon_set_pll_src(struct lpc11u6x_syscon_regs * syscon,uint32_t src)29 static void syscon_set_pll_src(struct lpc11u6x_syscon_regs *syscon,
30 			       uint32_t src)
31 {
32 	syscon->sys_pll_clk_sel = src;
33 	syscon->sys_pll_clk_uen = 0;
34 	syscon->sys_pll_clk_uen = 1;
35 }
36 
set_flash_access_time(uint32_t nr_cycles)37 static void set_flash_access_time(uint32_t nr_cycles)
38 {
39 	uint32_t *reg = (uint32_t *) LPC11U6X_FLASH_TIMING_REG;
40 
41 	*reg = (*reg & (~LPC11U6X_FLASH_TIMING_MASK)) | nr_cycles;
42 }
43 
syscon_setup_pll(struct lpc11u6x_syscon_regs * syscon,uint32_t msel,uint32_t psel)44 static void syscon_setup_pll(struct lpc11u6x_syscon_regs *syscon,
45 			     uint32_t msel, uint32_t psel)
46 {
47 	uint32_t val = msel & LPC11U6X_SYS_PLL_CTRL_MSEL_MASK;
48 
49 	val |= (psel & LPC11U6X_SYS_PLL_CTRL_PSEL_MASK) <<
50 		LPC11U6X_SYS_PLL_CTRL_PSEL_SHIFT;
51 	syscon->sys_pll_ctrl = val;
52 }
53 
syscon_pll_locked(struct lpc11u6x_syscon_regs * syscon)54 static bool syscon_pll_locked(struct lpc11u6x_syscon_regs *syscon)
55 {
56 	return (syscon->sys_pll_stat & 0x1) != 0;
57 }
58 
syscon_set_main_clock_source(struct lpc11u6x_syscon_regs * syscon,uint32_t src)59 static void syscon_set_main_clock_source(struct lpc11u6x_syscon_regs *syscon,
60 					 uint32_t src)
61 {
62 	syscon->main_clk_sel = src;
63 	syscon->main_clk_uen = 0;
64 	syscon->main_clk_uen = 1;
65 }
66 
syscon_ahb_clock_enable(struct lpc11u6x_syscon_regs * syscon,uint32_t mask,bool enable)67 static void syscon_ahb_clock_enable(struct lpc11u6x_syscon_regs *syscon,
68 				    uint32_t mask, bool enable)
69 {
70 	if (enable) {
71 		syscon->sys_ahb_clk_ctrl |= mask;
72 	} else {
73 		syscon->sys_ahb_clk_ctrl &= ~mask;
74 	}
75 }
76 
syscon_peripheral_reset(struct lpc11u6x_syscon_regs * syscon,uint32_t mask,bool reset)77 static void syscon_peripheral_reset(struct lpc11u6x_syscon_regs *syscon,
78 				    uint32_t mask, bool reset)
79 {
80 	if (reset) {
81 		syscon->p_reset_ctrl &= ~mask;
82 	} else {
83 		syscon->p_reset_ctrl |= mask;
84 	}
85 }
syscon_frg_init(struct lpc11u6x_syscon_regs * syscon)86 static void syscon_frg_init(struct lpc11u6x_syscon_regs *syscon)
87 {
88 	uint32_t div;
89 
90 	div = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / LPC11U6X_USART_CLOCK_RATE;
91 	if (!div) {
92 		div = 1;
93 	}
94 	syscon->frg_clk_div = div;
95 
96 	syscon_peripheral_reset(syscon, LPC11U6X_PRESET_CTRL_FRG, false);
97 	syscon->uart_frg_div = 0xFF;
98 	syscon->uart_frg_mult = ((CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / div)
99 				 * 256) / LPC11U6X_USART_CLOCK_RATE;
100 }
101 
syscon_frg_deinit(struct lpc11u6x_syscon_regs * syscon)102 static void syscon_frg_deinit(struct lpc11u6x_syscon_regs *syscon)
103 {
104 	syscon->uart_frg_div = 0x0;
105 	syscon_peripheral_reset(syscon, LPC11U6X_PRESET_CTRL_FRG, true);
106 }
107 
lpc11u6x_clock_control_on(const struct device * dev,clock_control_subsys_t sub_system)108 static int lpc11u6x_clock_control_on(const struct device *dev,
109 				     clock_control_subsys_t sub_system)
110 {
111 	const struct lpc11u6x_syscon_config *cfg = dev->config;
112 	struct lpc11u6x_syscon_data *data = dev->data;
113 	uint32_t clk_mask = 0, reset_mask = 0;
114 	int ret = 0, init_frg = 0;
115 
116 	k_mutex_lock(&data->mutex, K_FOREVER);
117 
118 	switch ((int) sub_system) {
119 	case LPC11U6X_CLOCK_I2C0:
120 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_I2C0;
121 		reset_mask = LPC11U6X_PRESET_CTRL_I2C0;
122 		break;
123 	case LPC11U6X_CLOCK_I2C1:
124 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_I2C1;
125 		reset_mask = LPC11U6X_PRESET_CTRL_I2C1;
126 		break;
127 	case LPC11U6X_CLOCK_GPIO:
128 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_GPIO |
129 			LPC11U6X_SYS_AHB_CLK_CTRL_PINT;
130 		break;
131 	case LPC11U6X_CLOCK_USART0:
132 		cfg->syscon->usart0_clk_div = 1;
133 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART0;
134 		break;
135 	case LPC11U6X_CLOCK_USART1:
136 		if (!data->frg_in_use++) {
137 			init_frg = 1;
138 		}
139 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART1;
140 		reset_mask = LPC11U6X_PRESET_CTRL_USART1;
141 		break;
142 	case LPC11U6X_CLOCK_USART2:
143 		if (!data->frg_in_use++) {
144 			init_frg = 1;
145 		}
146 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART2;
147 		reset_mask = LPC11U6X_PRESET_CTRL_USART2;
148 		break;
149 	case LPC11U6X_CLOCK_USART3:
150 		if (!data->frg_in_use++) {
151 			init_frg = 1;
152 		}
153 		data->usart34_in_use++;
154 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART3_4;
155 		reset_mask = LPC11U6X_PRESET_CTRL_USART3;
156 		break;
157 	case LPC11U6X_CLOCK_USART4:
158 		if (!data->frg_in_use++) {
159 			init_frg = 1;
160 		}
161 		data->usart34_in_use++;
162 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART3_4;
163 		reset_mask = LPC11U6X_PRESET_CTRL_USART4;
164 		break;
165 	default:
166 		k_mutex_unlock(&data->mutex);
167 		return -EINVAL;
168 	}
169 
170 	syscon_ahb_clock_enable(cfg->syscon, clk_mask, true);
171 	if (init_frg) {
172 		syscon_frg_init(cfg->syscon);
173 	}
174 	syscon_peripheral_reset(cfg->syscon, reset_mask, false);
175 	k_mutex_unlock(&data->mutex);
176 
177 	return ret;
178 }
179 
lpc11u6x_clock_control_off(const struct device * dev,clock_control_subsys_t sub_system)180 static int lpc11u6x_clock_control_off(const struct device *dev,
181 				      clock_control_subsys_t sub_system)
182 {
183 	const struct lpc11u6x_syscon_config *cfg = dev->config;
184 	struct lpc11u6x_syscon_data *data = dev->data;
185 	uint32_t clk_mask = 0, reset_mask = 0;
186 	int ret = 0, deinit_frg = 0;
187 
188 	k_mutex_lock(&data->mutex, K_FOREVER);
189 
190 	switch ((int) sub_system) {
191 	case LPC11U6X_CLOCK_I2C0:
192 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_I2C0;
193 		reset_mask = LPC11U6X_PRESET_CTRL_I2C0;
194 		break;
195 	case LPC11U6X_CLOCK_I2C1:
196 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_I2C1;
197 		reset_mask = LPC11U6X_PRESET_CTRL_I2C1;
198 		break;
199 	case LPC11U6X_CLOCK_GPIO:
200 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_GPIO |
201 			LPC11U6X_SYS_AHB_CLK_CTRL_PINT;
202 		break;
203 	case LPC11U6X_CLOCK_USART0:
204 		cfg->syscon->usart0_clk_div = 0;
205 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART0;
206 		break;
207 	case LPC11U6X_CLOCK_USART1:
208 		if (!(--data->frg_in_use)) {
209 			deinit_frg = 1;
210 		}
211 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART1;
212 		reset_mask = LPC11U6X_PRESET_CTRL_USART1;
213 		break;
214 	case LPC11U6X_CLOCK_USART2:
215 		if (!(--data->frg_in_use)) {
216 			deinit_frg = 1;
217 		}
218 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART2;
219 		reset_mask = LPC11U6X_PRESET_CTRL_USART2;
220 		break;
221 	case LPC11U6X_CLOCK_USART3:
222 		if (!(--data->frg_in_use)) {
223 			deinit_frg = 1;
224 		}
225 		if (!(--data->usart34_in_use)) {
226 			clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART3_4;
227 		}
228 		reset_mask = LPC11U6X_PRESET_CTRL_USART3;
229 		break;
230 	case LPC11U6X_CLOCK_USART4:
231 		if (!(--data->frg_in_use)) {
232 			deinit_frg = 1;
233 		}
234 		if (!(--data->usart34_in_use)) {
235 			clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART3_4;
236 		}
237 		reset_mask = LPC11U6X_PRESET_CTRL_USART4;
238 		break;
239 	default:
240 		k_mutex_unlock(&data->mutex);
241 		return -EINVAL;
242 	}
243 
244 	syscon_ahb_clock_enable(cfg->syscon, clk_mask, false);
245 	if (deinit_frg) {
246 		syscon_frg_deinit(cfg->syscon);
247 	}
248 	syscon_peripheral_reset(cfg->syscon, reset_mask, true);
249 	k_mutex_unlock(&data->mutex);
250 	return ret;
251 
252 }
253 
lpc11u6x_clock_control_get_rate(const struct device * dev,clock_control_subsys_t sub_system,uint32_t * rate)254 static int lpc11u6x_clock_control_get_rate(const struct device *dev,
255 					   clock_control_subsys_t sub_system,
256 					   uint32_t *rate)
257 {
258 	switch ((int) sub_system) {
259 	case LPC11U6X_CLOCK_I2C0:
260 	case LPC11U6X_CLOCK_I2C1:
261 	case LPC11U6X_CLOCK_GPIO:
262 	case LPC11U6X_CLOCK_USART0:
263 		*rate = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC;
264 		break;
265 	case LPC11U6X_CLOCK_USART1:
266 	case LPC11U6X_CLOCK_USART2:
267 	case LPC11U6X_CLOCK_USART3:
268 	case LPC11U6X_CLOCK_USART4:
269 		*rate = LPC11U6X_USART_CLOCK_RATE;
270 		break;
271 	default:
272 		return -EINVAL;
273 	}
274 	return 0;
275 }
276 
lpc11u6x_syscon_init(const struct device * dev)277 static int lpc11u6x_syscon_init(const struct device *dev)
278 {
279 	const struct lpc11u6x_syscon_config *cfg = dev->config;
280 	struct lpc11u6x_syscon_data *data = dev->data;
281 	uint32_t val;
282 
283 	k_mutex_init(&data->mutex);
284 	data->frg_in_use = 0;
285 	data->usart34_in_use = 0;
286 	/* Enable SRAM1 and USB ram if needed */
287 	val = 0;
288 #ifdef CONFIG_CLOCK_CONTROL_LPC11U6X_ENABLE_SRAM1
289 	val |= LPC11U6X_SYS_AHB_CLK_CTRL_SRAM1;
290 #endif /* CONFIG_CLOCK_CONTROL_LPC11U6X_ENABLE_SRAM1 */
291 #ifdef CONFIG_CLOCK_CONTROL_LPC11U6X_ENABLE_USB_RAM
292 	val |= LPC11U6X_SYS_AHB_CLK_CTRL_USB_SRAM;
293 #endif /* CONFIG_CLOCK_CONTROL_LPC11U6X_ENABLE_USB_RAM */
294 
295 	/* Enable IOCON (I/O Control) clock. */
296 	val |= LPC11U6X_SYS_AHB_CLK_CTRL_IOCON;
297 
298 	syscon_ahb_clock_enable(cfg->syscon, val, true);
299 
300 	/* Configure PLL output as the main clock source, with a frequency of
301 	 * 48MHz
302 	 */
303 #ifdef CONFIG_CLOCK_CONTROL_LPC11U6X_PLL_SRC_SYSOSC
304 	syscon_power_up(cfg->syscon, LPC11U6X_PDRUNCFG_SYSOSC_PD, true);
305 
306 	/* Wait ~500us */
307 	for (int i = 0; i < 2500; i++) {
308 	}
309 
310 	/* Configure PLL input */
311 	syscon_set_pll_src(cfg->syscon, LPC11U6X_SYS_PLL_CLK_SEL_SYSOSC);
312 
313 	pinctrl_apply_state(cfg->pincfg, PINCTRL_STATE_DEFAULT);
314 
315 #elif defined(CONFIG_CLOCK_CONTROL_LPC11U6X_PLL_SRC_IRC)
316 	syscon_power_up(cfg->syscon, LPC11U6X_PDRUNCFG_IRC_PD, true);
317 	syscon_set_pll_src(cfg->syscon, LPC11U6X_SYS_PLL_CLK_SEL_IRC);
318 #endif
319 	/* Flash access takes 3 clock cycles for main clock frequencies
320 	 * between 40MHz and 50MHz
321 	 */
322 	set_flash_access_time(LPC11U6X_FLASH_TIMING_3CYCLES);
323 
324 	/* Shutdown PLL to change divider/mult ratios */
325 	syscon_power_up(cfg->syscon, LPC11U6X_PDRUNCFG_PLL_PD, false);
326 
327 	/* Setup PLL to have 48MHz output */
328 	syscon_setup_pll(cfg->syscon, 3, 1);
329 
330 	/* Power up pll and wait */
331 	syscon_power_up(cfg->syscon, LPC11U6X_PDRUNCFG_PLL_PD, true);
332 
333 	while (!syscon_pll_locked(cfg->syscon)) {
334 	}
335 
336 	cfg->syscon->sys_ahb_clk_div = 1;
337 	syscon_set_main_clock_source(cfg->syscon, LPC11U6X_MAIN_CLK_SRC_PLLOUT);
338 	return 0;
339 }
340 
341 static const struct clock_control_driver_api lpc11u6x_clock_control_api = {
342 	.on = lpc11u6x_clock_control_on,
343 	.off = lpc11u6x_clock_control_off,
344 	.get_rate = lpc11u6x_clock_control_get_rate,
345 };
346 
347 
348 PINCTRL_DT_INST_DEFINE(0);
349 
350 static const struct lpc11u6x_syscon_config syscon_config = {
351 	.syscon = (struct lpc11u6x_syscon_regs *) DT_INST_REG_ADDR(0),
352 	.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0),
353 };
354 
355 static struct lpc11u6x_syscon_data syscon_data;
356 
357 DEVICE_DT_INST_DEFINE(0,
358 		    &lpc11u6x_syscon_init,
359 		    NULL,
360 		    &syscon_data, &syscon_config,
361 		    PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY,
362 		    &lpc11u6x_clock_control_api);
363