diff options
Diffstat (limited to 'hw/core')
-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 |
3 files changed, 181 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); |