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 alias, 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->alias = alias; 42 ncl->output = output; 43 ncl->clock = clk; 44 45 QLIST_INSERT_HEAD(&dev->clocks, ncl, node); 46 return ncl; 47 } 48 49 void qdev_finalize_clocklist(DeviceState *dev) 50 { 51 /* called by @dev's device_finalize() */ 52 NamedClockList *ncl, *ncl_next; 53 54 QLIST_FOREACH_SAFE(ncl, &dev->clocks, node, ncl_next) { 55 QLIST_REMOVE(ncl, node); 56 if (!ncl->alias) { 57 /* 58 * We kept a reference on the input clock to ensure it lives up to 59 * this point; it is used by the monitor to show the frequency. 60 */ 61 object_unref(OBJECT(ncl->clock)); 62 } 63 g_free(ncl->name); 64 g_free(ncl); 65 } 66 } 67 68 Clock *qdev_init_clock_out(DeviceState *dev, const char *name) 69 { 70 Clock *clk = CLOCK(object_new(TYPE_CLOCK)); 71 object_property_add_child(OBJECT(dev), name, OBJECT(clk)); 72 73 qdev_init_clocklist(dev, name, false, true, clk); 74 return clk; 75 } 76 77 Clock *qdev_init_clock_in(DeviceState *dev, const char *name, 78 ClockCallback *callback, void *opaque, 79 unsigned int events) 80 { 81 Clock *clk = CLOCK(object_new(TYPE_CLOCK)); 82 object_property_add_child(OBJECT(dev), name, OBJECT(clk)); 83 84 qdev_init_clocklist(dev, name, false, false, clk); 85 if (callback) { 86 clock_set_callback(clk, callback, opaque, events); 87 } 88 return clk; 89 } 90 91 void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks) 92 { 93 const struct ClockPortInitElem *elem; 94 95 for (elem = &clocks[0]; elem->name != NULL; elem++) { 96 Clock **clkp; 97 /* offset cannot be inside the DeviceState part */ 98 assert(elem->offset > sizeof(DeviceState)); 99 clkp = ((void *)dev) + elem->offset; 100 if (elem->is_output) { 101 *clkp = qdev_init_clock_out(dev, elem->name); 102 } else { 103 *clkp = qdev_init_clock_in(dev, elem->name, elem->callback, dev, 104 elem->callback_events); 105 } 106 } 107 } 108 109 static NamedClockList *qdev_get_clocklist(DeviceState *dev, const char *name) 110 { 111 NamedClockList *ncl; 112 113 QLIST_FOREACH(ncl, &dev->clocks, node) { 114 if (strcmp(name, ncl->name) == 0) { 115 return ncl; 116 } 117 } 118 119 return NULL; 120 } 121 122 Clock *qdev_get_clock_in(DeviceState *dev, const char *name) 123 { 124 NamedClockList *ncl; 125 126 assert(name); 127 128 ncl = qdev_get_clocklist(dev, name); 129 if (!ncl) { 130 error_report("Can not find clock-in '%s' for device type '%s'", 131 name, object_get_typename(OBJECT(dev))); 132 abort(); 133 } 134 assert(!ncl->output); 135 136 return ncl->clock; 137 } 138 139 Clock *qdev_get_clock_out(DeviceState *dev, const char *name) 140 { 141 NamedClockList *ncl; 142 143 assert(name); 144 145 ncl = qdev_get_clocklist(dev, name); 146 if (!ncl) { 147 error_report("Can not find clock-out '%s' for device type '%s'", 148 name, object_get_typename(OBJECT(dev))); 149 abort(); 150 } 151 assert(ncl->output); 152 153 return ncl->clock; 154 } 155 156 Clock *qdev_alias_clock(DeviceState *dev, const char *name, 157 DeviceState *alias_dev, const char *alias_name) 158 { 159 NamedClockList *ncl = qdev_get_clocklist(dev, name); 160 Clock *clk = ncl->clock; 161 162 ncl = qdev_init_clocklist(alias_dev, alias_name, true, ncl->output, clk); 163 164 object_property_add_link(OBJECT(alias_dev), alias_name, 165 TYPE_CLOCK, 166 (Object **) &ncl->clock, 167 NULL, OBJ_PROP_LINK_STRONG); 168 /* 169 * Since the link property has the OBJ_PROP_LINK_STRONG flag, the clk 170 * object reference count gets decremented on property deletion. 171 * However object_property_add_link does not increment it since it 172 * doesn't know the linked object. Increment it here to ensure the 173 * aliased clock stays alive during this device life-time. 174 */ 175 object_ref(OBJECT(clk)); 176 177 return clk; 178 } 179 180 void qdev_connect_clock_in(DeviceState *dev, const char *name, Clock *source) 181 { 182 assert(!dev->realized); 183 clock_set_source(qdev_get_clock_in(dev, name), source); 184 } 185