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