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