1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
|
/*
* 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 alias, 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->alias = alias;
ncl->output = output;
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->alias) {
/*
* We kept a reference on the input clock to ensure it lives up to
* this point; it is used by the monitor to show the frequency.
*/
object_unref(OBJECT(ncl->clock));
}
g_free(ncl->name);
g_free(ncl);
}
}
Clock *qdev_init_clock_out(DeviceState *dev, const char *name)
{
Clock *clk = CLOCK(object_new(TYPE_CLOCK));
object_property_add_child(OBJECT(dev), name, OBJECT(clk));
qdev_init_clocklist(dev, name, false, true, clk);
return clk;
}
Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
ClockCallback *callback, void *opaque,
unsigned int events)
{
Clock *clk = CLOCK(object_new(TYPE_CLOCK));
object_property_add_child(OBJECT(dev), name, OBJECT(clk));
qdev_init_clocklist(dev, name, false, false, clk);
if (callback) {
clock_set_callback(clk, callback, opaque, events);
}
return clk;
}
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 = ((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 = qdev_get_clocklist(dev, name);
Clock *clk = ncl->clock;
ncl = qdev_init_clocklist(alias_dev, alias_name, true, ncl->output, clk);
object_property_add_link(OBJECT(alias_dev), alias_name,
TYPE_CLOCK,
(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));
return clk;
}
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);
}
|