diff --git a/boards/nxp/lpcxpresso55s69/lpcxpresso55s69.dtsi b/boards/nxp/lpcxpresso55s69/lpcxpresso55s69.dtsi index e3d5b08fa19eb..caeca8fcb68e0 100644 --- a/boards/nxp/lpcxpresso55s69/lpcxpresso55s69.dtsi +++ b/boards/nxp/lpcxpresso55s69/lpcxpresso55s69.dtsi @@ -183,6 +183,9 @@ mikrobus_spi: &hs_lspi { &flexcomm0 { pinctrl-0 = <&pinmux_flexcomm0_usart>; pinctrl-names = "default"; + /* Select FROHF as input clock */ + clock-state-0 = <&fcclksel0 3>; + clock-state-names = "default"; }; &flexcomm2 { diff --git a/boards/nxp/lpcxpresso55s69/lpcxpresso55s69_lpc55s69_cpu0_defconfig b/boards/nxp/lpcxpresso55s69/lpcxpresso55s69_lpc55s69_cpu0_defconfig index 966a0228f10bd..2044e23b7d76e 100644 --- a/boards/nxp/lpcxpresso55s69/lpcxpresso55s69_lpc55s69_cpu0_defconfig +++ b/boards/nxp/lpcxpresso55s69/lpcxpresso55s69_lpc55s69_cpu0_defconfig @@ -10,6 +10,7 @@ CONFIG_SERIAL=y CONFIG_UART_INTERRUPT_DRIVEN=y CONFIG_ARM_MPU=y CONFIG_HW_STACK_PROTECTION=y +CONFIG_CLOCK_MGMT=y # Enable TrustZone-M CONFIG_TRUSTED_EXECUTION_SECURE=y diff --git a/doc/hardware/clock_mgmt/index.rst b/doc/hardware/clock_mgmt/index.rst new file mode 100644 index 0000000000000..2d9a922f9ae42 --- /dev/null +++ b/doc/hardware/clock_mgmt/index.rst @@ -0,0 +1,338 @@ +.. _clock-mgmt-guide: + +Clock Management +################ + +This is a high level to clock management in Zephyr. See :ref:`clock_mgmt_api` +for API reference material. + +Introduction +************ + +SOC clock control peripherals typically expose a mixture of clock generators, +clock outputs, and clock routing blocks to the system. These may include +PLLs, internal oscillators, multiplexers, or dividers/multipliers. The clock +structure of a system is typically very specific to the vendor/SOC itself. +Zephyr provides the clock management subsystem in order to enable clock +consumers and applications to manage clocks in a device-agnostic manner. + +Reading Clocks +************** + +Drivers must supply a subsystem identifier when reading or subscribing to +a callback for a clock. The clock management defines one subsystem identifier, +:c:macro:`CLOCK_MGMT_SUBSYS_DEFAULT`. Consumers may define additional private +identifiers if they need to query multiple clocks. Each identifier corresponds +to an index within the ``clocks`` property of the node defining the clock +consumer. + +Consumers can query a clock output rate via the :c:func:`clock_mgmt_get_rate` +function. This function returns the clock rate as a positive integer, +or a negative error value on failure. + +Consumers can also monitor a clock output rate. To do so, they may +register for a callback. Once a callback is initialized with +:c:func:`clock_mgmt_init_callback`, it can be registered with the +:c:func:`clock_mgmt_add_callback` function. Until the callback is removed +with :c:func:`clock_mgmt_remove_callback`, the driver will receive a callback +whenever the clock changes rate, starts, or stops. + +Setting Clocks +************** + +Clocks are primarily managed via "setpoints". A setpoint corresponds to a +specific group of clock settings, which may have systemwide effects, or simply +configure clocks for a given peripheral. For example, one setpoint may simply +configure the multiplexer and divider for a given peripheral. Another may +reconfigure the system level PLL, and modify the core clock rate. + +Setpoints are configured via devicetree, and are usually opaque to the +driver/application code consuming the clock. Instead of having direct +knowledge of a setpoint's contents, clock consumers select a setpoint +based on the power state they are targeting. The following setpoint +power states are defined: + +.. table:: Standardized state names + :align: center + + +-------------+-------------------------------------+-------------------------+ + | State | Identifier | Purpose | + +-------------+-------------------------------------+-------------------------+ + | ``default`` | :c:macro:`CLOCK_MGMT_STATE_DEFAULT` | Clock state when | + | | | the device is in | + | | | operational state | + +-------------+-------------------------------------+-------------------------+ + | ``sleep`` | :c:macro:`CLOCK_MGMT_STATE_SLEEP` | Clock state when | + | | | the device is in low | + | | | power or sleep modes | + +-------------+-------------------------------------+-------------------------+ + +Devicetree Representation +************************* + +Devicetree is used to define all system specific data for clock management. +The SOC itself will define the system clock tree. Then, the devicetree for +clock consumers may reference the system clock tree nodes to define clock +setpoints or access clock subsystems. + +The SOC level clock tree definition will be specific to the SOC, but +may look similar to the following: + +.. code-block:: devicetree + + clock_source: clock-source { + clock-id = "CLK_SOURCE"; + compatible = "clock-source"; + #clock-cells = <1>; + + clock_div: clock-div { + clock-id = "CLK_DIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + clock_output: clock-output { + compatible = "clock-output"; + clock-output; + clock-id = "CLK_OUTPUT"; + #clock-cells = <0>; + }; + }; + }; + +The peripheral devicetree will then reference these nodes to query clock rates, +and define setpoints: + +.. code-block:: devicetree + + periph0: periph@0 { + /* Clock subsystems */ + clocks = <&clock_output>; + clock-names = "default"; + /* Default clock setpoint */ + clock-setpoint-0 = <&clock_source 1000000 &clock_div 3>; + /* Sleep setpoint */ + clock-setpoint-1 = <&clock_source 200000 &clock_div 1>; + clock-setpoint-names = "default", "sleep"; + }; + +Note that the specifier cells for each clock node within a setpoint are +device specific. These specifiers allow configuration of the clock element, +such as setting a divider's division factor or selecting an output for a +multiplexer. + +Driver Usage +************ + +In order to use the clock management subsystem, a driver must call two C +macros. First, the driver must define clock management data. This can be +accomplished with the :c:macro:`CLOCK_MGMT_DEFINE` or +:c:macro:`CLOCK_MGMT_DT_INST_DEFINE` depending on if the driver is instance +based. Then the driver needs to initialize a pointer of type +:c:struct:`clock_mgmt` to pass to the clock management functions. This +can be done with the :c:macro:`CLOCK_MGMT_INIT` or +:c:macro:`CLOCK_MGMT_DT_INST_INIT`. The driver can then utilize the clock +management APIs. + +During device init, the device should apply the default clock management +state. This will allow the clock management subsystem to configure any +clocks the driver requires during its initialization phase. The driver +can then query clock rates. A complete example of implementing +clock management within a driver is provided below: + +.. code-block:: c + + /* A driver for the "mydev" compatible device */ + #define DT_DRV_COMPAT mydev + + ... + #include + ... + + struct mydev_config { + ... + /* Reference to clock management configuration */ + const struct clock_mgmt *clk; + ... + }; + + struct mydev_data { + /* Callback tracking structure */ + struct clock_mgmt_callback *callback; + }; + + ... + + void mydev_clock_cb_handler(struct clock_mgmt_callback *cb, + enum clock_mgmt_cb_reason reason) + { + ... + } + + static int mydev_init(const struct device *dev) + { + const struct mydev_config *config = dev->config; + int ret, clock_rate; + ... + /* Select "default" state at initialization time */ + ret = clock_mgmt_apply_state(config->clk, CLOCK_MGMT_STATE_DEFAULT); + if (ret < 0) { + return ret; + } + /* Query clock rate */ + ret = clock_mgmt_get_rate(config->clk, CLOCK_MGMT_SUBSYS_DEFAULT); + if (ret < 0) { + return ret; + } + /* Register for a callback if clock rate changes (optional) */ + clock_mgmt_init_callback(&data->callback, mydev_clock_cb_handler); + ret = clock_mgmt_add_callback(config->clk, CLOCK_MGMT_SUBSYS_DEFAULT, + &data->callback); + if (ret < 0) { + return ret; + } + ... + } + + #define MYDEV_DEFINE(i) \ + /* Define all clock management configuration for instance "i" */ \ + CLOCK_MGMT_DT_INST_DEFINE(i); \ + ... \ + static const struct mydev_config mydev_config_##i = { \ + ... \ + /* Keep a ref. to the clock management configuration */ \ + /* for instance "i" */ \ + .clk = CLOCK_MGMT_DT_INST_INIT(i), \ + ... \ + }; \ + static struct mydev_data mydev_data##i; \ + ... \ + \ + DEVICE_DT_INST_DEFINE(i, mydev_init, NULL, &mydev_data##i, \ + &mydev_config##i, ...); + + DT_INST_FOREACH_STATUS_OKAY(MYDEV_DEFINE) + +SOC Clock Management Implementation +*********************************** + +SOC level clock management is implemented via two "templated" functions. +These functions implement support for querying clock subsystem rates, and +apply clock setpoints. The build system will parse the "templated" functions, +and define per subsystem and per setpoint function implementations based on +them. Functions should be implemented using the DT macros defined in +:ref:`clock_mgmt_dt_api`, to maximize the optimization the complier can +apply to the generated functions. + +Ideally, a setpoint definition should expand to something like the following + +.. code-block:: c + + ... + if (0) { + /* Apply configuration for clock not in this setpoint */ + } + if (1) { + /* Apply configuration for clock present in this setpoint */ + } + if (0) { + /* Apply configuration for clock not in this setpoint */ + } + ... + +Each clock element within a devicetree will have a ``clock-id`` property. +These properties are critical to the SOC implementation, and allow the +handler functions to easily access each clock within the devicetree by +the ID. + +Templated functions are defined using the macros ``Z_CLOCK_MGMT_SUBSYS_TEMPL`` +and ``Z_CLOCK_MGMT_SETPOINT_TEMPL`` This approach is used as an alternative to +defining C macros that expand to function definitions to simply debugging +implementations, because the errors generated by functions defined within +a macro are often very difficult to debug. + +For a SOC clock tree like the following: + +.. code-block:: devicetree + + clock_source: clock-source { + clock-id = "CLK_SOURCE"; + compatible = "clock-source"; + #clock-cells = <1>; + + clock_div: clock-div { + clock-id = "CLK_DIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + clock_output: clock-output { + compatible = "clock-output"; + clock-output; + clock-id = "CLK_OUTPUT"; + #clock-cells = <0>; + }; + }; + }; + +The clock management templates might look like so: + +.. code-block:: c + + #define CLK_OUTPUT_ID 0x0 + + /* + * This template declares a clock subsystem rate handler. The parameters + * passed to this template are as follows: + * @param node: node ID for device with clocks property + * @param prop: clocks property of node + * @param idx: index of the clock subsystem within the clocks property + */ + Z_CLOCK_MGMT_SUBSYS_TEMPL(node, prop, idx) + { + /* Should expand to "CLK_OUTPUT_ID" */ + if (CONCAT(DT_STRING_TOKEN(DT_PHANDLE_BY_IDX(node, prop, idx), + clock_id), _ID) == CLK_OUTPUT_ID) { + return 1000; + } + /* Otherwise, querying rate not supported */ + return -ENOTSUP; + } + + /* + * This template declares a clock management setpoint. The parameters + * passed to this template are as follows: + * @param node: node ID for device with clock-control-state- property + * @param state: identifier for current state + */ + Z_CLOCK_MGMT_SETPOINT_TEMPL(node, state) + { + if (DT_CLOCK_STATE_HAS_ID(node, state, CLOCK_SOURCE)) { + set_my_clock_freq(DT_CLOCK_STATE_ID_READ_CELL_OR(node, CLOCK_SOURCE, + freq, state, 0)); + /* Fire callback for all clock subsystems */ + CLOCK_MGMT_FIRE_ALL_CALLBACKS(CLOCK_MGMT_RATE_CHANGED); + } + if (DT_CLOCK_STATE_HAS_ID(node, state, CLOCK_DIV)) { + set_my_clock_div(DT_CLOCK_STATE_ID_READ_CELL_OR(node, CLOCK_DIV, + divider, state, 0)); + /* Fire callback for clock output */ + CLOCK_MGMT_FIRE_CALLBACK(CLOCK_OUTPUT, CLOCK_MGMT_RATE_CHANGED); + } + } + +Generated clock management code will be placed in the build directory, in +the file ``clock_mgmt_soc_generated.c`` + + +.. _clock_mgmt_api: + +Clock Management API +******************** + +.. doxygengroup:: clock_mgmt_interface + +.. _clock_mgmt_dt_api: + +Devicetree Clock Management Helpers +=================================== + +.. doxygengroup:: devicetree-clock-mgmt diff --git a/doc/hardware/index.rst b/doc/hardware/index.rst index 72e9cc5fd0561..23f60316caf8e 100644 --- a/doc/hardware/index.rst +++ b/doc/hardware/index.rst @@ -9,6 +9,7 @@ Hardware Support arch/index.rst barriers/index.rst cache/index.rst + clock_mgmt/index.rst emulator/index.rst emulator/bus_emulators.rst peripherals/index.rst diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index f0236ef653a0e..d079b4f0a6a2d 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -23,6 +23,7 @@ add_subdirectory_ifdef(CONFIG_CACHE_MANAGEMENT cache) add_subdirectory_ifdef(CONFIG_CAN can) add_subdirectory_ifdef(CONFIG_CHARGER charger) add_subdirectory_ifdef(CONFIG_CLOCK_CONTROL clock_control) +add_subdirectory_ifdef(CONFIG_CLOCK_MGMT clock_mgmt) add_subdirectory_ifdef(CONFIG_CONSOLE console) add_subdirectory_ifdef(CONFIG_COREDUMP_DEVICE coredump) add_subdirectory_ifdef(CONFIG_COUNTER counter) diff --git a/drivers/Kconfig b/drivers/Kconfig index ac07add3c1f5c..f219495a3058a 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -15,6 +15,7 @@ source "drivers/cache/Kconfig" source "drivers/can/Kconfig" source "drivers/charger/Kconfig" source "drivers/clock_control/Kconfig" +source "drivers/clock_mgmt/Kconfig" source "drivers/console/Kconfig" source "drivers/coredump/Kconfig" source "drivers/counter/Kconfig" diff --git a/drivers/clock_mgmt/CMakeLists.txt b/drivers/clock_mgmt/CMakeLists.txt new file mode 100644 index 0000000000000..459e2c0debe3b --- /dev/null +++ b/drivers/clock_mgmt/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_linker_sources(RWDATA clock_mgmt_callbacks.ld) +zephyr_library_sources(clock_callbacks.c) + +# Helper macro to set clock management driver +macro(zephyr_clock_mgmt_driver_ifdef toggle driver) + if(${toggle}) + set(clock_mgmt_driver ${CMAKE_CURRENT_SOURCE_DIR}/${driver}) + endif() +endmacro() + +zephyr_clock_mgmt_driver_ifdef(CONFIG_CLOCK_MGMT_NXP_LPC55XXX clock_mgmt_lpc55xxx.c) + +# Force the build system to run again if the SOC clock management code +# is edited, so we can regenerate the clock code +set_property(DIRECTORY APPEND PROPERTY + CMAKE_CONFIGURE_DEPENDS ${clock_mgmt_driver}) +zephyr_sources(${PROJECT_BINARY_DIR}/clock_mgmt_soc_generated.c) +# Generate clock management code +execute_process(COMMAND + ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/gen_clock_mgmt.py + ${EDT_PICKLE} + ${clock_mgmt_driver} + ${PROJECT_BINARY_DIR}/clock_mgmt_soc_generated.c + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + RESULT_VARIABLE ret) +if(NOT "${ret}" STREQUAL "0") + message(FATAL_ERROR "gen_clock_mgmt.py failed with return code: ${ret}") +endif() diff --git a/drivers/clock_mgmt/Kconfig b/drivers/clock_mgmt/Kconfig new file mode 100644 index 0000000000000..b1e93ca1005db --- /dev/null +++ b/drivers/clock_mgmt/Kconfig @@ -0,0 +1,11 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +menuconfig CLOCK_MGMT + bool "Clock management drivers" + +if CLOCK_MGMT + +source "drivers/clock_mgmt/Kconfig.nxp_lpc55xxx" + +endif # CLOCK_MGMT diff --git a/drivers/clock_mgmt/Kconfig.nxp_lpc55xxx b/drivers/clock_mgmt/Kconfig.nxp_lpc55xxx new file mode 100644 index 0000000000000..0b239ebbf4874 --- /dev/null +++ b/drivers/clock_mgmt/Kconfig.nxp_lpc55xxx @@ -0,0 +1,9 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +config CLOCK_MGMT_NXP_LPC55XXX + bool "NXP LPC55xxx clock driver" + default y + depends on DT_HAS_NXP_LPC55XXX_SYSCON_ENABLED + help + Enable support for NXP LPC55XXX syscon clock management driver diff --git a/drivers/clock_mgmt/clock_callbacks.c b/drivers/clock_mgmt/clock_callbacks.c new file mode 100644 index 0000000000000..c74bff5906681 --- /dev/null +++ b/drivers/clock_mgmt/clock_callbacks.c @@ -0,0 +1,24 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +/** + * @brief Define data structure for clock management callback + * + * This macro defines the data structures used for a clock management callback. + * It will be called from the generic clock management code. However, only + * drivers will actually reference this data structure, so unused definitions + * will be discarded by the linker. + */ +#define CLOCK_CALLBACK_SLIST_DEFINE(clock_id) \ + /* Callback sys_slist. Linker will discard this if unused. */ \ + sys_slist_t Z_GENERIC_SECTION(.clock_callback_##clock_id) \ + clock_callback_##clock_id; + +/* Define all clock callback linked lists */ +DT_FOREACH_CLOCK_ID(CLOCK_CALLBACK_SLIST_DEFINE) diff --git a/drivers/clock_mgmt/clock_mgmt_callbacks.ld b/drivers/clock_mgmt/clock_mgmt_callbacks.ld new file mode 100644 index 0000000000000..165743db8b342 --- /dev/null +++ b/drivers/clock_mgmt/clock_mgmt_callbacks.ld @@ -0,0 +1,12 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "clock_mgmt_linker.h" + +clock_callbacks_start = .; +/* Link all used clock management callback structures here */ +DT_FOREACH_CLOCK_ID(CLOCK_CALLBACK_LD_SECTION) +clock_callbacks_end = .; diff --git a/drivers/clock_mgmt/clock_mgmt_linker.h b/drivers/clock_mgmt/clock_mgmt_linker.h new file mode 100644 index 0000000000000..7ef5fe23c94cd --- /dev/null +++ b/drivers/clock_mgmt/clock_mgmt_linker.h @@ -0,0 +1,26 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Note: this file is included by the linker snippet. + * Avoid including other headers from this one, as it will likely + * cause linker errors + */ + +/** + * @brief Define clock management callback linker section + * + * This macro defines the linker section for a given clock management callback. + * The section definitions allow us to only link in data structures for clocks + * that are actually referenced by a compiled driver, rather than for all + * clocks in the devicetree. + * @param clock_id clock ID, taken from clock node's clock-id property + */ + +#define CLOCK_CALLBACK_LD_SECTION(clock_id) \ + clock_callback_##clock_id##_start = .; \ + *(.clock_callback_##clock_id) \ + clock_callback_##clock_id##_end = .; diff --git a/drivers/clock_mgmt/clock_mgmt_lpc55xxx.c b/drivers/clock_mgmt/clock_mgmt_lpc55xxx.c new file mode 100644 index 0000000000000..1c639e0b6280f --- /dev/null +++ b/drivers/clock_mgmt/clock_mgmt_lpc55xxx.c @@ -0,0 +1,456 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +typedef int (*clock_mgmt_soc_subsys_t)(void); +typedef int (*clock_mgmt_soc_state_t)(void); + +#include +#include +#include "lpc55sxx_clocks.h" +#include + +/* Helper macros, used to apply clock configuration */ +#define LPC_CLOCK_SET_MUX_CB(node, pha, idx, clock_id, mux_id, output_id) \ + CLOCK_AttachClk(MUX_A(mux_id, DT_PHA_BY_IDX(node, pha, idx, selector))); \ + CLOCK_MGMT_FIRE_CALLBACK(output_id, CLOCK_MGMT_RATE_CHANGED); + +#define LPC_CLOCK_SET_MUX(node, pha, idx, clock_id, mux_id) \ + CLOCK_AttachClk(MUX_A(mux_id, DT_PHA_BY_IDX(node, pha, idx, selector))); \ + +#define LPC_CLOCK_MUX(node, state, clock_id) \ + DT_CLOCK_STATE_ID_READ_CELL_OR(node, clock_id, selector, state, 0) + +#define LPC_CLOCK_DIV(node, state, clock_id) \ + DT_CLOCK_STATE_ID_READ_CELL_OR(node, clock_id, divider, state, 1) + +#define LPC_CLOCK_SET_DIV_CB(node, pha, idx, clock_id, div_id, output_id) \ + CLOCK_SetClkDiv(div_id, DT_PHA_BY_IDX(node, pha, idx, divider), false); \ + CLOCK_MGMT_FIRE_CALLBACK(output_id, CLOCK_MGMT_RATE_CHANGED); + +#define LPC_CLOCK_SET_DIV(node, pha, idx, clock_id, div_id) \ + CLOCK_SetClkDiv(div_id, DT_PHA_BY_IDX(node, pha, idx, divider), false) + +#define FRG_LPC_CLOCK_SET_DIV_CB(node, pha, idx, clock_id, div_id, output_id) \ + CLOCK_SetClkDiv(div_id, DT_PHA_BY_IDX(node, pha, idx, divider) - 256, false); \ + CLOCK_MGMT_FIRE_CALLBACK(output_id, CLOCK_MGMT_RATE_CHANGED); + +#define LPC_CLOCK_PLL0_SSCG(node, state) \ + .pllsscg = {0x0, \ + (SYSCON_PLL0SSCG1_MDIV_EXT( \ + DT_CLOCK_STATE_ID_READ_CELL_OR(node, PLL0, \ + multiplier, state, 0)) | \ + SYSCON_PLL0SSCG1_SEL_EXT_MASK)}, \ + +#define LPC_CLOCK_PLL1_SSCG(node, state) + +#define LPC_CLOCK_PLL0_SSCG_POWER POWER_EnablePD(kPDRUNCFG_PD_PLL0_SSCG); +#define LPC_CLOCK_PLL1_SSCG_POWER + +#define LPC_CLOCK_SETUP_PLL(node, state, pll) \ + if (DT_CLOCK_STATE_HAS_ID(node, state, pll)) { \ + /* Get PLL rate */ \ + uint32_t pllrate = CLOCK_Get##pll##InClockRate(); \ + \ + notify_all_consumers = true; \ + \ + pllrate *= DT_CLOCK_STATE_ID_READ_CELL_OR(node, \ + pll, multiplier, state, 0); \ + pllrate /= LPC_CLOCK_DIV(node, state, pll); \ + if (DT_CLOCK_STATE_ID_READ_CELL_OR(node, \ + pll##_DIRECT0, selector, state, 0)) { \ + /* PLL is routed through postdiv */ \ + pllrate /= DT_CLOCK_STATE_ID_READ_CELL_OR(node, \ + pll, pdec, state, 1); \ + } \ + /* Do PLL setup */ \ + pll_setup_t pllsetup = { \ + .pllctrl = SYSCON_##pll##CTRL_CLKEN_MASK | \ + SYSCON_##pll##CTRL_SELI(DT_CLOCK_STATE_ID_READ_CELL_OR( \ + node, pll, seli, state, 0)) | \ + SYSCON_##pll##CTRL_SELP(DT_CLOCK_STATE_ID_READ_CELL_OR( \ + node, pll, selp, state, 0)) | \ + SYSCON_##pll##CTRL_SELR(DT_CLOCK_STATE_ID_READ_CELL_OR( \ + node, pll, selr, state, 0)), \ + .pllndec = SYSCON_##pll##NDEC_NDIV(LPC_CLOCK_DIV(node, state, pll)), \ + .pllpdec = SYSCON_##pll##NDEC_NDIV(DT_CLOCK_STATE_ID_READ_CELL_OR(node, \ + pll, pdec, state, 1)), \ + LPC_CLOCK_##pll##_SSCG(node, state) \ + .pllRate = pllrate, \ + .flags = PLL_SETUPFLAG_WAITLOCK, \ + }; \ + if (CLOCK_Set##pll##Freq(&pllsetup) != kStatus_PLL_Success) { \ + return -EINVAL; \ + } \ + } else if (DT_CLOCK_STATE_HAS_ID(node, state, pll##_BYPASS) && \ + LPC_CLOCK_MUX(node, state, pll##_BYPASS)) { \ + /* PLL is bypassed, but CLKEN mask still needs to be set */ \ + notify_all_consumers = true; \ + SYSCON->pll##CTRL |= SYSCON_##pll##CTRL_CLKEN_MASK; \ + } else if (LPC_CLOCK_MUX(node, state, pll##CLKSEL) > 3) { \ + /* PLL input selector is not clocked, power down PLL */ \ + POWER_EnablePD(kPDRUNCFG_PD_##pll); \ + LPC_CLOCK_##pll##_SSCG_POWER; \ + } \ + +/* + * This template declares a clock subsystem rate handler. The parameters + * passed to this template are as follows: + * @param node: node ID for device with clocks property + * @param prop: clocks property of node + * @param idx: index of the clock subsystem within the clocks property + */ +Z_CLOCK_MGMT_SUBSYS_TEMPL(node, prop, idx) +{ + switch (CONCAT(NXP_CLOCK_, DT_STRING_TOKEN( + DT_PHANDLE_BY_IDX(node, clocks, idx), clock_id))) { + case NXP_CLOCK_FXCOM0_CLOCK: + return CLOCK_GetFlexCommClkFreq(0); + case NXP_CLOCK_FXCOM1_CLOCK: + return CLOCK_GetFlexCommClkFreq(1); + case NXP_CLOCK_FXCOM2_CLOCK: + return CLOCK_GetFlexCommClkFreq(2); + case NXP_CLOCK_FXCOM3_CLOCK: + return CLOCK_GetFlexCommClkFreq(2); + case NXP_CLOCK_FXCOM4_CLOCK: + return CLOCK_GetFlexCommClkFreq(4); + case NXP_CLOCK_FXCOM5_CLOCK: + return CLOCK_GetFlexCommClkFreq(5); + case NXP_CLOCK_FXCOM6_CLOCK: + return CLOCK_GetFlexCommClkFreq(6); + case NXP_CLOCK_FXCOM7_CLOCK: + return CLOCK_GetFlexCommClkFreq(7); + default: + return -ENOTSUP; + } +} + +/* + * This template declares a clock management setpoint. The parameters + * passed to this template are as follows: + * @param node: node ID for device with clock-control-state- property + * @param state: identifier for current state + */ +Z_CLOCK_MGMT_SETPOINT_TEMPL(node, state) +{ + status_t res = kStatus_Success; + bool notify_all_consumers = false; + + if (DT_CLOCK_STATE_HAS_ID(node, state, PLUGLITCH12MHZCLK)) { + /* Enable PLU Glitch clock */ + SYSCON->CLOCK_CTRL |= SYSCON_CLOCK_CTRL_PLU_DEGLITCH_CLK_ENA_MASK; + } + if (DT_CLOCK_STATE_HAS_ID(node, state, XTAL32M)) { + /* Enable external 32Mhz clock */ + res = CLOCK_SetupExtClocking(DT_CLOCK_STATE_ID_READ_CELL_OR( + node, XTAL32M, freq, state, 0)); + if (res != kStatus_Success) { + return res; + } + } + if (DT_CLOCK_STATE_HAS_ID(node, state, CLK_IN_EN)) { + /* Enable system clock from XTAL */ + ANACTRL->XO32M_CTRL |= ANACTRL_XO32M_CTRL_ENABLE_SYSTEM_CLK_OUT_MASK; + CLOCK_MGMT_FIRE_CALLBACK(CLK_IN_EN, CLOCK_MGMT_STARTED); + } + if (DT_CLOCK_STATE_HAS_ID(node, state, CLK_USB_EN)) { + /* Enable USB clock from XTAL */ + ANACTRL->XO32M_CTRL |= ANACTRL_XO32M_CTRL_ENABLE_PLL_USB_OUT_MASK; + CLOCK_MGMT_FIRE_CALLBACK(CLK_USB_EN, CLOCK_MGMT_STARTED); + } + if (DT_CLOCK_STATE_HAS_ID(node, state, FRO_1M)) { + /* Enable 1MHZ FRO output */ + SYSCON->CLOCK_CTRL |= SYSCON_CLOCK_CTRL_FRO1MHZ_CLK_ENA_MASK; + CLOCK_MGMT_FIRE_CALLBACK(FRO_1M, CLOCK_MGMT_STARTED); + } + if (DT_CLOCK_STATE_HAS_ID(node, state, FRO_HF)) { + /* Setup FROHF (FRO96) */ + res = CLOCK_SetupFROClocking(MHZ(96)); + if (res != kStatus_Success) { + return -EINVAL; + } + CLOCK_MGMT_FIRE_CALLBACK(FRO_HF, CLOCK_MGMT_STARTED); + } + if (DT_CLOCK_STATE_HAS_ID(node, state, UTICKCLK)) { + /* Enable UTICK clock */ + SYSCON->CLOCK_CTRL |= SYSCON_CLOCK_CTRL_FRO1MHZ_UTICK_ENA_MASK; + CLOCK_MGMT_FIRE_CALLBACK(UTICKCLK, CLOCK_MGMT_STARTED); + } + if (DT_CLOCK_STATE_HAS_ID(node, state, FRO_32K)) { + if (DT_CLOCK_STATE_ID_READ_CELL_OR(node, FRO_32K, + freq, state, 0) != 32000) { + return -ENOTSUP; + } + /* Power up FRO32K */ + POWER_DisablePD(kPDRUNCFG_PD_FRO32K); + CLOCK_MGMT_FIRE_CALLBACK(FRO_32K, CLOCK_MGMT_STARTED); + } + if (DT_CLOCK_STATE_HAS_ID(node, state, XTAL32K)) { + if (DT_CLOCK_STATE_ID_READ_CELL_OR(node, XTAL32K, + freq, state, 0) != 32000) { + return -ENOTSUP; + } + /* Power up XTAL32K */ + POWER_DisablePD(kPDRUNCFG_PD_XTAL32K); + CLOCK_MGMT_FIRE_CALLBACK(XTAL32K, CLOCK_MGMT_STARTED); + } + if (DT_CLOCK_STATE_HAS_ID(node, state, FRO_32K) || + DT_CLOCK_STATE_HAS_ID(node, state, XTAL32K)) { + /* Enable RTC clock */ + CLOCK_EnableClock(kCLOCK_Rtc); + RTC->CTRL &= ~RTC_CTRL_SWRESET_MASK; + } + if (DT_CLOCK_STATE_HAS_ID(node, state, MCLK_IN)) { + /* Enable MCLK external input clock */ + CLOCK_SetupI2SMClkClocking(DT_CLOCK_STATE_ID_READ_CELL_OR(node, + MCLK_IN, freq, state, 0)); + } + if (DT_CLOCK_STATE_HAS_ID(node, state, PLU_CLKIN)) { + /* Enable PLU external input clock */ + CLOCK_SetupPLUClkInClocking(DT_CLOCK_STATE_ID_READ_CELL_OR(node, + PLU_CLKIN, freq, state, 0)); + } + if (DT_CLOCK_STATE_HAS_ID(node, state, OSTIMER32KHZCLK)) { + /* Enable 32K clock output to OSTimer */ + CLOCK_EnableOstimer32kClock(); + CLOCK_MGMT_FIRE_CALLBACK(OSTIMER32KHZCLK, CLOCK_MGMT_STARTED); + } + if (DT_CLOCK_STATE_HAS_ID(node, state, RTC_1HZ_CLK)) { + /* Enable 1Hz RTC clock */ + RTC->CTRL |= RTC_CTRL_RTC_EN_MASK; + CLOCK_MGMT_FIRE_CALLBACK(RTC_1HZ_CLK, CLOCK_MGMT_STARTED); + } + if (DT_CLOCK_STATE_HAS_ID(node, state, RTC_1KHZ_CLK)) { + /* Enable 1KHz RTC clock */ + RTC->CTRL |= RTC_CTRL_RTC1KHZ_EN_MASK; + CLOCK_MGMT_FIRE_CALLBACK(RTC_1KHZ_CLK, CLOCK_MGMT_STARTED); + } + /* Setup pre-pll clock muxes */ + /* 32K oscillator mux */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, RTCOSC32KSEL, LPC_CLOCK_SET_MUX, + state, CM_RTCOSC32KSEL); + /* PLL0 input mux */ + if (DT_CLOCK_STATE_HAS_ID(node, state, PLL0CLKSEL)) { + notify_all_consumers = true; + DT_CLOCK_STATE_APPLY_ID_VARGS(node, PLL0CLKSEL, + LPC_CLOCK_SET_MUX, + state, CM_PLL0CLKSEL); + } + if (DT_CLOCK_STATE_HAS_ID(node, state, PLL0_DIRECTO)) { + notify_all_consumers = true; + /* Set PLL0 direct output mux */ + SYSCON->PLL0CTRL |= LPC_CLOCK_MUX(node, state, PLL0_DIRECT0) ? + SYSCON_PLL0CTRL_BYPASSPOSTDIV2_MASK : 0; + } + if (DT_CLOCK_STATE_HAS_ID(node, state, PLL0_BYPASS)) { + notify_all_consumers = true; + /* PLL0 bypass mux */ + CLOCK_SetBypassPLL0(LPC_CLOCK_MUX(node, state, PLL0_BYPASS)); + } + /* PLL1 input mux */ + if (DT_CLOCK_STATE_HAS_ID(node, state, PLL1CLKSEL)) { + notify_all_consumers = true; + DT_CLOCK_STATE_APPLY_ID_VARGS(node, PLL1CLKSEL, + LPC_CLOCK_SET_MUX, + state, CM_PLL1CLKSEL); + } + if (DT_CLOCK_STATE_HAS_ID(node, state, PLL1_BYPASS)) { + notify_all_consumers = true; + /* PLL1 bypass mux */ + CLOCK_SetBypassPLL1(LPC_CLOCK_MUX(node, state, PLL1_BYPASS)); + } + /* Setup PLLs */ + LPC_CLOCK_SETUP_PLL(node, state, PLL0); + LPC_CLOCK_SETUP_PLL(node, state, PLL1); + /* This is a reimplementation of CLOCK_GetCoreSysClkFreq(), since + * the SYSCON MAINCLKSEL register values won't be set at this point. + * but we need to calculate the resulting core clock frequency. + */ + if (DT_CLOCK_STATE_HAS_ID(node, state, MAINCLKSELB)) { + notify_all_consumers = true; + if (LPC_CLOCK_MUX(node, state, MAINCLKSELB) == 0) { + /* Read MAINCLKSELA mux */ + if (LPC_CLOCK_MUX(node, state, MAINCLKSELA) == 0) { + SystemCoreClock = CLOCK_GetFro12MFreq(); + } else if (LPC_CLOCK_MUX(node, state, MAINCLKSELA) == 1) { + SystemCoreClock = CLOCK_GetExtClkFreq(); + } else if (LPC_CLOCK_MUX(node, state, MAINCLKSELA) == 2) { + SystemCoreClock = CLOCK_GetFro1MFreq(); + } else if (LPC_CLOCK_MUX(node, state, MAINCLKSELA) == 3) { + SystemCoreClock = CLOCK_GetFroHfFreq(); + } else { + return -EINVAL; + } + } else if (LPC_CLOCK_MUX(node, state, MAINCLKSELB) == 1) { + SystemCoreClock = CLOCK_GetPll0OutFreq(); + } else if (LPC_CLOCK_MUX(node, state, MAINCLKSELB) == 2) { + SystemCoreClock = CLOCK_GetPll1OutFreq(); + } else if (LPC_CLOCK_MUX(node, state, MAINCLKSELB) == 3) { + SystemCoreClock = CLOCK_GetOsc32KFreq(); + } else { + return -EINVAL; + } + SystemCoreClock /= LPC_CLOCK_DIV(node, state, AHBCLKDIV); + + /* Before we reconfigured to a (potentially faster) core + * clock we must set the flash wait state count and voltage + * level for the new core frequency. + */ + POWER_SetVoltageForFreq(SystemCoreClock); + /* CONFIG_TRUSTED_EXECUTION_NONSECURE is checked + * since the non-secure core will not have access to the + * flash, and cannot configure this parameter + */ + if (!IS_ENABLED(CONFIG_TRUSTED_EXECUTION_NONSECURE)) { + CLOCK_SetFLASHAccessCyclesForFreq(SystemCoreClock); + } + } + /* Setup clock divs */ + /* AHB clock div */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, AHBCLKDIV, LPC_CLOCK_SET_DIV_CB, + state, kCLOCK_DivAhbClk, SYSTEM_CLOCK); + /* Trace clock div */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, TRACECLKDIV, LPC_CLOCK_SET_DIV_CB, + state, kCLOCK_DivArmTrClkDiv, TRACE_CLOCK); + /* Systick core 0 clock div */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, SYSTICKCLKDIV0, LPC_CLOCK_SET_DIV_CB, + state, kCLOCK_DivSystickClk0, SYSTICK0_CLOCK); + /* Systick core 1 clock div */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, SYSTICKCLKDIV1, LPC_CLOCK_SET_DIV_CB, + state, kCLOCK_DivSystickClk1, SYSTICK1_CLOCK); + /* WDT clock div */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, WDTCLKDIV, LPC_CLOCK_SET_DIV_CB, + state, kCLOCK_DivWdtClk, WDT_CLOCK); + /* ADC clock div */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, ADCCLKDIV, LPC_CLOCK_SET_DIV_CB, + state, kCLOCK_DivAdcAsyncClk, ASYNCADC_CLOCK); + /* USB0 (FS) clock div */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, USB0CLKDIV, LPC_CLOCK_SET_DIV_CB, + state, kCLOCK_DivUsb0Clk, USB0_CLOCK); + /* MCLK clock div */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, MCLKDIV, LPC_CLOCK_SET_DIV_CB, + state, kCLOCK_DivMClk, MCLK_CLOCK); + /* SCT clock div */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, SCTCLKDIV, LPC_CLOCK_SET_DIV_CB, + state, kCLOCK_DivSctClk, SCT_CLOCK); + /* CLKOUT clock div */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, CLKOUTDIV, LPC_CLOCK_SET_DIV_CB, + state, kCLOCK_DivClkOut, CLKOUT_CLOCK); + /* SDIO clock div */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, SDIOCLKDIV, LPC_CLOCK_SET_DIV_CB, + state, kCLOCK_DivSdioClk, SDIO_CLOCK); + if (DT_CLOCK_STATE_HAS_ID(node, state, PLL0DIV)) { + /* PLL0DIV divider */ + notify_all_consumers = true; + CLOCK_SetClkDiv(kCLOCK_DivPll0Clk, + LPC_CLOCK_DIV(node, state, PLL0DIV), false); + } + if (DT_CLOCK_STATE_HAS_ID(node, state, FROHFDIV)) { + /* FROHF divider */ + notify_all_consumers = true; + CLOCK_SetClkDiv(kCLOCK_DivFrohfClk, + LPC_CLOCK_DIV(node, state, FROHFDIV), false); + } + /* Flexcomm FRG divs. Note the input to the FRG is multiplied by 256 */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, FRGCTRL0_DIV, + FRG_LPC_CLOCK_SET_DIV_CB, state, + kCLOCK_DivFlexFrg0, FXCOM0_CLOCK); + DT_CLOCK_STATE_APPLY_ID_VARGS(node, FRGCTRL1_DIV, + FRG_LPC_CLOCK_SET_DIV_CB, state, + kCLOCK_DivFlexFrg1, FXCOM1_CLOCK); + DT_CLOCK_STATE_APPLY_ID_VARGS(node, FRGCTRL2_DIV, + FRG_LPC_CLOCK_SET_DIV_CB, state, + kCLOCK_DivFlexFrg2, FXCOM2_CLOCK); + DT_CLOCK_STATE_APPLY_ID_VARGS(node, FRGCTRL3_DIV, + FRG_LPC_CLOCK_SET_DIV_CB, state, + kCLOCK_DivFlexFrg3, FXCOM3_CLOCK); + DT_CLOCK_STATE_APPLY_ID_VARGS(node, FRGCTRL4_DIV, + FRG_LPC_CLOCK_SET_DIV_CB, state, + kCLOCK_DivFlexFrg4, FXCOM4_CLOCK); + DT_CLOCK_STATE_APPLY_ID_VARGS(node, FRGCTRL5_DIV, + FRG_LPC_CLOCK_SET_DIV_CB, state, + kCLOCK_DivFlexFrg5, FXCOM5_CLOCK); + DT_CLOCK_STATE_APPLY_ID_VARGS(node, FRGCTRL6_DIV, + FRG_LPC_CLOCK_SET_DIV_CB, state, + kCLOCK_DivFlexFrg6, FXCOM6_CLOCK); + DT_CLOCK_STATE_APPLY_ID_VARGS(node, FRGCTRL7_DIV, + FRG_LPC_CLOCK_SET_DIV_CB, state, + kCLOCK_DivFlexFrg7, FXCOM7_CLOCK); + + /* Setup post-pll clock muxes */ + /* Main clock select A mux */ + if (DT_CLOCK_STATE_HAS_ID(node, state, MAINCLKSELA)) { + notify_all_consumers = true; + DT_CLOCK_STATE_APPLY_ID_VARGS(node, MAINCLKSELA, LPC_CLOCK_SET_MUX, + state, CM_MAINCLKSELA); + } + /* Main clock select B mux */ + if (DT_CLOCK_STATE_HAS_ID(node, state, MAINCLKSELB)) { + DT_CLOCK_STATE_APPLY_ID_VARGS(node, MAINCLKSELB, LPC_CLOCK_SET_MUX, + state, CM_MAINCLKSELB); + } + /* Trace clock select mux */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, TRACECLKSEL, LPC_CLOCK_SET_MUX_CB, + state, CM_TRACECLKSEL, TRACE_CLOCK); + /* Core 0 systick clock */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, SYSTICKCLKSEL0, LPC_CLOCK_SET_MUX_CB, + state, CM_SYSTICKCLKSEL0, SYSTICK0_CLOCK); + /* Core 1 systick clock */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, SYSTICKCLKSEL1, LPC_CLOCK_SET_MUX_CB, + state, CM_SYSTICKCLKSEL1, SYSTICK1_CLOCK); + /* ADC clock mux */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, ADCCLKSEL, LPC_CLOCK_SET_MUX_CB, + state, CM_ADCASYNCCLKSEL, ASYNCADC_CLOCK); + /* USB0 (FS) clock mux */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, USB0CLKSEL, LPC_CLOCK_SET_MUX_CB, + state, CM_USB0CLKSEL, USB0_CLOCK); + /* MCLK clock mux */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, MCLKCLKSEL, LPC_CLOCK_SET_MUX_CB, + state, CM_MCLKCLKSEL, MCLK_CLOCK); + /* SCT clock mux */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, SCTCLKSEL, LPC_CLOCK_SET_MUX_CB, + state, CM_SCTCLKSEL, SCT_CLOCK); + /* CLKOUT clock mux */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, CLKOUTSEL, LPC_CLOCK_SET_MUX_CB, + state, CM_CLKOUTCLKSEL, CLKOUT_CLOCK); + /* SDIO clock mux */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, SDIOCLKSEL, LPC_CLOCK_SET_MUX_CB, + state, CM_SDIOCLKSEL, SDIO_CLOCK); + /* CTIMER clock muxes */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, CTIMERCLKSEL0, LPC_CLOCK_SET_MUX_CB, + state, CM_CTIMERCLKSEL0, CTIMER0_CLOCK); + DT_CLOCK_STATE_APPLY_ID_VARGS(node, CTIMERCLKSEL1, LPC_CLOCK_SET_MUX_CB, + state, CM_CTIMERCLKSEL1, CTIMER1_CLOCK); + DT_CLOCK_STATE_APPLY_ID_VARGS(node, CTIMERCLKSEL2, LPC_CLOCK_SET_MUX_CB, + state, CM_CTIMERCLKSEL2, CTIMER2_CLOCK); + DT_CLOCK_STATE_APPLY_ID_VARGS(node, CTIMERCLKSEL3, LPC_CLOCK_SET_MUX_CB, + state, CM_CTIMERCLKSEL3, CTIMER3_CLOCK); + DT_CLOCK_STATE_APPLY_ID_VARGS(node, CTIMERCLKSEL4, LPC_CLOCK_SET_MUX_CB, + state, CM_CTIMERCLKSEL4, CTIMER4_CLOCK); + /* Flexcomm clock muxes */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, FCCLKSEL0, LPC_CLOCK_SET_MUX_CB, + state, CM_FXCOMCLKSEL0, FXCOM0_CLOCK); + DT_CLOCK_STATE_APPLY_ID_VARGS(node, FCCLKSEL1, LPC_CLOCK_SET_MUX_CB, + state, CM_FXCOMCLKSEL1, FXCOM1_CLOCK); + DT_CLOCK_STATE_APPLY_ID_VARGS(node, FCCLKSEL2, LPC_CLOCK_SET_MUX_CB, + state, CM_FXCOMCLKSEL2, FXCOM2_CLOCK); + DT_CLOCK_STATE_APPLY_ID_VARGS(node, FCCLKSEL3, LPC_CLOCK_SET_MUX_CB, + state, CM_FXCOMCLKSEL3, FXCOM3_CLOCK); + DT_CLOCK_STATE_APPLY_ID_VARGS(node, FCCLKSEL4, LPC_CLOCK_SET_MUX_CB, + state, CM_FXCOMCLKSEL4, FXCOM4_CLOCK); + DT_CLOCK_STATE_APPLY_ID_VARGS(node, FCCLKSEL5, LPC_CLOCK_SET_MUX_CB, + state, CM_FXCOMCLKSEL5, FXCOM5_CLOCK); + DT_CLOCK_STATE_APPLY_ID_VARGS(node, FCCLKSEL6, LPC_CLOCK_SET_MUX_CB, + state, CM_FXCOMCLKSEL6, FXCOM6_CLOCK); + DT_CLOCK_STATE_APPLY_ID_VARGS(node, FCCLKSEL7, LPC_CLOCK_SET_MUX_CB, + state, CM_FXCOMCLKSEL7, FXCOM7_CLOCK); + /* HSLSPI clock mux */ + DT_CLOCK_STATE_APPLY_ID_VARGS(node, HSLSPICLKSEL, LPC_CLOCK_SET_MUX_CB, + state, CM_HSLSPICLKSEL, HSLSPI_CLOCK); + if (notify_all_consumers) { + CLOCK_MGMT_FIRE_ALL_CALLBACKS(CLOCK_MGMT_RATE_CHANGED); + } + return res; +} diff --git a/drivers/serial/uart_mcux_flexcomm.c b/drivers/serial/uart_mcux_flexcomm.c index a15ba82873a26..457e0023eef5d 100644 --- a/drivers/serial/uart_mcux_flexcomm.c +++ b/drivers/serial/uart_mcux_flexcomm.c @@ -24,6 +24,10 @@ #include #endif +#ifdef CONFIG_CLOCK_MGMT +#include +#endif + #ifdef CONFIG_UART_ASYNC_API struct mcux_flexcomm_uart_dma_config { const struct device *dev; @@ -35,8 +39,12 @@ struct mcux_flexcomm_uart_dma_config { struct mcux_flexcomm_config { USART_Type *base; +#ifdef CONFIG_CLOCK_MGMT + const struct clock_mgmt *clk_mgmt; +#else const struct device *clock_dev; clock_control_subsys_t clock_subsys; +#endif uint32_t baud_rate; uint8_t parity; #ifdef CONFIG_UART_MCUX_FLEXCOMM_ISR_SUPPORT @@ -86,6 +94,10 @@ struct mcux_flexcomm_data { #ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE struct uart_config uart_config; #endif +#ifdef CONFIG_CLOCK_MGMT + const struct device *dev; + struct clock_mgmt_callback clock_callback; +#endif }; static int mcux_flexcomm_poll_in(const struct device *dev, unsigned char *c) @@ -355,9 +367,14 @@ static int mcux_flexcomm_uart_configure(const struct device *dev, const struct u /* Wait for USART to finish transmission and turn off */ USART_Deinit(config->base); +#ifdef CONFIG_CLOCK_MGMT + clock_freq = clock_mgmt_get_rate(config->clk_mgmt, + CLOCK_MGMT_SUBSYS_DEFAULT); +#else /* Get UART clock frequency */ clock_control_get_rate(config->clock_dev, config->clock_subsys, &clock_freq); +#endif /* Handle 9 bit mode */ USART_Enable9bitMode(config->base, nine_bit_mode); @@ -1000,16 +1017,56 @@ static void mcux_flexcomm_isr(const struct device *dev) } #endif /* CONFIG_UART_MCUX_FLEXCOMM_ISR_SUPPORT */ +static void mcux_flexcomm_uart_setup(const struct device *dev, uint32_t clock_rate) +{ + const struct mcux_flexcomm_config *config = dev->config; + usart_config_t usart_config; + usart_parity_mode_t parity_mode; + + if (config->parity == UART_CFG_PARITY_ODD) { + parity_mode = kUSART_ParityOdd; + } else if (config->parity == UART_CFG_PARITY_EVEN) { + parity_mode = kUSART_ParityEven; + } else { + parity_mode = kUSART_ParityDisabled; + } + + USART_GetDefaultConfig(&usart_config); + usart_config.enableTx = true; + usart_config.enableRx = true; + usart_config.parityMode = parity_mode; + usart_config.baudRate_Bps = config->baud_rate; + + USART_Init(config->base, &usart_config, clock_rate); +} + +void uart_mcux_flexcomm_clock_cb(struct clock_mgmt_callback *cb, + enum clock_mgmt_cb_reason reason) +{ + struct mcux_flexcomm_data *data = CONTAINER_OF(cb, struct mcux_flexcomm_data, + clock_callback); + const struct device *uart_dev = data->dev; + const struct mcux_flexcomm_config *config = uart_dev->config; + int clock_rate; + + clock_rate = clock_mgmt_get_rate(config->clk_mgmt, + CLOCK_MGMT_SUBSYS_DEFAULT); + /* Deinit USART */ + USART_Deinit(config->base); + /* Reconfigure USART */ + mcux_flexcomm_uart_setup(uart_dev, clock_rate); +} + static int mcux_flexcomm_init(const struct device *dev) { const struct mcux_flexcomm_config *config = dev->config; -#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE +#if defined(CONFIG_UART_USE_RUNTIME_CONFIGURE) || defined(CONFIG_CLOCK_MGMT) struct mcux_flexcomm_data *data = dev->data; +#endif +#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE struct uart_config *cfg = &data->uart_config; #endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ - usart_config_t usart_config; - usart_parity_mode_t parity_mode; uint32_t clock_freq; int err; @@ -1018,6 +1075,16 @@ static int mcux_flexcomm_init(const struct device *dev) return err; } +#ifdef CONFIG_CLOCK_MGMT + data->dev = dev; + clock_mgmt_apply_state(config->clk_mgmt, CLOCK_MGMT_STATE_DEFAULT); + clock_freq = clock_mgmt_get_rate(config->clk_mgmt, + CLOCK_MGMT_SUBSYS_DEFAULT); + mcux_flexcomm_uart_setup(dev, clock_freq); + clock_mgmt_init_callback(&data->clock_callback, uart_mcux_flexcomm_clock_cb); + clock_mgmt_add_callback(config->clk_mgmt, CLOCK_MGMT_SUBSYS_DEFAULT, + &data->clock_callback); +#else if (!device_is_ready(config->clock_dev)) { return -ENODEV; } @@ -1027,20 +1094,8 @@ static int mcux_flexcomm_init(const struct device *dev) &clock_freq)) { return -EINVAL; } - - if (config->parity == UART_CFG_PARITY_ODD) { - parity_mode = kUSART_ParityOdd; - } else if (config->parity == UART_CFG_PARITY_EVEN) { - parity_mode = kUSART_ParityEven; - } else { - parity_mode = kUSART_ParityDisabled; - } - - USART_GetDefaultConfig(&usart_config); - usart_config.enableTx = true; - usart_config.enableRx = true; - usart_config.parityMode = parity_mode; - usart_config.baudRate_Bps = config->baud_rate; + mcux_flexcomm_uart_setup(dev, clock_freq); +#endif #ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE cfg->baudrate = config->baud_rate; @@ -1051,8 +1106,6 @@ static int mcux_flexcomm_init(const struct device *dev) cfg->flow_ctrl = UART_CFG_FLOW_CTRL_NONE; #endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ - USART_Init(config->base, &usart_config, clock_freq); - #ifdef CONFIG_UART_MCUX_FLEXCOMM_ISR_SUPPORT config->irq_config_func(dev); #endif @@ -1183,15 +1236,25 @@ DT_INST_FOREACH_STATUS_OKAY(UART_MCUX_FLEXCOMM_RX_TIMEOUT_FUNC); #define UART_MCUX_FLEXCOMM_ASYNC_CFG(n) #endif /* CONFIG_UART_ASYNC_API */ +#ifdef CONFIG_CLOCK_MGMT +#define UART_MCUX_FLEXCOMM_CLOCK_DEFINE(n) CLOCK_MGMT_DT_INST_DEFINE(n) +#define UART_MCUX_FLEXCOMM_CLOCK_INIT(n) \ + .clk_mgmt = CLOCK_MGMT_DT_INST_INIT(n), +#else +#define UART_MCUX_FLEXCOMM_CLOCK_DEFINE(n) +#define UART_MCUX_FLEXCOMM_CLOCK_INIT(n) \ + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ + .clock_subsys = \ + (clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name), +#endif + #define UART_MCUX_FLEXCOMM_INIT_CFG(n) \ static const struct mcux_flexcomm_config mcux_flexcomm_##n##_config = { \ .base = (USART_Type *)DT_INST_REG_ADDR(n), \ - .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ - .clock_subsys = \ - (clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name), \ .baud_rate = DT_INST_PROP(n, current_speed), \ .parity = DT_INST_ENUM_IDX_OR(n, parity, UART_CFG_PARITY_NONE), \ .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + UART_MCUX_FLEXCOMM_CLOCK_INIT(n) \ UART_MCUX_FLEXCOMM_IRQ_CFG_FUNC_INIT(n) \ UART_MCUX_FLEXCOMM_ASYNC_CFG(n) \ }; @@ -1199,6 +1262,7 @@ static const struct mcux_flexcomm_config mcux_flexcomm_##n##_config = { \ #define UART_MCUX_FLEXCOMM_INIT(n) \ \ PINCTRL_DT_INST_DEFINE(n); \ + UART_MCUX_FLEXCOMM_CLOCK_DEFINE(n); \ \ static struct mcux_flexcomm_data mcux_flexcomm_##n##_data; \ \ diff --git a/dts/arm/nxp/nxp_lpc55S6x.dtsi b/dts/arm/nxp/nxp_lpc55S6x.dtsi index de2a8cc8180db..fb3fadf635a48 100644 --- a/dts/arm/nxp/nxp_lpc55S6x.dtsi +++ b/dts/arm/nxp/nxp_lpc55S6x.dtsi @@ -26,6 +26,7 @@ }; #include "nxp_lpc55S6x_common.dtsi" +#include "nxp_lpc55sxx_clocks.dtsi" /* * Explicitly enable IAP after we include the common LPC55S6X dtsi file, diff --git a/dts/arm/nxp/nxp_lpc55S6x_common.dtsi b/dts/arm/nxp/nxp_lpc55S6x_common.dtsi index 00cbb8fa0a592..3549246695737 100644 --- a/dts/arm/nxp/nxp_lpc55S6x_common.dtsi +++ b/dts/arm/nxp/nxp_lpc55S6x_common.dtsi @@ -104,6 +104,12 @@ #clock-cells = <1>; }; + syscon_mgmt: syscon-mgmt@0 { + compatible = "nxp,lpc55xxx-syscon"; + reg = <0x0 0x1000>; + #clock-cells = <0>; + }; + iap: flash-controller@34000 { compatible = "nxp,iap-fmc55"; reg = <0x34000 0x1000>; @@ -210,7 +216,7 @@ compatible = "nxp,lpc-flexcomm"; reg = <0x86000 0x1000>; interrupts = <14 0>; - clocks = <&syscon MCUX_FLEXCOMM0_CLK>; + clocks = <&fxcom0_clock>; status = "disabled"; }; @@ -218,7 +224,7 @@ compatible = "nxp,lpc-flexcomm"; reg = <0x87000 0x1000>; interrupts = <15 0>; - clocks = <&syscon MCUX_FLEXCOMM1_CLK>; + clocks = <&fxcom1_clock>; status = "disabled"; }; @@ -226,7 +232,7 @@ compatible = "nxp,lpc-flexcomm"; reg = <0x88000 0x1000>; interrupts = <16 0>; - clocks = <&syscon MCUX_FLEXCOMM2_CLK>; + clocks = <&fxcom2_clock>; status = "disabled"; }; @@ -234,7 +240,7 @@ compatible = "nxp,lpc-flexcomm"; reg = <0x89000 0x1000>; interrupts = <17 0>; - clocks = <&syscon MCUX_FLEXCOMM3_CLK>; + clocks = <&fxcom3_clock>; status = "disabled"; }; @@ -242,7 +248,7 @@ compatible = "nxp,lpc-flexcomm"; reg = <0x8a000 0x1000>; interrupts = <18 0>; - clocks = <&syscon MCUX_FLEXCOMM4_CLK>; + clocks = <&fxcom4_clock>; status = "disabled"; }; @@ -250,7 +256,7 @@ compatible = "nxp,lpc-flexcomm"; reg = <0x96000 0x1000>; interrupts = <19 0>; - clocks = <&syscon MCUX_FLEXCOMM5_CLK>; + clocks = <&fxcom5_clock>; status = "disabled"; }; @@ -258,7 +264,7 @@ compatible = "nxp,lpc-flexcomm"; reg = <0x97000 0x1000>; interrupts = <20 0>; - clocks = <&syscon MCUX_FLEXCOMM6_CLK>; + clocks = <&fxcom6_clock>; status = "disabled"; }; @@ -266,7 +272,7 @@ compatible = "nxp,lpc-flexcomm"; reg = <0x98000 0x1000>; interrupts = <21 0>; - clocks = <&syscon MCUX_FLEXCOMM7_CLK>; + clocks = <&fxcom7_clock>; status = "disabled"; }; @@ -288,7 +294,7 @@ <&gpio1 26 GPIO_ACTIVE_LOW>; */ reg = <0x9f000 0x1000>; interrupts = <59 0>; - clocks = <&syscon MCUX_HS_SPI_CLK>; + clocks = <&hslspi_clock>; status = "disabled"; #address-cells = <1>; #size-cells = <0>; diff --git a/dts/arm/nxp/nxp_lpc55sxx_clocks.dtsi b/dts/arm/nxp/nxp_lpc55sxx_clocks.dtsi new file mode 100644 index 0000000000000..5f54f5f5fac80 --- /dev/null +++ b/dts/arm/nxp/nxp_lpc55sxx_clocks.dtsi @@ -0,0 +1,809 @@ +/* + * Copyright 2024 NXP + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Generated from NXP MCUX clock data */ +&syscon_mgmt { + + /* Root clock sources */ + no_clock: no-clock { + clock-id = "NO_CLOCK"; + compatible = "clock-source"; + #clock-cells = <1>; + }; + + fro_12m: fro-12m { + clock-id = "FRO_12M"; + compatible = "clock-source"; + #clock-cells = <1>; + + pluglitch12mhzclk: pluglitch12mhzclk { + clock-id = "PLUGLITCH12MHZCLK"; + compatible = "clock-gate"; + #clock-cells = <1>; + + plu_glitch_12mhz_clock: plu-glitch-12mhz-clock { + compatible = "clock-output"; + clock-output; + clock-id = "PLU_GLITCH_12MHZ_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + + xtal32m: xtal32m { + clock-id = "XTAL32M"; + compatible = "clock-source"; + #clock-cells = <1>; + + clk_in_en: clk-in-en { + clock-id = "CLK_IN_EN"; + compatible = "clock-gate"; + #clock-cells = <1>; + }; + + clk_usb_en: clk-usb-en { + clock-id = "CLK_USB_EN"; + compatible = "clock-gate"; + #clock-cells = <1>; + + usb1_phy_clock: usb1-phy-clock { + compatible = "clock-output"; + clock-output; + clock-id = "USB1_PHY_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + + fro_1m: fro-1m { + clock-id = "FRO_1M"; + compatible = "clock-source"; + #clock-cells = <1>; + + wdtclkdiv: wdtclkdiv { + clock-id = "WDTCLKDIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + wdt_clock: wdt-clock { + compatible = "clock-output"; + clock-output; + clock-id = "WDT_CLOCK"; + #clock-cells = <0>; + }; + }; + + utickclk: utickclk { + clock-id = "UTICKCLK"; + compatible = "clock-gate"; + #clock-cells = <1>; + + utick_clock: utick-clock { + compatible = "clock-output"; + clock-output; + clock-id = "UTICK_CLOCK"; + #clock-cells = <0>; + }; + }; + + pluglitch1mhzclk: pluglitch1mhzclk { + clock-id = "PLUGLITCH1MHZCLK"; + compatible = "clock-gate"; + #clock-cells = <1>; + + plu_glitch_1mhz_clock: plu-glitch-1mhz-clock { + compatible = "clock-output"; + clock-output; + clock-id = "PLU_GLITCH_1MHZ_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + + fro_hf: fro-hf { + clock-id = "FRO_HF"; + compatible = "clock-source"; + #clock-cells = <1>; + + frohfdiv: frohfdiv { + clock-id = "FROHFDIV"; + compatible = "clock-div"; + #clock-cells = <1>; + }; + }; + + fro_32k: fro-32k { + clock-id = "FRO_32K"; + compatible = "clock-source"; + #clock-cells = <1>; + }; + + xtal32k: xtal32k { + clock-id = "XTAL32K"; + compatible = "clock-source"; + #clock-cells = <1>; + }; + + mclk_in: mclk-in { + clock-id = "MCLK_IN"; + compatible = "clock-source"; + #clock-cells = <1>; + }; + + plu_clkin: plu-clkin { + clock-id = "PLU_CLKIN"; + compatible = "clock-source"; + #clock-cells = <1>; + + pluclkin_clock: pluclkin-clock { + compatible = "clock-output"; + clock-output; + clock-id = "PLUCLKIN_CLOCK"; + #clock-cells = <0>; + }; + }; + + /* Clock muxes */ + mainclksela: mainclksela { + clock-id = "MAINCLKSELA"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&fro_12m &clk_in_en &fro_1m &fro_hf>; + }; + + rtcosc32ksel: rtcosc32ksel { + clock-id = "RTCOSC32KSEL"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&fro_32k &xtal32k>; + + ostimer32khzclk: ostimer32khzclk { + clock-id = "OSTIMER32KHZCLK"; + compatible = "clock-gate"; + #clock-cells = <1>; + + ostimer32khz_clock: ostimer32khz-clock { + compatible = "clock-output"; + clock-output; + clock-id = "OSTIMER32KHZ_CLOCK"; + #clock-cells = <0>; + }; + }; + + osc32khz_clock: osc32khz-clock { + compatible = "clock-output"; + clock-output; + clock-id = "OSC32KHZ_CLOCK"; + #clock-cells = <0>; + }; + + rtcclk1hzdiv: rtcclk1hzdiv { + clock-id = "RTCCLK1HZDIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + rtc_1hz_clk: rtc-1hz-clk { + clock-id = "RTC_1HZ_CLK"; + compatible = "clock-gate"; + #clock-cells = <1>; + + rtc1hz_clock: rtc1hz-clock { + compatible = "clock-output"; + clock-output; + clock-id = "RTC1HZ_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + + rtcclk1khzdiv: rtcclk1khzdiv { + clock-id = "RTCCLK1KHZDIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + rtc_1khz_clk: rtc-1khz-clk { + clock-id = "RTC_1KHZ_CLK"; + compatible = "clock-gate"; + #clock-cells = <1>; + + rtc1khz_clock: rtc1khz-clock { + compatible = "clock-output"; + clock-output; + clock-id = "RTC1KHZ_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + }; + + pll0clksel: pll0clksel { + clock-id = "PLL0CLKSEL"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&fro_12m &clk_in_en &fro_1m &rtcosc32ksel + &no_clock &no_clock &no_clock &no_clock>; + + pll0: pll0 { + clock-id = "PLL0"; + compatible = "nxp,lpc55xxx-pll"; + #clock-cells = <6>; + + pll0_pdec: pll0-pdec { + clock-id = "PLL0_PDEC"; + compatible = "clock-div"; + #clock-cells = <1>; + }; + }; + }; + + pll0_directo: pll0-directo { + clock-id = "PLL0_DIRECTO"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&pll0_pdec &pll0>; + }; + + pll0_bypass: pll0-bypass { + clock-id = "PLL0_BYPASS"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&pll0_directo &pll0clksel>; + + pll0div: pll0div { + clock-id = "PLL0DIV"; + compatible = "clock-div"; + #clock-cells = <1>; + }; + }; + + pll1clksel: pll1clksel { + clock-id = "PLL1CLKSEL"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&fro_12m &clk_in_en &fro_1m &rtcosc32ksel + &no_clock &no_clock &no_clock &no_clock>; + + pll1: pll1 { + clock-id = "PLL1"; + compatible = "nxp,lpc55xxx-pll"; + #clock-cells = <6>; + + pll1_pdec: pll1-pdec { + clock-id = "PLL1_PDEC"; + compatible = "clock-div"; + #clock-cells = <1>; + }; + }; + }; + + pll1_directo: pll1-directo { + clock-id = "PLL1_DIRECTO"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&pll1_pdec &pll1>; + }; + + pll1_bypass: pll1-bypass { + clock-id = "PLL1_BYPASS"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&pll1_directo &pll1clksel>; + }; + + mainclkselb: mainclkselb { + clock-id = "MAINCLKSELB"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclksela &pll0_bypass &pll1_bypass &rtcosc32ksel>; + + traceclkdiv: traceclkdiv { + clock-id = "TRACECLKDIV"; + compatible = "clock-div"; + #clock-cells = <1>; + }; + + systickclkdiv0: systickclkdiv0 { + clock-id = "SYSTICKCLKDIV0"; + compatible = "clock-div"; + #clock-cells = <1>; + }; + + systickclkdiv1: systickclkdiv1 { + clock-id = "SYSTICKCLKDIV1"; + compatible = "clock-div"; + #clock-cells = <1>; + }; + + ahbclkdiv: ahbclkdiv { + clock-id = "AHBCLKDIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + system_clock: system-clock { + compatible = "clock-output"; + clock-output; + clock-id = "SYSTEM_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + + traceclksel: traceclksel { + clock-id = "TRACECLKSEL"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&traceclkdiv &fro_1m &rtcosc32ksel &no_clock + &no_clock &no_clock &no_clock &no_clock>; + + trace_clock: trace-clock { + compatible = "clock-output"; + clock-output; + clock-id = "TRACE_CLOCK"; + #clock-cells = <0>; + }; + }; + + systickclksel0: systickclksel0 { + clock-id = "SYSTICKCLKSEL0"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&systickclkdiv0 &fro_1m &rtcosc32ksel &no_clock + &no_clock &no_clock &no_clock &no_clock>; + + systick0_clock: systick0-clock { + compatible = "clock-output"; + clock-output; + clock-id = "SYSTICK0_CLOCK"; + #clock-cells = <0>; + }; + }; + + systickclksel1: systickclksel1 { + clock-id = "SYSTICKCLKSEL1"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&systickclkdiv1 &fro_1m &rtcosc32ksel &no_clock + &no_clock &no_clock &no_clock &no_clock>; + + systick1_clock: systick1-clock { + compatible = "clock-output"; + clock-output; + clock-id = "SYSTICK1_CLOCK"; + #clock-cells = <0>; + }; + }; + + adcclksel: adcclksel { + clock-id = "ADCCLKSEL"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclkselb &pll0_bypass &fro_hf &no_clock + &no_clock &no_clock &no_clock &no_clock>; + + adcclkdiv: adcclkdiv { + clock-id = "ADCCLKDIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + asyncadc_clock: asyncadc-clock { + compatible = "clock-output"; + clock-output; + clock-id = "ASYNCADC_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + + usb0clksel: usb0clksel { + clock-id = "USB0CLKSEL"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclkselb &pll0_bypass &no_clock &fro_hf + &no_clock &pll1_bypass &no_clock &no_clock>; + + usb0clkdiv: usb0clkdiv { + clock-id = "USB0CLKDIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + usb0_clock: usb0-clock { + compatible = "clock-output"; + clock-output; + clock-id = "USB0_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + + mclkclksel: mclkclksel { + clock-id = "MCLKCLKSEL"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&fro_hf &pll0_bypass &no_clock &no_clock &no_clock + &no_clock &no_clock &no_clock>; + + mclkdiv: mclkdiv { + clock-id = "MCLKDIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + mclk_clock: mclk-clock { + compatible = "clock-output"; + clock-output; + clock-id = "MCLK_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + + sctclksel: sctclksel { + clock-id = "SCTCLKSEL"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclkselb &pll0_bypass &clk_in_en &fro_hf + &no_clock &mclk_in &no_clock &no_clock>; + + sctclkdiv: sctclkdiv { + clock-id = "SCTCLKDIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + sct_clock: sct-clock { + compatible = "clock-output"; + clock-output; + clock-id = "SCT_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + + clkoutsel: clkoutsel { + clock-id = "CLKOUTSEL"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclkselb &pll0_bypass &clk_in_en &fro_hf + &fro_1m &pll1_bypass &rtcosc32ksel &no_clock>; + + clkoutdiv: clkoutdiv { + clock-id = "CLKOUTDIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + clkout_clock: clkout-clock { + compatible = "clock-output"; + clock-output; + clock-id = "CLKOUT_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + + sdioclksel: sdioclksel { + clock-id = "SDIOCLKSEL"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclkselb &pll0_bypass &no_clock &fro_hf + &no_clock &pll1_bypass &no_clock &no_clock>; + + sdioclkdiv: sdioclkdiv { + clock-id = "SDIOCLKDIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + sdio_clock: sdio-clock { + compatible = "clock-output"; + clock-output; + clock-id = "SDIO_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + + ctimerclksel0: ctimerclksel0 { + clock-id = "CTIMERCLKSEL0"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclkselb &pll0_bypass &no_clock &fro_hf + &fro_1m &mclk_in &rtcosc32ksel &no_clock>; + + ctimer0_clock: ctimer0-clock { + compatible = "clock-output"; + clock-output; + clock-id = "CTIMER0_CLOCK"; + #clock-cells = <0>; + }; + }; + + ctimerclksel1: ctimerclksel1 { + clock-id = "CTIMERCLKSEL1"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclkselb &pll0_bypass &no_clock &fro_hf + &fro_1m &mclk_in &rtcosc32ksel &no_clock>; + + ctimer1_clock: ctimer1-clock { + compatible = "clock-output"; + clock-output; + clock-id = "CTIMER1_CLOCK"; + #clock-cells = <0>; + }; + }; + + ctimerclksel2: ctimerclksel2 { + clock-id = "CTIMERCLKSEL2"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclkselb &pll0_bypass &no_clock &fro_hf + &fro_1m &mclk_in &rtcosc32ksel &no_clock>; + + ctimer2_clock: ctimer2-clock { + compatible = "clock-output"; + clock-output; + clock-id = "CTIMER2_CLOCK"; + #clock-cells = <0>; + }; + }; + + ctimerclksel3: ctimerclksel3 { + clock-id = "CTIMERCLKSEL3"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclkselb &pll0_bypass &no_clock &fro_hf + &fro_1m &mclk_in &rtcosc32ksel &no_clock>; + + ctimer3_clock: ctimer3-clock { + compatible = "clock-output"; + clock-output; + clock-id = "CTIMER3_CLOCK"; + #clock-cells = <0>; + }; + }; + + ctimerclksel4: ctimerclksel4 { + clock-id = "CTIMERCLKSEL4"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclkselb &pll0_bypass &no_clock &fro_hf + &fro_1m &mclk_in &rtcosc32ksel &no_clock>; + + ctimer4_clock: ctimer4-clock { + compatible = "clock-output"; + clock-output; + clock-id = "CTIMER4_CLOCK"; + #clock-cells = <0>; + }; + }; + + fcclksel0: fcclksel0 { + clock-id = "FCCLKSEL0"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclkselb &pll0div &fro_12m &frohfdiv &fro_1m + &mclk_in &rtcosc32ksel &no_clock>; + + frgctrl0_mul: frgctrl0-mul { + clock-id = "FRGCTRL0_MUL"; + compatible = "clock-multiplier"; + #clock-cells = <1>; + + frgctrl0_div: frgctrl0-div { + clock-id = "FRGCTRL0_DIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + fxcom0_clock: fxcom0-clock { + compatible = "clock-output"; + clock-output; + clock-id = "FXCOM0_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + }; + + fcclksel1: fcclksel1 { + clock-id = "FCCLKSEL1"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclkselb &pll0div &fro_12m &frohfdiv &fro_1m + &mclk_in &rtcosc32ksel &no_clock>; + + frgctrl1_mul: frgctrl1-mul { + clock-id = "FRGCTRL1_MUL"; + compatible = "clock-multiplier"; + #clock-cells = <1>; + + frgctrl1_div: frgctrl1-div { + clock-id = "FRGCTRL1_DIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + fxcom1_clock: fxcom1-clock { + compatible = "clock-output"; + clock-output; + clock-id = "FXCOM1_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + }; + + fcclksel2: fcclksel2 { + clock-id = "FCCLKSEL2"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclkselb &pll0div &fro_12m &frohfdiv &fro_1m + &mclk_in &rtcosc32ksel &no_clock>; + + frgctrl2_mul: frgctrl2-mul { + clock-id = "FRGCTRL2_MUL"; + compatible = "clock-multiplier"; + #clock-cells = <1>; + + frgctrl2_div: frgctrl2-div { + clock-id = "FRGCTRL2_DIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + fxcom2_clock: fxcom2-clock { + compatible = "clock-output"; + clock-output; + clock-id = "FXCOM2_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + }; + + fcclksel3: fcclksel3 { + clock-id = "FCCLKSEL3"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclkselb &pll0div &fro_12m &frohfdiv &fro_1m + &mclk_in &rtcosc32ksel &no_clock>; + + frgctrl3_mul: frgctrl3-mul { + clock-id = "FRGCTRL3_MUL"; + compatible = "clock-multiplier"; + #clock-cells = <1>; + + frgctrl3_div: frgctrl3-div { + clock-id = "FRGCTRL3_DIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + fxcom3_clock: fxcom3-clock { + compatible = "clock-output"; + clock-output; + clock-id = "FXCOM3_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + }; + + fcclksel4: fcclksel4 { + clock-id = "FCCLKSEL4"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclkselb &pll0div &fro_12m &frohfdiv &fro_1m + &mclk_in &rtcosc32ksel &no_clock>; + + frgctrl4_mul: frgctrl4-mul { + clock-id = "FRGCTRL4_MUL"; + compatible = "clock-multiplier"; + #clock-cells = <1>; + + frgctrl4_div: frgctrl4-div { + clock-id = "FRGCTRL4_DIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + fxcom4_clock: fxcom4-clock { + compatible = "clock-output"; + clock-output; + clock-id = "FXCOM4_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + }; + + fcclksel5: fcclksel5 { + clock-id = "FCCLKSEL5"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclkselb &pll0div &fro_12m &frohfdiv &fro_1m + &mclk_in &rtcosc32ksel &no_clock>; + + frgctrl5_mul: frgctrl5-mul { + clock-id = "FRGCTRL5_MUL"; + compatible = "clock-multiplier"; + #clock-cells = <1>; + + frgctrl5_div: frgctrl5-div { + clock-id = "FRGCTRL5_DIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + fxcom5_clock: fxcom5-clock { + compatible = "clock-output"; + clock-output; + clock-id = "FXCOM5_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + }; + + fcclksel6: fcclksel6 { + clock-id = "FCCLKSEL6"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclkselb &pll0div &fro_12m &frohfdiv &fro_1m + &mclk_in &rtcosc32ksel &no_clock>; + + frgctrl6_mul: frgctrl6-mul { + clock-id = "FRGCTRL6_MUL"; + compatible = "clock-multiplier"; + #clock-cells = <1>; + + frgctrl6_div: frgctrl6-div { + clock-id = "FRGCTRL6_DIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + fxcom6_clock: fxcom6-clock { + compatible = "clock-output"; + clock-output; + clock-id = "FXCOM6_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + }; + + fcclksel7: fcclksel7 { + clock-id = "FCCLKSEL7"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclkselb &pll0div &fro_12m &frohfdiv &fro_1m + &mclk_in &rtcosc32ksel &no_clock>; + + frgctrl7_mul: frgctrl7-mul { + clock-id = "FRGCTRL7_MUL"; + compatible = "clock-multiplier"; + #clock-cells = <1>; + + frgctrl7_div: frgctrl7-div { + clock-id = "FRGCTRL7_DIV"; + compatible = "clock-div"; + #clock-cells = <1>; + + fxcom7_clock: fxcom7-clock { + compatible = "clock-output"; + clock-output; + clock-id = "FXCOM7_CLOCK"; + #clock-cells = <0>; + }; + }; + }; + }; + + hslspiclksel: hslspiclksel { + clock-id = "HSLSPICLKSEL"; + compatible = "clock-mux"; + #clock-cells = <1>; + input-sources = <&mainclkselb &pll0div &fro_12m &frohfdiv &fro_1m + &no_clock &rtcosc32ksel &no_clock>; + + hslspi_clock: hslspi-clock { + compatible = "clock-output"; + clock-output; + clock-id = "HSLSPI_CLOCK"; + #clock-cells = <0>; + }; + }; +}; diff --git a/dts/bindings/arm/nxp,lpc-flexcomm.yaml b/dts/bindings/arm/nxp,lpc-flexcomm.yaml index ffc27753c3dce..0131cddc25caf 100644 --- a/dts/bindings/arm/nxp,lpc-flexcomm.yaml +++ b/dts/bindings/arm/nxp,lpc-flexcomm.yaml @@ -5,7 +5,7 @@ description: LPC Flexcomm node compatible: "nxp,lpc-flexcomm" -include: [base.yaml, pinctrl-device.yaml] +include: [base.yaml, pinctrl-device.yaml, clock-device.yaml] properties: reg: diff --git a/dts/bindings/clock-mgmt/clock-device.yaml b/dts/bindings/clock-mgmt/clock-device.yaml new file mode 100644 index 0000000000000..c572788741e33 --- /dev/null +++ b/dts/bindings/clock-mgmt/clock-device.yaml @@ -0,0 +1,52 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: | + This file needs to be included by devices that need to specify clock + controller states. The maximum number of states currently defined is 5 + (clock-state-0...clock-state-4) but can be incremented if required. + + Clock state nodes are used to configure a clock controller, by setting + properties on clock nodes declared as children of the clock controller. + Clock states may represent a full clock tree, but there is no requirement + for them to. + +properties: + clock-state-0: + type: phandle-array + specifier-space: clock + description: | + Clock configuration/s for the first state. Content should be a series of + references to the clock nodes declared as children of the clock + controller. The specifier cells to these clock nodes are specific to the + implementation of the system's clock controller + + clock-state-1: + type: phandle-array + specifier-space: clock + description: | + Clock configuration/s for the second state. See clock-state-0. + + clock-state-2: + type: phandle-array + specifier-space: clock + description: | + Clock configuration/s for the third state. See clock-state-0. + + clock-state-3: + type: phandle-array + specifier-space: clock + description: | + Clock configuration/s for the fourth state. See clock-state-0. + + clock-state-4: + type: phandle-array + specifier-space: clock + description: | + Clock configuration/s for the fifth state. See clock-state-0. + + clock-state-names: + type: string-array + description: | + Names for the provided states. The number of names needs to match the + number of states. diff --git a/dts/bindings/clock-mgmt/clock-div.yaml b/dts/bindings/clock-mgmt/clock-div.yaml new file mode 100644 index 0000000000000..0de6463a81d3d --- /dev/null +++ b/dts/bindings/clock-mgmt/clock-div.yaml @@ -0,0 +1,15 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: Generic clock divider + +compatible: "clock-div" + +include: [clock-node.yaml, base.yaml] + +properties: + "#clock-cells": + const: 1 + +clock-cells: + - divider diff --git a/dts/bindings/clock-mgmt/clock-gate.yaml b/dts/bindings/clock-mgmt/clock-gate.yaml new file mode 100644 index 0000000000000..96e0971e31105 --- /dev/null +++ b/dts/bindings/clock-mgmt/clock-gate.yaml @@ -0,0 +1,15 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: Generic clock gate node + +compatible: "clock-gate" + +include: [clock-node.yaml, base.yaml] + +properties: + "#clock-cells": + const: 1 + +clock-cells: + - enable diff --git a/dts/bindings/clock-mgmt/clock-multiplier.yaml b/dts/bindings/clock-mgmt/clock-multiplier.yaml new file mode 100644 index 0000000000000..17ae3cc5ab204 --- /dev/null +++ b/dts/bindings/clock-mgmt/clock-multiplier.yaml @@ -0,0 +1,15 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: Generic clock multiplier + +compatible: "clock-multiplier" + +include: [clock-node.yaml, base.yaml] + +properties: + "#clock-cells": + const: 1 + +clock-cells: + - multiplier diff --git a/dts/bindings/clock-mgmt/clock-mux.yaml b/dts/bindings/clock-mgmt/clock-mux.yaml new file mode 100644 index 0000000000000..a2152cf62d23b --- /dev/null +++ b/dts/bindings/clock-mgmt/clock-mux.yaml @@ -0,0 +1,22 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: Generic clock multiplexor + +compatible: "clock-mux" + +include: [clock-node.yaml, base.yaml] + +properties: + "#clock-cells": + const: 1 + + input-sources: + type: phandles + required: true + description: | + List of all input sources provided to the mux. These sources should + be references to other clock nodes within the clock tree. + +clock-cells: + - selector diff --git a/dts/bindings/clock-mgmt/clock-node.yaml b/dts/bindings/clock-mgmt/clock-node.yaml new file mode 100644 index 0000000000000..3d07eaa016f30 --- /dev/null +++ b/dts/bindings/clock-mgmt/clock-node.yaml @@ -0,0 +1,22 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +# Common fields for nodes within clock controller clock tree + +properties: + "#clock-cells": + type: int + required: true + description: Number of items to expect in a node specifier + + clock-output: + type: boolean + description: | + Set if the clock node is usable as a clock source within the system. + + clock-id: + type: string + required: true + description: | + Clock identifier. Unique string identifying this clock within the + system clock tree diff --git a/dts/bindings/clock-mgmt/clock-output.yaml b/dts/bindings/clock-mgmt/clock-output.yaml new file mode 100644 index 0000000000000..732db7ef5c41b --- /dev/null +++ b/dts/bindings/clock-mgmt/clock-output.yaml @@ -0,0 +1,12 @@ +# Copyright 2024 +# SPDX-License-Identifier: Apache-2.0 + +description: Generic clock output + +compatible: "clock-output" + +include: [clock-node.yaml, base.yaml] + +properties: + "#clock-cells": + const: 0 diff --git a/dts/bindings/clock-mgmt/clock-source.yaml b/dts/bindings/clock-mgmt/clock-source.yaml new file mode 100644 index 0000000000000..6e7c471f9d9f4 --- /dev/null +++ b/dts/bindings/clock-mgmt/clock-source.yaml @@ -0,0 +1,15 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: Generic clock source + +compatible: "clock-source" + +include: [clock-node.yaml, base.yaml] + +properties: + "#clock-cells": + const: 1 + +clock-cells: + - frequency diff --git a/dts/bindings/clock-mgmt/nxp,lpc55xxx-pll.yaml b/dts/bindings/clock-mgmt/nxp,lpc55xxx-pll.yaml new file mode 100644 index 0000000000000..b9ecc02d707b8 --- /dev/null +++ b/dts/bindings/clock-mgmt/nxp,lpc55xxx-pll.yaml @@ -0,0 +1,20 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: LPC55xxx PLL + +compatible: "nxp,lpc55xxx-pll" + +include: [clock-node.yaml, base.yaml] + +properties: + "#clock-cells": + const: 6 + +clock-cells: + - multiplier + - divider + - pdec + - seli + - selp + - selr diff --git a/dts/bindings/clock-mgmt/nxp,lpc55xxx-syscon.yaml b/dts/bindings/clock-mgmt/nxp,lpc55xxx-syscon.yaml new file mode 100644 index 0000000000000..b5c61a34941d0 --- /dev/null +++ b/dts/bindings/clock-mgmt/nxp,lpc55xxx-syscon.yaml @@ -0,0 +1,15 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: LPC55xxx SYSCON + +compatible: "nxp,lpc55xxx-syscon" + +include: [clock-controller.yaml, base.yaml] + +properties: + reg: + required: true + + "#clock-cells": + const: 0 diff --git a/include/zephyr/devicetree/clock_mgmt.h b/include/zephyr/devicetree/clock_mgmt.h new file mode 100644 index 0000000000000..9ff7b25c3a55d --- /dev/null +++ b/include/zephyr/devicetree/clock_mgmt.h @@ -0,0 +1,370 @@ +/** + * @file + * @brief Clock Management Devicetree macro public API header file. + */ + +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DEVICETREE_CLOCK_MGMT_H_ +#define ZEPHYR_INCLUDE_DEVICETREE_CLOCK_MGMT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * @defgroup devicetree-clock-mgmt Devicetree Clock Management API + * @ingroup devicetree + * @{ + */ + +/** @cond INTERNAL_HIDDEN */ + +/** @brief DT_CLOCK_STATE_PHA + * Helper to add a layer of indirection when expanding state, since we want + * to expand to an integer value (IE clock_state_0) + */ +#define DT_CLOCK_STATE_PHA(state) clock_state_ ## state + +/* @brief _DT_CLOCK_STATE_ID_TOKEN + * Internal macro that prepends CLOCK_ID to token. We require this additional + * level of indirection because the DT_CLOCK_STATE_ID_TOKEN macro utilizes + * EMPTY to achieve deferred expansion of the "token" argument + */ +#define _DT_CLOCK_STATE_ID_TOKEN(token) CLOCK_ID_ ## token +/** @brief DT_CLOCK_STATE_ID_TOKEN + * Helper to get the string "clock-id" property from a clock node, and + * transform it into a C token. This will transform the clock-id + * "VND_CLOCK_ID" into the C token CLOCK_ID_VND_CLOCK_ID + */ +#define DT_CLOCK_STATE_ID_TOKEN(token) _DT_CLOCK_STATE_ID_TOKEN(token) EMPTY + +/** @brief DT_CLOCK_STATE_APPLY_ID_INTERNAL + * This helper adds a layer of indirection, to insure that the "state" value + * is expanded before it is passed to underlying macros + */ +#define DT_CLOCK_STATE_APPLY_ID_INTERNAL(node, _clock_id, fn, state) \ + IF_ENABLED(DT_CLOCK_STATE_HAS_ID(node, _clock_id, state), \ + (fn(node, DT_CLOCK_STATE_PHA(state), \ + DT_CLOCK_STATE_ID_PH_IDX(node, _clock_id, state), \ + DT_CLOCK_STATE_ID_TOKEN( \ + DT_STRING_UPPER_TOKEN( \ + DT_PHANDLE_BY_IDX(node, \ + DT_CLOCK_STATE_PHA(state), \ + DT_CLOCK_STATE_ID_PH_IDX(node, _clock_id, state)), \ + clock_id))))) + +/** @brief DT_CLOCK_STATE_APPLY_ID_INTERNAL_VARGS + * This helper adds a layer of indirection, to insure that the "state" value + * is expanded before it is passed to underlying macros + */ +#define DT_CLOCK_STATE_APPLY_ID_INTERNAL_VARGS(node, _clock_id, fn, state, ...) \ + IF_ENABLED(DT_CLOCK_STATE_HAS_ID(node, _clock_id, state), \ + (fn(node, DT_CLOCK_STATE_PHA(state), \ + DT_CLOCK_STATE_ID_PH_IDX(node, _clock_id, state), \ + DT_CLOCK_STATE_ID_TOKEN( \ + DT_STRING_UPPER_TOKEN( \ + DT_PHANDLE_BY_IDX(node, \ + DT_CLOCK_STATE_PHA(state), \ + DT_CLOCK_STATE_ID_PH_IDX(node, _clock_id, state)), \ + clock_id)), __VA_ARGS__))) +/** @endcond */ + +/** + * @brief Test if a node's clock-"state" property refers to a clock node with + * a given clock ID + * + * This expands to 1 if given node's clock-"state" property has a phandle + * reference to a clock node with the given clock ID. + * Otherwise, it expands to 0. + * + * Example devicetree fragment: + * + * div: clock-div { + * clock-id = "VND_CLOCK_DIV"; + * }; + * + * src: clock-source { + * clock-id = "VND_CLOCK_SRC"; + * }; + * + * node: vnd-device { + * clock-state-0 = <&div 3>; + * clock-state-1 = <&src 3>; <&div 1>; + * clock-state-2 = <&src 3>; <&div 1>; + * clock-state-3 = <&src 1>; + * clock-state-names = "default", "sleep", "custom"; + * }; + * + * Example usage: + * + * #define CLOCK_STATE_CUSTOM 2 + * + * DT_CLOCK_STATE_HAS_ID(DT_NODELABEL(node), VND_CLOCK_DIV, CLOCK_STATE_DEFAULT) // 1 + * DT_CLOCK_STATE_HAS_ID(DT_NODELABEL(node), VND_CLOCK_SRC, CLOCK_STATE_DEFAULT) // 0 + * DT_CLOCK_STATE_HAS_ID(DT_NODELABEL(node), VND_CLOCK_DIV, CLOCK_STATE_SLEEP) // 1 + * DT_CLOCK_STATE_HAS_ID(DT_NODELABEL(node), VND_CLOCK_SRC, CLOCK_STATE_SLEEP) // 1 + * DT_CLOCK_STATE_HAS_ID(DT_NODELABEL(node), VND_CLOCK_DIV, CLOCK_STATE_CUSTOM) // 0 + * DT_CLOCK_STATE_HAS_ID(DT_NODELABEL(node), VND_CLOCK_SRC, CLOCK_STATE_CUSTOM) // 1 + * + * @param node node identifier; may or may not have any clock-state-"state" property + * @param clock_id clock ID to check for in node clock state + * @param state state identifier. Integer index equal to clock-state-"n" value + * in devicetree + * @return 1 if the ID is present for the given state, 0 otherwise + */ +#define DT_CLOCK_STATE_HAS_ID(node, clock_id, state) \ + IS_ENABLED(DT_CAT6(node, _CLOCK_STATE_, state, _, clock_id, _EXISTS)) + +/** + * @brief Get index of a clock ID phandle within a clock state + * + * This helper returns the index of a phandle for a given clock ID within + * a clock state. If the clock ID does not exist within the clock state, using + * this macro is an error. + * + * Example devicetree fragment: + * + * div: clock-div { + * compatible = "clock-div" + * }; + * + * src: clock-source { + * compatible = "clock-source" + * }; + * + * node: vnd-device { + * clock-state-0 = <&div 3>; + * clock-state-1 = <&src 3>; <&div 1>; + * clock-state-2 = <&src 3>; <&div 1>; + * clock-state-3 = <&src 1>; + * clock-state-names = "default", "sleep", "custom"; + * }; + * + * Example usage: + * + * #define CLOCK_STATE_CUSTOM 2 + * + * DT_CLOCK_STATE_ID_PH_IDX(DT_NODELABEL(node), VND_CLOCK_DIV, CLOCK_STATE_DEFAULT) // 0 + * DT_CLOCK_STATE_ID_PH_IDX(DT_NODELABEL(node), VND_CLOCK_DIV, CLOCK_STATE_SLEEP) // 1 + * DT_CLOCK_STATE_ID_PH_IDX(DT_NODELABEL(node), VND_CLOCK_SRC, CLOCK_STATE_SLEEP) // 0 + * DT_CLOCK_STATE_ID_PH_IDX(DT_NODELABEL(node), VND_CLOCK_SRC, CLOCK_STATE_CUSTOM) // 0 + * + * @param node node identifier. Must have a clock-state-"state" property. + * @param clock_id clock ID to get phandle index for + * @param state state identifier. Integer index equal to clock-state-"n" value + * in devicetree + * @return value present in the selected cell + */ +#define DT_CLOCK_STATE_ID_PH_IDX(node, clock_id, state) \ + DT_CAT6(node, _CLOCK_STATE_, state, _, clock_id, _IDX) + +/** + * @brief Read a specifier cell for a given clock ID within a clock state + * + * This helper returns the specifier cell value for a given clock ID within + * a clock state. If the clock ID does not exist within the clock state, using + * this macro is an error. + * + * Example devicetree bindings + * + * clock-div.yaml: + * ... + * clock-cells: + * - div + * ... + * + * clock-source.yaml: + * ... + * clock-cells: + * - freq + * ... + * + * Example devicetree fragment: + * + * div: clock-div { + * compatible = "clock-div" + * clock-id = "VND_CLOCK_DIV"; + * }; + * + * src: clock-source { + * compatible = "clock-source" + * clock-id = "VND_CLOCK_SRC"; + * }; + * + * node: vnd-device { + * clock-state-0 = <&div 3>; + * clock-state-1 = <&src 3>; <&div 1>; + * clock-state-2 = <&src 3>; <&div 1>; + * clock-state-3 = <&src 1>; + * clock-state-names = "default", "sleep", "custom"; + * }; + * + * Example usage: + * + * #define CLOCK_STATE_CUSTOM 2 + * + * DT_CLOCK_STATE_ID_READ_CELL(DT_NODELABEL(node), VND_CLOCK_DIV, div, CLOCK_STATE_DEFAULT) // 3 + * DT_CLOCK_STATE_ID_READ_CELL(DT_NODELABEL(node), VND_CLOCK_DIV, div, CLOCK_STATE_SLEEP) // 1 + * DT_CLOCK_STATE_ID_READ_CELL(DT_NODELABEL(node), VND_CLOCK_SRC, freq, CLOCK_STATE_SLEEP) // 3 + * DT_CLOCK_STATE_ID_READ_CELL(DT_NODELABEL(node), VND_CLOCK_SRC, freq, CLOCK_STATE_CUSTOM) // 1 + * + * @param node node identifier. Must have a clock-state-"state" property. + * @param clock_id clock ID to read specifier cell for. + * @param name lowercase-and-underscores clock-names cell value name to check + * @param state state identifier. Integer index equal to clock-state-"n" value + * in devicetree + * @return value present in the selected cell + */ +#define DT_CLOCK_STATE_ID_READ_CELL(node, clock_id, name, state) \ + DT_PHA_BY_IDX(node, DT_CLOCK_STATE_PHA(state), \ + DT_CLOCK_STATE_ID_PH_IDX(node, clock_id, state), \ + name) + + +/** + * @brief Read a specifier cell for a given clock ID within a clock state + * with a fallback value + * + * This helper is similar to @ref DT_CLOCK_STATE_ID_READ_CELL, but allows + * the user to provide a fallback value that will be used if the clock ID + * is not present within the given clock state + * @param node node identifier. Must have a clock-state-"state" property. + * @param clock_id clock ID to read specifier cell for. + * @param name lowercase-and-underscores clock-names cell value name to check + * @param state state identifier. Integer index equal to clock-state-"n" value + * in devicetree + * @param fallback Fallback value to use if clock ID is not present + * @return value present in the selected cell, or fallback value + */ +#define DT_CLOCK_STATE_ID_READ_CELL_OR(node, clock_id, name, state, fallback) \ + COND_CODE_1(DT_CLOCK_STATE_HAS_ID(node, clock_id, state), \ + (DT_PHA_BY_IDX(node, DT_CLOCK_STATE_PHA(state), \ + DT_CLOCK_STATE_ID_PH_IDX(node, clock_id, state), \ + name)), (fallback)) + + +/** + * @brief Call macro to apply clock ID configuration for given state + * + * This helper will call the given macro function if a phandle referring to + * the clock node with the provided clock ID exists for a given state. If + * no such phandle exists, this macro expands to nothing. + * + * The macro function takes the following arguments: + * - node: Node with the clocks-state-"state" property + * - pha: name of the phandle property for clocks-state-"state" + * - idx: index of the phandle with the given clock_id in the + * clock-state-"state" property + * - clock_id: C token for the given clock ID, with CLOCK_ID_ prepended. + * For example, "VND_CLOCK_ID" would become CLOCK_ID_VND_CLOCK_ID + * + * Example C code: + * + * ``` + * #define CLOCK_ID_VND_CLOCK_DIV 0x100 + * + * void apply_clock_div(uint32_t clock_id, uint32_t div_val); + * + * #define APPLY_CLOCK_DIV(node, pha, idx, clock_id) \ + * apply_clock_div(clock_id, DT_PHA_BY_IDX(node, pha, idx, div)); + * ``` + * + * Example devicetree fragment: + * ``` + * + * div: clock-div { + * compatible = "clock-div" + * clock-id = "VND_CLOCK_DIV"; + * }; + * + * node: vnd-device { + * clock-state-0 = <&div 3>; + * clock-state-names = "default"; + * }; + * ``` + * + * Example usage: + * + * ``` + * // Expands to: apply_clock_div(0x100, 3); + * DT_CLOCK_STATE_APPLY_ID(DT_NODELABEL(node), VND_CLOCK_DIV, APPLY_CLOCK_DIV, + * CLOCK_STATE_DEFAULT) + * // Expands to nothing + * DT_CLOCK_STATE_APPLY_ID(DT_NODELABEL(node), VND_CLOCK_SOURCE, APPLY_CLOCK_DIV, + * CLOCK_STATE_DEFAULT) + * ``` + * + * Resulting C code: + * + * ``` + * apply_clock(0x100, 3); + * ``` + * + * @param node_id node identifier. Must have a clock-state-"state" property. + * @param clock_id clock ID to apply setting for + * @param fn macro function to call to apply clock state + * @param state state identifier. Integer index equal to clock-state-"n" value + * in devicetree + */ +#define DT_CLOCK_STATE_APPLY_ID(node_id, clock_id, fn, state) \ + DT_CLOCK_STATE_APPLY_ID_INTERNAL(node_id, clock_id, fn, state) + +/** + * @brief Call macro to apply clock ID configuration for given state + * + * This helper will call the given macro function if a phandle referring to + * the clock node with the provided clock ID exists for a given state. If + * no such phandle exists, this macro expands to nothing. + * + * The macro @p fn takes multiple arguments. The first 4 are the following: + * - node: Node with the clocks-state-"state" property + * - pha: name of the phandle property for clocks-state-"state" + * - idx: index of the phandle with the given clock_id in the + * clock-state-"state" property + * - clock_id: C token for the given clock ID, with CLOCK_ID_ prepended. + * For example, "VND_CLOCK_ID" would become CLOCK_ID_VND_CLOCK_ID + * + * @param node_id node identifier. Must have a clock-state-"state" property. + * @param clock_id clock ID to apply setting for + * @param fn macro function to call to apply clock state + * @param state state identifier. Integer index equal to clock-state-"n" value + * in devicetree + * @param ... variable number of arguments to pass to @p fn + * The remaining arguments are passed in by the caller + */ +#define DT_CLOCK_STATE_APPLY_ID_VARGS(node_id, clock_id, fn, state, ...) \ + DT_CLOCK_STATE_APPLY_ID_INTERNAL_VARGS(node_id, clock_id, fn, state, __VA_ARGS__) + +/** + * @brief Number of clock management states for a node identifier + * Gets the number of clock management states for a given node identifier + * @param node_id Node identifier to get the number of states for + */ +#define DT_NUM_CLOCK_MGMT_STATES(node_id) \ + DT_CAT(node_id, _CLOCK_STATE_NUM) + +/** + * @brief Check if a clock subsystem is referenced by a node in the + * devicetree + * @param clock_id Clock subsystem identifier to check + * @return 1 if ID is used, 0 otherwise + */ +#define DT_CLOCK_ID_USED(clock_id) DT_CAT3(DT_CLOCK_ID_, clock_id, _USED) + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + + +#endif /* ZEPHYR_INCLUDE_DEVICETREE_CLOCK_MGMT_H_ */ diff --git a/include/zephyr/drivers/clock_mgmt.h b/include/zephyr/drivers/clock_mgmt.h new file mode 100644 index 0000000000000..1e9f4a779468b --- /dev/null +++ b/include/zephyr/drivers/clock_mgmt.h @@ -0,0 +1,515 @@ +/* + * Copyright 2024 NXP + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * Public APIs for clock management + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_CLOCK_MGMT_H_ +#define ZEPHYR_INCLUDE_DRIVERS_CLOCK_MGMT_H_ + +/** + * @brief Clock Management Interface + * @defgroup clock_mgmt_interface Clock management Interface + * @ingroup io_interfaces + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/** + * @name Clock Management States + * @{ + */ + +/** Default state (state used when the device is in operational state). */ +#define CLOCK_MGMT_STATE_DEFAULT 0U +/** Sleep state (state used when the device is in low power mode). */ +#define CLOCK_MGMT_STATE_SLEEP 1U + +/** This and higher values refer to custom private states. */ +#define CLOCK_MGMT_STATE_PRIV_START 2U + +/** @} */ + +/** + * @name Clock Subsys Names + * @{ + */ + +/** Default clock subsystem */ +#define CLOCK_MGMT_SUBSYS_DEFAULT 0U +/** This and higher values refer to custom private clock subsystems */ +#define CLOCK_MGMT_SUBSYS_PRIV_START 1U + +/** @} */ + +/** @cond INTERNAL_HIDDEN */ + +/** + * @brief Defines name for clock management setpoint function + */ +#define Z_CLOCK_MGMT_SETPOINT_FUNC_NAME(node, state_idx) \ + z_clock_setpoint_##node##_state_##state_idx + +/** + * @brief Define a clock management state + * This macro defines the extern function prototype for a clock management + * function. The function is implemented within the generated soc clock + * management code. + */ +#define Z_CLOCK_MGMT_STATE_DEFINE(state_idx, node) \ + extern clock_setpoint_t Z_CLOCK_MGMT_SETPOINT_FUNC_NAME(node, state_idx); + +/** + * @brief Initialize a clock management state + * This macro handles initialization of a clock management state for a + * given node, by defining the function pointer to use + */ +#define Z_CLOCK_MGMT_STATE_INIT(state_idx, node) \ + &Z_CLOCK_MGMT_SETPOINT_FUNC_NAME(node, state_idx) + +/** + * @brief Define a clock management callback + * This macro defines a clock management callback prototype. Note that the + * callback is declared as extern, since it is implemented in the + * clock_callbacks.c common code + */ +#define Z_CLOCK_MGMT_CALLBACK_DEFINE(node, prop, idx) \ + extern sys_slist_t _CONCAT(clock_callback_, DT_STRING_TOKEN( \ + DT_PHANDLE_BY_IDX(node, prop, idx), clock_id)); + +/** + * @brief Init a clock management callback + * This macro gets a reference to a clock management callback. This reference + * is what results in the linker keeping this callback symbol, since it + * is not referenced within the clock_callbacks.c common code. + */ +#define Z_CLOCK_MGMT_CALLBACK_INIT(node, prop, idx) \ + _CONCAT(&clock_callback_, DT_STRING_TOKEN( \ + DT_PHANDLE_BY_IDX(node, prop, idx), clock_id)) + + +/** + * @brief Defines base name for clock management structures given a node id + * Provides a unique base name for clock management structures given a node + * identifier. This can be pasted with a suffix to identify the specific + * structure being initialized. + */ +#define Z_CLOCK_MGMT_NODE_NAME(node_id) clock_mgmt_##node_id + +/** + * @brief Defines name for clock management rate read function + */ +#define Z_CLOCK_MGMT_SUBSYS_FUNC_NAME(node_id, prop, idx) \ + z_clock_rate_##node_id##_subsys_##idx + + +/** + * @brief Defines a reference to clock management read function + * Defines an extern reference to the clock management read function, which + * is implemented in the generated SOC clock management code + */ +#define Z_CLOCK_MGMT_SUBSYS_DEFINE(node_id, prop, idx) \ + extern clock_rate_t Z_CLOCK_MGMT_SUBSYS_FUNC_NAME(node_id, prop, idx); + + +/** + * @brief Gets reference to clock management rate read function + */ +#define Z_CLOCK_MGMT_SUBSYS_INIT(node_id, prop, idx) \ + &Z_CLOCK_MGMT_SUBSYS_FUNC_NAME(node_id, prop, idx) + + +/** @endcond */ + +/** + * Definition for clock rate read functions. This is internal to the clock + * management subsystem + */ +typedef int (*clock_rate_t)(void); +/** + * Definition for clock setpoint functions. This is internal to the clock + * management subsystem + */ +typedef int (*clock_setpoint_t)(void); + +/** Clock management configuration */ +struct clock_mgmt { + /** Functions to read rate for each clock subsystem */ + const clock_rate_t *rates; + /** Array of callback lists, one per subsystem */ + sys_slist_t **callbacks; + /** Count of clock subsystems for this instance */ + uint8_t clocks_cnt; + /** Clock states. Each state corresponds to a clock tree setting */ + const clock_setpoint_t *states; + /** Count of clock states for this instance */ + uint8_t state_cnt; +}; + +/** + * @brief Clock callback reasons + * + * Reasons for a clock callback to occur. One of these will be passed to the + * driver when a clock callback is issued, so that the driver can decide + * what action to take + */ +enum clock_mgmt_cb_reason { + /** Rate of the clock changed */ + CLOCK_MGMT_RATE_CHANGED, + /** Clock started */ + CLOCK_MGMT_STARTED, + /** Clock stopped */ + CLOCK_MGMT_STOPPED, +}; + +struct clock_mgmt_callback; + +/** + * @typedef clock_mgmt_callback_handler_t + * @brief Define the application clock callback handler function signature + * + * @param cb Original struct clock_mgmt_callback owning this handler + * @param reason Reason callback occurred + * + * Note: cb pointer can be used to retrieve private data through + * CONTAINER_OF() if original struct clock_mgmt_callback is stored in + * another private structure. + */ +typedef void (*clock_mgmt_callback_handler_t)(struct clock_mgmt_callback *cb, + enum clock_mgmt_cb_reason reason); +/** + * @brief Clock management callback structure + * + * Used to register a callback for clock change events in the driver consuming + * the clock. As many callbacks as needed can be added as long as each of them + * are unique pointers of struct clock_mgmt_callback. + * Beware such pointers must not be allocated on the stack. + * + * Note: to help setting the callback, clock_mgmt_init_callback() below + */ +struct clock_mgmt_callback { + /** This is meant to be used in the driver and the user should not + * mess with it + */ + sys_snode_t node; + + /** Actual callback function being called when relevant */ + clock_mgmt_callback_handler_t handler; +}; + + +/** + * @brief Fire clock callback for a clock ID + * + * This macro issues a clock callback for a given clock identifier. If no + * macros are registered for the clock identifier, the macro will simply + * return + * @param clock_id clock identifier to fire callback for + * @param reason Reason clock callback has fired + */ +#define CLOCK_MGMT_FIRE_CALLBACK(clock_id, reason) \ + /* Extern symbols- defined by linker */ \ + extern sys_slist_t clock_callback_##clock_id##_start; \ + extern sys_slist_t clock_callback_##clock_id##_end; \ + \ + if (&clock_callback_##clock_id##_start != \ + &clock_callback_##clock_id##_end) { \ + struct clock_mgmt_callback *cb, *tmp; \ + \ + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&clock_callback_##clock_id##_start, \ + cb, tmp, node) { \ + __ASSERT(cb->handler, "No callback handler!"); \ + cb->handler(cb, reason); \ + } \ + } + +/** + * @brief Fire clock callback for all clock IDs + * + * This macro issues a clock callback for all clock identifiers in the + * system. It may be useful if a root clock source is changed. + * @param reason Reason clock callback has fired + */ +#define CLOCK_MGMT_FIRE_ALL_CALLBACKS(reason) \ + /* Extern symbols- defined by linker */ \ + extern sys_slist_t clock_callbacks_start; \ + extern sys_slist_t clock_callbacks_end; \ + sys_slist_t *curr = &clock_callbacks_start; \ + \ + while (curr != &clock_callbacks_end) { \ + struct clock_mgmt_callback *cb, *tmp; \ + \ + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(curr, \ + cb, tmp, node) { \ + __ASSERT(cb->handler, "No callback handler!"); \ + cb->handler(cb, reason); \ + } \ + curr++; \ + } + +/** + * @brief Defines clock management information for a given node identifier. + * + * This macro should be called during the device definition. It will define + * and initialize the clock management configuration for the device represented + * by node_id. Clock subsystems as well as clock management states will be + * initialized by this macro. + * @param node_id node identifier to define clock management data for + */ +#define CLOCK_MGMT_DEFINE(node_id) \ + DT_FOREACH_PROP_ELEM_SEP(node_id, clocks, \ + Z_CLOCK_MGMT_CALLBACK_DEFINE, (;)) \ + DT_FOREACH_PROP_ELEM_SEP(node_id, clocks, \ + Z_CLOCK_MGMT_SUBSYS_DEFINE, (;)) \ + LISTIFY(DT_NUM_CLOCK_MGMT_STATES(node_id), \ + Z_CLOCK_MGMT_STATE_DEFINE, (;), node_id) \ + static const clock_rate_t* \ + _CONCAT(Z_CLOCK_MGMT_NODE_NAME(node_id), _rates)[] = { \ + DT_FOREACH_PROP_ELEM_SEP(node_id, clocks, \ + Z_CLOCK_MGMT_SUBSYS_INIT, (,)) \ + }; \ + static const clock_setpoint_t* \ + _CONCAT(Z_CLOCK_MGMT_NODE_NAME(node_id), _states)[] = { \ + LISTIFY(DT_NUM_CLOCK_MGMT_STATES(node_id), \ + Z_CLOCK_MGMT_STATE_INIT, (,), node_id) \ + }; \ + static sys_slist_t* \ + _CONCAT(Z_CLOCK_MGMT_NODE_NAME(node_id), _cb)[] = { \ + DT_FOREACH_PROP_ELEM_SEP(node_id, clocks, \ + Z_CLOCK_MGMT_CALLBACK_INIT, (,)) \ + }; \ + static const struct clock_mgmt \ + _CONCAT(Z_CLOCK_MGMT_NODE_NAME(node_id), _cfg) = { \ + .rates = (clock_rate_t *) \ + _CONCAT(Z_CLOCK_MGMT_NODE_NAME(node_id), _rates), \ + .callbacks = _CONCAT(Z_CLOCK_MGMT_NODE_NAME(node_id), _cb), \ + .clocks_cnt = DT_NUM_CLOCKS(node_id), \ + .states = (clock_setpoint_t *) \ + _CONCAT(Z_CLOCK_MGMT_NODE_NAME(node_id), _states), \ + .state_cnt = DT_NUM_CLOCK_MGMT_STATES(node_id), \ + } + +/** + * @brief Defines clock management information for a given driver instance + * Equivalent to CLOCK_MGMT_DEFINE(DT_DRV_INST(inst)) + * @param inst Driver instance number + */ +#define CLOCK_MGMT_DT_INST_DEFINE(inst) CLOCK_MGMT_DEFINE(DT_DRV_INST(inst)) + +/** + * @brief Initializes clock management information for a given node identifier. + * + * This macro should be used during device initialization, in combination with + * #CLOCK_MGMT_DEFINE. It will get a reference to the clock management + * structure defined with #CLOCK_MGMT_DEFINE + * For example, a driver could initialize clock + * management information like so: + * ``` + * struct vnd_device_cfg { + * ... + * struct clock_mgmt clock_mgmt; + * ... + * }; + * ... + * #define VND_DEVICE_INIT(node_id) \ + * CLOCK_MGMT_DEFINE(node_id); \ + * \ + * struct vnd_device_cfg cfg_##node_id = { \ + * .clock_mgmt = CLOCK_MGMT_INIT(node_id), \ + * } + * ``` + * @param node_id: Node identifier to initialize clock management structure for + */ +#define CLOCK_MGMT_INIT(node_id) &_CONCAT(Z_CLOCK_MGMT_NODE_NAME(node_id), _cfg) + +/** + * @brief Initializes clock management information for a given driver instance + * + * Equivalent to CLOCK_MGMT_INIT(DT_DRV_INST(inst)) + * @param inst Driver instance number + */ +#define CLOCK_MGMT_DT_INST_INIT(inst) CLOCK_MGMT_INIT(DT_DRV_INST(inst)) + +/** + * @brief Get clock rate for given subsystem + * + * Gets output clock rate for provided clock identifier. Clock identifiers + * start with CLOCK_MGMT_SUBSYS_DEFAULT, and drivers can define additional + * identifiers. Each identifier refers to the nth clock node in the clocks + * devicetree property for the devicetree node representing this driver. + * @param clk_cfg Clock management structure + * @param clk_idx subsys clock index in clocks property + * @return -EINVAL if parameters are invalid + * @return -ENOENT if output id could not be found + * @return -ENOTSUP if clock is not supported + * @return -EIO if clock could not be read + * @return frequency of clock output in HZ + */ +static inline int clock_mgmt_get_rate(const struct clock_mgmt *clk_cfg, + uint8_t clk_idx) +{ + if (!clk_cfg) { + return -EINVAL; + } + + if (clk_cfg->clocks_cnt <= clk_idx) { + return -ENOENT; + } + + /* Call clock management driver function */ + return clk_cfg->rates[clk_idx](); +} + +/** + * @brief Set new clock state + * + * Sets new clock state. This function will apply a clock state as defined + * in devicetree. Clock states can configure clocks systemwide, or only for + * the relevant peripheral driver. Clock states are defined as clock-state-"n" + * properties of the devicetree node for the given driver. + * @param clk_cfg Clock management structure + * @param state_idx Clock state index + * @return -EINVAL if parameters are invalid + * @return -ENOENT if state index could not be found + * @return -ENOTSUP if state is not supported by hardware + * @return -EIO if state could not be set + * @return -EBUSY if clocks cannot be modified at this time + * @return 0 on success + */ +static inline int clock_mgmt_apply_state(const struct clock_mgmt *clk_cfg, + uint8_t state_idx) +{ + if (!clk_cfg) { + return -EINVAL; + } + + if (clk_cfg->state_cnt <= state_idx) { + return -ENOENT; + } + + /* Call clock management driver function */ + return clk_cfg->states[state_idx](); +} + + +/** @cond INTERNAL_HIDDEN */ + +/** + * @brief Helper to add or remove clock control callback + * @param callbacks A pointer to the original list of callbacks + * @param callback A pointer of the callback to insert or remove from the list + * @param set A boolean indicating insertion or removal of the callback + */ +static inline int clock_mgmt_manage_callback(sys_slist_t *callbacks, + struct clock_mgmt_callback *callback, + bool set) +{ + __ASSERT(callback, "No callback!"); + __ASSERT(callback->handler, "No callback handler!"); + + if (!sys_slist_is_empty(callbacks)) { + if (!sys_slist_find_and_remove(callbacks, &callback->node)) { + if (!set) { + return -EINVAL; + } + } + } else if (!set) { + return -EINVAL; + } + + if (set) { + sys_slist_prepend(callbacks, &callback->node); + } + + return 0; +} + +/** @endcond */ + +/** + * @brief Helper to initialize a struct clock_mgmt_callback properly + * @param callback A valid Application's callback structure pointer. + * @param handler A valid handler function pointer. + */ +static inline void clock_mgmt_init_callback(struct clock_mgmt_callback *callback, + clock_mgmt_callback_handler_t handler) +{ + __ASSERT(callback, "Callback pointer should not be NULL"); + __ASSERT(handler, "Callback handler pointer should not be NULL"); + + callback->handler = handler; +} + +/** + * @brief Add application clock callback for a given subsystem + * @param clk_cfg Clock management structure + * @param clk_idx Subsystem clock index in clocks property + * @param callback A valid Application's callback structure pointer. + * @return -EINVAL if parameters are invalid + * @return -ENOENT if subsystem index could not be found + * @return 0 on success + */ +static inline int clock_mgmt_add_callback(const struct clock_mgmt *clk_cfg, + uint8_t clk_idx, + struct clock_mgmt_callback *callback) +{ + if (!clk_cfg || !callback) { + return -EINVAL; + } + + if (clk_cfg->clocks_cnt <= clk_idx) { + return -ENOENT; + } + + /* Add callback to sys_slist_t */ + return clock_mgmt_manage_callback(clk_cfg->callbacks[clk_idx], + callback, true); +} + +/** + * @brief Remove application clock callback for a given subsystem + * @param clk_cfg Clock management structure + * @param clk_idx Subsystem clock index in clocks property + * @param callback A valid Application's callback structure pointer. + * @return -EINVAL if parameters are invalid + * @return -ENOENT if subsystem index could not be found + * @return 0 on success + */ +static inline int clock_mgmt_remove_callback(const struct clock_mgmt *clk_cfg, + uint8_t clk_idx, + struct clock_mgmt_callback *callback) +{ + if (!clk_cfg || !callback) { + return -EINVAL; + } + + if (clk_cfg->clocks_cnt <= clk_idx) { + return -ENOENT; + } + + /* Remove callback from sys_slist_t */ + return clock_mgmt_manage_callback(clk_cfg->callbacks[clk_idx], + callback, false); +} + + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_DRIVERS_CLOCK_MGMT_H_ */ diff --git a/scripts/build/gen_clock_mgmt.py b/scripts/build/gen_clock_mgmt.py new file mode 100644 index 0000000000000..4c52b700bbf18 --- /dev/null +++ b/scripts/build/gen_clock_mgmt.py @@ -0,0 +1,191 @@ +""" +This module preprocesses the clock management code for an SOC, to generate +functions to apply each setpoint and query each subsystem referenced in the +system devicetree. This is used as an alternative to defining clock +management functions as macros, to simplify debugging for implementers +""" + +import argparse +import os +import sys +import pickle +from pathlib import Path +import re + +ZEPHYR_BASE = str(Path(__file__).resolve().parents[2]) +sys.path.insert(0, os.path.join(ZEPHYR_BASE, "scripts", "dts", + "python-devicetree", "src")) + + +def parse_args(): + """ + Parses program arguments + """ + parser = argparse.ArgumentParser(prog="gen_clock_mgmt.py", + description="Generate clock management C code", + allow_abbrev=False) + parser.add_argument("edt_pickle", type=str, + help="edt pickle file describing devicetree") + parser.add_argument("soc_clocks", type=str, + help="SOC clock management file to process") + parser.add_argument("output_file", type=str, + help="Processed SOC clock management file to create") + return parser.parse_args() + + +def find_func_bounds(stream, curr_line, curr_lc): + """ + Finds the bounds of a function, returning a string with the contents + of the function + @param stream file Stream to read the function from + @param curr_line Portion of the first line to parse, after the function + name has been declared + @param curr_lc Current line count in the file. Used for error reporting. + @return string containing the function definition. The first line within + "curr_line" will not be included + """ + line = curr_line + line_count = curr_lc + func_line = curr_line + setpoint_templ = "" + brace_level = 0 + + # Look for the opening bracket of the function + while brace_level == 0 and line != "": + for (idx, char) in enumerate(line): + if char == '{': + brace_level = 1 + opening_line = line + opening_line_num = line_count + line = line[idx + 1:] + break + # Search next line + if brace_level == 0: + line = stream.readline() + line_count += 1 + setpoint_templ += line + if line == "": + sys.exit(f"Error: could not find '{{' after function declaration on" + f" line {func_line}") + ## Opening bracket was found. Locate closing bracket. + while brace_level != 0 and line != "": + for char in line: + if char == '{': + brace_level += 1 + if char == '}': + brace_level -= 1 + if brace_level == 0: + break + setpoint_templ += line + if brace_level != 0: + # Search next line + line = stream.readline() + line_count += 1 + if line == "": + sys.exit(f"Error: could not find matching '}}' for '{{' on line " + f"{opening_line_num}: {opening_line}") + return (setpoint_templ, line_count) + +def gen_node_dictionaries(edt): + """ + Generate dictionary mapping every node path with setpoints to + a count of the setpoints for that node, and dictionary mapping + every node path with clock subsystems to an array of indices with + subsystems for that node which use clock-output nodes + @param edt: EDTLib instance describing devicetree + """ + node_setpoint_counts = {} + node_clock_indices = {} + for node in edt.nodes: + if node.status == "okay": + clock_state_props = [prop for name, prop in node.props.items() + if re.match("clock-state-[0-9]+", name)] + if "clocks" in node.props: + node_clock_indices[f"DT_{node.z_path_id}"] = [] + for (idx, clock_ph) in enumerate(node.props["clocks"].val): + if "clock-id" in clock_ph.controller.props: + # Add this index to the array + node_clock_indices[f"DT_{node.z_path_id}"].append(idx) + if len(clock_state_props) > 0: + node_setpoint_counts[f"DT_{node.z_path_id}"] = len(clock_state_props) + return (node_setpoint_counts, node_clock_indices) + + + +def main(): + """ + Main entry point for script. Generates clock management C code, + which is used by clock management subsystem to apply clock states + """ + args = parse_args() + if os.path.isfile(args.edt_pickle): + with open(args.edt_pickle, 'rb') as file: + edt = pickle.load(file) + else: + sys.exit(f"Error: could not open edt pickle file {args.edt_pickle}") + + (node_setpoint_counts, node_clock_indices) = gen_node_dictionaries(edt) + + # In order to generate clock management code, we need to define one + # implementation of the SOC clock get rate function per each clock + # subsystem used in the devicetree, as well as one implementation of + # the SOC clock set rate function per each clock setpoint in the devicetree. + + # This process requires us to identify the SOC clock get rate/set rate + # function within the soc clock management template, and then write + # instances of these functions per each clock subystem or setpoint. + + output_file = open(args.output_file, "w") + + if not os.path.isfile(args.soc_clocks): + sys.exit(f"Error: could not open file {args.soc_clocks} for reading") + + line_count = 1 + with open(args.soc_clocks, 'r') as clock_input: + line = clock_input.readline() + while line != "": + setpoint_match = re.match(r'.*Z_CLOCK_MGMT_SETPOINT_TEMPL' + r'\((\w+),\s*(\w+)\)', line) + subsys_match = re.match(r'.*Z_CLOCK_MGMT_SUBSYS_TEMPL' + r'\((\w+),\s*(\w+),\s*(\w+)\)', line) + if setpoint_match: + setpoint_templ = line + line = line[setpoint_match.span()[1]:] + (func, line_count) = find_func_bounds(clock_input, line, line_count) + setpoint_templ += func + for (node_id, state_cnt) in node_setpoint_counts.items(): + # Generate a function for each node setpoint + for state_idx in range(state_cnt): + func = (setpoint_templ + .replace(setpoint_match.group(0), + f"int Z_CLOCK_MGMT_SETPOINT_FUNC_NAME({node_id}, " + f"{state_idx})(void)") + .replace(setpoint_match.group(1), node_id) + .replace(setpoint_match.group(2), str(state_idx))) + output_file.write(func) + elif subsys_match: + subsys_templ = line + line = line[subsys_match.span()[1]:] + (func, line_count) = find_func_bounds(clock_input, line, line_count) + subsys_templ += func + # Generate a function for each node clock subsys + for (node_id, indices) in node_clock_indices.items(): + for subsys_idx in indices: + func = (subsys_templ + .replace(subsys_match.group(0), + f"int Z_CLOCK_MGMT_SUBSYS_FUNC_NAME({node_id}, clocks," + f"{subsys_idx})(void)") + .replace(subsys_match.group(1), node_id) + .replace(subsys_match.group(2), 'clocks') + .replace(subsys_match.group(3), str(subsys_idx))) + output_file.write(func) + else: + output_file.write(line) + line = clock_input.readline() + line_count += 1 + + # Close output file, to clean up + output_file.close() + +if __name__ == "__main__": + main() diff --git a/scripts/dts/gen_defines.py b/scripts/dts/gen_defines.py index b23a4cb76fe2b..d0ab5743b3c1f 100755 --- a/scripts/dts/gen_defines.py +++ b/scripts/dts/gen_defines.py @@ -354,6 +354,10 @@ def write_special_props(node): write_fixed_partitions(node) write_gpio_hogs(node) + # Macros special to Zephyr's clock support implementation + write_clocks(node) + + def write_ranges(node): # ranges property: edtlib knows the right #address-cells and # #size-cells of parent and child, and can therefore pack the @@ -618,6 +622,77 @@ def write_gpio_hogs(node): for macro, val in macro2val.items(): out_dt_define(macro, val) +def write_clocks(node): + """ + Generate clock specific definitions. Given the following + devicetree fragment: + + &clock-controller { + clk_div: clock-div { + clock-id = "VND_CLOCK_DIV"; + }; + clk_src: clock-source { + clock-id = "VND_CLOCK_SOURCE"; + }; + } + + soc { + vnd-device { + clock-state-0 = <&clk_div 1 &clk_src 1000>; + clock-state-1 = <&clk_src 500 &clk_div 500>; + }; + }; + + The following macros will be generated: + + #define DT_N_S_soc_S_vnd_device_CLOCK_STATE_NUM 2 + + #define DT_N_S_soc_S_vnd_device_CLOCK_STATE_0_VND_CLOCK_DIV_EXISTS 1 + #define DT_N_S_soc_S_vnd_device_CLOCK_STATE_0_VND_CLOCK_DIV_IDX 0 + #define DT_N_S_soc_S_vnd_device_CLOCK_STATE_0_VND_CLOCK_SOURCE_EXISTS 1 + #define DT_N_S_soc_S_vnd_device_CLOCK_STATE_0_VND_CLOCK_SOURCE_IDX 1 + + #define DT_N_S_soc_S_vnd_device_CLOCK_STATE_1_VND_CLOCK_SOURCE_EXISTS 1 + #define DT_N_S_soc_S_vnd_device_CLOCK_STATE_1_VND_CLOCK_SOURCE_IDX 0 + #define DT_N_S_soc_S_vnd_device_CLOCK_STATE_1_VND_CLOCK_DIV_EXISTS 1 + #define DT_N_S_soc_S_vnd_device_CLOCK_STATE_1_VND_CLOCK_DIV_IDX 1 + + These macros are required to enable the C code supporting clock + control to easily check if a clock-state-n property contains + a phandle with a given clock-id, and extract the index of that phandle + if it is present. + """ + + out_comment("Clock control (clock-state-) properties:") + + # Find clock-state- properties + clock_state_props = [prop for name, prop in node.props.items() + if re.match("clock-state-[0-9]+", name)] + clock_state_props.sort(key=lambda prop: prop.name) + + # Check indices + for i, prop in enumerate(clock_state_props): + if prop.name != "clock-state-" + str(i): + sys.exit(f"missing 'clock-state-{i}' property on {node!r} " + "- indices should be contiguous and start from zero") + + # Write the number of CLOCK_STATE properties + out_dt_define(f"{node.z_path_id}_CLOCK_STATE_NUM", len(clock_state_props)) + if len(clock_state_props) == 0: + return + + # Node has clock-state properties, write remaining definitions we need + for state_idx, clock_state in enumerate(clock_state_props): + for ph_idx, clock_node_ph in enumerate(clock_state.val): + if not 'clock-id' in clock_node_ph.controller.props: + sys.exit(f"Node {clock_node_ph.controller!r} does not have a " + "clock-id property") + # Read the clock-id property for the controller of this phandle + clock_id = clock_node_ph.controller.props['clock-id'].val + # Write out required definitions + out_dt_define(f"{node.z_path_id}_CLOCK_STATE_{state_idx}_{clock_id}_EXISTS", 1) + out_dt_define(f"{node.z_path_id}_CLOCK_STATE_{state_idx}_{clock_id}_IDX", ph_idx) + def write_vanilla_props(node): # Writes macros for any and all properties defined in the # "properties" section of the binding for the node. @@ -991,6 +1066,27 @@ def write_global_macros(edt): out_define( f"DT_COMPAT_{str2ident(compat)}_BUS_{str2ident(bus)}", 1) + out_comment('Macros to iterate over enabled clock outputs\n') + + + clock_ids = [] + used_clock_ids = set() + for node in edt.nodes: + if "clock-id" in node.props: + # Record the clock ID + clock_ids.append(node.props["clock-id"].val) + if "clocks" in node.props: + if node.status == "okay": + # Check if clock node has clock-id property + for clock_ph in node.props["clocks"].val: + if "clock-id" in clock_ph.controller.props: + used_clock_ids.add(clock_ph.controller.props["clock-id"].val) + out_dt_define("FOREACH_CLOCK_ID(fn)", + " ".join(f"fn({clock_id})" for clock_id in clock_ids)) + for clock_id in used_clock_ids: + out_dt_define(f"CLOCK_ID_{clock_id}_USED", 1) + + def str2ident(s): # Converts 's' to a form suitable for (part of) an identifier diff --git a/soc/nxp/lpc/lpc55xxx/lpc55sxx_clocks.h b/soc/nxp/lpc/lpc55xxx/lpc55sxx_clocks.h new file mode 100644 index 0000000000000..f8e70983104ed --- /dev/null +++ b/soc/nxp/lpc/lpc55xxx/lpc55sxx_clocks.h @@ -0,0 +1,152 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define NXP_CLK_TYPE_SHIFT 16 +#define NXP_CLK_TYPE_NO_CLOCK (0 << NXP_CLK_TYPE_SHIFT) + +/* NO_CLOCK clocks */ +#define NXP_CLOCK_NO_CLOCK (NXP_CLK_TYPE_NO_CLOCK | 0) + +#define NXP_CLK_TYPE_CLOCK_SOURCE (1 << NXP_CLK_TYPE_SHIFT) + +/* CLOCK_SOURCE clocks */ +#define NXP_CLOCK_FRO_12M (NXP_CLK_TYPE_CLOCK_SOURCE | 0) +#define NXP_CLOCK_XTAL32M (NXP_CLK_TYPE_CLOCK_SOURCE | 1) +#define NXP_CLOCK_FRO_1M (NXP_CLK_TYPE_CLOCK_SOURCE | 2) +#define NXP_CLOCK_FRO_HF (NXP_CLK_TYPE_CLOCK_SOURCE | 3) +#define NXP_CLOCK_FRO_32K (NXP_CLK_TYPE_CLOCK_SOURCE | 4) +#define NXP_CLOCK_XTAL32K (NXP_CLK_TYPE_CLOCK_SOURCE | 5) +#define NXP_CLOCK_MCLK_IN (NXP_CLK_TYPE_CLOCK_SOURCE | 6) +#define NXP_CLOCK_PLU_CLKIN (NXP_CLK_TYPE_CLOCK_SOURCE | 7) + +#define NXP_CLK_TYPE_CLOCK_ENABLE (2 << NXP_CLK_TYPE_SHIFT) + +/* CLOCK_ENABLE clocks */ +#define NXP_CLOCK_PLUGLITCH12MHZCLK (NXP_CLK_TYPE_CLOCK_ENABLE | 0) +#define NXP_CLOCK_CLK_IN_EN (NXP_CLK_TYPE_CLOCK_ENABLE | 1) +#define NXP_CLOCK_CLK_USB_EN (NXP_CLK_TYPE_CLOCK_ENABLE | 2) +#define NXP_CLOCK_UTICKCLK (NXP_CLK_TYPE_CLOCK_ENABLE | 3) +#define NXP_CLOCK_PLUGLITCH1MHZCLK (NXP_CLK_TYPE_CLOCK_ENABLE | 4) +#define NXP_CLOCK_OSTIMER32KHZCLK (NXP_CLK_TYPE_CLOCK_ENABLE | 5) +#define NXP_CLOCK_RTC_1HZ_CLK (NXP_CLK_TYPE_CLOCK_ENABLE | 6) +#define NXP_CLOCK_RTC_1KHZ_CLK (NXP_CLK_TYPE_CLOCK_ENABLE | 7) + +#define NXP_CLK_TYPE_TOP_CLOCK_OUTPUT (3 << NXP_CLK_TYPE_SHIFT) + +/* TOP_CLOCK_OUTPUT clocks */ +#define NXP_CLOCK_PLU_GLITCH_12MHZ_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 0) +#define NXP_CLOCK_USB1_PHY_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 1) +#define NXP_CLOCK_WDT_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 2) +#define NXP_CLOCK_UTICK_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 3) +#define NXP_CLOCK_PLU_GLITCH_1MHZ_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 4) +#define NXP_CLOCK_PLUCLKIN_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 5) +#define NXP_CLOCK_OSTIMER32KHZ_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 6) +#define NXP_CLOCK_OSC32KHZ_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 7) +#define NXP_CLOCK_RTC1HZ_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 8) +#define NXP_CLOCK_RTC1KHZ_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 9) +#define NXP_CLOCK_SYSTEM_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 10) +#define NXP_CLOCK_TRACE_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 11) +#define NXP_CLOCK_SYSTICK0_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 12) +#define NXP_CLOCK_SYSTICK1_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 13) +#define NXP_CLOCK_ASYNCADC_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 14) +#define NXP_CLOCK_USB0_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 15) +#define NXP_CLOCK_MCLK_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 16) +#define NXP_CLOCK_SCT_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 17) +#define NXP_CLOCK_CLKOUT_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 18) +#define NXP_CLOCK_SDIO_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 19) +#define NXP_CLOCK_CTIMER0_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 20) +#define NXP_CLOCK_CTIMER1_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 21) +#define NXP_CLOCK_CTIMER2_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 22) +#define NXP_CLOCK_CTIMER3_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 23) +#define NXP_CLOCK_CTIMER4_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 24) +#define NXP_CLOCK_FXCOM0_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 25) +#define NXP_CLOCK_FXCOM1_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 26) +#define NXP_CLOCK_FXCOM2_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 27) +#define NXP_CLOCK_FXCOM3_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 28) +#define NXP_CLOCK_FXCOM4_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 29) +#define NXP_CLOCK_FXCOM5_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 30) +#define NXP_CLOCK_FXCOM6_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 31) +#define NXP_CLOCK_FXCOM7_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 32) +#define NXP_CLOCK_HSLSPI_CLOCK (NXP_CLK_TYPE_TOP_CLOCK_OUTPUT | 33) + +#define NXP_CLK_TYPE_PRESCALER (4 << NXP_CLK_TYPE_SHIFT) + +/* PRESCALER clocks */ +#define NXP_CLOCK_WDTCLKDIV (NXP_CLK_TYPE_PRESCALER | 0) +#define NXP_CLOCK_FROHFDIV (NXP_CLK_TYPE_PRESCALER | 1) +#define NXP_CLOCK_RTCCLK1HZDIV (NXP_CLK_TYPE_PRESCALER | 2) +#define NXP_CLOCK_RTCCLK1KHZDIV (NXP_CLK_TYPE_PRESCALER | 3) +#define NXP_CLOCK_PLL0_PDEC (NXP_CLK_TYPE_PRESCALER | 4) +#define NXP_CLOCK_PLL0DIV (NXP_CLK_TYPE_PRESCALER | 5) +#define NXP_CLOCK_PLL1_PDEC (NXP_CLK_TYPE_PRESCALER | 6) +#define NXP_CLOCK_TRACECLKDIV (NXP_CLK_TYPE_PRESCALER | 7) +#define NXP_CLOCK_SYSTICKCLKDIV0 (NXP_CLK_TYPE_PRESCALER | 8) +#define NXP_CLOCK_SYSTICKCLKDIV1 (NXP_CLK_TYPE_PRESCALER | 9) +#define NXP_CLOCK_AHBCLKDIV (NXP_CLK_TYPE_PRESCALER | 10) +#define NXP_CLOCK_ADCCLKDIV (NXP_CLK_TYPE_PRESCALER | 11) +#define NXP_CLOCK_USB0CLKDIV (NXP_CLK_TYPE_PRESCALER | 12) +#define NXP_CLOCK_MCLKDIV (NXP_CLK_TYPE_PRESCALER | 13) +#define NXP_CLOCK_SCTCLKDIV (NXP_CLK_TYPE_PRESCALER | 14) +#define NXP_CLOCK_CLKOUTDIV (NXP_CLK_TYPE_PRESCALER | 15) +#define NXP_CLOCK_SDIOCLKDIV (NXP_CLK_TYPE_PRESCALER | 16) +#define NXP_CLOCK_FRGCTRL0_MUL (NXP_CLK_TYPE_PRESCALER | 17) +#define NXP_CLOCK_FRGCTRL0_DIV (NXP_CLK_TYPE_PRESCALER | 18) +#define NXP_CLOCK_FRGCTRL1_MUL (NXP_CLK_TYPE_PRESCALER | 19) +#define NXP_CLOCK_FRGCTRL1_DIV (NXP_CLK_TYPE_PRESCALER | 20) +#define NXP_CLOCK_FRGCTRL2_MUL (NXP_CLK_TYPE_PRESCALER | 21) +#define NXP_CLOCK_FRGCTRL2_DIV (NXP_CLK_TYPE_PRESCALER | 22) +#define NXP_CLOCK_FRGCTRL3_MUL (NXP_CLK_TYPE_PRESCALER | 23) +#define NXP_CLOCK_FRGCTRL3_DIV (NXP_CLK_TYPE_PRESCALER | 24) +#define NXP_CLOCK_FRGCTRL4_MUL (NXP_CLK_TYPE_PRESCALER | 25) +#define NXP_CLOCK_FRGCTRL4_DIV (NXP_CLK_TYPE_PRESCALER | 26) +#define NXP_CLOCK_FRGCTRL5_MUL (NXP_CLK_TYPE_PRESCALER | 27) +#define NXP_CLOCK_FRGCTRL5_DIV (NXP_CLK_TYPE_PRESCALER | 28) +#define NXP_CLOCK_FRGCTRL6_MUL (NXP_CLK_TYPE_PRESCALER | 29) +#define NXP_CLOCK_FRGCTRL6_DIV (NXP_CLK_TYPE_PRESCALER | 30) +#define NXP_CLOCK_FRGCTRL7_MUL (NXP_CLK_TYPE_PRESCALER | 31) +#define NXP_CLOCK_FRGCTRL7_DIV (NXP_CLK_TYPE_PRESCALER | 32) + +#define NXP_CLK_TYPE_CLOCK_SELECT (5 << NXP_CLK_TYPE_SHIFT) + +/* CLOCK_SELECT clocks */ +#define NXP_CLOCK_MAINCLKSELA (NXP_CLK_TYPE_CLOCK_SELECT | 0) +#define NXP_CLOCK_RTCOSC32KSEL (NXP_CLK_TYPE_CLOCK_SELECT | 1) +#define NXP_CLOCK_PLL0CLKSEL (NXP_CLK_TYPE_CLOCK_SELECT | 2) +#define NXP_CLOCK_PLL0_DIRECTO (NXP_CLK_TYPE_CLOCK_SELECT | 3) +#define NXP_CLOCK_PLL0_BYPASS (NXP_CLK_TYPE_CLOCK_SELECT | 4) +#define NXP_CLOCK_PLL1CLKSEL (NXP_CLK_TYPE_CLOCK_SELECT | 5) +#define NXP_CLOCK_PLL1_DIRECTO (NXP_CLK_TYPE_CLOCK_SELECT | 6) +#define NXP_CLOCK_PLL1_BYPASS (NXP_CLK_TYPE_CLOCK_SELECT | 7) +#define NXP_CLOCK_MAINCLKSELB (NXP_CLK_TYPE_CLOCK_SELECT | 8) +#define NXP_CLOCK_TRACECLKSEL (NXP_CLK_TYPE_CLOCK_SELECT | 9) +#define NXP_CLOCK_SYSTICKCLKSEL0 (NXP_CLK_TYPE_CLOCK_SELECT | 10) +#define NXP_CLOCK_SYSTICKCLKSEL1 (NXP_CLK_TYPE_CLOCK_SELECT | 11) +#define NXP_CLOCK_ADCCLKSEL (NXP_CLK_TYPE_CLOCK_SELECT | 12) +#define NXP_CLOCK_USB0CLKSEL (NXP_CLK_TYPE_CLOCK_SELECT | 13) +#define NXP_CLOCK_MCLKCLKSEL (NXP_CLK_TYPE_CLOCK_SELECT | 14) +#define NXP_CLOCK_SCTCLKSEL (NXP_CLK_TYPE_CLOCK_SELECT | 15) +#define NXP_CLOCK_CLKOUTSEL (NXP_CLK_TYPE_CLOCK_SELECT | 16) +#define NXP_CLOCK_SDIOCLKSEL (NXP_CLK_TYPE_CLOCK_SELECT | 17) +#define NXP_CLOCK_CTIMERCLKSEL0 (NXP_CLK_TYPE_CLOCK_SELECT | 18) +#define NXP_CLOCK_CTIMERCLKSEL1 (NXP_CLK_TYPE_CLOCK_SELECT | 19) +#define NXP_CLOCK_CTIMERCLKSEL2 (NXP_CLK_TYPE_CLOCK_SELECT | 20) +#define NXP_CLOCK_CTIMERCLKSEL3 (NXP_CLK_TYPE_CLOCK_SELECT | 21) +#define NXP_CLOCK_CTIMERCLKSEL4 (NXP_CLK_TYPE_CLOCK_SELECT | 22) +#define NXP_CLOCK_FCCLKSEL0 (NXP_CLK_TYPE_CLOCK_SELECT | 23) +#define NXP_CLOCK_FCCLKSEL1 (NXP_CLK_TYPE_CLOCK_SELECT | 24) +#define NXP_CLOCK_FCCLKSEL2 (NXP_CLK_TYPE_CLOCK_SELECT | 25) +#define NXP_CLOCK_FCCLKSEL3 (NXP_CLK_TYPE_CLOCK_SELECT | 26) +#define NXP_CLOCK_FCCLKSEL4 (NXP_CLK_TYPE_CLOCK_SELECT | 27) +#define NXP_CLOCK_FCCLKSEL5 (NXP_CLK_TYPE_CLOCK_SELECT | 28) +#define NXP_CLOCK_FCCLKSEL6 (NXP_CLK_TYPE_CLOCK_SELECT | 29) +#define NXP_CLOCK_FCCLKSEL7 (NXP_CLK_TYPE_CLOCK_SELECT | 30) +#define NXP_CLOCK_HSLSPICLKSEL (NXP_CLK_TYPE_CLOCK_SELECT | 31) + +#define NXP_CLK_TYPE_PLL (6 << NXP_CLK_TYPE_SHIFT) + +/* PLL clocks */ +#define NXP_CLOCK_PLL0 (NXP_CLK_TYPE_PLL | 0) +#define NXP_CLOCK_PLL1 (NXP_CLK_TYPE_PLL | 1)