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 void clock_call_callback(Clock *clk, ClockEvent event) 68 { 69 /* 70 * Call the Clock's callback for this event, if it has one and 71 * is interested in this event. 72 */ 73 if (clk->callback && (clk->callback_events & event)) { 74 clk->callback(clk->callback_opaque, event); 75 } 76 } 77 78 static void clock_propagate_period(Clock *clk, bool call_callbacks) 79 { 80 Clock *child; 81 82 QLIST_FOREACH(child, &clk->children, sibling) { 83 if (child->period != clk->period) { 84 if (call_callbacks) { 85 clock_call_callback(child, ClockPreUpdate); 86 } 87 child->period = clk->period; 88 trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk), 89 CLOCK_PERIOD_TO_HZ(clk->period), 90 call_callbacks); 91 if (call_callbacks) { 92 clock_call_callback(child, ClockUpdate); 93 } 94 clock_propagate_period(child, call_callbacks); 95 } 96 } 97 } 98 99 void clock_propagate(Clock *clk) 100 { 101 assert(clk->source == NULL); 102 trace_clock_propagate(CLOCK_PATH(clk)); 103 clock_propagate_period(clk, true); 104 } 105 106 void clock_set_source(Clock *clk, Clock *src) 107 { 108 /* changing clock source is not supported */ 109 assert(!clk->source); 110 111 trace_clock_set_source(CLOCK_PATH(clk), CLOCK_PATH(src)); 112 113 clk->period = src->period; 114 QLIST_INSERT_HEAD(&src->children, clk, sibling); 115 clk->source = src; 116 clock_propagate_period(clk, false); 117 } 118 119 static void clock_disconnect(Clock *clk) 120 { 121 if (clk->source == NULL) { 122 return; 123 } 124 125 trace_clock_disconnect(CLOCK_PATH(clk)); 126 127 clk->source = NULL; 128 QLIST_REMOVE(clk, sibling); 129 } 130 131 char *clock_display_freq(Clock *clk) 132 { 133 return freq_to_str(clock_get_hz(clk)); 134 } 135 136 static void clock_initfn(Object *obj) 137 { 138 Clock *clk = CLOCK(obj); 139 140 QLIST_INIT(&clk->children); 141 } 142 143 static void clock_finalizefn(Object *obj) 144 { 145 Clock *clk = CLOCK(obj); 146 Clock *child, *next; 147 148 /* clear our list of children */ 149 QLIST_FOREACH_SAFE(child, &clk->children, sibling, next) { 150 clock_disconnect(child); 151 } 152 153 /* remove us from source's children list */ 154 clock_disconnect(clk); 155 156 g_free(clk->canonical_path); 157 } 158 159 static const TypeInfo clock_info = { 160 .name = TYPE_CLOCK, 161 .parent = TYPE_OBJECT, 162 .instance_size = sizeof(Clock), 163 .instance_init = clock_initfn, 164 .instance_finalize = clock_finalizefn, 165 }; 166 167 static void clock_register_types(void) 168 { 169 type_register_static(&clock_info); 170 } 171 172 type_init(clock_register_types) 173