blob: 554ec072d4da39b81e4db72243af94d402be42cc [file] [log] [blame]
/*
* 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);