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 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 for multiplier and divider effects.
  72     */
  73    return muldiv64(clk->period, clk->multiplier, clk->divider);
  74}
  75
  76static 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
  87static 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
 109void 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
 116void 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
 129static 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
 141char *clock_display_freq(Clock *clk)
 142{
 143    return freq_to_str(clock_get_hz(clk));
 144}
 145
 146void 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
 156static void clock_initfn(Object *obj)
 157{
 158    Clock *clk = CLOCK(obj);
 159
 160    clk->multiplier = 1;
 161    clk->divider = 1;
 162
 163    QLIST_INIT(&clk->children);
 164}
 165
 166static 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
 182static 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
 190static void clock_register_types(void)
 191{
 192    type_register_static(&clock_info);
 193}
 194
 195type_init(clock_register_types)
 196