123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- /*
- * 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 "qemu/error-report.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));
- 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);
- /*
- * Since the link property has the OBJ_PROP_LINK_STRONG flag, the clk
- * object reference count gets decremented on property deletion.
- * However object_property_add_link does not increment it since it
- * doesn't know the linked object. Increment it here to ensure the
- * aliased clock stays alive during this device life-time.
- */
- object_ref(OBJECT(clk));
- }
- 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,
- unsigned int events)
- {
- NamedClockList *ncl;
- assert(name);
- ncl = qdev_init_clocklist(dev, name, false, NULL);
- if (callback) {
- clock_set_callback(ncl->clock, callback, opaque, events);
- }
- return ncl->clock;
- }
- void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks)
- {
- const struct ClockPortInitElem *elem;
- for (elem = &clocks[0]; elem->name != NULL; elem++) {
- Clock **clkp;
- /* offset cannot be inside the DeviceState part */
- assert(elem->offset > sizeof(DeviceState));
- clkp = (Clock **)(((void *) dev) + elem->offset);
- if (elem->is_output) {
- *clkp = qdev_init_clock_out(dev, elem->name);
- } else {
- *clkp = qdev_init_clock_in(dev, elem->name, elem->callback, dev,
- elem->callback_events);
- }
- }
- }
- 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);
- if (!ncl) {
- error_report("Can not find clock-in '%s' for device type '%s'",
- name, object_get_typename(OBJECT(dev)));
- abort();
- }
- 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);
- if (!ncl) {
- error_report("Can not find clock-out '%s' for device type '%s'",
- name, object_get_typename(OBJECT(dev)));
- abort();
- }
- 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;
- }
- void qdev_connect_clock_in(DeviceState *dev, const char *name, Clock *source)
- {
- assert(!dev->realized);
- clock_set_source(qdev_get_clock_in(dev, name), source);
- }
|