diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index 4cbd3ac47d6..949d87d1a66 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -77,3 +77,4 @@ endif() zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_AST10X0 clock_control_ast10x0.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_MAX32 clock_control_max32.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_AUXPLL clock_control_nrf_auxpll.c) diff --git a/drivers/clock_control/Kconfig b/drivers/clock_control/Kconfig index b1b64f4a3ca..55821056047 100644 --- a/drivers/clock_control/Kconfig +++ b/drivers/clock_control/Kconfig @@ -90,4 +90,6 @@ source "drivers/clock_control/Kconfig.pwm" source "drivers/clock_control/Kconfig.rpi_pico" +source "drivers/clock_control/Kconfig.nrf_auxpll" + endif # CLOCK_CONTROL diff --git a/drivers/clock_control/Kconfig.nrf_auxpll b/drivers/clock_control/Kconfig.nrf_auxpll new file mode 100644 index 00000000000..413452c1ac4 --- /dev/null +++ b/drivers/clock_control/Kconfig.nrf_auxpll @@ -0,0 +1,9 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config CLOCK_CONTROL_NRF_AUXPLL + bool "nRF Auxiliary PLL driver" + default y + depends on DT_HAS_NORDIC_NRF_AUXPLL_ENABLED + help + Driver for nRF Auxiliary PLL. diff --git a/drivers/clock_control/clock_control_nrf_auxpll.c b/drivers/clock_control/clock_control_nrf_auxpll.c new file mode 100644 index 00000000000..4bd6c7bef82 --- /dev/null +++ b/drivers/clock_control/clock_control_nrf_auxpll.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nordic_nrf_auxpll + +#include + +#include +#include +#include +#include +#include +#include + +#include + +struct clock_control_nrf_auxpll_config { + NRF_AUXPLL_Type *auxpll; + uint32_t ref_clk_hz; + uint32_t ficr_ctune; + nrf_auxpll_config_t cfg; + uint16_t frequency; + nrf_auxpll_ctrl_outsel_t out_div; +}; + +static int clock_control_nrf_auxpll_on(const struct device *dev, clock_control_subsys_t sys) +{ + const struct clock_control_nrf_auxpll_config *config = dev->config; + + ARG_UNUSED(sys); + + nrf_auxpll_task_trigger(config->auxpll, NRF_AUXPLL_TASK_START); + + while (!nrf_auxpll_mode_locked_check(config->auxpll)) { + } + + return 0; +} + +static int clock_control_nrf_auxpll_off(const struct device *dev, clock_control_subsys_t sys) +{ + const struct clock_control_nrf_auxpll_config *config = dev->config; + + ARG_UNUSED(sys); + + nrf_auxpll_task_trigger(config->auxpll, NRF_AUXPLL_TASK_STOP); + + while (nrf_auxpll_running_check(config->auxpll)) { + } + + return 0; +} + +static int clock_control_nrf_auxpll_get_rate(const struct device *dev, clock_control_subsys_t sys, + uint32_t *rate) +{ + const struct clock_control_nrf_auxpll_config *config = dev->config; + uint8_t ratio; + + ARG_UNUSED(sys); + + ratio = nrf_auxpll_static_ratio_get(config->auxpll); + + *rate = (ratio * config->ref_clk_hz + + (config->ref_clk_hz * (uint64_t)config->frequency) / + (AUXPLL_AUXPLLCTRL_FREQUENCY_FREQUENCY_MaximumDiv + 1U)) / + config->out_div; + + return 0; +} + +static enum clock_control_status clock_control_nrf_auxpll_get_status(const struct device *dev, + clock_control_subsys_t sys) +{ + const struct clock_control_nrf_auxpll_config *config = dev->config; + + ARG_UNUSED(sys); + + if (nrf_auxpll_mode_locked_check(config->auxpll)) { + return CLOCK_CONTROL_STATUS_ON; + } + + return CLOCK_CONTROL_STATUS_OFF; +} + +static struct clock_control_driver_api clock_control_nrf_auxpll_api = { + .on = clock_control_nrf_auxpll_on, + .off = clock_control_nrf_auxpll_off, + .get_rate = clock_control_nrf_auxpll_get_rate, + .get_status = clock_control_nrf_auxpll_get_status, +}; + +static int clock_control_nrf_auxpll_init(const struct device *dev) +{ + const struct clock_control_nrf_auxpll_config *config = dev->config; + + nrf_auxpll_ctrl_frequency_set(config->auxpll, config->frequency); + + nrf_auxpll_lock(config->auxpll); + nrf_auxpll_trim_ctune_set(config->auxpll, sys_read8(config->ficr_ctune)); + nrf_auxpll_config_set(config->auxpll, &config->cfg); + nrf_auxpll_ctrl_outsel_set(config->auxpll, config->out_div); + nrf_auxpll_unlock(config->auxpll); + + nrf_auxpll_ctrl_mode_set(config->auxpll, NRF_AUXPLL_CTRL_MODE_LOCKED); + + return 0; +} + +#define CLOCK_CONTROL_NRF_AUXPLL_DEFINE(n) \ + static const struct clock_control_nrf_auxpll_config config##n = { \ + .auxpll = (NRF_AUXPLL_Type *)DT_INST_REG_ADDR(n), \ + .ref_clk_hz = DT_PROP(DT_INST_CLOCKS_CTLR(n), clock_frequency), \ + .ficr_ctune = DT_REG_ADDR(DT_INST_PHANDLE(n, nordic_ficrs)) + \ + DT_INST_PHA(n, nordic_ficrs, offset), \ + .cfg = \ + { \ + .outdrive = DT_INST_PROP(n, nordic_out_drive), \ + .current_tune = DT_INST_PROP(n, nordic_current_tune), \ + .sdm_off = DT_INST_PROP(n, nordic_sdm_disable), \ + .dither_off = DT_INST_PROP(n, nordic_dither_disable), \ + .range = DT_INST_ENUM_IDX(n, nordic_range), \ + }, \ + .frequency = DT_INST_PROP(n, nordic_frequency), \ + .out_div = DT_INST_PROP(n, nordic_out_div), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, clock_control_nrf_auxpll_init, NULL, NULL, &config##n, \ + PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, \ + &clock_control_nrf_auxpll_api); + +DT_INST_FOREACH_STATUS_OKAY(CLOCK_CONTROL_NRF_AUXPLL_DEFINE)