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