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 "hw/clock.h" 16 #include "trace.h" 17 18 #define CLOCK_PATH(_clk) (_clk->canonical_path) 19 20 void clock_setup_canonical_path(Clock *clk) 21 { 22 g_free(clk->canonical_path); 23 clk->canonical_path = object_get_canonical_path(OBJECT(clk)); 24 } 25 26 void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque) 27 { 28 clk->callback = cb; 29 clk->callback_opaque = opaque; 30 } 31 32 void clock_clear_callback(Clock *clk) 33 { 34 clock_set_callback(clk, NULL, NULL); 35 } 36 37 bool clock_set(Clock *clk, uint64_t period) 38 { 39 if (clk->period == period) { 40 return false; 41 } 42 trace_clock_set(CLOCK_PATH(clk), CLOCK_PERIOD_TO_NS(clk->period), 43 CLOCK_PERIOD_TO_NS(period)); 44 clk->period = period; 45 46 return true; 47 } 48 49 static void clock_propagate_period(Clock *clk, bool call_callbacks) 50 { 51 Clock *child; 52 53 QLIST_FOREACH(child, &clk->children, sibling) { 54 if (child->period != clk->period) { 55 child->period = clk->period; 56 trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk), 57 CLOCK_PERIOD_TO_NS(clk->period), 58 call_callbacks); 59 if (call_callbacks && child->callback) { 60 child->callback(child->callback_opaque); 61 } 62 clock_propagate_period(child, call_callbacks); 63 } 64 } 65 } 66 67 void clock_propagate(Clock *clk) 68 { 69 assert(clk->source == NULL); 70 trace_clock_propagate(CLOCK_PATH(clk)); 71 clock_propagate_period(clk, true); 72 } 73 74 void clock_set_source(Clock *clk, Clock *src) 75 { 76 /* changing clock source is not supported */ 77 assert(!clk->source); 78 79 trace_clock_set_source(CLOCK_PATH(clk), CLOCK_PATH(src)); 80 81 clk->period = src->period; 82 QLIST_INSERT_HEAD(&src->children, clk, sibling); 83 clk->source = src; 84 clock_propagate_period(clk, false); 85 } 86 87 static void clock_disconnect(Clock *clk) 88 { 89 if (clk->source == NULL) { 90 return; 91 } 92 93 trace_clock_disconnect(CLOCK_PATH(clk)); 94 95 clk->source = NULL; 96 QLIST_REMOVE(clk, sibling); 97 } 98 99 static void clock_initfn(Object *obj) 100 { 101 Clock *clk = CLOCK(obj); 102 103 QLIST_INIT(&clk->children); 104 } 105 106 static void clock_finalizefn(Object *obj) 107 { 108 Clock *clk = CLOCK(obj); 109 Clock *child, *next; 110 111 /* clear our list of children */ 112 QLIST_FOREACH_SAFE(child, &clk->children, sibling, next) { 113 clock_disconnect(child); 114 } 115 116 /* remove us from source's children list */ 117 clock_disconnect(clk); 118 119 g_free(clk->canonical_path); 120 } 121 122 static const TypeInfo clock_info = { 123 .name = TYPE_CLOCK, 124 .parent = TYPE_OBJECT, 125 .instance_size = sizeof(Clock), 126 .instance_init = clock_initfn, 127 .instance_finalize = clock_finalizefn, 128 }; 129 130 static void clock_register_types(void) 131 { 132 type_register_static(&clock_info); 133 } 134 135 type_init(clock_register_types) 136