qemu/hw/core/clock.c
<<
>>
Prefs
   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
  21void 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
  27Clock *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
  42void 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
  50void clock_clear_callback(Clock *clk)
  51{
  52    clock_set_callback(clk, NULL, NULL, 0);
  53}
  54
  55bool 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
  67static 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
  78static 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) {
  84            if (call_callbacks) {
  85                clock_call_callback(child, ClockPreUpdate);
  86            }
  87            child->period = clk->period;
  88            trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk),
  89                               CLOCK_PERIOD_TO_HZ(clk->period),
  90                               call_callbacks);
  91            if (call_callbacks) {
  92                clock_call_callback(child, ClockUpdate);
  93            }
  94            clock_propagate_period(child, call_callbacks);
  95        }
  96    }
  97}
  98
  99void 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
 106void 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
 119static 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
 131char *clock_display_freq(Clock *clk)
 132{
 133    return freq_to_str(clock_get_hz(clk));
 134}
 135
 136static void clock_initfn(Object *obj)
 137{
 138    Clock *clk = CLOCK(obj);
 139
 140    QLIST_INIT(&clk->children);
 141}
 142
 143static 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
 159static 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
 167static void clock_register_types(void)
 168{
 169    type_register_static(&clock_info);
 170}
 171
 172type_init(clock_register_types)
 173