From 0faa5e474c04c903f654261b85c33bfa03c0755a Mon Sep 17 00:00:00 2001 From: Daniel DeGrasse Date: Tue, 1 Oct 2024 16:43:18 -0500 Subject: [PATCH] doc: hardware: add documentation for clock management subsystem Add documentation for clock management subsystem. This documentation includes descriptions of the clock management consumer API, as well as implementation guidelines for clock drivers themselves Signed-off-by: Daniel DeGrasse --- .../clock_management/images/apply-state.svg | 4 + .../images/clock-callbacks.svg | 4 + .../clock_management/images/rate-request.svg | 4 + .../clock_management/images/read-rate.svg | 4 + .../images/runtime-clock-resolution.svg | 4 + doc/hardware/clock_management/index.rst | 795 ++++++++++++++++++ doc/hardware/index.rst | 1 + 7 files changed, 816 insertions(+) create mode 100644 doc/hardware/clock_management/images/apply-state.svg create mode 100644 doc/hardware/clock_management/images/clock-callbacks.svg create mode 100644 doc/hardware/clock_management/images/rate-request.svg create mode 100644 doc/hardware/clock_management/images/read-rate.svg create mode 100644 doc/hardware/clock_management/images/runtime-clock-resolution.svg create mode 100644 doc/hardware/clock_management/index.rst diff --git a/doc/hardware/clock_management/images/apply-state.svg b/doc/hardware/clock_management/images/apply-state.svg new file mode 100644 index 000000000000000..22d5779627ec813 --- /dev/null +++ b/doc/hardware/clock_management/images/apply-state.svg @@ -0,0 +1,4 @@ + + + +clock_management_apply_state()uart_devuart_outputuart_divuart_muxApplying a Clock StateVendor Specific Clock DriversClock Management SubsystemClock Consumersclock_configure()clock_configure() \ No newline at end of file diff --git a/doc/hardware/clock_management/images/clock-callbacks.svg b/doc/hardware/clock_management/images/clock-callbacks.svg new file mode 100644 index 000000000000000..5126cec2cd3f440 --- /dev/null +++ b/doc/hardware/clock_management/images/clock-callbacks.svg @@ -0,0 +1,4 @@ + + + +uart_devConsumer callbackuart_outputclock_notify()uart_divclock_notify()uart_muxIssuing Clock Callbacksuart_dev2Consumer callbackclock_configure()Vendor Specific Clock DriversClock Management SubsystemClock Consumers \ No newline at end of file diff --git a/doc/hardware/clock_management/images/rate-request.svg b/doc/hardware/clock_management/images/rate-request.svg new file mode 100644 index 000000000000000..c870183c3edcb4d --- /dev/null +++ b/doc/hardware/clock_management/images/rate-request.svg @@ -0,0 +1,4 @@ + + + +clock_management_req_rate()uart_devclock_round_rate()uart_outputclock_round_rate()uart_divclock_round_rate()clock_round_rate()uart_muxexternal_oscfixed_sourceQuery Best RateConfigure Sourcesclock_set_rate()clock_set_rate()uart_divclock_set_rate()uart_muxexternal_oscfixed_sourceGeneric Clock Framework DriversVendor Specific Clock DriversClock Management SubsystemClock Consumers \ No newline at end of file diff --git a/doc/hardware/clock_management/images/read-rate.svg b/doc/hardware/clock_management/images/read-rate.svg new file mode 100644 index 000000000000000..7f00373d7b6af8b --- /dev/null +++ b/doc/hardware/clock_management/images/read-rate.svg @@ -0,0 +1,4 @@ + + + +clock_management_get_rate()uart_devclock_get_rate()uart_outputclock_get_rate()uart_divclock_get_rate()uart_muxexternal_oscfixed_sourceReading Clock RatesGeneric Clock Framework DriversVendor Specific Clock DriversClock Management SubsystemClock Consumers \ No newline at end of file diff --git a/doc/hardware/clock_management/images/runtime-clock-resolution.svg b/doc/hardware/clock_management/images/runtime-clock-resolution.svg new file mode 100644 index 000000000000000..545eadb173de921 --- /dev/null +++ b/doc/hardware/clock_management/images/runtime-clock-resolution.svg @@ -0,0 +1,4 @@ + + + +
uart_output
uart_div
clock_mangement_req_rate()
uart2_dev
clock_round_rate()
uart2_output
clock_notify()
returns -ENOTSUP
Clock Consumers
Clock Management Subsystem
Vendor Specific Clock Drivers
Request New Rate
Consumer Rejects New Rate
\ No newline at end of file diff --git a/doc/hardware/clock_management/index.rst b/doc/hardware/clock_management/index.rst new file mode 100644 index 000000000000000..c21558e9d43bd24 --- /dev/null +++ b/doc/hardware/clock_management/index.rst @@ -0,0 +1,795 @@ +.. _clock-management-guide: + +Clock Management +################ + +This is a high level overview of clock management in Zephyr. See +:ref:`clock_management_api` for API reference material. + +Introduction +************ + +Clock topology within a system typically consists of a mixture of clock +generators, clock dividers, clock gates and clock selection blocks. 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. + +Clock Management versus Clock Drivers +===================================== + +.. note:: + + This section describes the clock management subsystem API and usage. For + documentation on how to implement clock producer drivers, see + :ref:`clock-producers`. + +The clock management subsystem is split into two portions: the consumer facing +clock management API, and the internal clock driver API. The clock driver API is +used by clock producers to query and set rates of their parent clocks, as well +as receive reconfiguration notifications when the state of the clock tree +changes. Each clock producer must implement the clock driver API, but clock +consumers should only interact with clocks using the clock management API. + +This approach is required because clock consumers should not have knowledge of +how their underlying clock states are applied or defined, as the data is often +hardware specific. Consumers may apply states directly, or request a frequency +range, which can then be satisfied by one of the defined states. For details on +the operation of the clock subsystem, see :ref:`clock_subsystem_operation`. + +Accessing Clock Outputs +*********************** + +In order to interact with a clock output, clock consumers must define a clock +output device. For devices defined in devicetree, using clocks defined within +their ``clock-outputs`` property, :c:macro:`CLOCK_MANAGEMENT_DT_DEFINE_OUTPUT` or +similar may be used. For software applications consuming a clock, +:c:macro:`CLOCK_MANAGEMENT_DEFINE_OUTPUT` should be used. + +Clock consumers may then initialize their clock output device using +:c:macro:`CLOCK_MANAGEMENT_DT_GET_OUTPUT` or similar, for consumer devices defined in +devicetree, or :c:macro:`CLOCK_MANAGEMENT_GET_OUTPUT` for software applications +consuming a clock. + +Reading Clocks +************** + +Once a diver has defined a clock output and initialized it, the clock rate can +be read using :c:func:`clock_management_get_rate`. This will return the frequency of +the clock output in Hz, or a negative error value if the clock could not be +read. + +Consumers can also monitor a clock output rate. To do so, the application must +first enable :kconfig:option:`CONFIG_CLOCK_MANAGEMENT_RUNTIME`. The application may +then set a callback of type :c:type:`clock_management_callback_handler_t` using +:c:func:`clock_management_set_callback`. One callback is supported per consumer. + +The clock management subsystem will issue a callback directly before applying a +new clock rate, and directly after the new rate is applied. See +:c:struct:`clock_management_event` for more details. + +Setting Clock States +******************** + +Each clock output defines a set of states. Clock consumers can set these states +directly, using :c:func:`clock_management_apply_state`. States are described in +devicetree, and are opaque to the driver/application code consuming the clock. + +Each clock consumer described in devicetree can set named clock states for each +clock output. These states are described by the ``clock-state-n`` properties +present on each consumer. The consumer can access states using macros like +:c:macro:`CLOCK_MANAGEMENT_DT_GET_STATE` + +Devicetree Representation +************************* + +Devicetree is used to define all system specific data for clock management. The +SOC (and any external clock producers) will define clock producers within the +devicetree. Then, the devicetree for clock consumers may reference the clock +producer nodes to configure the clock tree or access clock outputs. + +The devicetree definition for clock producers will be specific to the system, +but may look similar to the following: + +.. code-block:: devicetree + + clock_source: clock-source { + compatible = "fixed-clock"; + clock-frequency = ; + #clock-cells = <0>; + + clock_div: clock-div@50000000 { + compatible = "vnd,clock-div"; + #clock-cells = <1>; + reg = <0x5000000>; + + clock_output: clock-output { + compatible = "clock-output"; + #clock-cells = <1>; + }; + }; + }; + +At the board level, applications will define clock states for each clock output +node, which may either directly configure parent clock nodes to realize a +frequency, or simply define a frequency to request from the parent clock at +runtime (which will only function if +:kconfig:option:`CONFIG_CLOCK_MANAGEMENT_SET_RATE` is enabled). + +.. code-block:: devicetree + + &clock_output { + clock_output_state_default: clock-output-state-default { + compatible = "clock-state"; + clocks = <&clock_div 1>; + clock-frequency = + }; + clock_output_state_sleep: clock-output-state-sleep { + compatible = "clock-state"; + clocks = <&clock_div 5>; + clock-frequency = + }; + clock_output_state_runtime: clock-output-state-runtime { + compatible = "clock-state"; + /* Will issue runtime frequency request */ + clock-frequency = ; + }; + }; + +Note that the specifier cells for each clock node within a state 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. + +Clock consumers will then reference the clock output nodes and their states in +order to define and access clock producers, and apply states. A peripheral clock +consumer's devicetree might look like so: + +.. code-block:: devicetree + + periph0: periph@0 { + compatible = "vnd,mydev"; + /* Clock outputs */ + clock-outputs= <&clock_output>; + clock-output-names = "default"; + /* Default clock state */ + clock-state-0 = <&clock_output_state_default>; + /* Sleep state */ + clock-state-1 = <&clock_output_state_sleep>; + clock-state-names = "default", "sleep"; + }; + +Requesting Clock Rates +====================== + +In some applications, the user may not want to directly configure clock nodes +within their devicetree. The clock management subsystem allows applications to +request a clock rate directly as well, by using :c:func:`clock_management_req_rate`. +If any states satisfy the frequency range request, the first state that fits the +provided constraints will be applied. Otherwise if +:kconfig:option:`CONFIG_CLOCK_MANAGEMENT_SET_RATE` is set, the clock management +subsystem will perform runtime calculations to apply a rate within the requested +range. If runtime rate calculation support is disabled, the request will fail if +no defined states satisfy it. + +No guarantees are made on how accurate a resulting rate will be versus the +requested value. + +Gating Unused Clocks +==================== + +When :kconfig:option:`CONFIG_CLOCK_MANAGEMENT_RUNTIME` is enabled, it is possible to +gate unused clocks within the system, by calling +:c:func:`clock_management_disable_unused`. + +Locking Clock States and Requests +================================= + +When :kconfig:option:`CONFIG_CLOCK_MANAGEMENT_RUNTIME` is enabled, requests issued +via :c:func:`clock_management_req_rate` to the same clock by different consumers will +be aggregated to form a "combined" request for that clock. This means that a +request may be denied if it is incompatible with the existing set of aggregated +clock requests. Clock states do not place a request on the clock they configure +by default- if a clock state should "lock" the clock to prevent the frequency +changing, it should be defined with the ``locking-state`` boolean property. +This can be useful for critical system clocks, such as the core clock. + +Generally when multiple clocks are expected to be reconfigured at runtime, +:kconfig:option:`CONFIG_CLOCK_MANAGEMENT_RUNTIME` should be enabled to avoid +unexpected rate changes for consumers. Otherwise clock states should be defined +in such a way that each consumer can reconfigure itself without affecting other +clock consumers in the system. + + +Driver Usage +************ + +In order to use the clock management subsystem, a driver must define and +initialize a :c:struct:`clock_output` for the clock it wishes to interact with. +The clock output structure can be defined with +:c:macro:`CLOCK_MANAGEMENT_DT_DEFINE_OUTPUT`, and then accessed with +:c:macro:`CLOCK_MANAGEMENT_DT_GET_OUTPUT`. Note that both these macros also have +versions that allow the driver to access an output by name or index, if +multiple clocks are present within the ``clock-outputs`` property for the +device. + +In order to configure a clock, the driver may either request a supported +clock rate range via :c:func:`clock_management_req_rate`, or apply a clock state +directly via :c:func:`clock_management_apply_state`. For most applications, +:c:func:`clock_management_apply_state` is recommended, as this allows the application +to customize the clock properties that are set using devicetree. +:c:func:`clock_management_req_rate` should only be used in cases where the driver +knows the frequency range it should use, and cannot accept a frequency outside +of that range. + +Drivers can define states of type :c:type:`clock_management_state_t` using +:c:macro:`CLOCK_MANAGEMENT_DT_GET_STATE`, or the name/index based versions of this +macro. + +For example, if a peripheral devicetree was defined like so: + +.. code-block:: devicetree + + periph0: periph@0 { + compatible = "vnd,mydev"; + /* Clock outputs */ + clock-outputs= <&periph_hs_clock &periph_lp_clock>; + clock-output-names = "high-speed", "low-power"; + /* Default clock state */ + clock-state-0 = <&hs_clock_default &lp_clock_default>; + /* Sleep state */ + clock-state-1 = <&hs_clock_sleep &lp_clock_sleep>; + clock-state-names = "default", "sleep"; + }; + +The following C code could be used to apply the default state for the +``high-speed`` clocks, and sleep state for the ``low-power`` clock: + +.. code-block:: c + + /* A driver for the "vnd,mydev" compatible device */ + #define DT_DRV_COMPAT vnd_mydev + + ... + #include + ... + + struct mydev_config { + ... + /* Reference to high-speed clock */ + const struct clock_output *hs_clk; + /* Reference to low-power clock */ + const struct clock_output *lp_clk; + /* high-speed clock default state */ + const clock_management_state_t hs_default_state; + /* low-power sleep state */ + const clock_management_state_t lp_sleep_state; + ... + }; + + ... + + int hs_clock_cb(const struct clock_management_event *ev, const void *data) + { + const struct device *dev = (const struct device *)data; + + if (ev->new_rate > HS_MAX_CLK_RATE) { + /* Can't support this new rate */ + return -ENOTSUP; + } + if (ev->type == CLOCK_MANAGEMENT_POST_RATE_CHANGE) { + /* Handle clock rate change ... */ + } + ... + return 0; + } + + static int mydev_init(const struct device *dev) + { + const struct mydev_config *config = dev->config; + int hs_clk_rate, lp_clk_rate; + ... + /* Set high-speed clock to default state */ + hs_clock_rate = clock_management_apply_state(config->hs_clk, config->hs_default_state); + if (hs_clock_rate < 0) { + return hs_clock_rate; + } + /* Register for a callback if high-speed clock changes rate */ + ret = clock_management_set_callback(config->hs_clk, hs_clock_cb, dev); + if (ret < 0) { + return ret; + } + /* Set low-speed clock to sleep state */ + lp_clock_rate = clock_management_apply_state(config->lp_clk, config->lp_sleep_state); + if (lp_clock_rate < 0) { + return lp_clock_rate; + } + ... + } + + #define MYDEV_DEFINE(i) \ + /* Define clock outputs for high-speed and low-power clocks */ \ + CLOCK_MANAGEMENT_DT_INST_DEFINE_OUTPUT_BY_NAME(i, high_speed); \ + CLOCK_MANAGEMENT_DT_INST_DEFINE_OUTPUT_BY_NAME(i, low_power); \ + ... \ + static const struct mydev_config mydev_config_##i = { \ + ... \ + /* Initialize clock outputs */ \ + .hs_clk = CLOCK_MANAGEMENT_DT_INST_GET_OUTPUT_BY_NAME(i, high_speed),\ + .lp_clk = CLOCK_MANAGEMENT_DT_INST_GET_OUTPUT_BY_NAME(i, low_power),\ + /* Read states for high-speed and low-power */ \ + .hs_default_state = CLOCK_MANAGEMENT_DT_INST_GET_STATE(i, high_speed,\ + default), \ + .lp_sleep_state = CLOCK_MANAGEMENT_DT_INST_GET_STATE(i, low_power, \ + sleep), \ + ... \ + }; \ + 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) + +.. _clock_management_api: + +Clock Management API +******************** + +.. doxygengroup:: clock_management_interface + +.. _clock_management_dt_api: + +Devicetree Clock Management Helpers +=================================== + +.. doxygengroup:: devicetree-clock-management + + +.. _clock-producers: + +Clock Producers +*************** + +This is a high level overview of clock producers in Zephyr. See +:ref:`clock_driver_api` for API reference material. + +Introduction +============ + +Although consumers interact with the clock management subsystem via the +:ref:`clock_management_api`, producers must implement the clock driver API. This +API allows producers to read and set their parent clocks, as well as receive +reconfiguration notifications if their parent changes rate. + +Clock Driver Implementation +=========================== + +Each node within a clock tree should be implemented within a clock driver. Clock +nodes are typically defined as elements in the clock tree. For example, a +multiplexer, divider, and PLL would all be considered independent nodes. Each +node should implement the :ref:`clock_driver_api`. + +Clock nodes are represented by :c:struct:`clk` structures. These structures +store clock specific hardware data (which the driver may place in ROM or RAM, +depending on implementation needs), as well as a reference to the clock's API +and a list of the clock's children. For more details on defining and +accessing these structures, see :ref:`clock_model_api`. + +Note that in order to conserve flash, many of the APIs of the clock driver layer +are only enabled when certain Kconfigs are set. A list of these API functions is +given below: + +.. table:: Optional Clock Driver APIs + :align: center + + +-----------------------------------------------------+----------------------------+ + | Kconfig | API Functions | + +-----------------------------------------------------+----------------------------+ + | :kconfig:option:`CONFIG_CLOCK_MANAGEMENT_RUNTIME` | :c:func:`clock_notify` | + +-----------------------------------------------------+----------------------------+ + | :kconfig:option:`CONFIG_CLOCK_MANAGEMENT_SET_RATE` | :c:func:`clock_set_rate`, | + | | :c:func:`clock_round_rate` | + +-----------------------------------------------------+----------------------------+ + +These API functions **must** still be implemented by each clock driver, but they +can should be compiled out when these Kconfig options are not set. + +Clock drivers will **must** hold a reference to their parent clock device, if +one exists. And **must** not reference their child clock devices directly. + +These constraints are required because the clock subsystem determines which clock +devices can be discarded from the build at link time based on which clock devices +are referenced. If a parent clock is not referenced, that clock and any of its +parents would be discarded. However if a child clock is directly referenced, +that child clock would be linked in regardless of if a consumer was actually +using it. + +Clock consumers hold references to the clock output nodes they are using, which +then reference their parent clock producers, which in turn reference their +parents. These reference chains allow the clock management subsystem to only +link in the clocks that the application actually needs. + + +Getting Clock Structures +------------------------ + +A reference to a clock structure can be obtained with :c:macro:`CLOCK_DT_GET`. +Note that as described above, this should only be used to reference the parent +clock(s) of a producer. + +Defining Clock Structures +------------------------- + +Clock structures may be defined with :c:macro:`CLOCK_DT_INST_DEFINE` or +:c:macro:`CLOCK_DT_DEFINE`. Usage of this macro is very similar to the +:c:macro:`DEVICE_DT_DEFINE`. Clocks are defined as :c:struct:`clk` structures +instead of as :c:struct:`device` structures in order to reduce the flash impact +of the framework. + +Root clock structures (a clock without any parents) **must** be defined with +:c:macro:`ROOT_CLOCK_DT_INST_DEFINE` or :c:macro:`ROOT_CLOCK_DT_DEFINE`. This +is needed because the implementation of :c:func:`clock_management_disable_unused` +will call :c:func:`clock_notify` on root clocks only, so if a root clock +is not notified then it and its children will not be able to determine if +they can power off safely. + +See below for a simple example of defining a (non root) clock structure: + +.. code-block:: c + + #define DT_DRV_COMPAT clock_output + + ... + /* API implementations */ + ... + + const struct clock_driver_api clock_output_api = { + ... + }; + + #define CLOCK_OUTPUT_DEFINE(inst) \ + CLOCK_DT_INST_DEFINE(inst, \ + /* Clock data is simply a pointer to the parent */ \ + CLOCK_DT_GET(DT_INST_PARENT(inst)), \ + &clock_output_api); + + DT_INST_FOREACH_STATUS_OKAY(CLOCK_OUTPUT_DEFINE) + +Clock Node Specifier Data +------------------------- + +Clock nodes in devicetree will define a set of specifiers with their DT binding, +which are used to configure the clock directly. When an application references a +clock node with the compatible ``vnd,clock-node``, the clock management +subsystem expects the following macros be defined: + +* ``Z_CLOCK_MANAGEMENT_VND_CLOCK_NODE_DATA_DEFINE``: defines any static structures + needed by this clock node (IE a C structure) + +* ``Z_CLOCK_MANAGEMENT_VND_CLOCK_NODE_DATA_GET``: gets a reference to any static + structure defined by the ``DATA_DEFINE`` macro. This is used to initialize the + ``void *`` passed to :c:func:`clock_configure`, so for many clock nodes this + macro can simply expand to an integer value (which may be used for a register + setting) + +As an example, for the following devicetree: + +.. code-block:: devicetree + + clock_source: clock-source { + compatible = "fixed-clock"; + clock-frequency = <10000000>; + #clock-cells = <0>; + + clock_div: clock-div@50000000 { + compatible = "vnd,clock-div"; + #clock-cells = <1>; + reg = <0x5000000>; + + clock_output: clock-output { + compatible = "clock-output"; + #clock-cells = <0>; + }; + }; + }; + + .... + + &clock_output { + clock_output_state_default: clock-output-state-default { + compatible = "clock-state"; + clocks = <&clock_div 1>; + clock-frequency = + }; + } + + .... + + periph0: periph@0 { + /* Clock outputs */ + clock-outputs= <&clock_output>; + clock-output-names = "default"; + /* Default clock state */ + clock-state-0 = <&clock_output_state_default>; + clock-state-names = "default"; + }; + +The clock subsystem would expect the following macros be defined: + +* ``Z_CLOCK_MANAGEMENT_VND_CLOCK_DIV_DATA_DEFINE`` +* ``Z_CLOCK_MANAGEMENT_VND_CLOCK_DIV_DATA_GET`` + +These macros should be defined within a header file. The header file can then +be added to the list of clock management driver headers to include using the +CMake function ``add_clock_management_header`` or ``add_clock_management_header_ifdef``. + +Output Clock Nodes +------------------ + +Clock trees should define output clock nodes as leaf nodes within their +devicetree. These nodes must have the compatible :dtcompatible:`clock-output`, +and are the nodes which clock consumers will reference. The clock management +framework will handle defining clock drivers for each of these nodes. + +Common Clock Drivers +-------------------- + +For some common clock nodes, generic drivers already exist to simplify vendor +implementations. For a list, see the table below: + + +.. table:: Common Clock Drivers + :align: center + + +-------------------------------------+--------------------------------------------+ + | DT compatible | Use Case | + +-------------------------------------+--------------------------------------------+ + | :dtcompatible:`fixed-clock` | Fixed clock sources that cannot be gated | + +-------------------------------------+--------------------------------------------+ + | :dtcompatible:`clock-source` | Gateable clock sources with a fixed rate | + +-------------------------------------+--------------------------------------------+ + +Implementation Guidelines +------------------------- + +Implementations of each clock driver API will be vendor specific, but some +general guidance on implementing each API is provided below: + +* :c:func:`clock_get_rate` + + * Read parent rate, and calculate rate this node will produce based on node + specific settings. + * Multiplexers will instead read the rate of their active parent. + * Sources will likely return a fixed rate, or 0 if the source is gated. For + fixed sources, see :dtcompatible:`fixed-clock`. + +* :c:func:`clock_configure` + + * Cast the ``void *`` provided in the API call to the data type this clock + driver uses for configuration. + * Calculate the new rate that will be set after this configuration is applied. + * Call :c:func:`clock_children_check_rate` to verify that children can accept + the new rate. If the return value is less than zero, don't change the clock. + * Call :c:func:`clock_children_notify_pre_change` to notify children the + clock is about to change. + * Reconfigure the clock. + * Call :c:func:`clock_children_notify_post_change` to notify children the + clock has just changed. + +* :c:func:`clock_notify` + + * Read the node specific settings to determine the rate this node will + produce, based on the clock management event provided in this call. + * Return an error if this rate cannot be supported by the node. + * Forward the notification of clock reconfiguration to children by calling + :c:func:`clock_notify_children` with the new rate. + * Multiplexers may also return ``-ENOTCONN`` to indicate they are not + using the output of the clock specified by ``parent``. + * If the return code of :c:func:`clock_notify_children` is + :c:macro:`CLK_NO_CHILDREN`, the clock may safely power off its output. + +* :c:func:`clock_round_rate` + + * Determine what rate should be requested from the parent in order + to produce the requested rate. + * Call :c:func:`clock_round_rate` on the parent clock to determine if + the parent can produce the needed rate. + * Calculate the best possible rate this node can produce based on the + parent's best rate. + * Call :c:func:`clock_children_check_rate` to verify that children can accept + the new rate. If the return value is less than zero, propagate this error. + * Multiplexers will typically implement this function by calling + :c:func:`clock_round_rate` on all parents, and returning the best + rate found. + +* :c:func:`clock_set_rate` + + * Similar implementation to :c:func:`clock_round_rate`, but once all + settings needed for a given rate have been applied, actually configure it. + * Call :c:func:`clock_set_rate` on the parent clock to configure the needed + rate. + * Call :c:func:`clock_children_notify_pre_change` to notify children the + clock is about to change. + * Reconfigure the clock. + * Call :c:func:`clock_children_notify_post_change` to notify children the + clock has just changed. + +.. _clock_driver_api: + +Clock Driver API +================ + +.. doxygengroup:: clock_driver_interface + +.. _clock_model_api: + +Clock Model API +=============== + +.. doxygengroup:: clock_model + + +.. _clock_subsystem_operation: + +Clock Management Subsystem Operation +************************************ + +The below section is intended to provide an overview of how the clock management +subsystem operates, given a hypothetical clock tree and clock consumers. For the +purpose of this example, consider a clock tree for a UART clock output, which +sources its clock from a divider. This divider's input is a multiplexer, which +can select between a fixed internal clock or external clock input. Two UART +devices use this clock output as their clock source. A topology like this might +be described in devicetree like so: + +.. code-block:: devicetree + + uart_mux: uart-mux@40001000 { + compatible = "vnd,clock-mux"; + reg = <0x40001000> + #clock-cells = <1>; + input-sources = <&fixed_source &external_osc>; + + uart_div: uart-div@40001200 { + compatible = "vnd,clock-div"; + #clock-cells = <1>; + reg = <0x40001200>; + + uart_output: clock-output { + compatible = "clock-output"; + #clock-cells = <0>; + }; + }; + }; + + + fixed_source: fixed-source { + compatible = "fixed-clock"; + clock-frequency = ; + }; + + external_osc: external-osc { + compatible = "fixed-clock"; + /* User's board can override this + * based on oscillator they use */ + clock-frequency = <0>; + }; + + uart_dev: uart-dev { + compatible = "vnd,uart-dev"; + clock-outputs = <&uart_output>; + clock-output-names = "default"; + }; + + uart_dev2: uart-dev { + compatible = "vnd,uart-dev"; + clock-outputs = <&uart_output>; + clock-output-names = "default"; + }; + +At the board level, a frequency will be defined for the external clock. +Furthermore, states for the UART clock output will be defined, and assigned +to the first UART device: + +.. code-block:: devicetree + + &uart_output { + uart_default: uart-default { + compatible = "clock-state"; + /* Select external source, divide by 4 */ + clocks = <&uart_div 4 &uart_mux 1>; + clock-frequency = ; + }; + }; + + &external_osc { + clock-frequency = ; + }; + + &uart_dev { + clock-state-0 = <&uart_default>; + clock-state-names = "default"; + }; + + +Now, let's consider some examples of how consumers would interact with the +clock management subsystem. + +Reading Clock Rates +=================== + +To read a clock rate, the clock consumer would first call +:c:func:`clock_management_get_rate` on its clock output structure. In turn, the clock +management subsystem would call :c:func:`clock_get_rate` on the parent of the +clock output. The implementation of that driver would call +:c:func:`clock_get_rate` on its parent. This chain of calls would continue until +a root clock was reached. At this point, each clock would necessary calculations +on the parent rate before returning the result. These calls would look like so: + +.. figure:: images/read-rate.svg + +Applying Clock States +===================== + +When a consumer applies a clock state, :c:func:`clock_configure` will be called +on each clock node specified by the states ``clocks`` property with the vendor +specific data given by that node's specifier. These calls would look like so: + +.. figure:: images/apply-state.svg + +Requesting Runtime Rates +======================== + +When requesting a clock rate, the consumer will either apply a pre-defined state +using :c:func:`clock_configure` if a pre-defined state satisfies the clock +request, or runtime rate resolution will be used (if +:kconfig:option:`CONFIG_CLOCK_MANAGEMENT_SET_RATE` is enabled). + +For runtime rate resolution, there are two phases: querying the best clock setup +using :c:func:`clock_round_rate`, and applying it using +:c:func:`clock_set_rate`. During the query phase, clock devices will report the +rate nearest to the requested value they can support. During the application +phase, the clock will actually configure to the requested rate. The call +graph for this process looks like so: + +.. figure:: images/rate-request.svg + +Clock Callbacks +=============== + +When reconfiguring, clock producers should notify their children clocks via +:c:func:`clock_notify_children`, which will call :c:func:`clock_notify` on all +children of the clock. The helpers :c:func:`clock_children_check_rate`, +:c:func:`clock_children_notify_pre_change`, and +:c:func:`clock_children_notify_post_change` are available to check that children +can support a given rate, notify them before changing to a new rate, and notify +then once a new rate is applied respectively. For the case of +:c:func:`clock_configure`, the notify chain might look like so: + +.. figure:: images/clock-callbacks.svg + +Runtime Clock Resolution +======================== + +The clock management subsystem will automatically calculate the combined +frequency constraint imposed on a clock output by all its consumers when +:kconfig:option:`CONFIG_CLOCK_MANAGEMENT_RUNTIME` is enabled. When a parent +clock is attempting to reconfigure and calls +:c:func:`clock_children_check_rate`, the clock output devices will +verify the new frequency fits within their constraints automatically, so +clock consumers do not need to handle this case. For the case below, +assume that ``uart_output`` has already received a request that sets its +frequency constraints. + +.. figure:: images/runtime-clock-resolution.svg + +Note that each clock output starts with no constraints set. A consumer must +make a request to enforce a constraint. A consumer may modify a constraint it +has set by requesting a new constraint, which may be less restrictive than +the original setting. + +If two clock consumers share a clock output node, and both make conflicting +requests to the clock output, the first consumer to make a request will be +given priority, and the second will be rejected. diff --git a/doc/hardware/index.rst b/doc/hardware/index.rst index 72e9cc5fd0561ad..39c396b90f3d1fe 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_management/index.rst emulator/index.rst emulator/bus_emulators.rst peripherals/index.rst