1 /* 2 * Hardware Clocks 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/cutils.h" 16 #include "qapi/visitor.h" 17 #include "sysemu/qtest.h" 18 #include "hw/clock.h" 19 #include "trace.h" 20 21 #define CLOCK_PATH(_clk) (_clk->canonical_path) 22 23 void clock_setup_canonical_path(Clock *clk) 24 { 25 g_free(clk->canonical_path); 26 clk->canonical_path = object_get_canonical_path(OBJECT(clk)); 27 } 28 29 Clock *clock_new(Object *parent, const char *name) 30 { 31 Object *obj; 32 Clock *clk; 33 34 obj = object_new(TYPE_CLOCK); 35 object_property_add_child(parent, name, obj); 36 object_unref(obj); 37 38 clk = CLOCK(obj); 39 clock_setup_canonical_path(clk); 40 41 return clk; 42 } 43 44 void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque, 45 unsigned int events) 46 { 47 assert(OBJECT(clk)->parent); 48 clk->callback = cb; 49 clk->callback_opaque = opaque; 50 clk->callback_events = events; 51 } 52 53 bool clock_set(Clock *clk, uint64_t period) 54 { 55 if (clk->period == period) { 56 return false; 57 } 58 trace_clock_set(CLOCK_PATH(clk), CLOCK_PERIOD_TO_HZ(clk->period), 59 CLOCK_PERIOD_TO_HZ(period)); 60 clk->period = period; 61 62 return true; 63 } 64 65 static uint64_t clock_get_child_period(Clock *clk) 66 { 67 /* 68 * Return the period to be used for child clocks, which is the parent 69 * clock period adjusted for multiplier and divider effects. 70 */ 71 return muldiv64(clk->period, clk->multiplier, clk->divider); 72 } 73 74 static void clock_call_callback(Clock *clk, ClockEvent event) 75 { 76 /* 77 * Call the Clock's callback for this event, if it has one and 78 * is interested in this event. 79 */ 80 if (clk->callback && (clk->callback_events & event)) { 81 clk->callback(clk->callback_opaque, event); 82 } 83 } 84 85 static void clock_propagate_period(Clock *clk, bool call_callbacks) 86 { 87 Clock *child; 88 uint64_t child_period = clock_get_child_period(clk); 89 90 QLIST_FOREACH(child, &clk->children, sibling) { 91 if (child->period != child_period) { 92 if (call_callbacks) { 93 clock_call_callback(child, ClockPreUpdate); 94 } 95 child->period = child_period; 96 trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk), 97 CLOCK_PERIOD_TO_HZ(child->period), 98 call_callbacks); 99 if (call_callbacks) { 100 clock_call_callback(child, ClockUpdate); 101 } 102 clock_propagate_period(child, call_callbacks); 103 } 104 } 105 } 106 107 void clock_propagate(Clock *clk) 108 { 109 trace_clock_propagate(CLOCK_PATH(clk)); 110 clock_propagate_period(clk, true); 111 } 112 113 void clock_set_source(Clock *clk, Clock *src) 114 { 115 /* changing clock source is not supported */ 116 assert(!clk->source); 117 118 trace_clock_set_source(CLOCK_PATH(clk), CLOCK_PATH(src)); 119 120 clk->period = clock_get_child_period(src); 121 QLIST_INSERT_HEAD(&src->children, clk, sibling); 122 clk->source = src; 123 clock_propagate_period(clk, false); 124 } 125 126 static void clock_disconnect(Clock *clk) 127 { 128 if (clk->source == NULL) { 129 return; 130 } 131 132 trace_clock_disconnect(CLOCK_PATH(clk)); 133 134 clk->source = NULL; 135 QLIST_REMOVE(clk, sibling); 136 } 137 138 char *clock_display_freq(Clock *clk) 139 { 140 return freq_to_str(clock_get_hz(clk)); 141 } 142 143 bool clock_set_mul_div(Clock *clk, uint32_t multiplier, uint32_t divider) 144 { 145 assert(divider != 0); 146 147 if (clk->multiplier == multiplier && clk->divider == divider) { 148 return false; 149 } 150 151 trace_clock_set_mul_div(CLOCK_PATH(clk), clk->multiplier, multiplier, 152 clk->divider, divider); 153 clk->multiplier = multiplier; 154 clk->divider = divider; 155 156 return true; 157 } 158 159 static void clock_period_prop_get(Object *obj, Visitor *v, const char *name, 160 void *opaque, Error **errp) 161 { 162 Clock *clk = CLOCK(obj); 163 uint64_t period = clock_get(clk); 164 visit_type_uint64(v, name, &period, errp); 165 } 166 167 static void clock_unparent(Object *obj) 168 { 169 /* 170 * Callback are registered by the parent, which might die anytime after 171 * it's unparented the children. Avoid having a callback to a deleted 172 * object in case the clock is still referenced somewhere else (eg: by 173 * a clock output). 174 */ 175 clock_set_callback(CLOCK(obj), NULL, NULL, 0); 176 } 177 178 static void clock_initfn(Object *obj) 179 { 180 Clock *clk = CLOCK(obj); 181 182 clk->multiplier = 1; 183 clk->divider = 1; 184 185 QLIST_INIT(&clk->children); 186 187 if (qtest_enabled()) { 188 object_property_add(obj, "qtest-clock-period", "uint64", 189 clock_period_prop_get, NULL, NULL, NULL); 190 } 191 } 192 193 static void clock_finalizefn(Object *obj) 194 { 195 Clock *clk = CLOCK(obj); 196 Clock *child, *next; 197 198 /* clear our list of children */ 199 QLIST_FOREACH_SAFE(child, &clk->children, sibling, next) { 200 clock_disconnect(child); 201 } 202 203 /* remove us from source's children list */ 204 clock_disconnect(clk); 205 206 g_free(clk->canonical_path); 207 } 208 209 static void clock_class_init(ObjectClass *klass, void *data) 210 { 211 klass->unparent = clock_unparent; 212 } 213 214 static const TypeInfo clock_info = { 215 .name = TYPE_CLOCK, 216 .parent = TYPE_OBJECT, 217 .instance_size = sizeof(Clock), 218 .instance_init = clock_initfn, 219 .class_init = clock_class_init, 220 .instance_finalize = clock_finalizefn, 221 }; 222 223 static void clock_register_types(void) 224 { 225 type_register_static(&clock_info); 226 } 227 228 type_init(clock_register_types) 229