master
c 191 lines 5.19 KB
Raw
1 /*
2 * Device's clock input and output
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/error-report.h"
16 #include "hw/core/qdev-clock.h"
17 #include "hw/core/qdev.h"
18 #include "qapi/error.h"
19
20 /*
21 * qdev_init_clocklist:
22 * Add a new clock in a device
23 */
24 static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char *name,
25 bool alias, bool output, Clock *clk)
26 {
27 NamedClockList *ncl;
28
29 /*
30 * Clock must be added before realize() so that we can compute the
31 * clock's canonical path during device_realize().
32 */
33 assert(!dev->realized);
34
35 /*
36 * The ncl structure is freed by qdev_finalize_clocklist() which will
37 * be called during @dev's device_finalize().
38 */
39 ncl = g_new0(NamedClockList, 1);
40 ncl->name = g_strdup(name);
41 ncl->alias = alias;
42 ncl->output = output;
43 ncl->clock = clk;
44
45 QLIST_INSERT_HEAD(&dev->clocks, ncl, node);
46 return ncl;
47 }
48
49 void qdev_finalize_clocklist(DeviceState *dev)
50 {
51 /* called by @dev's device_finalize() */
52 NamedClockList *ncl, *ncl_next;
53
54 QLIST_FOREACH_SAFE(ncl, &dev->clocks, node, ncl_next) {
55 QLIST_REMOVE(ncl, node);
56 if (!ncl->alias) {
57 /*
58 * We kept a reference on the input clock to ensure it lives up to
59 * this point; it is used by the monitor to show the frequency.
60 */
61 object_unref(OBJECT(ncl->clock));
62 }
63 g_free(ncl->name);
64 g_free(ncl);
65 }
66 }
67
68 Clock *qdev_init_clock_out(DeviceState *dev, const char *name)
69 {
70 Clock *clk = CLOCK(object_new(TYPE_CLOCK));
71 object_property_add_child(OBJECT(dev), name, OBJECT(clk));
72
73 qdev_init_clocklist(dev, name, false, true, clk);
74 return clk;
75 }
76
77 Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
78 ClockCallback *callback, void *opaque,
79 unsigned int events)
80 {
81 Clock *clk = CLOCK(object_new(TYPE_CLOCK));
82 object_property_add_child(OBJECT(dev), name, OBJECT(clk));
83
84 qdev_init_clocklist(dev, name, false, false, clk);
85 if (callback) {
86 clock_set_callback(clk, callback, opaque, events);
87 }
88 return clk;
89 }
90
91 void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks)
92 {
93 const struct ClockPortInitElem *elem;
94
95 for (elem = &clocks[0]; elem->name != NULL; elem++) {
96 Clock **clkp;
97 /* offset cannot be inside the DeviceState part */
98 assert(elem->offset > sizeof(DeviceState));
99 clkp = ((void *)dev) + elem->offset;
100 if (elem->is_output) {
101 *clkp = qdev_init_clock_out(dev, elem->name);
102 } else {
103 *clkp = qdev_init_clock_in(dev, elem->name, elem->callback, dev,
104 elem->callback_events);
105 }
106 }
107 }
108
109 static NamedClockList *qdev_get_clocklist(DeviceState *dev, const char *name)
110 {
111 NamedClockList *ncl;
112
113 QLIST_FOREACH(ncl, &dev->clocks, node) {
114 if (strcmp(name, ncl->name) == 0) {
115 return ncl;
116 }
117 }
118
119 return NULL;
120 }
121
122 Clock *qdev_get_clock_in(DeviceState *dev, const char *name)
123 {
124 NamedClockList *ncl;
125
126 assert(name);
127
128 ncl = qdev_get_clocklist(dev, name);
129 if (!ncl) {
130 error_report("Can not find clock-in '%s' for device type '%s'",
131 name, object_get_typename(OBJECT(dev)));
132 abort();
133 }
134 assert(!ncl->output);
135
136 return ncl->clock;
137 }
138
139 Clock *qdev_get_clock_out(DeviceState *dev, const char *name)
140 {
141 NamedClockList *ncl;
142
143 assert(name);
144
145 ncl = qdev_get_clocklist(dev, name);
146 if (!ncl) {
147 error_report("Can not find clock-out '%s' for device type '%s'",
148 name, object_get_typename(OBJECT(dev)));
149 abort();
150 }
151 assert(ncl->output);
152
153 return ncl->clock;
154 }
155
156 Clock *qdev_alias_clock(DeviceState *dev, const char *name,
157 DeviceState *alias_dev, const char *alias_name)
158 {
159 NamedClockList *ncl = qdev_get_clocklist(dev, name);
160 Clock *clk;
161
162 if (!ncl) {
163 error_report("Can not find clock '%s' for device type '%s'",
164 name, object_get_typename(OBJECT(dev)));
165 abort();
166 }
167 clk = ncl->clock;
168
169 ncl = qdev_init_clocklist(alias_dev, alias_name, true, ncl->output, clk);
170
171 object_property_add_link(OBJECT(alias_dev), alias_name,
172 TYPE_CLOCK,
173 (Object **) &ncl->clock,
174 NULL, OBJ_PROP_LINK_STRONG);
175 /*
176 * Since the link property has the OBJ_PROP_LINK_STRONG flag, the clk
177 * object reference count gets decremented on property deletion.
178 * However object_property_add_link does not increment it since it
179 * doesn't know the linked object. Increment it here to ensure the
180 * aliased clock stays alive during this device life-time.
181 */
182 object_ref(OBJECT(clk));
183
184 return clk;
185 }
186
187 void qdev_connect_clock_in(DeviceState *dev, const char *name, Clock *source)
188 {
189 assert(!dev->realized);
190 clock_set_source(qdev_get_clock_in(dev, name), source);
191 }