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 66 ncl->clock = clk; 67 68 QLIST_INSERT_HEAD(&dev->clocks, ncl, node); 69 return ncl; 70 } 71 72 void qdev_finalize_clocklist(DeviceState *dev) 73 { 74 /* called by @dev's device_finalize() */ 75 NamedClockList *ncl, *ncl_next; 76 77 QLIST_FOREACH_SAFE(ncl, &dev->clocks, node, ncl_next) { 78 QLIST_REMOVE(ncl, node); 79 if (!ncl->output && !ncl->alias) { 80 /* 81 * We kept a reference on the input clock to ensure it lives up to 82 * this point so we can safely remove the callback. 83 * It avoids having a callback to a deleted object if ncl->clock 84 * is still referenced somewhere else (eg: by a clock output). 85 */ 86 clock_clear_callback(ncl->clock); 87 object_unref(OBJECT(ncl->clock)); 88 } 89 g_free(ncl->name); 90 g_free(ncl); 91 } 92 } 93 94 Clock *qdev_init_clock_out(DeviceState *dev, const char *name) 95 { 96 NamedClockList *ncl; 97 98 assert(name); 99 100 ncl = qdev_init_clocklist(dev, name, true, NULL); 101 102 return ncl->clock; 103 } 104 105 Clock *qdev_init_clock_in(DeviceState *dev, const char *name, 106 ClockCallback *callback, void *opaque) 107 { 108 NamedClockList *ncl; 109 110 assert(name); 111 112 ncl = qdev_init_clocklist(dev, name, false, NULL); 113 114 if (callback) { 115 clock_set_callback(ncl->clock, callback, opaque); 116 } 117 return ncl->clock; 118 } 119 120 void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks) 121 { 122 const struct ClockPortInitElem *elem; 123 124 for (elem = &clocks[0]; elem->name != NULL; elem++) { 125 Clock **clkp; 126 /* offset cannot be inside the DeviceState part */ 127 assert(elem->offset > sizeof(DeviceState)); 128 clkp = (Clock **)(((void *) dev) + elem->offset); 129 if (elem->is_output) { 130 *clkp = qdev_init_clock_out(dev, elem->name); 131 } else { 132 *clkp = qdev_init_clock_in(dev, elem->name, elem->callback, dev); 133 } 134 } 135 } 136 137 static NamedClockList *qdev_get_clocklist(DeviceState *dev, const char *name) 138 { 139 NamedClockList *ncl; 140 141 QLIST_FOREACH(ncl, &dev->clocks, node) { 142 if (strcmp(name, ncl->name) == 0) { 143 return ncl; 144 } 145 } 146 147 return NULL; 148 } 149 150 Clock *qdev_get_clock_in(DeviceState *dev, const char *name) 151 { 152 NamedClockList *ncl; 153 154 assert(name); 155 156 ncl = qdev_get_clocklist(dev, name); 157 if (!ncl) { 158 error_report("Can not find clock-in '%s' for device type '%s'", 159 name, object_get_typename(OBJECT(dev))); 160 abort(); 161 } 162 assert(!ncl->output); 163 164 return ncl->clock; 165 } 166 167 Clock *qdev_get_clock_out(DeviceState *dev, const char *name) 168 { 169 NamedClockList *ncl; 170 171 assert(name); 172 173 ncl = qdev_get_clocklist(dev, name); 174 if (!ncl) { 175 error_report("Can not find clock-out '%s' for device type '%s'", 176 name, object_get_typename(OBJECT(dev))); 177 abort(); 178 } 179 assert(ncl->output); 180 181 return ncl->clock; 182 } 183 184 Clock *qdev_alias_clock(DeviceState *dev, const char *name, 185 DeviceState *alias_dev, const char *alias_name) 186 { 187 NamedClockList *ncl; 188 189 assert(name && alias_name); 190 191 ncl = qdev_get_clocklist(dev, name); 192 193 qdev_init_clocklist(alias_dev, alias_name, ncl->output, ncl->clock); 194 195 return ncl->clock; 196 } 197 198 void qdev_connect_clock_in(DeviceState *dev, const char *name, Clock *source) 199 { 200 assert(!dev->realized); 201 clock_set_source(qdev_get_clock_in(dev, name), source); 202 } 203