diff options
-rw-r--r-- | hw/core/Makefile.objs | 2 | ||||
-rw-r--r-- | hw/core/qdev-clock.c | 168 | ||||
-rw-r--r-- | hw/core/qdev.c | 12 | ||||
-rw-r--r-- | include/hw/qdev-clock.h | 104 | ||||
-rw-r--r-- | include/hw/qdev-core.h | 12 | ||||
-rw-r--r-- | tests/Makefile.include | 1 |
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 \ |