================================
TISCI PM Clock API Documentation
================================

Introduction
============

This document describes the TISCI API set for configuring and controlling the
clocks within the system.

Clock configuration and control APIs
------------------------------------

+------------------+--------------------------------------------+
| TISCI Message ID |             Message Name                   |
+==================+============================================+
| 0x0100           | :ref:`pm_clocks_msg_set_clock`             |
+------------------+--------------------------------------------+
| 0x0101           | :ref:`pm_clocks_msg_get_clock`             |
+------------------+--------------------------------------------+
| 0x0102           | :ref:`pm_clocks_msg_set_clock_parent`      |
+------------------+--------------------------------------------+
| 0x0103           | :ref:`pm_clocks_msg_get_clock_parent`      |
+------------------+--------------------------------------------+
| 0x0104           | :ref:`pm_clocks_msg_get_num_clock_parents` |
+------------------+--------------------------------------------+
| 0x010c           | :ref:`pm_clocks_msg_set_freq`              |
+------------------+--------------------------------------------+
| 0x010d           | :ref:`pm_clocks_msg_query_freq`            |
+------------------+--------------------------------------------+
| 0x010e           | :ref:`pm_clocks_msg_get_freq`              |
+------------------+--------------------------------------------+

.. note::
   Reference :ref:`pub_soc_family_doc` to see clock IDs and device IDs for
   your SoC.

.. note::
   The SSC controls are not yet available in the system firmware.

Macros Used in this Document
----------------------------

.. sysfwapimacro:: TISCI_MSG_VALUE_CLOCK_SW_STATE_UNREQ
.. sysfwapimacro:: TISCI_MSG_VALUE_CLOCK_SW_STATE_AUTO
.. sysfwapimacro:: TISCI_MSG_VALUE_CLOCK_SW_STATE_REQ
.. sysfwapimacro:: TISCI_MSG_VALUE_CLOCK_HW_STATE_NOT_READY
.. sysfwapimacro:: TISCI_MSG_VALUE_CLOCK_HW_STATE_READY
.. sysfwapimacro:: TISCI_MSG_FLAG_CLOCK_ALLOW_SSC
.. sysfwapimacro:: TISCI_MSG_FLAG_CLOCK_ALLOW_FREQ_CHANGE
.. sysfwapimacro:: TISCI_MSG_FLAG_CLOCK_INPUT_TERM
.. sysfwapimacro:: TISCI_MSG_FLAG_CLOCK_SSC_ACTIVE

Large Clock Index Compatibility
-------------------------------

Clock indexes identify clocks connected to devices. There are typically a
small number of clocks connected to each device. The original API was
designed with an 8-bit field to represent clock indexes allowing a maximum
clock index of 254, which 255 being reserved to indicate an invalid clock
index.

The number of clocks connected to devices on newer SoCs has multiplied
rapidly and has exceeded the 254 limit, largely due to very wide input muxes.
The API has been extended to support these larger clock indexes while still
remaining forward and reverse compatible. This change requires some action by
users of the API when indexes larger than 254 are present.

For each clock index within a message a 32-bit field has been appended to the
end of the message. If a clock index is less than or equal to 254, the extra
field is unused and the clock index is stored in the original 8-bit field. If
the clock index is 255 or larger, the value 255 is stored is the original
8-bit field and the actual value is stored in the extra 32-bit field. This
applies to both requests and responses from the firmware.

Note that if a host with large clock index support attempts to send a clock
index 255 or greater to a firmware without large clock index support, the
firmware will interpret the 255 value in the 8-bit clock index field as
invalid and NAK the request. Similarly, if a request is made that requires
the firmware to respond with a value 255 or greater (such as the number of
clock parents) the firmware will return a NAK. If a host without large
clock index support makes a request that requires a firmware with large
clock index support to respond with a value of 255 or greater it will return
an ACK but place 255 in the 8-bit field. This should be interpreted by the
host as an invalid clock index.

The host can also request the ABI version. ABI versions prior to 2.6 lack
large clock index support.

.. _pm_clocks_msg_set_clock:

TISCI_MSG_SET_CLOCK
===================

Objective
----------

Setup a hardware device's clock state

Usage
-----

+------------------------+--------+
| **Message Type**       | Normal |
+------------------------+--------+
| **Secure Queue Only?** | No     |
+------------------------+--------+

This requests for finer control of hardware device's clocks. This allows for
configuration for hardware blocks that require customization of the specific
input clocks. NOTE: each of the clock IDs are relative to the hardware block.

TISCI Message ID
----------------

.. sysfwapimacro:: TISCI_MSG_SET_CLOCK

Message Data Structures
-----------------------

.. sysfwapistruct:: tisci_msg_set_clock_req

.. sysfwapistruct:: tisci_msg_set_clock_resp

.. _pm_clocks_msg_get_clock:

TISCI_MSG_GET_CLOCK
===================

Objective
---------

Get the state of a clock to or from a hardware block.

Usage
-----

+------------------------+--------+
| **Message Type**       | Normal |
+------------------------+--------+
| **Secure Queue Only?** | No     |
+------------------------+--------+

This requests for additional information about the state of a clock
within the system including the state it is programmed to and the actual
state of the clock.

TISCI Message ID
----------------

.. sysfwapimacro:: TISCI_MSG_GET_CLOCK

Message Data Structures
-----------------------

.. sysfwapistruct:: tisci_msg_get_clock_req

.. sysfwapistruct:: tisci_msg_get_clock_resp

.. _pm_clocks_msg_set_clock_parent:

TISCI_MSG_SET_CLOCK_PARENT
==========================

Objective
---------

SoC specific customization for setting up a specific clock parent ID for the
various clock input options for a hardware block's clock.

Usage
-----

+------------------------+--------+
| **Message Type**       | Normal |
+------------------------+--------+
| **Secure Queue Only?** | No     |
+------------------------+--------+

This is rarely used customization that may be required based on the usecase of
the system where the reset input clock option may not suffice for the usecase
attempted.

Message ID
----------

.. sysfwapimacro:: TISCI_MSG_SET_CLOCK_PARENT

Message Data Structures
-----------------------

.. sysfwapistruct:: tisci_msg_set_clock_parent_req

.. sysfwapistruct:: tisci_msg_set_clock_parent_resp

.. _pm_clocks_msg_get_clock_parent:

TISCI_MSG_GET_CLOCK_PARENT
==========================

Objective
---------

Query the clock parent currently configured for a specific clock source of a
hardware block

Usage
-----

+------------------------+--------+
| **Message Type**       | Normal |
+------------------------+--------+
| **Secure Queue Only?** | No     |
+------------------------+--------+

This is typically used to confirm the current clock parent to ensure that the
requisite usecase for the hardware block can be satisfied. Note that if the
programmed value in the underlying hardware is invalid, a NAK is returned for
this message. Once a valid parent is programmed via a successful
TISCI_MSG_SET_CLOCK_PARENT call, calls to TISCI_MSG_GET_CLOCK_PARENT will
succeed.


Message ID
----------

.. sysfwapimacro:: TISCI_MSG_GET_CLOCK_PARENT

Message Data Structures
-----------------------

.. sysfwapistruct:: tisci_msg_get_clock_parent_req

.. sysfwapistruct:: tisci_msg_get_clock_parent_resp

.. _pm_clocks_msg_get_num_clock_parents:

TISCI_MSG_GET_NUM_CLOCK_PARENTS
===============================

Objective
---------

Query for the number of parent clock paths available for a specific hardware
block's clock.

Usage
-----

+------------------------+--------+
| **Message Type**       | Normal |
+------------------------+--------+
| **Secure Queue Only?** | No     |
+------------------------+--------+

This is typically used to get the max number of clock parent options available
for a specific hardware block's clock.

Message ID
----------

.. sysfwapimacro:: TISCI_MSG_GET_NUM_CLOCK_PARENTS

Message Data Structures
-----------------------

.. sysfwapistruct:: tisci_msg_get_num_clock_parents_req

.. sysfwapistruct:: tisci_msg_get_num_clock_parents_resp

.. _pm_clocks_msg_set_freq:

TISCI_MSG_SET_FREQ
==================

Objective
---------

Setup a clock frequency for a hardware block's clock.

Usage
-----

+------------------------+--------+
| **Message Type**       | Normal |
+------------------------+--------+
| **Secure Queue Only?** | No     |
+------------------------+--------+

This is typically desired when the default frequency of the hardware block's
clock is not appropriate for the usecase desired.

NOTE: Normally clock frequency management is automatically done by TISCI entity.
In case of specific requests, TISCI evaluates capability to achieve requested
range and responds with success/failure message.

This sets the desired frequency for a clock within an allowable range. This
message will fail on an enabled clock unless MSG_FLAG_CLOCK_ALLOW_FREQ_CHANGE is
set for the clock. Additionally, if other clocks have their frequency modified
due to this message, they also must have the MSG_FLAG_CLOCK_ALLOW_FREQ_CHANGE or
be disabled.

Calling set frequency on a clock input to the SoC pseudo-device will inform the
PMMC of that clock's frequency. Setting a frequency of zero will indicate the
clock is disabled.

Calling set frequency on clock outputs from the SoC pseudo-device will function
similarly to setting the clock frequency on a device.

Message ID
----------

.. sysfwapimacro:: TISCI_MSG_SET_FREQ

Message Data Structures
-----------------------

.. sysfwapistruct:: tisci_msg_set_freq_req

.. sysfwapistruct:: tisci_msg_set_freq_resp

.. _pm_clocks_msg_query_freq:

TISCI_MSG_QUERY_FREQ
====================

Objective
---------

Query to find closest match possible for a target frequency.

Usage
-----

+------------------------+--------+
| **Message Type**       | Normal |
+------------------------+--------+
| **Secure Queue Only?** | No     |
+------------------------+--------+

This message does no real operation, instead, it requests the system control
entity to respond with the best frequency that can match a frequency range
provided.

NOTE: This is a snapshot view. In a multi processing system, it is very well
possible that another processing entity might change the configuration after
one entity has queried for best match capability. Only a SET_CLOCK_FREQ will
guarantee the frequency is configured.

Message ID
----------

.. sysfwapimacro:: TISCI_MSG_QUERY_FREQ

Message Data Structures
-----------------------

.. sysfwapistruct:: tisci_msg_query_freq_req

.. sysfwapistruct:: tisci_msg_query_freq_resp

.. _pm_clocks_msg_get_freq:

TISCI_MSG_GET_FREQ
==================

Objective
---------

Get the clock frequency of a specific clock which belongs to a hardware block.

Usage
-----

+------------------------+--------+
| **Message Type**       | Normal |
+------------------------+--------+
| **Secure Queue Only?** | No     |
+------------------------+--------+

This is most used functionality and is meant for usage when the driver
controlling the hardware block requires to know the input clock frequency for
configuring internal dividers / multipliers as required.

Message ID
----------

.. sysfwapimacro:: TISCI_MSG_GET_FREQ

Message Data Structures
-----------------------

.. sysfwapistruct:: tisci_msg_get_freq_req

.. sysfwapistruct:: tisci_msg_get_freq_resp


Power Management Clock Frequency Configuration Example
======================================================

.. _pm_clock_set_without_mux:

Power Management Clock Frequency Configuration without Multiplexers
-------------------------------------------------------------------

An example sequence for setting the clock frequency for a given module is as given below.
The input to this code snippet is as below:

- ``device_id``: Refer to the :doc:`../../5_soc_doc/am6x/devices`
- ``clk_id``: Refer to the :doc:`../../5_soc_doc/am6x/clocks`. Choose the Ids which are marked "Input Clock".
- ``min_hz``: Minimum Frequency which should be set for this clock.
- ``freq_hz``: Target Frequency which should be set for this clock.
- ``max_hz``: Maximum Frequency which should be set for this clock.

The simplified sequence of operations is as below:

.. image:: ../../img/pm/Clocks_sequence_diagram.jpg

.. code-block:: c

    /* Allocate space for the messages */
    u32 tisci_msg_data[13];
    /* Pointer to the header */
    struct tisci_header *hdr = (struct tisci_header *) tisci_msg_data;
    struct tisci_msg_get_freq_req *get_freq_req;
    struct tisci_msg_get_freq_resp *get_freq_resp;
    struct tisci_msg_set_freq_req *set_freq_req;
    struct tisci_msg_query_freq_resp *query_freq_resp;
    int ret = 0;

    /* Initialize the message data with 0s */
    memset(tisci_msg_data, 0, sizeof(tisci_msg_data));
    /* Get the current frequency of the device and corresponding clock */
    hdr->host = 0x01;
    hdr->flags = TISCI_MSG_FLAG_AOP;
    /* Message for Getting the frequency for the clock of the device of interest */
    hdr->type = TISCI_MSG_GET_FREQ;
    get_freq_req = (struct tisci_msg_get_freq_req *) (hdr + 1);
    /* Give the device ID from the devices list in the SoC Documentation */
    get_freq_req->device = device_id;
    /* The clock IDs can be less than 255 or greater than 255. If the clock
     * ID is greater than 255, then set 2 fields in the message.
     */
    if (clk_id >= 255) {
        get_freq_req->clk = 255;
        get_freq_req->clk32 = clk_id;
    } else {
        get_freq_req->clk = clk_id;
    }

    /* Send a message to the System firmware on a specific Secure Proxy Queue.
     * The same structure for the message data. The same can be used as the
     * message data is copied to the secure proxy registers.
     */
    ret = send_receive_with_timeout(TISCI_MSG_QUEUE_NONSEC_HIGH_TX,
                    (void *)tisci_msg_data,
                    sizeof(tisci_msg_data),
                    (void *)tisci_msg_data, sizeof(tisci_msg_data));

    if (ret < 0)
        return ret;

    get_freq_resp = (struct tisci_msg_get_freq_resp *) (hdr + 1);
    printf("Current: ");
    print_freq(get_freq_resp->freq_hz);
    printf("\n");

    printf("Sending query: ");
    print_freq(min_hz);
    printf(", ");
    print_freq(freq_hz);
    printf(", ");
    print_freq(max_hz);
    printf("\n");

    /* Initialize the message data with 0s */
    memset(tisci_msg_data, 0, sizeof(tisci_msg_data));
    hdr->host = 0x01;
    hdr->flags = TISCI_MSG_FLAG_AOP;
    /* Message for querying if the frequency is possible for the clock of the device of interest */
    hdr->type = TISCI_MSG_QUERY_FREQ;
    set_freq_req = (struct tisci_msg_set_freq_req *) (hdr + 1);
    /* Give the device ID from the devices list in the SoC Documentation */
    set_freq_req->device = device_id;
    /* The clock IDs can be less than 255 or greater than 255. If the clock
     * ID is greater than 255, then set 2 fields in the message.
     */
    if (clk_id >= 255) {
        set_freq_req->clk = 255;
        set_freq_req->clk32 = clk_id;
    } else {
        set_freq_req->clk = clk_id;
    }
    set_freq_req->min_freq_hz = min_hz;
    set_freq_req->target_freq_hz = freq_hz;
    set_freq_req->max_freq_hz = max_hz;

    /* Send a message to the System firmware on a specific Secure Proxy Queue.
     * The same structure for the message data. The same can be used as the
     * message data is copied to the secure proxy registers.
     */
    ret = send_receive_with_timeout(TISCI_MSG_QUEUE_NONSEC_HIGH_TX,
                    (void *)tisci_msg_data,
                    sizeof(tisci_msg_data),
                    (void *)tisci_msg_data, sizeof(tisci_msg_data));

    if (ret == 0 && !(hdr->flags & TISCI_MSG_FLAG_ACK))
        ret = -ENODEV;

    if (ret == 0) {
        query_freq_resp = (struct tisci_msg_query_freq_resp *) (hdr + 1);
        printf("Query response: ");
        print_freq(query_freq_resp->freq_hz);
        printf("\n");

        if (query_freq_resp->freq_hz < min_hz || query_freq_resp->freq_hz > max_hz)
            ret = -EINVAL;
    }
    if (ret < 0)
        return ret;

    freq_hz = query_freq_resp->freq_hz;
    printf("Sending set: ");
    print_freq(freq_hz);
    printf(", ");
    print_freq(freq_hz);
    printf(", ");
    print_freq(freq_hz);
    printf("\n");

    /* Initialize the message data with 0s */
    memset(tisci_msg_data, 0, sizeof(tisci_msg_data));
    hdr->host = 0x01;
    hdr->flags = TISCI_MSG_FLAG_AOP;
    /* Message for setting the frequency for the clock of the device of interest. Actually changes the frequency */
    hdr->type = TISCI_MSG_SET_FREQ;
    set_freq_req = (struct tisci_msg_set_freq_req *) (hdr + 1);
    /* Give the device ID from the devices list in the SoC Documentation */
    set_freq_req->device = device_id;
    /* The clock IDs can be less than 255 or greater than 255. If the clock
     * ID is greater than 255, then set 2 fields in the message.
     */
    if (clk_id >= 255) {
        set_freq_req->clk = 255;
        set_freq_req->clk32 = clk_id;
    } else {
        set_freq_req->clk = clk_id;
    }
    set_freq_req->min_freq_hz = freq_hz;
    set_freq_req->target_freq_hz = freq_hz;
    set_freq_req->max_freq_hz = freq_hz;

    /* Send a message to the System firmware on a specific Secure Proxy Queue.
     * The same structure for the message data. The same can be used as the
     * message data is copied to the secure proxy registers.
     */
    ret = send_receive_with_timeout(TISCI_MSG_QUEUE_NONSEC_HIGH_TX,
                    (void *)tisci_msg_data,
                    sizeof(tisci_msg_data),
                    (void *)tisci_msg_data, sizeof(tisci_msg_data));

    if (ret == 0 && !(hdr->flags & TISCI_MSG_FLAG_ACK))
        ret = -ENODEV;
    if (ret < 0)
        return ret;

    /* Initialize the message data with 0s */
    memset(tisci_msg_data, 0, sizeof(tisci_msg_data));
    hdr->host = 0x01;
    hdr->flags = TISCI_MSG_FLAG_AOP;
    /* Message for Getting the frequency for the clock of the device of interest */
    hdr->type = TISCI_MSG_GET_FREQ;
    get_freq_req = (struct tisci_msg_get_freq_req *) (hdr + 1);
    /* Give the device ID from the devices list in the SoC Documentation */
    get_freq_req->device = device_id;
    /* The clock IDs can be less than 255 or greater than 255. If the clock
     * ID is greater than 255, then set 2 fields in the message.
     */
    if (clk_id >= 255) {
        get_freq_req->clk = 255;
        get_freq_req->clk32 = clk_id;
    } else {
        get_freq_req->clk = clk_id;
    }

    /* Send a message to the System firmware on a specific Secure Proxy Queue.
     * The same structure for the message data. The same can be used as the
     * message data is copied to the secure proxy registers.
     */
    ret = send_receive_with_timeout(TISCI_MSG_QUEUE_NONSEC_HIGH_TX,
                    (void *)tisci_msg_data,
                    sizeof(tisci_msg_data),
                    (void *)tisci_msg_data, sizeof(tisci_msg_data));

    if (ret == 0) {
        get_freq_resp = (struct tisci_msg_get_freq_resp *) (hdr + 1);
        printf("New: ");
        print_freq(get_freq_resp->freq_hz);
        printf("\n");

        if (freq_hz != get_freq_resp->freq_hz)
            ret = -EINVAL;
    }
    if (ret < 0)
        return ret;


.. _pm_clock_set_with_mux:

Power Management Clock Frequency Configuration Example with Mux programming
---------------------------------------------------------------------------

An example pseudo sequence for setting the clock frequency while searching for the
right multiplexer parent is as below:

- ``device_id``: Refer to the :doc:`../../5_soc_doc/am6x/devices`
- ``clk_id``: Refer to the :doc:`../../5_soc_doc/am6x/clocks`. Choose the Ids which are marked "Input Clock".
- ``min_hz``: Minimum Frequency which should be set for this clock.
- ``freq_hz``: Target Frequency which should be set for this clock.
- ``max_hz``: Maximum Frequency which should be set for this clock.

The flow chart for the same is as below:

.. image:: ../../img/pm/pm_clocks_example.svg



.. code-block:: c

    /* Check if the clock is enabled or not */
    status = TISCI_MSG_GET_CLOCK(device_id,
                                 clk_id,
                                 &clockStatus);
    if (status == PASS)
    {
        /* Get the number of parents for the clock */
        status = TISCI_MSG_GET_NUM_CLOCK_PARENTS(device_id,
                                                clk_id,
                                                &numParents);
        if ((status == PASS) && (numParents > 1U))
        {
            status = TISCI_MSG_GET_CLOCK_PARENT(device_id, clk_id, &origParent);
        }
    }
    if (status == PASS)
    {
        /* Disabling the clock */
        status = TISCI_MSG_SET_CLOCK(device_id,
                                    clk_id,
                                    TISCI_MSG_VALUE_CLOCK_SW_STATE_UNREQ);
    }
    if (status == PASS)
    {
        foundParent = 0U;
        /* Try to loop and change parents of the clock */
        for(i=0U;i<numParents;i++)
        {
            if (numParents > 1U)
            {
                /* Setting the new parent */
                status = TISCI_MSG_SET_CLOCK_PARENT(
                                            device_id,
                                            clk_id,
                                            clk_id+i+1U);
                /* Check if the clock can be set to desirable freq. */
                if (status == PASS)
                {
                    moduleClockParentChanged = 1U;
                }
            }
            if (status == PASS)
            {
                status = TISCI_MSG_QUERY_FREQ(device_id,
                                              clk_id,
                                              freq_hz,
                                              &respfreq_hz);
            }
            if ((status == PASS) && (respfreq_hz == freq_hz))
            {
                foundParent = 1U;
                break;
            }
        }
    }
    if ((status == PASS) && (numParents == 0U))
    {
        status = TISCI_MSG_QUERY_FREQ(device_id,
                                      clk_id,
                                      freq_hz,
                                      &respfreq_hz);
        if ((status == PASS) && (respfreq_hz == freq_hz))
        {
            foundParent = 1U;
        }
    }
    if (foundParent == 1U)
    {
        /* Set the clock at the desirable frequency*/
        status = TISCI_MSG_SET_FREQ(
                                device_id,
                                clk_id,
                                freq_hz,
                                TISCI_MSG_FLAG_CLOCK_ALLOW_FREQ_CHANGE);
    }
    else
    {
        status = CSL_EFAIL;
    }
    if ((status == PASS) &&
        (clockStatus == (uint32_t) TISCI_MSG_VALUE_CLOCK_SW_STATE_UNREQ))
    {
        /* Restore the clock again to original state */
        status = TISCI_MSG_SET_CLOCK(
                                     device_id,
                                     clk_id,
                                     clockStatus);
    }
    finalStatus = status;
    if ((status != PASS) && (moduleClockParentChanged == 1U))
    {
        /* Setting the original parent if failure */
        (void) TISCI_MSG_SET_CLOCK_PARENT(
                                    device_id,
                                    clk_id,
                                    origParent);
    }
 
