1 /* 2 * Device's clock input and output 3 * 4 * Copyright GreenSocs 2016-2020 5 * 6 * Authors: 7 * Frederic Konrad 8 * Damien Hedde 9 * 10 * This work is licensed under the terms of the GNU GPL, version 2 or later. 11 * See the COPYING file in the top-level directory. 12 */ 13 14 #include "qemu/osdep.h" 15 #include "qemu/error-report.h" 16 #include "hw/qdev-clock.h" 17 #include "hw/qdev-core.h" 18 #include "qapi/error.h" 19 20 /* 21 * qdev_init_clocklist: 22 * Add a new clock in a device 23 */ 24 static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char *name, 25 bool output, Clock *clk) 26 { 27 NamedClockList *ncl; 28 29 /* 30 * Clock must be added before realize() so that we can compute the 31 * clock's canonical path during device_realize(). 32 */ 33 assert(!dev->realized); 34 35 /* 36 * The ncl structure is freed by qdev_finalize_clocklist() which will 37 * be called during @dev's device_finalize(). 38 */ 39 ncl = g_new0(NamedClockList, 1); 40 ncl->name = g_strdup(name); 41 ncl->output = output; 42 ncl->alias = (clk != NULL); 43 44 /* 45 * Trying to create a clock whose name clashes with some other 46 * clock or property is a bug in the caller and we will abort(). 47 */ 48 if (clk == NULL) { 49 clk = CLOCK(object_new(TYPE_CLOCK)); 50 object_property_add_child(OBJECT(dev), name, OBJECT(clk)); 51 if (output) { 52 /* 53 * Remove object_new()'s initial reference. 54 * Note that for inputs, the reference created by object_new() 55 * will be deleted in qdev_finalize_clocklist(). 56 */ 57 object_unref(OBJECT(clk)); 58 } 59 } else { 60 object_property_add_link(OBJECT(dev), name, 61 object_get_typename(OBJECT(clk)), 62 (Object **) &ncl->clock, 63 NULL, OBJ_PROP_LINK_STRONG); 64 /* 65 * Since the link property has the OBJ_PROP_LINK_STRONG flag, the clk 66 * object reference count gets decremented on property deletion. 67 * However object_property_add_link does not increment it since it 68 * doesn't know the linked object. Increment it here to ensure the 69 * aliased clock stays alive during this device life-time. 70 */ 71 object_ref(OBJECT(clk)); 72 } 73 74 ncl->clock = clk; 75 76 QLIST_INSERT_HEAD(&dev->clocks, ncl, node); 77 return ncl; 78 } 79 80 void qdev_finalize_clocklist(DeviceState *dev) 81 { 82 /* called by @dev's device_finalize() */ 83 NamedClockList *ncl, *ncl_next; 84 85 QLIST_FOREACH_SAFE(ncl, &dev->clocks, node, ncl_next) { 86 QLIST_REMOVE(ncl, node); 87 if (!ncl->output && !ncl->alias) { 88 /* 89 * We kept a reference on the input clock to ensure it lives up to 90 * this point so we can safely remove the callback. 91 * It avoids having a callback to a deleted object if ncl->clock 92 * is still referenced somewhere else (eg: by a clock output). 93 */ 94 clock_clear_callback(ncl->clock); 95 object_unref(OBJECT(ncl->clock)); 96 } 97 g_free(ncl->name); 98 g_free(ncl); 99 } 100 } 101 102 Clock *qdev_init_clock_out(DeviceState *dev, const char *name) 103 { 104 NamedClockList *ncl; 105 106 assert(name); 107 108 ncl = qdev_init_clocklist(dev, name, true, NULL); 109 110 return ncl->clock; 111 } 112 113 Clock *qdev_init_clock_in(DeviceState *dev, const char *name, 114 ClockCallback *callback, void *opaque, 115 unsigned int events) 116 { 117 NamedClockList *ncl; 118 119 assert(name); 120 121 ncl = qdev_init_clocklist(dev, name, false, NULL); 122 123 if (callback) { 124 clock_set_callback(ncl->clock, callback, opaque, events); 125 } 126 return ncl->clock; 127 } 128 129 void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks) 130 { 131 const struct ClockPortInitElem *elem; 132 133 for (elem = &clocks[0]; elem->name != NULL; elem++) { 134 Clock **clkp; 135 /* offset cannot be inside the DeviceState part */ 136 assert(elem->offset > sizeof(DeviceState)); 137 clkp = ((void *)dev) + elem->offset; 138 if (elem->is_output) { 139 *clkp = qdev_init_clock_out(dev, elem->name); 140 } else { 141 *clkp = qdev_init_clock_in(dev, elem->name, elem->callback, dev, 142 elem->callback_events); 143 } 144 } 145 } 146 147 static NamedClockList *qdev_get_clocklist(DeviceState *dev, const char *name) 148 { 149 NamedClockList *ncl; 150 151 QLIST_FOREACH(ncl, &dev->clocks, node) { 152 if (strcmp(name, ncl->name) == 0) { 153 return ncl; 154 } 155 } 156 157 return NULL; 158 } 159 160 Clock *qdev_get_clock_in(DeviceState *dev, const char *name) 161 { 162 NamedClockList *ncl; 163 164 assert(name); 165 166 ncl = qdev_get_clocklist(dev, name); 167 if (!ncl) { 168 error_report("Can not find clock-in '%s' for device type '%s'", 169 name, object_get_typename(OBJECT(dev))); 170 abort(); 171 } 172 assert(!ncl->output); 173 174 return ncl->clock; 175 } 176 177 Clock *qdev_get_clock_out(DeviceState *dev, const char *name) 178 { 179 NamedClockList *ncl; 180 181 assert(name); 182 183 ncl = qdev_get_clocklist(dev, name); 184 if (!ncl) { 185 error_report("Can not find clock-out '%s' for device type '%s'", 186 name, object_get_typename(OBJECT(dev))); 187 abort(); 188 } 189 assert(ncl->output); 190 191 return ncl->clock; 192 } 193 194 Clock *qdev_alias_clock(DeviceState *dev, const char *name, 195 DeviceState *alias_dev, const char *alias_name) 196 { 197 NamedClockList *ncl; 198 199 assert(name && alias_name); 200 201 ncl = qdev_get_clocklist(dev, name); 202 203 qdev_init_clocklist(alias_dev, alias_name, ncl->output, ncl->clock); 204 205 return ncl->clock; 206 } 207 208 void qdev_connect_clock_in(DeviceState *dev, const char *name, Clock *source) 209 { 210 assert(!dev->realized); 211 clock_set_source(qdev_get_clock_in(dev, name), source); 212 } 213