aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/core/Makefile.objs2
-rw-r--r--hw/core/qdev-clock.c168
-rw-r--r--hw/core/qdev.c12
-rw-r--r--include/hw/qdev-clock.h104
-rw-r--r--include/hw/qdev-core.h12
-rw-r--r--tests/Makefile.include1
6 files changed, 298 insertions, 1 deletions
diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs
index 115df55087..1d540ed6e7 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/hw/core/qdev-clock.c b/hw/core/qdev-clock.c
new file mode 100644
index 0000000000..62035aef83
--- /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 85f062def7..dd77a56067 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);
diff --git a/include/hw/qdev-clock.h b/include/hw/qdev-clock.h
new file mode 100644
index 0000000000..b3b3a3e021
--- /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 1405b8a990..d87d989e72 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/tests/Makefile.include b/tests/Makefile.include
index 51de676298..03a74b60f6 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 \