[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH v7 3/9] qdev: add clock input&output support to devices.
From: |
Alistair Francis |
Subject: |
Re: [PATCH v7 3/9] qdev: add clock input&output support to devices. |
Date: |
Mon, 24 Feb 2020 17:27:03 -0800 |
/On Mon, Feb 24, 2020 at 9:12 AM Damien Hedde <address@hidden> wrote
>
> Add functions to easily handle clocks with devices.
> Clock inputs and outputs should be used to handle clock propagation
> between devices.
> The API is very similar the GPIO API.
>
> This is based on the original work of Frederic Konrad.
>
> Signed-off-by: Damien Hedde <address@hidden>
> ---
>
> I did not changed the constness of @name pointer field in
> NamedClockList structure.
> There is no obstacle to do it but the fact that we need to free the
> allocated data it points to. It is possible to make it const and
> hack/remove the const to call g_free but I don't know if its
> allowed in qemu.
>
> v7:
> + update ClockIn/Out types
> + qdev_connect_clock_out function removed / qdev_connect_clock_in added
> instead
> + qdev_pass_clock renamed to qdev_alias_clock
> + various small fixes (typos, comment, asserts) (Peter)
> + move device's instance_finalize code related to clock in qdev-clock.c
> ---
> include/hw/qdev-clock.h | 105 +++++++++++++++++++++++++
> include/hw/qdev-core.h | 12 +++
> hw/core/qdev-clock.c | 169 ++++++++++++++++++++++++++++++++++++++++
> hw/core/qdev.c | 12 +++
> hw/core/Makefile.objs | 2 +-
> tests/Makefile.include | 1 +
> 6 files changed, 300 insertions(+), 1 deletion(-)
> create mode 100644 include/hw/qdev-clock.h
> create mode 100644 hw/core/qdev-clock.c
>
> diff --git a/include/hw/qdev-clock.h b/include/hw/qdev-clock.h
> new file mode 100644
> index 0000000000..899a95ca6a
> --- /dev/null
> +++ b/include/hw/qdev-clock.h
> @@ -0,0 +1,105 @@
> +/*
> + * Device's clock input and output
> + *
> + * Copyright GreenSocs 2016-2020
> + *
> + * Authors:
> + * Frederic Konrad
> + * Damien Hedde
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#ifndef QDEV_CLOCK_H
> +#define QDEV_CLOCK_H
> +
> +#include "hw/clock.h"
> +
> +/**
> + * qdev_init_clock_in:
> + * @dev: the device to add an input clock to
> + * @name: the name of the clock (can't be NULL).
> + * @callback: optional callback to be called on update or NULL.
> + * @opaque: argument for the callback
> + * @returns: a pointer to the newly added clock
> + *
> + * Add an input clock to device @dev as a clock named @name.
> + * This adds a child<> property.
> + * The callback will be called with @opaque as opaque parameter.
> + */
> +Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
> + ClockCallback *callback, void *opaque);
> +
> +/**
> + * qdev_init_clock_out:
> + * @dev: the device to add an output clock to
> + * @name: the name of the clock (can't be NULL).
> + * @callback: optional callback to be called on update or NULL.
qdev_init_clock_out() doesn't have a callback.
> + * @returns: a pointer to the newly added clock
> + *
> + * Add an output clock to device @dev as a clock named @name.
> + * This adds a child<> property.
> + */
> +Clock *qdev_init_clock_out(DeviceState *dev, const char *name);
> +
> +/**
> + * qdev_get_clock_in:
> + * @dev: the device which has the clock
> + * @name: the name of the clock (can't be NULL).
> + * @returns: a pointer to the clock
> + *
> + * Get the input clock @name from @dev or NULL if does not exist.
> + */
> +Clock *qdev_get_clock_in(DeviceState *dev, const char *name);
> +
> +/**
> + * qdev_get_clock_out:
> + * @dev: the device which has the clock
> + * @name: the name of the clock (can't be NULL).
> + * @returns: a pointer to the clock
> + *
> + * Get the output clock @name from @dev or NULL if does not exist.
> + */
> +Clock *qdev_get_clock_out(DeviceState *dev, const char *name);
> +
> +/**
> + * qdev_connect_clock_in:
> + * @dev: a device
> + * @name: the name of an input clock in @dev
> + * @source: the source clock (an output clock of another device for example)
> + *
> + * Set the source clock of input clock @name of device @dev to @source.
> + * @source period update will be propagated to @name clock.
> + */
> +static inline void qdev_connect_clock_in(DeviceState *dev, const char *name,
> + Clock *source)
> +{
> + clock_set_source(qdev_get_clock_in(dev, name), source);
> +}
> +
> +/**
> + * qdev_alias_clock:
> + * @dev: the device which has the clock
> + * @name: the name of the clock in @dev (can't be NULL)
> + * @alias_dev: the device to add the clock
> + * @alias_name: the name of the clock in @container
> + * @returns: a pointer to the clock
> + *
> + * Add a clock @alias_name in @alias_dev which is an alias of the clock @name
> + * in @dev. The direction _in_ or _out_ will the same as the original.
> + * An alias clock must not be modified or used by @alias_dev and should
> + * typically be only only for device composition purpose.
> + */
> +Clock *qdev_alias_clock(DeviceState *dev, const char *name,
> + DeviceState *alias_dev, const char *alias_name);
> +
> +/**
> + * qdev_finalize_clocklist:
> + * @dev: the device being finalize
It probably should be:
@dev: the device being finalized
> + *
> + * Clear the clocklist from @dev. Only used internally in qdev.
> + */
> +void qdev_finalize_clocklist(DeviceState *dev);
> +
> +#endif /* QDEV_CLOCK_H */
> diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
> index 1405b8a990..d87d989e72 100644
> --- a/include/hw/qdev-core.h
> +++ b/include/hw/qdev-core.h
> @@ -149,6 +149,17 @@ struct NamedGPIOList {
> QLIST_ENTRY(NamedGPIOList) node;
> };
>
> +typedef struct Clock Clock;
> +typedef struct NamedClockList NamedClockList;
> +
> +struct NamedClockList {
> + char *name;
> + Clock *clock;
> + bool output;
> + bool alias;
> + QLIST_ENTRY(NamedClockList) node;
> +};
> +
> /**
> * DeviceState:
> * @realized: Indicates whether the device has been fully constructed.
> @@ -171,6 +182,7 @@ struct DeviceState {
> bool allow_unplug_during_migration;
> BusState *parent_bus;
> QLIST_HEAD(, NamedGPIOList) gpios;
> + QLIST_HEAD(, NamedClockList) clocks;
> QLIST_HEAD(, BusState) child_bus;
> int num_child_bus;
> int instance_id_alias;
> diff --git a/hw/core/qdev-clock.c b/hw/core/qdev-clock.c
> new file mode 100644
> index 0000000000..9af0159517
> --- /dev/null
> +++ b/hw/core/qdev-clock.c
> @@ -0,0 +1,169 @@
> +/*
> + * Device's clock input and output
> + *
> + * Copyright GreenSocs 2016-2020
> + *
> + * Authors:
> + * Frederic Konrad
> + * Damien Hedde
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/qdev-clock.h"
> +#include "hw/qdev-core.h"
> +#include "qapi/error.h"
> +
> +/*
> + * qdev_init_clocklist:
> + * Add a new clock in a device
> + */
> +static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char
> *name,
> + bool output, Clock *clk)
> +{
> + NamedClockList *ncl;
> +
> + /*
> + * Clock must be added before realize() so that we can compute the
> + * clock's canonical path durint device_realize().
s/durint/during/g
> + */
> + assert(!dev->realized);
> +
> + /*
> + * The ncl structure is freed by qdev_finalize_clocklist() which will
> + * be called during @dev's device_finalize().
> + */
> + ncl = g_new0(NamedClockList, 1);
> + ncl->name = g_strdup(name);
> + ncl->output = output;
> + ncl->alias = (clk != NULL);
> +
> + /*
> + * Trying to create a clock whose name clashes with some other
> + * clock or property is a bug in the caller and we will abort().
> + */
> + if (clk == NULL) {
> + clk = CLOCK(object_new(TYPE_CLOCK));
> + object_property_add_child(OBJECT(dev), name, OBJECT(clk),
> &error_abort);
> + if (output) {
> + /*
> + * Remove object_new()'s initial reference.
> + * Note that for inputs, the reference created by object_new()
> + * will be deleted in qdev_finalize_clocklist().
> + */
> + object_unref(OBJECT(clk));
> + }
> + } else {
> + object_property_add_link(OBJECT(dev), name,
> + object_get_typename(OBJECT(clk)),
> + (Object **) &ncl->clock,
> + NULL, OBJ_PROP_LINK_STRONG, &error_abort);
> + }
> +
> + ncl->clock = clk;
> +
> + QLIST_INSERT_HEAD(&dev->clocks, ncl, node);
> + return ncl;
> +}
> +
> +void qdev_finalize_clocklist(DeviceState *dev)
> +{
> + /* called by @dev's device_finalize() */
> + NamedClockList *ncl, *ncl_next;
> +
> + QLIST_FOREACH_SAFE(ncl, &dev->clocks, node, ncl_next) {
> + QLIST_REMOVE(ncl, node);
> + if (!ncl->output && !ncl->alias) {
> + /*
> + * We kept a reference on the input clock to ensure it lives up
> to
> + * this point so we can safely remove the callback.
> + * It avoids having a callback to a deleted object if ncl->clock
> + * is still referenced somewhere else (eg: by a clock output).
> + */
> + clock_clear_callback(ncl->clock);
> + object_unref(OBJECT(ncl->clock));
> + }
> + g_free(ncl->name);
> + g_free(ncl);
> + }
> +}
> +
> +Clock *qdev_init_clock_out(DeviceState *dev, const char *name)
> +{
> + NamedClockList *ncl;
> +
> + assert(name);
> +
> + ncl = qdev_init_clocklist(dev, name, true, NULL);
> +
> + return ncl->clock;
> +}
> +
> +Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
> + ClockCallback *callback, void *opaque)
> +{
> + NamedClockList *ncl;
> +
> + assert(name);
> +
> + ncl = qdev_init_clocklist(dev, name, false, NULL);
> +
> + if (callback) {
> + clock_set_callback(ncl->clock, callback, opaque);
> + }
> + return ncl->clock;
> +}
> +
> +static NamedClockList *qdev_get_clocklist(DeviceState *dev, const char *name)
> +{
> + NamedClockList *ncl;
> +
> + QLIST_FOREACH(ncl, &dev->clocks, node) {
> + if (strcmp(name, ncl->name) == 0) {
> + return ncl;
> + }
> + }
> +
> + assert(false);
Remove this.
Otherwise it looks good.
Alistair
> + return NULL;
> +}
> +
> +Clock *qdev_get_clock_in(DeviceState *dev, const char *name)
> +{
> + NamedClockList *ncl;
> +
> + assert(name);
> +
> + ncl = qdev_get_clocklist(dev, name);
> + assert(!ncl->output);
> +
> + return ncl->clock;
> +}
> +
> +Clock *qdev_get_clock_out(DeviceState *dev, const char *name)
> +{
> + NamedClockList *ncl;
> +
> + assert(name);
> +
> + ncl = qdev_get_clocklist(dev, name);
> + assert(ncl->output);
> +
> + return ncl->clock;
> +}
> +
> +Clock *qdev_alias_clock(DeviceState *dev, const char *name,
> + DeviceState *alias_dev, const char *alias_name)
> +{
> + NamedClockList *ncl;
> +
> + assert(name && alias_name);
> +
> + ncl = qdev_get_clocklist(dev, name);
> +
> + qdev_init_clocklist(alias_dev, alias_name, ncl->output, ncl->clock);
> +
> + return ncl->clock;
> +}
> diff --git a/hw/core/qdev.c b/hw/core/qdev.c
> index 3937d1eb1a..f390697348 100644
> --- a/hw/core/qdev.c
> +++ b/hw/core/qdev.c
> @@ -37,6 +37,7 @@
> #include "hw/qdev-properties.h"
> #include "hw/boards.h"
> #include "hw/sysbus.h"
> +#include "hw/qdev-clock.h"
> #include "migration/vmstate.h"
> #include "trace.h"
>
> @@ -855,6 +856,7 @@ static void device_set_realized(Object *obj, bool value,
> Error **errp)
> DeviceClass *dc = DEVICE_GET_CLASS(dev);
> HotplugHandler *hotplug_ctrl;
> BusState *bus;
> + NamedClockList *ncl;
> Error *local_err = NULL;
> bool unattached_parent = false;
> static int unattached_count;
> @@ -902,6 +904,13 @@ static void device_set_realized(Object *obj, bool value,
> Error **errp)
> */
> g_free(dev->canonical_path);
> dev->canonical_path = object_get_canonical_path(OBJECT(dev));
> + QLIST_FOREACH(ncl, &dev->clocks, node) {
> + if (ncl->alias) {
> + continue;
> + } else {
> + clock_setup_canonical_path(ncl->clock);
> + }
> + }
>
> if (qdev_get_vmsd(dev)) {
> if (vmstate_register_with_alias_id(VMSTATE_IF(dev),
> @@ -1025,6 +1034,7 @@ static void device_initfn(Object *obj)
> dev->allow_unplug_during_migration = false;
>
> QLIST_INIT(&dev->gpios);
> + QLIST_INIT(&dev->clocks);
> }
>
> static void device_post_init(Object *obj)
> @@ -1054,6 +1064,8 @@ static void device_finalize(Object *obj)
> */
> }
>
> + qdev_finalize_clocklist(dev);
> +
> /* Only send event if the device had been completely realized */
> if (dev->pending_deleted_event) {
> g_assert(dev->canonical_path);
> diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs
> index e3d796fdd4..2fdcb7dd00 100644
> --- a/hw/core/Makefile.objs
> +++ b/hw/core/Makefile.objs
> @@ -7,7 +7,7 @@ common-obj-y += hotplug.o
> common-obj-y += vmstate-if.o
> # irq.o needed for qdev GPIO handling:
> common-obj-y += irq.o
> -common-obj-y += clock.o
> +common-obj-y += clock.o qdev-clock.o
>
> common-obj-$(CONFIG_SOFTMMU) += reset.o
> common-obj-$(CONFIG_SOFTMMU) += qdev-fw.o
> diff --git a/tests/Makefile.include b/tests/Makefile.include
> index edcbd475aa..5a4511a86f 100644
> --- a/tests/Makefile.include
> +++ b/tests/Makefile.include
> @@ -436,6 +436,7 @@ tests/test-qdev-global-props$(EXESUF):
> tests/test-qdev-global-props.o \
> hw/core/fw-path-provider.o \
> hw/core/reset.o \
> hw/core/vmstate-if.o \
> + hw/core/clock.o hw/core/qdev-clock.o \
> $(test-qapi-obj-y)
> tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
> migration/vmstate.o migration/vmstate-types.o migration/qemu-file.o \
> --
> 2.24.1
>
>
- [PATCH v7 0/9] Clock framework API, Damien Hedde, 2020/02/24
- [PATCH v7 3/9] qdev: add clock input&output support to devices., Damien Hedde, 2020/02/24
- Re: [PATCH v7 3/9] qdev: add clock input&output support to devices.,
Alistair Francis <=
- [PATCH v7 2/9] hw/core/clock-vmstate: define a vmstate entry for clock state, Damien Hedde, 2020/02/24
- [PATCH v7 4/9] qdev-clock: introduce an init array to ease the device construction, Damien Hedde, 2020/02/24
- [PATCH v7 1/9] hw/core/clock: introduce clock object, Damien Hedde, 2020/02/24
- [PATCH v7 5/9] docs/clocks: add device's clock documentation, Damien Hedde, 2020/02/24
- [PATCH v7 9/9] qdev-monitor: print the device's clock with info qtree, Damien Hedde, 2020/02/24
- [PATCH v7 8/9] hw/arm/xilinx_zynq: connect uart clocks to slcr, Damien Hedde, 2020/02/24