aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2020-04-30 15:45:34 +0100
committerPeter Maydell <peter.maydell@linaro.org>2020-04-30 15:45:34 +0100
commit126eeee6c7b516e0a348dd4d60e59dbfa4b4b513 (patch)
tree2b0239fe13bdbbf7ebf002c35668402a0498203a /docs
parent16aaacb307ed607b9780c12702c44f0fe52edc7e (diff)
parent6f7b6947a6639fff15c6a0956adf0f5ec004b789 (diff)
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20200430-1' into staging
target-arm queue: * xlnx-zdma: Fix endianness handling of descriptor loading * nrf51: Fix last GPIO CNF address * gicv3: Use gicr_typer in arm_gicv3_icc_reset * msf2: Add EMAC block to SmartFusion2 SoC * New clock modelling framework * hw/arm: versal: Setup the ADMA with 128bit bus-width * Cadence: gem: fix wraparound in 64bit descriptors * cadence_gem: clear RX control descriptor * target/arm: Vectorize integer comparison vs zero * hw/arm/virt: dt: add kaslr-seed property * hw/arm: xlnx-zcu102: Disable unsupported FDT firmware nodes # gpg: Signature made Thu 30 Apr 2020 15:43:54 BST # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20200430-1: (30 commits) hw/arm: xlnx-zcu102: Disable unsupported FDT firmware nodes hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102 device_tree: Constify compat in qemu_fdt_node_path() device_tree: Allow name wildcards in qemu_fdt_node_path() target/arm/cpu: Update coding style to make checkpatch.pl happy target/arm: Make cpu_register() available for other files target/arm: Restrict the Address Translate write operation to TCG accel hw/arm/virt: dt: add kaslr-seed property hw/arm/virt: dt: move creation of /secure-chosen to create_fdt() target/arm: Vectorize integer comparison vs zero net: cadence_gem: clear RX control descriptor Cadence: gem: fix wraparound in 64bit descriptors hw/arm: versal: Setup the ADMA with 128bit bus-width qdev-monitor: print the device's clock with info qtree hw/arm/xilinx_zynq: connect uart clocks to slcr hw/char/cadence_uart: add clock support hw/misc/zynq_slcr: add clock generation for uarts docs/clocks: add device's clock documentation qdev-clock: introduce an init array to ease the device construction qdev: add clock input&output support to devices. ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'docs')
-rw-r--r--docs/devel/clocks.rst391
-rw-r--r--docs/devel/index.rst1
2 files changed, 392 insertions, 0 deletions
diff --git a/docs/devel/clocks.rst b/docs/devel/clocks.rst
new file mode 100644
index 0000000000..e5da28e211
--- /dev/null
+++ b/docs/devel/clocks.rst
@@ -0,0 +1,391 @@
+Modelling a clock tree in QEMU
+==============================
+
+What are clocks?
+----------------
+
+Clocks are QOM objects developed for the purpose of modelling 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`` (in C code, the macro
+``TYPE_CLOCK``).
+
+Clocks are typically used with devices where they are used to model inputs
+and outputs. They are created in a similar way to GPIOs. Inputs and outputs
+of different devices can be connected 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 the ``include/hw/clock.h`` header and device
+related functions are defined in the ``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 units of 2 :sup:`-32` ns. 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. This allows
+the current period of a clock to be fetched 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. The suffixes are:
+
+- ``_ns`` for handling periods in nanoseconds
+- ``_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 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).
+Output is simpler; 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.
+
+.. code-block:: c
+
+ /* device structure containing pointers 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
+ * the clk_in field of a MyDeviceState structure with callback
+ * clk_in_callback.
+ * + a clock output named "clk_out" whose pointer is stored in
+ * the 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 hierarchy 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()`` or ``clock_set_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:
+
+.. code-block:: c
+
+ Clock *clk = qdev_get_clock_in(DEVICE(mydev), "clk_in");
+
+or:
+
+.. code-block:: c
+
+ 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);``
+configures ``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 to 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:
+
+.. code-block:: c
+
+ 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 get 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 connected.
+
+Unconnected input clocks
+------------------------
+
+A newly created input clock is disabled (period of 0). This 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 desired behaviour, ``clock_set()``,
+``clock_set_ns()`` or ``clock_set_hz()`` should be called on the Clock
+object during device instance init. For example:
+
+.. code-block:: c
+
+ 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, use the functions ``clock_get()``,
+``clock_get_ns()`` or ``clock_get_hz()``.
+
+It is also possible to register a callback on clock frequency changes.
+Here is an example:
+
+.. code-block:: c
+
+ void clock_callback(void *opaque) {
+ MyDeviceState *s = (MyDeviceState *) opaque;
+ /*
+ * 'opaque' is the argument passed to qdev_init_clock_in();
+ * usually this will be the device state pointer.
+ */
+
+ /* 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:
+
+.. code-block:: c
+
+ 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:
+
+.. code-block:: c
+
+ 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 update 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 calling
+``clock_set[_ns|_hz]()`` (with the same arguments) then
+``clock_propagate()`` on the clock. Thus, setting the clock value can
+be 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.
+``qdev_alias_clock()`` works for both input and output clocks.
+
+For example, if device B is a child of device A,
+``device_a_instance_init()`` may do something like this:
+
+.. code-block:: c
+
+ void device_a_instance_init(Object *obj)
+ {
+ AState *A = DEVICE_A(obj);
+ BState *B;
+ /* create object B 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, object B
+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. This
+diagram describes the example with an input clock::
+
+ +--------------------------+
+ | Device A |
+ | +--------------+ |
+ | | Device B | |
+ | | +-------+ | |
+ >>"b_clk">>>| "clk" | | |
+ | (in) | | (in) | | |
+ | | +-------+ | |
+ | +--------------+ |
+ +--------------------------+
+
+Migration
+---------
+
+Clock state is 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
+are two solutions.
+
+Clock states can be migrated by adding an entry into the device
+vmstate description. You should use the ``VMSTATE_CLOCK`` macro for this.
+This is typically used to migrate an input clock state. For example:
+
+.. code-block:: c
+
+ 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 clock states using the
+device state. The functions ``clock_set[_ns|_hz]()`` can be used during the
+``post_load()`` migration callback.
+
+When adding clock support to an existing device, if you care about
+migration compatibility you will need to be careful, as simply adding
+a ``VMSTATE_CLOCK()`` line will break compatibility. Instead, you can
+put the ``VMSTATE_CLOCK()`` line into a vmstate subsection with a
+suitable ``needed`` function, and use ``clock_set()`` in a
+``pre_load()`` function to set the default value that will be used if
+the source virtual machine in the migration does not send the clock
+state.
+
+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 a9e1200dff..bb8238c5d6 100644
--- a/docs/devel/index.rst
+++ b/docs/devel/index.rst
@@ -27,3 +27,4 @@ Contents:
bitops
reset
s390-dasd-ipl
+ clocks