qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Peter Maydell <peter.maydell@linaro.org>
To: qemu-devel@nongnu.org
Subject: [PULL 11/31] qdev: add clock input&output support to devices.
Date: Thu, 30 Apr 2020 12:51:22 +0100	[thread overview]
Message-ID: <20200430115142.13430-12-peter.maydell@linaro.org> (raw)
In-Reply-To: <20200430115142.13430-1-peter.maydell@linaro.org>

From: Damien Hedde <damien.hedde@greensocs.com>

Add functions to easily handle clocks with devices.
Clock inputs and outputs should be used to handle clock propagation
between devices.
The API is very similar the GPIO API.

This is based on the original work of Frederic Konrad.

Signed-off-by: Damien Hedde <damien.hedde@greensocs.com>
Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-id: 20200406135251.157596-4-damien.hedde@greensocs.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/core/Makefile.objs   |   2 +-
 tests/Makefile.include  |   1 +
 include/hw/qdev-clock.h | 104 +++++++++++++++++++++++++
 include/hw/qdev-core.h  |  12 +++
 hw/core/qdev-clock.c    | 168 ++++++++++++++++++++++++++++++++++++++++
 hw/core/qdev.c          |  12 +++
 6 files changed, 298 insertions(+), 1 deletion(-)
 create mode 100644 include/hw/qdev-clock.h
 create mode 100644 hw/core/qdev-clock.c

diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs
index 115df550874..1d540ed6e78 100644
--- a/hw/core/Makefile.objs
+++ b/hw/core/Makefile.objs
@@ -7,7 +7,7 @@ common-obj-y += hotplug.o
 common-obj-y += vmstate-if.o
 # irq.o needed for qdev GPIO handling:
 common-obj-y += irq.o
-common-obj-y += clock.o
+common-obj-y += clock.o qdev-clock.o
 
 common-obj-$(CONFIG_SOFTMMU) += reset.o
 common-obj-$(CONFIG_SOFTMMU) += qdev-fw.o
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 51de6762983..03a74b60f6b 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -439,6 +439,7 @@ tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \
 	hw/core/fw-path-provider.o \
 	hw/core/reset.o \
 	hw/core/vmstate-if.o \
+	hw/core/clock.o hw/core/qdev-clock.o \
 	$(test-qapi-obj-y)
 tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
 	migration/vmstate.o migration/vmstate-types.o migration/qemu-file.o \
diff --git a/include/hw/qdev-clock.h b/include/hw/qdev-clock.h
new file mode 100644
index 00000000000..b3b3a3e021c
--- /dev/null
+++ b/include/hw/qdev-clock.h
@@ -0,0 +1,104 @@
+/*
+ * Device's clock input and output
+ *
+ * Copyright GreenSocs 2016-2020
+ *
+ * Authors:
+ *  Frederic Konrad
+ *  Damien Hedde
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef QDEV_CLOCK_H
+#define QDEV_CLOCK_H
+
+#include "hw/clock.h"
+
+/**
+ * qdev_init_clock_in:
+ * @dev: the device to add an input clock to
+ * @name: the name of the clock (can't be NULL).
+ * @callback: optional callback to be called on update or NULL.
+ * @opaque: argument for the callback
+ * @returns: a pointer to the newly added clock
+ *
+ * Add an input clock to device @dev as a clock named @name.
+ * This adds a child<> property.
+ * The callback will be called with @opaque as opaque parameter.
+ */
+Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
+                          ClockCallback *callback, void *opaque);
+
+/**
+ * qdev_init_clock_out:
+ * @dev: the device to add an output clock to
+ * @name: the name of the clock (can't be NULL).
+ * @returns: a pointer to the newly added clock
+ *
+ * Add an output clock to device @dev as a clock named @name.
+ * This adds a child<> property.
+ */
+Clock *qdev_init_clock_out(DeviceState *dev, const char *name);
+
+/**
+ * qdev_get_clock_in:
+ * @dev: the device which has the clock
+ * @name: the name of the clock (can't be NULL).
+ * @returns: a pointer to the clock
+ *
+ * Get the input clock @name from @dev or NULL if does not exist.
+ */
+Clock *qdev_get_clock_in(DeviceState *dev, const char *name);
+
+/**
+ * qdev_get_clock_out:
+ * @dev: the device which has the clock
+ * @name: the name of the clock (can't be NULL).
+ * @returns: a pointer to the clock
+ *
+ * Get the output clock @name from @dev or NULL if does not exist.
+ */
+Clock *qdev_get_clock_out(DeviceState *dev, const char *name);
+
+/**
+ * qdev_connect_clock_in:
+ * @dev: a device
+ * @name: the name of an input clock in @dev
+ * @source: the source clock (an output clock of another device for example)
+ *
+ * Set the source clock of input clock @name of device @dev to @source.
+ * @source period update will be propagated to @name clock.
+ */
+static inline void qdev_connect_clock_in(DeviceState *dev, const char *name,
+                                         Clock *source)
+{
+    clock_set_source(qdev_get_clock_in(dev, name), source);
+}
+
+/**
+ * qdev_alias_clock:
+ * @dev: the device which has the clock
+ * @name: the name of the clock in @dev (can't be NULL)
+ * @alias_dev: the device to add the clock
+ * @alias_name: the name of the clock in @container
+ * @returns: a pointer to the clock
+ *
+ * Add a clock @alias_name in @alias_dev which is an alias of the clock @name
+ * in @dev. The direction _in_ or _out_ will the same as the original.
+ * An alias clock must not be modified or used by @alias_dev and should
+ * typically be only only for device composition purpose.
+ */
+Clock *qdev_alias_clock(DeviceState *dev, const char *name,
+                        DeviceState *alias_dev, const char *alias_name);
+
+/**
+ * qdev_finalize_clocklist:
+ * @dev: the device being finalized
+ *
+ * Clear the clocklist from @dev. Only used internally in qdev.
+ */
+void qdev_finalize_clocklist(DeviceState *dev);
+
+#endif /* QDEV_CLOCK_H */
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index 1405b8a990a..d87d989e72a 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -149,6 +149,17 @@ struct NamedGPIOList {
     QLIST_ENTRY(NamedGPIOList) node;
 };
 
+typedef struct Clock Clock;
+typedef struct NamedClockList NamedClockList;
+
+struct NamedClockList {
+    char *name;
+    Clock *clock;
+    bool output;
+    bool alias;
+    QLIST_ENTRY(NamedClockList) node;
+};
+
 /**
  * DeviceState:
  * @realized: Indicates whether the device has been fully constructed.
@@ -171,6 +182,7 @@ struct DeviceState {
     bool allow_unplug_during_migration;
     BusState *parent_bus;
     QLIST_HEAD(, NamedGPIOList) gpios;
+    QLIST_HEAD(, NamedClockList) clocks;
     QLIST_HEAD(, BusState) child_bus;
     int num_child_bus;
     int instance_id_alias;
diff --git a/hw/core/qdev-clock.c b/hw/core/qdev-clock.c
new file mode 100644
index 00000000000..62035aef830
--- /dev/null
+++ b/hw/core/qdev-clock.c
@@ -0,0 +1,168 @@
+/*
+ * Device's clock input and output
+ *
+ * Copyright GreenSocs 2016-2020
+ *
+ * Authors:
+ *  Frederic Konrad
+ *  Damien Hedde
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/qdev-clock.h"
+#include "hw/qdev-core.h"
+#include "qapi/error.h"
+
+/*
+ * qdev_init_clocklist:
+ * Add a new clock in a device
+ */
+static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char *name,
+                                           bool output, Clock *clk)
+{
+    NamedClockList *ncl;
+
+    /*
+     * Clock must be added before realize() so that we can compute the
+     * clock's canonical path during device_realize().
+     */
+    assert(!dev->realized);
+
+    /*
+     * The ncl structure is freed by qdev_finalize_clocklist() which will
+     * be called during @dev's device_finalize().
+     */
+    ncl = g_new0(NamedClockList, 1);
+    ncl->name = g_strdup(name);
+    ncl->output = output;
+    ncl->alias = (clk != NULL);
+
+    /*
+     * Trying to create a clock whose name clashes with some other
+     * clock or property is a bug in the caller and we will abort().
+     */
+    if (clk == NULL) {
+        clk = CLOCK(object_new(TYPE_CLOCK));
+        object_property_add_child(OBJECT(dev), name, OBJECT(clk), &error_abort);
+        if (output) {
+            /*
+             * Remove object_new()'s initial reference.
+             * Note that for inputs, the reference created by object_new()
+             * will be deleted in qdev_finalize_clocklist().
+             */
+            object_unref(OBJECT(clk));
+        }
+    } else {
+        object_property_add_link(OBJECT(dev), name,
+                                 object_get_typename(OBJECT(clk)),
+                                 (Object **) &ncl->clock,
+                                 NULL, OBJ_PROP_LINK_STRONG, &error_abort);
+    }
+
+    ncl->clock = clk;
+
+    QLIST_INSERT_HEAD(&dev->clocks, ncl, node);
+    return ncl;
+}
+
+void qdev_finalize_clocklist(DeviceState *dev)
+{
+    /* called by @dev's device_finalize() */
+    NamedClockList *ncl, *ncl_next;
+
+    QLIST_FOREACH_SAFE(ncl, &dev->clocks, node, ncl_next) {
+        QLIST_REMOVE(ncl, node);
+        if (!ncl->output && !ncl->alias) {
+            /*
+             * We kept a reference on the input clock to ensure it lives up to
+             * this point so we can safely remove the callback.
+             * It avoids having a callback to a deleted object if ncl->clock
+             * is still referenced somewhere else (eg: by a clock output).
+             */
+            clock_clear_callback(ncl->clock);
+            object_unref(OBJECT(ncl->clock));
+        }
+        g_free(ncl->name);
+        g_free(ncl);
+    }
+}
+
+Clock *qdev_init_clock_out(DeviceState *dev, const char *name)
+{
+    NamedClockList *ncl;
+
+    assert(name);
+
+    ncl = qdev_init_clocklist(dev, name, true, NULL);
+
+    return ncl->clock;
+}
+
+Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
+                            ClockCallback *callback, void *opaque)
+{
+    NamedClockList *ncl;
+
+    assert(name);
+
+    ncl = qdev_init_clocklist(dev, name, false, NULL);
+
+    if (callback) {
+        clock_set_callback(ncl->clock, callback, opaque);
+    }
+    return ncl->clock;
+}
+
+static NamedClockList *qdev_get_clocklist(DeviceState *dev, const char *name)
+{
+    NamedClockList *ncl;
+
+    QLIST_FOREACH(ncl, &dev->clocks, node) {
+        if (strcmp(name, ncl->name) == 0) {
+            return ncl;
+        }
+    }
+
+    return NULL;
+}
+
+Clock *qdev_get_clock_in(DeviceState *dev, const char *name)
+{
+    NamedClockList *ncl;
+
+    assert(name);
+
+    ncl = qdev_get_clocklist(dev, name);
+    assert(!ncl->output);
+
+    return ncl->clock;
+}
+
+Clock *qdev_get_clock_out(DeviceState *dev, const char *name)
+{
+    NamedClockList *ncl;
+
+    assert(name);
+
+    ncl = qdev_get_clocklist(dev, name);
+    assert(ncl->output);
+
+    return ncl->clock;
+}
+
+Clock *qdev_alias_clock(DeviceState *dev, const char *name,
+                        DeviceState *alias_dev, const char *alias_name)
+{
+    NamedClockList *ncl;
+
+    assert(name && alias_name);
+
+    ncl = qdev_get_clocklist(dev, name);
+
+    qdev_init_clocklist(alias_dev, alias_name, ncl->output, ncl->clock);
+
+    return ncl->clock;
+}
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index 85f062def72..dd77a560672 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -37,6 +37,7 @@
 #include "hw/qdev-properties.h"
 #include "hw/boards.h"
 #include "hw/sysbus.h"
+#include "hw/qdev-clock.h"
 #include "migration/vmstate.h"
 #include "trace.h"
 
@@ -855,6 +856,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
     DeviceClass *dc = DEVICE_GET_CLASS(dev);
     HotplugHandler *hotplug_ctrl;
     BusState *bus;
+    NamedClockList *ncl;
     Error *local_err = NULL;
     bool unattached_parent = false;
     static int unattached_count;
@@ -902,6 +904,13 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
          */
         g_free(dev->canonical_path);
         dev->canonical_path = object_get_canonical_path(OBJECT(dev));
+        QLIST_FOREACH(ncl, &dev->clocks, node) {
+            if (ncl->alias) {
+                continue;
+            } else {
+                clock_setup_canonical_path(ncl->clock);
+            }
+        }
 
         if (qdev_get_vmsd(dev)) {
             if (vmstate_register_with_alias_id(VMSTATE_IF(dev),
@@ -1025,6 +1034,7 @@ static void device_initfn(Object *obj)
     dev->allow_unplug_during_migration = false;
 
     QLIST_INIT(&dev->gpios);
+    QLIST_INIT(&dev->clocks);
 }
 
 static void device_post_init(Object *obj)
@@ -1054,6 +1064,8 @@ static void device_finalize(Object *obj)
          */
     }
 
+    qdev_finalize_clocklist(dev);
+
     /* Only send event if the device had been completely realized */
     if (dev->pending_deleted_event) {
         g_assert(dev->canonical_path);
-- 
2.20.1



  parent reply	other threads:[~2020-04-30 11:58 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-04-30 11:51 [PULL 00/31] target-arm queue Peter Maydell
2020-04-30 11:51 ` [PULL 01/31] dma/xlnx-zdma: Fix descriptor loading (MEM) wrt endianness Peter Maydell
2020-04-30 11:51 ` [PULL 02/31] dma/xlnx-zdma: Fix descriptor loading (REG) " Peter Maydell
2020-04-30 11:51 ` [PULL 03/31] nrf51: Fix last GPIO CNF address Peter Maydell
2020-04-30 11:51 ` [PULL 04/31] bugfix: Use gicr_typer in arm_gicv3_icc_reset Peter Maydell
2020-04-30 11:51 ` [PULL 05/31] Typo: Correct the name of CPU hotplug memory region Peter Maydell
2020-04-30 11:51 ` [PULL 06/31] hw/net: Add Smartfusion2 emac block Peter Maydell
2020-04-30 11:51 ` [PULL 07/31] msf2: Add EMAC block to SmartFusion2 SoC Peter Maydell
2020-04-30 11:51 ` [PULL 08/31] tests/boot_linux_console: Add ethernet test to SmartFusion2 Peter Maydell
2020-04-30 11:51 ` [PULL 09/31] hw/core/clock: introduce clock object Peter Maydell
2020-04-30 14:35   ` Peter Maydell
2020-10-17 11:47   ` Philippe Mathieu-Daudé
2020-10-20 16:06     ` Philippe Mathieu-Daudé
2020-10-20 16:46       ` Peter Maydell
2020-10-20 17:46         ` Philippe Mathieu-Daudé
2020-04-30 11:51 ` [PULL 10/31] hw/core/clock-vmstate: define a vmstate entry for clock state Peter Maydell
2020-04-30 11:51 ` Peter Maydell [this message]
2020-04-30 11:51 ` [PULL 12/31] qdev-clock: introduce an init array to ease the device construction Peter Maydell
2020-04-30 11:51 ` [PULL 13/31] docs/clocks: add device's clock documentation Peter Maydell
2020-04-30 11:51 ` [PULL 14/31] hw/misc/zynq_slcr: add clock generation for uarts Peter Maydell
2020-04-30 11:51 ` [PULL 15/31] hw/char/cadence_uart: add clock support Peter Maydell
2020-04-30 11:51 ` [PULL 16/31] hw/arm/xilinx_zynq: connect uart clocks to slcr Peter Maydell
2020-04-30 11:51 ` [PULL 17/31] qdev-monitor: print the device's clock with info qtree Peter Maydell
2020-04-30 11:51 ` [PULL 18/31] hw/arm: versal: Setup the ADMA with 128bit bus-width Peter Maydell
2020-04-30 11:51 ` [PULL 19/31] Cadence: gem: fix wraparound in 64bit descriptors Peter Maydell
2020-04-30 11:51 ` [PULL 20/31] net: cadence_gem: clear RX control descriptor Peter Maydell
2020-04-30 11:51 ` [PULL 21/31] target/arm: Vectorize integer comparison vs zero Peter Maydell
2020-04-30 11:51 ` [PULL 22/31] hw/arm/virt: dt: move creation of /secure-chosen to create_fdt() Peter Maydell
2020-04-30 11:51 ` [PULL 23/31] hw/arm/virt: dt: add kaslr-seed property Peter Maydell
2020-04-30 11:51 ` [PULL 24/31] target/arm: Restrict the Address Translate write operation to TCG accel Peter Maydell
2020-04-30 11:51 ` [PULL 25/31] target/arm: Make cpu_register() available for other files Peter Maydell
2020-04-30 11:51 ` [PULL 26/31] target/arm/cpu: Use ARRAY_SIZE() to iterate over ARMCPUInfo[] Peter Maydell
2020-04-30 14:30   ` Peter Maydell
2020-04-30 11:51 ` [PULL 27/31] target/arm/cpu: Update coding style to make checkpatch.pl happy Peter Maydell
2020-04-30 14:52   ` Philippe Mathieu-Daudé
2020-04-30 11:51 ` [PULL 28/31] device_tree: Allow name wildcards in qemu_fdt_node_path() Peter Maydell
2020-04-30 11:51 ` [PULL 29/31] device_tree: Constify compat " Peter Maydell
2020-04-30 11:51 ` [PULL 30/31] hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102 Peter Maydell
2020-04-30 11:51 ` [PULL 31/31] hw/arm: xlnx-zcu102: Disable unsupported FDT firmware nodes Peter Maydell
2020-05-01  2:05 ` [PULL 00/31] target-arm queue 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=20200430115142.13430-12-peter.maydell@linaro.org \
    --to=peter.maydell@linaro.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).