diff mbox series

[v6,6/9] docs/clocks: add device's clock documentation

Message ID 20190904093843.8765-7-damien.hedde@greensocs.con (mailing list archive)
State New, archived
Headers show
Series Clock framework API | expand

Commit Message

damien.hedde@greensocs.con Sept. 4, 2019, 9:38 a.m. UTC
From: Damien Hedde <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>
 docs/devel/clock.txt | 246 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 246 insertions(+)
 create mode 100644 docs/devel/clock.txt
diff mbox series


diff --git a/docs/devel/clock.txt b/docs/devel/clock.txt
new file mode 100644
index 0000000000..18f79922d0
--- /dev/null
+++ b/docs/devel/clock.txt
@@ -0,0 +1,246 @@ 
+What are clocks
+Clocks are objects representing input and output clocks of objects. They are
+QOM objects developed for the purpose of modeling the distribution of clocks in
+This allows 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 objects are ClockIn for the input and ClockOut for the output. The QOM
+names are respectively CLOCK_IN and CLOCK_OUT.
+CLOCK_IN and CLOCK_OUT are typically child of some device and created in a
+similar way as gpios. ClockIn and ClockOut of different devices can be
+connected together. It is possible to create a clock which is not related
+to a device; for example to model a clock source of a machine.
+Here is an example of devices with clocks:
+                             +-------------------+     +--------------------+
+                             |      Device B     |     |      Device C      |
+  +--------------------+     |           +-----+ |     | +-----+            |
+  |      Device A      |     |           |Clock|>>----->>|Clock|            |
+  |            +-----+ |     | +-----+   |Out 3| |     | |In  5|            |
+  |            |Clock|>>--+-->>|Clock|   +-----+ |     | +-----+            |
+  |            |Out 1| |  |  | |In 2 |   +-----+ |     | +-----+            |
+  |            +-----+ |  |  | +-----+   |Clock|>>----->>|Clock|            |
+  +--------------------+  |  |           |Out 4| |     | |In  6|            |
+                          |  |           +-----+ |     | +-----+            |
+                          |  +-------------------+     +--------------------+
+                          |
+                          |  +--------------------+
+                          |  |      Device D      |
+                          |  | +-----+            |
+                          +-->>|Clock|            |
+                             | |In  7|            |
+                             | +-----+            |
+                             +--------------------+
+Clocks are defined in include/hw/clock.h header and device related functions
+are defined in hw/qdev-clock.h header.
+The clock state
+The state of a clock is its frequency; it is stored as an integer representing
+it in Hertz. 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.
+Only the CLOCK_IN object keeps the value of a clock; this allows a device to
+fetch the current input frequency at any time. When an output is updated, the
+value is immediately propagated to all connected CLOCK_IN.
+Adding clocks to a device
+Adding clocks to a device must be done during the init method of the Device
+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 clock.
+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 ClockIn/ClockOut 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: 1 input and 1
+/* device structure containing pointer to the clock objects */
+typedef struct MyDeviceState {
+    DeviceState parent_obj;
+    ClockIn *clk_in;
+    ClockOut *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),
+/* 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);
+    [...]
+Connecting two clocks together
+Let's say we have 2 devices A and B. A has an output clock named "clk_out" and
+B has an input clock named "clk_in".
+The clocks are connected together using the function qdev_connect_clock:
+qdev_connect_clock(B, "clk_in", A, "clk_out", &error_abort);
+The device which has the input must be the first argument.
+It is possible to connect several input clocks to the same output. Every
+input callback will be called when the output changes.
+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 has a stored frequency value of 0. It means the
+clock will be considered as disabled until one sets a new frequency to the
+output clock it is connected to. If the clock remains unconnected it will
+always keep its initial value of 0.
+If this is not the wanted behaviour, clock_init_frequency should be called
+on the ClockIn object during device instance init.
+For example:
+clk = qdev_init_clock_in(DEVICE(dev), "clk-in", clk_in_callback, dev);
+clock_init_frequency(clk, 100 * 1000 * 1000); // default value is 100Mhz
+Using clock input frequency
+A device can get the current frequency of an input using the
+clock_get_frequency(). It returns the last set frequency (or the init value).
+It is also possible to register a callback on input 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 frequency */
+    fprintf(stdout, "device new frequency is %" PRIu64 "Hz\n",
+                    clock_get_frequency(dev->my_clk_input));
+Changing a clock output
+A device can change its outputs using the clock_set_frequency function. It
+will trigger updates on every connected inputs.
+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_set_frequency(dev->clkout, 1000 * 1000 * 1000); /* 1Ghz */
+This operation must be done while holding the qemu io lock.
+One can change clocks only when it is allowed to have side effects on other
+objects. In consequence, it is forbidden:
++ during migration,
++ and in the init phase of reset.
+Forwarding 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_pass_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 or 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 */
+    [...]
+    /* forward B's clock to A */
+    qdev_pass_clock(A, "b_clk", B, "clk");
+    /*
+     * Now A has a clock "b_clk" which forwards to
+     * the "clk" of its child B.
+     */
+This function does not return any clock object. It is not possible to add
+a callback on a forwarded input clock: in the above example, only B can use
+the clock.
+Only the ClockIn object has a state. ClockOut is not concerned by migration.
+In case the frequency of in input clock is needed for a device's migration,
+this state must be migrated. The VMSTATE_CLOCKIN macro defines an entry to
+be added in a vmstate description.
+For example, if a device has a clock input and the device state looks like:
+MyDeviceState {
+    DeviceState parent_obj;
+    ClockIn *clk;
+Then, to add the clock frequency to the device's migrated state, the vmstate
+description is:
+VMStateDescription my_device_vmstate = {
+    .name = "my_device",
+    .fields = (VMStateField[]) {
+        VMSTATE_CLOCKIN(clk, MyDeviceState),
+    }
+When adding a input clock support to an existing device, you must care about
+migration compatibility. To this end, you can use the clock_init_frequency in
+a pre_load function to setup a default value in case the source vm does not
+migrate the frequency.