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 { 116 NamedClockList *ncl; 117 118 assert(name); 119 120 ncl = qdev_init_clocklist(dev, name, false, NULL); 121 122 if (callback) { 123 clock_set_callback(ncl->clock, callback, opaque); 124 } 125 return ncl->clock; 126 } 127 128 void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks) 129 { 130 const struct ClockPortInitElem *elem; 131 132 for (elem = &clocks[0]; elem->name != NULL; elem++) { 133 Clock **clkp; 134 /* offset cannot be inside the DeviceState part */ 135 assert(elem->offset > sizeof(DeviceState)); 136 clkp = (Clock **)(((void *) dev) + elem->offset); 137 if (elem->is_output) { 138 *clkp = qdev_init_clock_out(dev, elem->name); 139 } else { 140 *clkp = qdev_init_clock_in(dev, elem->name, elem->callback, dev); 141 } 142 } 143 } 144 145 static NamedClockList *qdev_get_clocklist(DeviceState *dev, const char *name) 146 { 147 NamedClockList *ncl; 148 149 QLIST_FOREACH(ncl, &dev->clocks, node) { 150 if (strcmp(name, ncl->name) == 0) { 151 return ncl; 152 } 153 } 154 155 return NULL; 156 } 157 158 Clock *qdev_get_clock_in(DeviceState *dev, const char *name) 159 { 160 NamedClockList *ncl; 161 162 assert(name); 163 164 ncl = qdev_get_clocklist(dev, name); 165 if (!ncl) { 166 error_report("Can not find clock-in '%s' for device type '%s'", 167 name, object_get_typename(OBJECT(dev))); 168 abort(); 169 } 170 assert(!ncl->output); 171 172 return ncl->clock; 173 } 174 175 Clock *qdev_get_clock_out(DeviceState *dev, const char *name) 176 { 177 NamedClockList *ncl; 178 179 assert(name); 180 181 ncl = qdev_get_clocklist(dev, name); 182 if (!ncl) { 183 error_report("Can not find clock-out '%s' for device type '%s'", 184 name, object_get_typename(OBJECT(dev))); 185 abort(); 186 } 187 assert(ncl->output); 188 189 return ncl->clock; 190 } 191 192 Clock *qdev_alias_clock(DeviceState *dev, const char *name, 193 DeviceState *alias_dev, const char *alias_name) 194 { 195 NamedClockList *ncl; 196 197 assert(name && alias_name); 198 199 ncl = qdev_get_clocklist(dev, name); 200 201 qdev_init_clocklist(alias_dev, alias_name, ncl->output, ncl->clock); 202 203 return ncl->clock; 204 } 205 206 void qdev_connect_clock_in(DeviceState *dev, const char *name, Clock *source) 207 { 208 assert(!dev->realized); 209 clock_set_source(qdev_get_clock_in(dev, name), source); 210 } 211