All of lore.kernel.org
 help / color / mirror / Atom feed
From: Damien Hedde <damien.hedde@greensocs.com>
To: qemu-devel@nongnu.org
Cc: Damien Hedde <damien.hedde@greensocs.com>,
	peter.maydell@linaro.org, berrange@redhat.com,
	ehabkost@redhat.com, pbonzini@redhat.com, alistair@alistair23.me,
	mark.burton@greensocs.com, qemu-arm@nongnu.org,
	marcandre.lureau@redhat.com, edgar.iglesias@gmail.com,
	philmd@redhat.com
Subject: [PATCH v7 5/9] docs/clocks: add device's clock documentation
Date: Mon, 24 Feb 2020 18:02:57 +0100	[thread overview]
Message-ID: <20200224170301.246623-6-damien.hedde@greensocs.com> (raw)
In-Reply-To: <20200224170301.246623-1-damien.hedde@greensocs.com>

Add the documentation about the clock inputs and outputs in devices.

This is based on the original work of Frederic Konrad.

Signed-off-by: Damien Hedde <damien.hedde@greensocs.com>
---

v7:
 + update ClockIn/Out types
---
 docs/devel/clocks.rst | 356 ++++++++++++++++++++++++++++++++++++++++++
 docs/devel/index.rst  |   1 +
 2 files changed, 357 insertions(+)
 create mode 100644 docs/devel/clocks.rst

diff --git a/docs/devel/clocks.rst b/docs/devel/clocks.rst
new file mode 100644
index 0000000000..cc5563691d
--- /dev/null
+++ b/docs/devel/clocks.rst
@@ -0,0 +1,356 @@
+
+What are clocks
+===============
+
+Clocks are QOM objects developed for the purpose of modeling the
+distribution of clocks in QEMU.
+
+They allow us to model the clock distribution of a platform and detect
+configuration errors in the clock tree such as badly configured PLL, clock
+source selection or disabled clock.
+
+The object is *Clock* and its QOM name is ``CLOCK``.
+
+Clocks are typically used with devices where they are used to model inputs
+and outputs. They are created in a similar way as gpios. Inputs and outputs
+of different devices can be connect together.
+
+In these cases a Clock object is a child of a Device object but this is not
+a requirement. Clocks can be independent of devices. For example it is possible
+to create a clock outside of any device to model the main clock source of a
+machine.
+
+Here is an example of clocks::
+
+    +---------+      +----------------------+   +--------------+
+    | Clock 1 |      |       Device B       |   |   Device C   |
+    |         |      | +-------+  +-------+ |   | +-------+    |
+    |         |>>-+-->>|Clock 2|  |Clock 3|>>--->>|Clock 6|    |
+    +---------+   |  | | (in)  |  | (out) | |   | | (in)  |    |
+                  |  | +-------+  +-------+ |   | +-------+    |
+                  |  |            +-------+ |   +--------------+
+                  |  |            |Clock 4|>>
+                  |  |            | (out) | |   +--------------+
+                  |  |            +-------+ |   |   Device D   |
+                  |  |            +-------+ |   | +-------+    |
+                  |  |            |Clock 5|>>--->>|Clock 7|    |
+                  |  |            | (out) | |   | | (in)  |    |
+                  |  |            +-------+ |   | +-------+    |
+                  |  +----------------------+   |              |
+                  |                             | +-------+    |
+                  +----------------------------->>|Clock 8|    |
+                                                | | (in)  |    |
+                                                | +-------+    |
+                                                +--------------+
+
+Clocks are defined in include/hw/clock.h header and device related functions
+are defined in include/hw/qdev-clock.h header.
+
+The clock state
+===============
+
+The state of a clock is its period; it is stored as an integer representing
+it in 2^-32ns unit. The special value of 0 is used to represent the clock being
+inactive or gated. The clocks do not model the signal itself (pin toggling)
+or other properties such as the duty cycle.
+
+All clocks contain this state: outputs as well as inputs. It allows to fetch
+the current period of a clock at any time. When a clock is updated, the
+value is immediately propagated to all connected clocks in the tree.
+
+To ease interaction with clocks. Helpers with a unit suffix are defined for
+every clock state setter or getter. They are:
+  + ``_ns`` for handling periods in nanosecond, and
+  + ``_hz`` for handling frequencies in hertz.
+The 0 period value is converted to 0 in hertz and vice versa. 0 always means
+that the clock is disabled.
+
+Adding a new a clock
+====================
+
+Adding clocks to a device must be done during the init method of the Device
+instance.
+
+To add an input clock to a device, the function qdev_init_clock_in must be used.
+It takes the name, a callback and an opaque parameter for the callback (this will
+be explained in a following section below).
+Output is more simple, only the name is required. Typically::
+
+    qdev_init_clock_in(DEVICE(dev), "clk_in", clk_in_callback, dev);
+    qdev_init_clock_out(DEVICE(dev), "clk_out");
+
+Both functions return the created Clock pointer, which should be saved in the
+device's state structure for further use.
+
+These objects will be automatically deleted by the QOM reference mechanism.
+
+Note that it is possible to create a static array describing clock inputs and
+outputs. The function ``qdev_init_clocks()`` must be called with the array as
+parameter to initialize the clocks: it has the same behaviour as calling the
+``qdev_init_clock_in/out()`` for each clock in the array. To ease the array
+construction, some macros are defined in include/hw/qdev-clock.h.
+As an example, the following creates 2 clocks to a device: one input and one
+output.
+
+::
+
+    /* device structure containing pointer to the clock objects */
+    typedef struct MyDeviceState {
+        DeviceState parent_obj;
+        Clock *clk_in;
+        Clock *clk_out;
+    } MyDeviceState;
+
+    /*
+     * callback for the input clock (see "Callback on input clock
+     * change" section below for more information).
+     */
+    static void clk_in_callback(void *opaque);
+
+    /*
+     * static array describing clocks:
+     * + a clock input named "clk_in", whose pointer is stored in
+     *   clk_in field of a MyDeviceState structure with callback
+     *   clk_in_callback.
+     * + a clock output named "clk_out" whose pointer is stored in
+     *   clk_out field of a MyDeviceState structure.
+     */
+    static const ClockPortInitArray mydev_clocks = {
+        QDEV_CLOCK_IN(MyDeviceState, clk_in, clk_in_callback),
+        QDEV_CLOCK_OUT(MyDeviceState, clk_out),
+        QDEV_CLOCK_END
+    };
+
+    /* device initialization function */
+    static void mydev_init(Object *obj)
+    {
+        /* cast to MyDeviceState */
+        MyDeviceState *mydev = MYDEVICE(obj);
+        /* create and fill the pointer fields in the MyDeviceState */
+        qdev_init_clocks(mydev, mydev_clocks);
+        [...]
+    }
+
+An alternative way to create a clock is to simply call
+``object_new(TYPE_CLOCK)``. In that case the clock will neither be an input nor
+an output of a device. After the whole QOM hieracrhy of the clock has been set
+``clock_setup_canonical_path()`` should be called.
+
+At creation, the period of the clock is 0: the clock is disabled. You can
+change it using ``clock_set[_ns|_hz]()``.
+
+Note that if you are creating a clock with a fixed period which will never
+change (for example the main clock source of a board), then you'll have
+nothing else to do. This value will be propagated to other clocks when
+connecting the clocks together and devices will fetch the right value during
+the first reset.
+
+Retrieving clocks from a device
+===============================
+
+``qdev_get_clock_in()`` and ``dev_get_clock_out()`` are available to get the clock inputs or outputs of a device. For example::
+
+    Clock *clk = qdev_get_clock_in(DEVICE(mydev), "clk_in");
+
+or::
+
+    Clock *clk = qdev_get_clock_out(DEVICE(mydev), "clk_out");
+
+Connecting two clocks together
+==============================
+
+To connect two clocks together, use the ``clock_set_source()`` function.
+Given two clocks ``clk1``, and ``clk2``, ``clock_set_source(clk2, clk1);``
+configure ``clk2`` to follow the ``clk1`` period changes. Every time ``clk1``
+is updated, ``clk2`` will be updated too.
+
+When connecting clock between devices, prefer using the
+``qdev_connect_clock_in()`` function set the source of an input device clock.
+For example, to connect the input clock ``clk2`` of ``devB`` to the output
+clock ``clk1`` of ``devA``, do::
+
+    qdev_connect_clock_in(devB, "clk2", qdev_get_clock_out(devA, "clk1"))
+
+We used ``qdev_get_clock_out()`` above, but any clock can drive an input clock,
+even another input clock. The following diagram shows some
+examples of connections. Note also that a clock can drive several other clocks.
+
+::
+
+  +------------+  +--------------------------------------------------+
+  |  Device A  |  |                   Device B                       |
+  |            |  |               +---------------------+            |
+  |            |  |               |       Device C      |            |
+  |  +-------+ |  | +-------+     | +-------+ +-------+ |  +-------+ |
+  |  |Clock 1|>>-->>|Clock 2|>>+-->>|Clock 3| |Clock 5|>>>>|Clock 6|>>
+  |  | (out) | |  | | (in)  |  |  | | (in)  | | (out) | |  | (out) | |
+  |  +-------+ |  | +-------+  |  | +-------+ +-------+ |  +-------+ |
+  +------------+  |            |  +---------------------+            |
+                  |            |                                     |
+                  |            |  +--------------+                   |
+                  |            |  |   Device D   |                   |
+                  |            |  | +-------+    |                   |
+                  |            +-->>|Clock 4|    |                   |
+                  |               | | (in)  |    |                   |
+                  |               | +-------+    |                   |
+                  |               +--------------+                   |
+                  +--------------------------------------------------+
+
+In the above example, when *Clock 1* is updated by *Device A*, three clocks gets the new clock period value: *Clock 2*, Clock 3* and *Clock 4*.
+
+It is not possible to disconnect a clock or to change the clock connection
+after it is done.
+
+Unconnected input clocks
+========================
+
+A newly created input clock is disabled (period of 0). It means the clock will
+be considered as disabled until the period is updated. If the clock remains
+unconnected it will always keep its initial value of 0. If this is not the
+wanted behaviour, ``clock_set()``, ``clock_set_ns()`` or ``clock_set_hz()``
+should be called on the Clock object during device instance init. For example::
+
+    clk = qdev_init_clock_in(DEVICE(dev), "clk-in", clk_in_callback,
+                             dev);
+    /* set initial value to 10ns / 100MHz */
+    clock_set_ns(clk, 10);
+
+Fetching clock frequency/period
+===============================
+
+To get the current state of a clock, the function ``clock_get()``,
+``clock_get_ns()`` or ``clock_get_hz()`` must be used.
+
+It is also possible to register a callback on clock frequency changes.
+Here is an example::
+
+    void clock_callback(void *opaque) {
+        MyDeviceState *s = (MyDeviceState *) opaque;
+        /*
+         * opaque may not be the device state pointer, but most
+         * probably it is. (It depends on what is given to the
+         * qdev_init_clock_in function)
+         */
+
+        /* do something with the new period */
+        fprintf(stdout, "device new period is %" PRIu64 "ns\n",
+                        clock_get_ns(dev->my_clk_input));
+    }
+
+Changing a clock period
+=======================
+
+A device can change its outputs using the ``clock_update()``,
+``clock_update_ns()`` or ``clock_update_hz()`` function. It will trigger
+updates on every connected input.
+
+For example, let's say that we have an output clock *clkout* and we have a
+pointer to it in the device state because we did the following in init phase::
+
+    dev->clkout = qdev_init_clock_out(DEVICE(dev), "clkout");
+
+Then at any time (apart from the cases listed below), it is possible to
+change the clock value by doing::
+
+    clock_update_hz(dev->clkout, 1000 * 1000 * 1000); /* 1Ghz */
+
+Because updating a clock may trigger any side effects through connected clocks
+and their callbacks, this operation must be done while holding the qemu io lock.
+
+For the same reason, one can updates clocks only when it is allowed to have
+side effects on other objects. In consequence, it is forbidden:
++ during migration,
++ and in the enter phase of reset.
+
+Note that calling ``clock_update[_ns|_hz]()`` is equivalent to call
+``clock_set[_ns|_hz]()`` (with the same arguments) then ``clock_propagate()`` on
+the clock. Thus, setting the clock value can separated from triggering the
+side-effects. This is often required to factorize code to handle reset and
+migration in devices.
+
+Aliasing clocks
+=================
+
+Sometimes, one needs to forward, or inherit, a clock from another device.
+Typically, when doing device composition, a device might expose a sub-device's
+clock without interfering with it.
+The function ``qdev_alias_clock()`` can be used to achieve this behaviour. Note
+that it is possible to expose the clock under a different name. This works for
+both inputs and outputs.
+
+For example, if device B is a child of device A, ``device_a_instance_init()``
+may do something like this::
+
+    void device_a_instance_init(Object *obj)
+    {
+        AState *A = DEVICE_A(obj);
+        BState *B;
+        /* create B object as child of A */
+        [...]
+        qdev_alias_clock(B, "clk", A, "b_clk");
+        /*
+         * Now A has a clock "b_clk" which is an alias to
+         * the clock "clk" of its child B.
+         */
+    }
+
+This function does not return any clock object. The new clock has the same
+direction (input or output) as the original one. This function only adds a link
+to the existing clock. In the above example, B object remains the only object
+allowed to use the clock and device A must not try to change the clock period
+or set a callback to the clock. Here follows a diagram describing the example
+with an input clock::
+
+    +--------------------------+
+    |        Device A          |
+    |         +--------------+ |
+    |         |   Device B   | |
+    |         | +-------+    | |
+    >>"b_clk">>>| "clk" |    | |
+    |  (in)   | |  (in) |    | |
+    |         | +-------+    | |
+    |         +--------------+ |
+    +--------------------------+
+
+Migration
+=========
+
+Clock state are not migrated automatically. Every device must handle its
+clock migration. Alias clocks must not be migrated.
+
+To ensure clock states are restored correctly during migration, there is two
+solutions.
+
+Clocks states can be migrated by adding an entry into the device
+vmstate description. To this purpose, the ``VMSTATE_CLOCK`` macro defines
+such an entry and should be used. This is typically used to migrate an input
+clock state. The following example describes it::
+
+    MyDeviceState {
+        DeviceState parent_obj;
+        [...] /* some fields */
+        Clock *clk;
+    };
+
+    VMStateDescription my_device_vmstate = {
+        .name = "my_device",
+        .fields = (VMStateField[]) {
+            [...], /* other migrated fields */
+            VMSTATE_CLOCK(clk, MyDeviceState),
+            VMSTATE_END_OF_LIST()
+        }
+    };
+
+The second solution is to restore the clock state using information already
+at our disposal. This can be used to restore output clocks states using the
+device state. The functions ``clock_set[_ns|_hz]()`` can be used during
+``post_load()`` migration callback.
+
+When adding a clock support to an existing device, if you care about migration
+compatibility. To this end, you can use ``clock_set()`` in a ``pre_load()``
+function to setup a default value in case the source virtual machine does not
+send the clock state. You may also need to use a vmstate subsection.
+
+Care should be taken not to use ``clock_update[_ns|_hz]()`` or
+``clock_propagate()`` during the whole migration procedure because it will
+trigger side effects to other devices in an unknown state.
diff --git a/docs/devel/index.rst b/docs/devel/index.rst
index 4dc2ca8d71..f9c8c668ee 100644
--- a/docs/devel/index.rst
+++ b/docs/devel/index.rst
@@ -25,3 +25,4 @@ Contents:
    tcg-plugins
    bitops
    reset
+   clocks
-- 
2.24.1



  parent reply	other threads:[~2020-02-24 17:32 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-02-24 17:02 [PATCH v7 0/9] Clock framework API Damien Hedde
2020-02-24 17:02 ` [PATCH v7 1/9] hw/core/clock: introduce clock object Damien Hedde
2020-02-25  0:02   ` Alistair Francis
2020-02-25  9:48     ` Damien Hedde
2020-02-24 17:02 ` [PATCH v7 2/9] hw/core/clock-vmstate: define a vmstate entry for clock state Damien Hedde
2020-02-25  0:05   ` Alistair Francis
2020-02-24 17:02 ` [PATCH v7 3/9] qdev: add clock input&output support to devices Damien Hedde
2020-02-25  1:27   ` Alistair Francis
2020-02-25 10:04     ` Damien Hedde
2020-02-24 17:02 ` [PATCH v7 4/9] qdev-clock: introduce an init array to ease the device construction Damien Hedde
2020-02-24 17:02 ` Damien Hedde [this message]
2020-02-24 17:02 ` [PATCH v7 6/9] hw/misc/zynq_slcr: add clock generation for uarts Damien Hedde
2020-02-24 17:02 ` [PATCH v7 7/9] hw/char/cadence_uart: add clock support Damien Hedde
2020-02-24 17:03 ` [PATCH v7 8/9] hw/arm/xilinx_zynq: connect uart clocks to slcr Damien Hedde
2020-02-24 17:03 ` [PATCH v7 9/9] qdev-monitor: print the device's clock with info qtree Damien Hedde
2020-02-24 18:05 ` [PATCH v7 0/9] Clock framework API no-reply
2020-02-24 19:16 ` no-reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200224170301.246623-6-damien.hedde@greensocs.com \
    --to=damien.hedde@greensocs.com \
    --cc=alistair@alistair23.me \
    --cc=berrange@redhat.com \
    --cc=edgar.iglesias@gmail.com \
    --cc=ehabkost@redhat.com \
    --cc=marcandre.lureau@redhat.com \
    --cc=mark.burton@greensocs.com \
    --cc=pbonzini@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=philmd@redhat.com \
    --cc=qemu-arm@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.