/* * Copyright (c) 2020, Seagate Technology LLC * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nxp_lpc11u6x_syscon #include #include #include #include #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);