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/qdev-clock.h"
17 #include "hw/qdev-core.h"
18 #include "qapi/error.h"
19
20 /*
21 * qdev_init_clocklist:
22 * Add a new clock in a device
23 */
qdev_init_clocklist(DeviceState * dev,const char * name,bool alias,bool output,Clock * clk)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
qdev_finalize_clocklist(DeviceState * dev)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
qdev_init_clock_out(DeviceState * dev,const char * name)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
qdev_init_clock_in(DeviceState * dev,const char * name,ClockCallback * callback,void * opaque,unsigned int events)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
qdev_init_clocks(DeviceState * dev,const ClockPortInitArray clocks)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
qdev_get_clocklist(DeviceState * dev,const char * name)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
qdev_get_clock_in(DeviceState * dev,const char * name)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
qdev_get_clock_out(DeviceState * dev,const char * name)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
qdev_alias_clock(DeviceState * dev,const char * name,DeviceState * alias_dev,const char * alias_name)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 = ncl->clock;
161
162 ncl = qdev_init_clocklist(alias_dev, alias_name, true, ncl->output, clk);
163
164 object_property_add_link(OBJECT(alias_dev), alias_name,
165 TYPE_CLOCK,
166 (Object **) &ncl->clock,
167 NULL, OBJ_PROP_LINK_STRONG);
168 /*
169 * Since the link property has the OBJ_PROP_LINK_STRONG flag, the clk
170 * object reference count gets decremented on property deletion.
171 * However object_property_add_link does not increment it since it
172 * doesn't know the linked object. Increment it here to ensure the
173 * aliased clock stays alive during this device life-time.
174 */
175 object_ref(OBJECT(clk));
176
177 return clk;
178 }
179
qdev_connect_clock_in(DeviceState * dev,const char * name,Clock * source)180 void qdev_connect_clock_in(DeviceState *dev, const char *name, Clock *source)
181 {
182 assert(!dev->realized);
183 clock_set_source(qdev_get_clock_in(dev, name), source);
184 }
185