]>
Commit | Line | Data |
---|---|---|
4cba075e PM |
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 | void clock_set(Clock *clk, uint64_t period) | |
38 | { | |
39 | trace_clock_set(CLOCK_PATH(clk), CLOCK_PERIOD_TO_NS(clk->period), | |
40 | CLOCK_PERIOD_TO_NS(period)); | |
41 | clk->period = period; | |
42 | } | |
43 | ||
44 | static void clock_propagate_period(Clock *clk, bool call_callbacks) | |
45 | { | |
46 | Clock *child; | |
47 | ||
48 | QLIST_FOREACH(child, &clk->children, sibling) { | |
49 | if (child->period != clk->period) { | |
50 | child->period = clk->period; | |
51 | trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk), | |
52 | CLOCK_PERIOD_TO_NS(clk->period), | |
53 | call_callbacks); | |
54 | if (call_callbacks && child->callback) { | |
55 | child->callback(child->callback_opaque); | |
56 | } | |
57 | clock_propagate_period(child, call_callbacks); | |
58 | } | |
59 | } | |
60 | } | |
61 | ||
62 | void clock_propagate(Clock *clk) | |
63 | { | |
64 | assert(clk->source == NULL); | |
65 | trace_clock_propagate(CLOCK_PATH(clk)); | |
66 | clock_propagate_period(clk, true); | |
67 | } | |
68 | ||
69 | void clock_set_source(Clock *clk, Clock *src) | |
70 | { | |
71 | /* changing clock source is not supported */ | |
72 | assert(!clk->source); | |
73 | ||
74 | trace_clock_set_source(CLOCK_PATH(clk), CLOCK_PATH(src)); | |
75 | ||
76 | clk->period = src->period; | |
77 | QLIST_INSERT_HEAD(&src->children, clk, sibling); | |
78 | clk->source = src; | |
79 | clock_propagate_period(clk, false); | |
80 | } | |
81 | ||
82 | static void clock_disconnect(Clock *clk) | |
83 | { | |
84 | if (clk->source == NULL) { | |
85 | return; | |
86 | } | |
87 | ||
88 | trace_clock_disconnect(CLOCK_PATH(clk)); | |
89 | ||
90 | clk->source = NULL; | |
91 | QLIST_REMOVE(clk, sibling); | |
92 | } | |
93 | ||
94 | static void clock_initfn(Object *obj) | |
95 | { | |
96 | Clock *clk = CLOCK(obj); | |
97 | ||
98 | QLIST_INIT(&clk->children); | |
99 | } | |
100 | ||
101 | static void clock_finalizefn(Object *obj) | |
102 | { | |
103 | Clock *clk = CLOCK(obj); | |
104 | Clock *child, *next; | |
105 | ||
106 | /* clear our list of children */ | |
107 | QLIST_FOREACH_SAFE(child, &clk->children, sibling, next) { | |
108 | clock_disconnect(child); | |
109 | } | |
110 | ||
111 | /* remove us from source's children list */ | |
112 | clock_disconnect(clk); | |
113 | ||
114 | g_free(clk->canonical_path); | |
115 | } | |
116 | ||
117 | static const TypeInfo clock_info = { | |
118 | .name = TYPE_CLOCK, | |
119 | .parent = TYPE_OBJECT, | |
120 | .instance_size = sizeof(Clock), | |
121 | .instance_init = clock_initfn, | |
122 | .instance_finalize = clock_finalizefn, | |
123 | }; | |
124 | ||
125 | static void clock_register_types(void) | |
126 | { | |
127 | type_register_static(&clock_info); | |
128 | } | |
129 | ||
130 | type_init(clock_register_types) |