| /* | 
 |  * Copyright (c) 2020, Seagate Technology LLC | 
 |  * | 
 |  * SPDX-License-Identifier: Apache-2.0 | 
 |  */ | 
 |  | 
 | #define DT_DRV_COMPAT nxp_lpc11u6x_syscon | 
 |  | 
 | #include <zephyr/devicetree.h> | 
 | #include <zephyr/device.h> | 
 | #include <zephyr/kernel.h> | 
 |  | 
 | #include <zephyr/drivers/clock_control/lpc11u6x_clock_control.h> | 
 |  | 
 | #include "clock_control_lpc11u6x.h" | 
 |  | 
 | static void syscon_power_up(struct lpc11u6x_syscon_regs *syscon, | 
 | 			    uint32_t bit, bool enable) | 
 | { | 
 | 	if (enable) { | 
 | 		syscon->pd_run_cfg = (syscon->pd_run_cfg & ~bit) | 
 | 			| LPC11U6X_PDRUNCFG_MASK; | 
 | 	} else { | 
 | 		syscon->pd_run_cfg = syscon->pd_run_cfg | bit | 
 | 			| LPC11U6X_PDRUNCFG_MASK; | 
 | 	} | 
 | } | 
 |  | 
 | static void syscon_set_pll_src(struct lpc11u6x_syscon_regs *syscon, | 
 | 			       uint32_t src) | 
 | { | 
 | 	syscon->sys_pll_clk_sel = src; | 
 | 	syscon->sys_pll_clk_uen = 0; | 
 | 	syscon->sys_pll_clk_uen = 1; | 
 | } | 
 |  | 
 | static void set_flash_access_time(uint32_t nr_cycles) | 
 | { | 
 | 	uint32_t *reg = (uint32_t *) LPC11U6X_FLASH_TIMING_REG; | 
 |  | 
 | 	*reg = (*reg & (~LPC11U6X_FLASH_TIMING_MASK)) | nr_cycles; | 
 | } | 
 |  | 
 | static void syscon_setup_pll(struct lpc11u6x_syscon_regs *syscon, | 
 | 			     uint32_t msel, uint32_t psel) | 
 | { | 
 | 	uint32_t val = msel & LPC11U6X_SYS_PLL_CTRL_MSEL_MASK; | 
 |  | 
 | 	val |= (psel & LPC11U6X_SYS_PLL_CTRL_PSEL_MASK) << | 
 | 		LPC11U6X_SYS_PLL_CTRL_PSEL_SHIFT; | 
 | 	syscon->sys_pll_ctrl = val; | 
 | } | 
 |  | 
 | static bool syscon_pll_locked(struct lpc11u6x_syscon_regs *syscon) | 
 | { | 
 | 	return (syscon->sys_pll_stat & 0x1) != 0; | 
 | } | 
 |  | 
 | static void syscon_set_main_clock_source(struct lpc11u6x_syscon_regs *syscon, | 
 | 					 uint32_t src) | 
 | { | 
 | 	syscon->main_clk_sel = src; | 
 | 	syscon->main_clk_uen = 0; | 
 | 	syscon->main_clk_uen = 1; | 
 | } | 
 |  | 
 | static void syscon_ahb_clock_enable(struct lpc11u6x_syscon_regs *syscon, | 
 | 				    uint32_t mask, bool enable) | 
 | { | 
 | 	if (enable) { | 
 | 		syscon->sys_ahb_clk_ctrl |= mask; | 
 | 	} else { | 
 | 		syscon->sys_ahb_clk_ctrl &= ~mask; | 
 | 	} | 
 | } | 
 |  | 
 | static void syscon_peripheral_reset(struct lpc11u6x_syscon_regs *syscon, | 
 | 				    uint32_t mask, bool reset) | 
 | { | 
 | 	if (reset) { | 
 | 		syscon->p_reset_ctrl &= ~mask; | 
 | 	} else { | 
 | 		syscon->p_reset_ctrl |= mask; | 
 | 	} | 
 | } | 
 | static void syscon_frg_init(struct lpc11u6x_syscon_regs *syscon) | 
 | { | 
 | 	uint32_t div; | 
 |  | 
 | 	div = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / LPC11U6X_USART_CLOCK_RATE; | 
 | 	if (!div) { | 
 | 		div = 1; | 
 | 	} | 
 | 	syscon->frg_clk_div = div; | 
 |  | 
 | 	syscon_peripheral_reset(syscon, LPC11U6X_PRESET_CTRL_FRG, false); | 
 | 	syscon->uart_frg_div = 0xFF; | 
 | 	syscon->uart_frg_mult = ((CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / div) | 
 | 				 * 256) / LPC11U6X_USART_CLOCK_RATE; | 
 | } | 
 |  | 
 | static void syscon_frg_deinit(struct lpc11u6x_syscon_regs *syscon) | 
 | { | 
 | 	syscon->uart_frg_div = 0x0; | 
 | 	syscon_peripheral_reset(syscon, LPC11U6X_PRESET_CTRL_FRG, true); | 
 | } | 
 |  | 
 | static int lpc11u6x_clock_control_on(const struct device *dev, | 
 | 				     clock_control_subsys_t sub_system) | 
 | { | 
 | 	const struct lpc11u6x_syscon_config *cfg = dev->config; | 
 | 	struct lpc11u6x_syscon_data *data = dev->data; | 
 | 	uint32_t clk_mask = 0, reset_mask = 0; | 
 | 	int ret = 0, init_frg = 0; | 
 |  | 
 | 	k_mutex_lock(&data->mutex, K_FOREVER); | 
 |  | 
 | 	switch ((int) sub_system) { | 
 | 	case LPC11U6X_CLOCK_I2C0: | 
 | 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_I2C0; | 
 | 		reset_mask = LPC11U6X_PRESET_CTRL_I2C0; | 
 | 		break; | 
 | 	case LPC11U6X_CLOCK_I2C1: | 
 | 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_I2C1; | 
 | 		reset_mask = LPC11U6X_PRESET_CTRL_I2C1; | 
 | 		break; | 
 | 	case LPC11U6X_CLOCK_GPIO: | 
 | 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_GPIO | | 
 | 			LPC11U6X_SYS_AHB_CLK_CTRL_PINT; | 
 | 		break; | 
 | 	case LPC11U6X_CLOCK_USART0: | 
 | 		cfg->syscon->usart0_clk_div = 1; | 
 | 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART0; | 
 | 		break; | 
 | 	case LPC11U6X_CLOCK_USART1: | 
 | 		if (!data->frg_in_use++) { | 
 | 			init_frg = 1; | 
 | 		} | 
 | 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART1; | 
 | 		reset_mask = LPC11U6X_PRESET_CTRL_USART1; | 
 | 		break; | 
 | 	case LPC11U6X_CLOCK_USART2: | 
 | 		if (!data->frg_in_use++) { | 
 | 			init_frg = 1; | 
 | 		} | 
 | 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART2; | 
 | 		reset_mask = LPC11U6X_PRESET_CTRL_USART2; | 
 | 		break; | 
 | 	case LPC11U6X_CLOCK_USART3: | 
 | 		if (!data->frg_in_use++) { | 
 | 			init_frg = 1; | 
 | 		} | 
 | 		data->usart34_in_use++; | 
 | 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART3_4; | 
 | 		reset_mask = LPC11U6X_PRESET_CTRL_USART3; | 
 | 		break; | 
 | 	case LPC11U6X_CLOCK_USART4: | 
 | 		if (!data->frg_in_use++) { | 
 | 			init_frg = 1; | 
 | 		} | 
 | 		data->usart34_in_use++; | 
 | 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART3_4; | 
 | 		reset_mask = LPC11U6X_PRESET_CTRL_USART4; | 
 | 		break; | 
 | 	default: | 
 | 		k_mutex_unlock(&data->mutex); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	syscon_ahb_clock_enable(cfg->syscon, clk_mask, true); | 
 | 	if (init_frg) { | 
 | 		syscon_frg_init(cfg->syscon); | 
 | 	} | 
 | 	syscon_peripheral_reset(cfg->syscon, reset_mask, false); | 
 | 	k_mutex_unlock(&data->mutex); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int lpc11u6x_clock_control_off(const struct device *dev, | 
 | 				      clock_control_subsys_t sub_system) | 
 | { | 
 | 	const struct lpc11u6x_syscon_config *cfg = dev->config; | 
 | 	struct lpc11u6x_syscon_data *data = dev->data; | 
 | 	uint32_t clk_mask = 0, reset_mask = 0; | 
 | 	int ret = 0, deinit_frg = 0; | 
 |  | 
 | 	k_mutex_lock(&data->mutex, K_FOREVER); | 
 |  | 
 | 	switch ((int) sub_system) { | 
 | 	case LPC11U6X_CLOCK_I2C0: | 
 | 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_I2C0; | 
 | 		reset_mask = LPC11U6X_PRESET_CTRL_I2C0; | 
 | 		break; | 
 | 	case LPC11U6X_CLOCK_I2C1: | 
 | 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_I2C1; | 
 | 		reset_mask = LPC11U6X_PRESET_CTRL_I2C1; | 
 | 		break; | 
 | 	case LPC11U6X_CLOCK_GPIO: | 
 | 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_GPIO | | 
 | 			LPC11U6X_SYS_AHB_CLK_CTRL_PINT; | 
 | 		break; | 
 | 	case LPC11U6X_CLOCK_USART0: | 
 | 		cfg->syscon->usart0_clk_div = 0; | 
 | 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART0; | 
 | 		break; | 
 | 	case LPC11U6X_CLOCK_USART1: | 
 | 		if (!(--data->frg_in_use)) { | 
 | 			deinit_frg = 1; | 
 | 		} | 
 | 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART1; | 
 | 		reset_mask = LPC11U6X_PRESET_CTRL_USART1; | 
 | 		break; | 
 | 	case LPC11U6X_CLOCK_USART2: | 
 | 		if (!(--data->frg_in_use)) { | 
 | 			deinit_frg = 1; | 
 | 		} | 
 | 		clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART2; | 
 | 		reset_mask = LPC11U6X_PRESET_CTRL_USART2; | 
 | 		break; | 
 | 	case LPC11U6X_CLOCK_USART3: | 
 | 		if (!(--data->frg_in_use)) { | 
 | 			deinit_frg = 1; | 
 | 		} | 
 | 		if (!(--data->usart34_in_use)) { | 
 | 			clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART3_4; | 
 | 		} | 
 | 		reset_mask = LPC11U6X_PRESET_CTRL_USART3; | 
 | 		break; | 
 | 	case LPC11U6X_CLOCK_USART4: | 
 | 		if (!(--data->frg_in_use)) { | 
 | 			deinit_frg = 1; | 
 | 		} | 
 | 		if (!(--data->usart34_in_use)) { | 
 | 			clk_mask = LPC11U6X_SYS_AHB_CLK_CTRL_USART3_4; | 
 | 		} | 
 | 		reset_mask = LPC11U6X_PRESET_CTRL_USART4; | 
 | 		break; | 
 | 	default: | 
 | 		k_mutex_unlock(&data->mutex); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	syscon_ahb_clock_enable(cfg->syscon, clk_mask, false); | 
 | 	if (deinit_frg) { | 
 | 		syscon_frg_deinit(cfg->syscon); | 
 | 	} | 
 | 	syscon_peripheral_reset(cfg->syscon, reset_mask, true); | 
 | 	k_mutex_unlock(&data->mutex); | 
 | 	return ret; | 
 |  | 
 | } | 
 |  | 
 | static int lpc11u6x_clock_control_get_rate(const struct device *dev, | 
 | 					   clock_control_subsys_t sub_system, | 
 | 					   uint32_t *rate) | 
 | { | 
 | 	switch ((int) sub_system) { | 
 | 	case LPC11U6X_CLOCK_I2C0: | 
 | 	case LPC11U6X_CLOCK_I2C1: | 
 | 	case LPC11U6X_CLOCK_GPIO: | 
 | 	case LPC11U6X_CLOCK_USART0: | 
 | 		*rate = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC; | 
 | 		break; | 
 | 	case LPC11U6X_CLOCK_USART1: | 
 | 	case LPC11U6X_CLOCK_USART2: | 
 | 	case LPC11U6X_CLOCK_USART3: | 
 | 	case LPC11U6X_CLOCK_USART4: | 
 | 		*rate = LPC11U6X_USART_CLOCK_RATE; | 
 | 		break; | 
 | 	default: | 
 | 		return -EINVAL; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int lpc11u6x_syscon_init(const struct device *dev) | 
 | { | 
 | 	const struct lpc11u6x_syscon_config *cfg = dev->config; | 
 | 	struct lpc11u6x_syscon_data *data = dev->data; | 
 | 	uint32_t val; | 
 |  | 
 | 	k_mutex_init(&data->mutex); | 
 | 	data->frg_in_use = 0; | 
 | 	data->usart34_in_use = 0; | 
 | 	/* Enable SRAM1 and USB ram if needed */ | 
 | 	val = 0; | 
 | #ifdef CONFIG_CLOCK_CONTROL_LPC11U6X_ENABLE_SRAM1 | 
 | 	val |= LPC11U6X_SYS_AHB_CLK_CTRL_SRAM1; | 
 | #endif /* CONFIG_CLOCK_CONTROL_LPC11U6X_ENABLE_SRAM1 */ | 
 | #ifdef CONFIG_CLOCK_CONTROL_LPC11U6X_ENABLE_USB_RAM | 
 | 	val |= LPC11U6X_SYS_AHB_CLK_CTRL_USB_SRAM; | 
 | #endif /* CONFIG_CLOCK_CONTROL_LPC11U6X_ENABLE_USB_RAM */ | 
 |  | 
 | 	/* Enable IOCON (I/O Control) clock. */ | 
 | 	val |= LPC11U6X_SYS_AHB_CLK_CTRL_IOCON; | 
 |  | 
 | 	syscon_ahb_clock_enable(cfg->syscon, val, true); | 
 |  | 
 | 	/* Configure PLL output as the main clock source, with a frequency of | 
 | 	 * 48MHz | 
 | 	 */ | 
 | #ifdef CONFIG_CLOCK_CONTROL_LPC11U6X_PLL_SRC_SYSOSC | 
 | 	syscon_power_up(cfg->syscon, LPC11U6X_PDRUNCFG_SYSOSC_PD, true); | 
 |  | 
 | 	/* Wait ~500us */ | 
 | 	for (int i = 0; i < 2500; i++) { | 
 | 	} | 
 |  | 
 | 	/* Configure PLL input */ | 
 | 	syscon_set_pll_src(cfg->syscon, LPC11U6X_SYS_PLL_CLK_SEL_SYSOSC); | 
 |  | 
 | 	pinctrl_apply_state(cfg->pincfg, PINCTRL_STATE_DEFAULT); | 
 |  | 
 | #elif defined(CONFIG_CLOCK_CONTROL_LPC11U6X_PLL_SRC_IRC) | 
 | 	syscon_power_up(cfg->syscon, LPC11U6X_PDRUNCFG_IRC_PD, true); | 
 | 	syscon_set_pll_src(cfg->syscon, LPC11U6X_SYS_PLL_CLK_SEL_IRC); | 
 | #endif | 
 | 	/* Flash access takes 3 clock cycles for main clock frequencies | 
 | 	 * between 40MHz and 50MHz | 
 | 	 */ | 
 | 	set_flash_access_time(LPC11U6X_FLASH_TIMING_3CYCLES); | 
 |  | 
 | 	/* Shutdown PLL to change divider/mult ratios */ | 
 | 	syscon_power_up(cfg->syscon, LPC11U6X_PDRUNCFG_PLL_PD, false); | 
 |  | 
 | 	/* Setup PLL to have 48MHz output */ | 
 | 	syscon_setup_pll(cfg->syscon, 3, 1); | 
 |  | 
 | 	/* Power up pll and wait */ | 
 | 	syscon_power_up(cfg->syscon, LPC11U6X_PDRUNCFG_PLL_PD, true); | 
 |  | 
 | 	while (!syscon_pll_locked(cfg->syscon)) { | 
 | 	} | 
 |  | 
 | 	cfg->syscon->sys_ahb_clk_div = 1; | 
 | 	syscon_set_main_clock_source(cfg->syscon, LPC11U6X_MAIN_CLK_SRC_PLLOUT); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static const struct clock_control_driver_api lpc11u6x_clock_control_api = { | 
 | 	.on = lpc11u6x_clock_control_on, | 
 | 	.off = lpc11u6x_clock_control_off, | 
 | 	.get_rate = lpc11u6x_clock_control_get_rate, | 
 | }; | 
 |  | 
 |  | 
 | PINCTRL_DT_INST_DEFINE(0); | 
 |  | 
 | static const struct lpc11u6x_syscon_config syscon_config = { | 
 | 	.syscon = (struct lpc11u6x_syscon_regs *) DT_INST_REG_ADDR(0), | 
 | 	.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0), | 
 | }; | 
 |  | 
 | static struct lpc11u6x_syscon_data syscon_data; | 
 |  | 
 | DEVICE_DT_INST_DEFINE(0, | 
 | 		    &lpc11u6x_syscon_init, | 
 | 		    NULL, | 
 | 		    &syscon_data, &syscon_config, | 
 | 		    PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, | 
 | 		    &lpc11u6x_clock_control_api); |