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