qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

qdev-clock.c (5905B)


      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  */
     24 static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char *name,
     25                                            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->output = output;
     42     ncl->alias = (clk != NULL);
     43 
     44     /*
     45      * Trying to create a clock whose name clashes with some other
     46      * clock or property is a bug in the caller and we will abort().
     47      */
     48     if (clk == NULL) {
     49         clk = CLOCK(object_new(TYPE_CLOCK));
     50         object_property_add_child(OBJECT(dev), name, OBJECT(clk));
     51         if (output) {
     52             /*
     53              * Remove object_new()'s initial reference.
     54              * Note that for inputs, the reference created by object_new()
     55              * will be deleted in qdev_finalize_clocklist().
     56              */
     57             object_unref(OBJECT(clk));
     58         }
     59     } else {
     60         object_property_add_link(OBJECT(dev), name,
     61                                  object_get_typename(OBJECT(clk)),
     62                                  (Object **) &ncl->clock,
     63                                  NULL, OBJ_PROP_LINK_STRONG);
     64         /*
     65          * Since the link property has the OBJ_PROP_LINK_STRONG flag, the clk
     66          * object reference count gets decremented on property deletion.
     67          * However object_property_add_link does not increment it since it
     68          * doesn't know the linked object. Increment it here to ensure the
     69          * aliased clock stays alive during this device life-time.
     70          */
     71         object_ref(OBJECT(clk));
     72     }
     73 
     74     ncl->clock = clk;
     75 
     76     QLIST_INSERT_HEAD(&dev->clocks, ncl, node);
     77     return ncl;
     78 }
     79 
     80 void qdev_finalize_clocklist(DeviceState *dev)
     81 {
     82     /* called by @dev's device_finalize() */
     83     NamedClockList *ncl, *ncl_next;
     84 
     85     QLIST_FOREACH_SAFE(ncl, &dev->clocks, node, ncl_next) {
     86         QLIST_REMOVE(ncl, node);
     87         if (!ncl->output && !ncl->alias) {
     88             /*
     89              * We kept a reference on the input clock to ensure it lives up to
     90              * this point so we can safely remove the callback.
     91              * It avoids having a callback to a deleted object if ncl->clock
     92              * is still referenced somewhere else (eg: by a clock output).
     93              */
     94             clock_clear_callback(ncl->clock);
     95             object_unref(OBJECT(ncl->clock));
     96         }
     97         g_free(ncl->name);
     98         g_free(ncl);
     99     }
    100 }
    101 
    102 Clock *qdev_init_clock_out(DeviceState *dev, const char *name)
    103 {
    104     NamedClockList *ncl;
    105 
    106     assert(name);
    107 
    108     ncl = qdev_init_clocklist(dev, name, true, NULL);
    109 
    110     return ncl->clock;
    111 }
    112 
    113 Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
    114                           ClockCallback *callback, void *opaque,
    115                           unsigned int events)
    116 {
    117     NamedClockList *ncl;
    118 
    119     assert(name);
    120 
    121     ncl = qdev_init_clocklist(dev, name, false, NULL);
    122 
    123     if (callback) {
    124         clock_set_callback(ncl->clock, callback, opaque, events);
    125     }
    126     return ncl->clock;
    127 }
    128 
    129 void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks)
    130 {
    131     const struct ClockPortInitElem *elem;
    132 
    133     for (elem = &clocks[0]; elem->name != NULL; elem++) {
    134         Clock **clkp;
    135         /* offset cannot be inside the DeviceState part */
    136         assert(elem->offset > sizeof(DeviceState));
    137         clkp = (Clock **)(((void *) dev) + elem->offset);
    138         if (elem->is_output) {
    139             *clkp = qdev_init_clock_out(dev, elem->name);
    140         } else {
    141             *clkp = qdev_init_clock_in(dev, elem->name, elem->callback, dev,
    142                                        elem->callback_events);
    143         }
    144     }
    145 }
    146 
    147 static NamedClockList *qdev_get_clocklist(DeviceState *dev, const char *name)
    148 {
    149     NamedClockList *ncl;
    150 
    151     QLIST_FOREACH(ncl, &dev->clocks, node) {
    152         if (strcmp(name, ncl->name) == 0) {
    153             return ncl;
    154         }
    155     }
    156 
    157     return NULL;
    158 }
    159 
    160 Clock *qdev_get_clock_in(DeviceState *dev, const char *name)
    161 {
    162     NamedClockList *ncl;
    163 
    164     assert(name);
    165 
    166     ncl = qdev_get_clocklist(dev, name);
    167     if (!ncl) {
    168         error_report("Can not find clock-in '%s' for device type '%s'",
    169                      name, object_get_typename(OBJECT(dev)));
    170         abort();
    171     }
    172     assert(!ncl->output);
    173 
    174     return ncl->clock;
    175 }
    176 
    177 Clock *qdev_get_clock_out(DeviceState *dev, const char *name)
    178 {
    179     NamedClockList *ncl;
    180 
    181     assert(name);
    182 
    183     ncl = qdev_get_clocklist(dev, name);
    184     if (!ncl) {
    185         error_report("Can not find clock-out '%s' for device type '%s'",
    186                      name, object_get_typename(OBJECT(dev)));
    187         abort();
    188     }
    189     assert(ncl->output);
    190 
    191     return ncl->clock;
    192 }
    193 
    194 Clock *qdev_alias_clock(DeviceState *dev, const char *name,
    195                         DeviceState *alias_dev, const char *alias_name)
    196 {
    197     NamedClockList *ncl;
    198 
    199     assert(name && alias_name);
    200 
    201     ncl = qdev_get_clocklist(dev, name);
    202 
    203     qdev_init_clocklist(alias_dev, alias_name, ncl->output, ncl->clock);
    204 
    205     return ncl->clock;
    206 }
    207 
    208 void qdev_connect_clock_in(DeviceState *dev, const char *name, Clock *source)
    209 {
    210     assert(!dev->realized);
    211     clock_set_source(qdev_get_clock_in(dev, name), source);
    212 }