]>
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" | |
b7cd9c1e | 15 | #include "qemu/cutils.h" |
4cba075e PM |
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 | ||
5ebc6648 LM |
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 | ||
5ee0abed PM |
42 | void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque, |
43 | unsigned int events) | |
4cba075e PM |
44 | { |
45 | clk->callback = cb; | |
46 | clk->callback_opaque = opaque; | |
5ee0abed | 47 | clk->callback_events = events; |
4cba075e PM |
48 | } |
49 | ||
50 | void clock_clear_callback(Clock *clk) | |
51 | { | |
5ee0abed | 52 | clock_set_callback(clk, NULL, NULL, 0); |
4cba075e PM |
53 | } |
54 | ||
15aa2876 | 55 | bool clock_set(Clock *clk, uint64_t period) |
4cba075e | 56 | { |
15aa2876 PMD |
57 | if (clk->period == period) { |
58 | return false; | |
59 | } | |
a6414d3b LM |
60 | trace_clock_set(CLOCK_PATH(clk), CLOCK_PERIOD_TO_HZ(clk->period), |
61 | CLOCK_PERIOD_TO_HZ(period)); | |
4cba075e | 62 | clk->period = period; |
15aa2876 PMD |
63 | |
64 | return true; | |
4cba075e PM |
65 | } |
66 | ||
5ee0abed PM |
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 | ||
4cba075e PM |
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) { | |
e4341623 PM |
84 | if (call_callbacks) { |
85 | clock_call_callback(child, ClockPreUpdate); | |
86 | } | |
4cba075e PM |
87 | child->period = clk->period; |
88 | trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk), | |
a6414d3b | 89 | CLOCK_PERIOD_TO_HZ(clk->period), |
4cba075e | 90 | call_callbacks); |
5ee0abed PM |
91 | if (call_callbacks) { |
92 | clock_call_callback(child, ClockUpdate); | |
4cba075e PM |
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 | ||
b7cd9c1e PM |
131 | char *clock_display_freq(Clock *clk) |
132 | { | |
133 | return freq_to_str(clock_get_hz(clk)); | |
134 | } | |
135 | ||
4cba075e PM |
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) |