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