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