xref: /openbmc/qemu/hw/core/clock.c (revision 4f8f199fa569492bb07efee02489f521629d275d)
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 "qemu/cutils.h"
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  
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  
42  void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque,
43                          unsigned int events)
44  {
45      clk->callback = cb;
46      clk->callback_opaque = opaque;
47      clk->callback_events = events;
48  }
49  
50  void clock_clear_callback(Clock *clk)
51  {
52      clock_set_callback(clk, NULL, NULL, 0);
53  }
54  
55  bool clock_set(Clock *clk, uint64_t period)
56  {
57      if (clk->period == period) {
58          return false;
59      }
60      trace_clock_set(CLOCK_PATH(clk), CLOCK_PERIOD_TO_HZ(clk->period),
61                      CLOCK_PERIOD_TO_HZ(period));
62      clk->period = period;
63  
64      return true;
65  }
66  
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
71       * clock period adjusted for multiplier and divider effects.
72       */
73      return muldiv64(clk->period, clk->multiplier, clk->divider);
74  }
75  
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  
87  static void clock_propagate_period(Clock *clk, bool call_callbacks)
88  {
89      Clock *child;
90      uint64_t child_period = clock_get_child_period(clk);
91  
92      QLIST_FOREACH(child, &clk->children, sibling) {
93          if (child->period != child_period) {
94              if (call_callbacks) {
95                  clock_call_callback(child, ClockPreUpdate);
96              }
97              child->period = child_period;
98              trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk),
99                                 CLOCK_PERIOD_TO_HZ(child->period),
100                                 call_callbacks);
101              if (call_callbacks) {
102                  clock_call_callback(child, ClockUpdate);
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  
123      clk->period = clock_get_child_period(src);
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  
141  char *clock_display_freq(Clock *clk)
142  {
143      return freq_to_str(clock_get_hz(clk));
144  }
145  
146  bool clock_set_mul_div(Clock *clk, uint32_t multiplier, uint32_t divider)
147  {
148      assert(divider != 0);
149  
150      if (clk->multiplier == multiplier && clk->divider == divider) {
151          return false;
152      }
153  
154      trace_clock_set_mul_div(CLOCK_PATH(clk), clk->multiplier, multiplier,
155                              clk->divider, divider);
156      clk->multiplier = multiplier;
157      clk->divider = divider;
158  
159      return true;
160  }
161  
162  static void clock_initfn(Object *obj)
163  {
164      Clock *clk = CLOCK(obj);
165  
166      clk->multiplier = 1;
167      clk->divider = 1;
168  
169      QLIST_INIT(&clk->children);
170  }
171  
172  static void clock_finalizefn(Object *obj)
173  {
174      Clock *clk = CLOCK(obj);
175      Clock *child, *next;
176  
177      /* clear our list of children */
178      QLIST_FOREACH_SAFE(child, &clk->children, sibling, next) {
179          clock_disconnect(child);
180      }
181  
182      /* remove us from source's children list */
183      clock_disconnect(clk);
184  
185      g_free(clk->canonical_path);
186  }
187  
188  static const TypeInfo clock_info = {
189      .name              = TYPE_CLOCK,
190      .parent            = TYPE_OBJECT,
191      .instance_size     = sizeof(Clock),
192      .instance_init     = clock_initfn,
193      .instance_finalize = clock_finalizefn,
194  };
195  
196  static void clock_register_types(void)
197  {
198      type_register_static(&clock_info);
199  }
200  
201  type_init(clock_register_types)
202