From patchwork Tue Oct 2 14:24:36 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Damien Hedde X-Patchwork-Id: 10623767 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E609715A7 for ; Tue, 2 Oct 2018 14:38:13 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D208520174 for ; Tue, 2 Oct 2018 14:38:13 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C5560201B1; Tue, 2 Oct 2018 14:38:13 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.7 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 3E34D201A4 for ; Tue, 2 Oct 2018 14:38:12 +0000 (UTC) Received: from localhost ([::1]:44019 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1g7LoR-0007OU-41 for patchwork-qemu-devel@patchwork.kernel.org; Tue, 02 Oct 2018 10:38:11 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:46817) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1g7Lmn-0005Jj-MF for qemu-devel@nongnu.org; Tue, 02 Oct 2018 10:36:37 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1g7LdC-0004Q1-Gl for qemu-devel@nongnu.org; Tue, 02 Oct 2018 10:26:37 -0400 Received: from greensocs.com ([193.104.36.180]:53816) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1g7Ld9-0004Kz-8Z; Tue, 02 Oct 2018 10:26:31 -0400 Received: from localhost (localhost [127.0.0.1]) by greensocs.com (Postfix) with ESMTP id 13427442AFF; Tue, 2 Oct 2018 16:26:07 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=greensocs.com; s=mail; t=1538490367; bh=W368a874LLn3wLZ0ssLjyDwxQKF21HbKz4tSIYNoBNs=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=f9F5H1XGRNIhraILjBGUQJdJiSUXw3i5Se/7cI44HYWT0griEQd5yrizC+qXEhIdT 5Bm9zt1uVUsGc6xvGtVNpP+pyHzbqFbcmR1o/Xih64ioMqJzyaRIBbwJ9uyhoNT0Xn bEeax0OG/lKXdkBd0w60LMNafWyhqCvjzESaE5wg= X-Virus-Scanned: amavisd-new at greensocs.com Authentication-Results: gs-01.greensocs.com (amavisd-new); dkim=pass (1024-bit key) header.d=greensocs.com header.b=ax2IQLwP; dkim=pass (1024-bit key) header.d=greensocs.com header.b=ax2IQLwP Received: from greensocs.com ([127.0.0.1]) by localhost (gs-01.greensocs.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id bsD2aumRFfpu; Tue, 2 Oct 2018 16:26:05 +0200 (CEST) Received: by greensocs.com (Postfix, from userid 998) id B5BF2442AED; Tue, 2 Oct 2018 16:26:04 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=greensocs.com; s=mail; t=1538490364; bh=W368a874LLn3wLZ0ssLjyDwxQKF21HbKz4tSIYNoBNs=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=ax2IQLwPsJndXD0kUMaoqrxUImvC87i1XxxTIPu2IdsRfCEtlIa/9oXZ8coN26wrW X/ZBtS90kLbdH7s4RwUEedZSb/p1QMmfu4SaoTsXGw+to8NjFcFHdzTQE6A5CB7IhR 4KlAesA5Yv07iLX380Wc0A+RQmEXJi4CW+c4Y6+4= Received: from kouign-amann.hive.antfield.fr (antfield.tima.u-ga.fr [147.171.129.253]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: damien.hedde@greensocs.com) by greensocs.com (Postfix) with ESMTPSA id 55599442AE7; Tue, 2 Oct 2018 16:26:04 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=greensocs.com; s=mail; t=1538490364; bh=W368a874LLn3wLZ0ssLjyDwxQKF21HbKz4tSIYNoBNs=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=ax2IQLwPsJndXD0kUMaoqrxUImvC87i1XxxTIPu2IdsRfCEtlIa/9oXZ8coN26wrW X/ZBtS90kLbdH7s4RwUEedZSb/p1QMmfu4SaoTsXGw+to8NjFcFHdzTQE6A5CB7IhR 4KlAesA5Yv07iLX380Wc0A+RQmEXJi4CW+c4Y6+4= From: Damien Hedde To: qemu-devel@nongnu.org Date: Tue, 2 Oct 2018 16:24:36 +0200 Message-Id: <20181002142443.30976-3-damien.hedde@greensocs.com> X-Mailer: git-send-email 2.19.0 In-Reply-To: <20181002142443.30976-1-damien.hedde@greensocs.com> References: <20181002142443.30976-1-damien.hedde@greensocs.com> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 193.104.36.180 Subject: [Qemu-devel] [PATCH v5 2/9] qdev: add clock input&output support to devices. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: edgar.iglesias@xilinx.com, peter.maydell@linaro.org, alistair@alistair23.me, mark.burton@greensocs.com, saipava@xilinx.com, qemu-arm@nongnu.org, Damien Hedde , pbonzini@redhat.com, konrad@adacore.com, luc.michel@greensocs.com Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP Add functions to easily add input or output clocks to a device. The clock port objects are added as children of the device. A function allows to connect two clocks together. It should be called by some toplevel to make a connection between 2 (sub-)devices. Also add a function which forwards a port to another device. This function allows, in the case of device composition, to expose a sub-device clock port as its own clock port. This is really an alias: when forwarding an input, only one callback can be registered on it since there is only one Clockin object behind all aliases. This is based on the original work of Frederic Konrad. Signed-off-by: Damien Hedde Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- include/hw/qdev-clock.h | 62 ++++++++++++++++++ include/hw/qdev-core.h | 14 ++++ include/hw/qdev.h | 1 + hw/core/qdev-clock.c | 140 ++++++++++++++++++++++++++++++++++++++++ hw/core/qdev.c | 29 +++++++++ hw/core/Makefile.objs | 2 +- 6 files changed, 247 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..d76aa9f479 --- /dev/null +++ b/include/hw/qdev-clock.h @@ -0,0 +1,62 @@ +#ifndef QDEV_CLOCK_H +#define QDEV_CLOCK_H + +#include "hw/clock-port.h" +#include "hw/qdev-core.h" +#include "qapi/error.h" + +/** + * qdev_init_clock_in: + * @dev: the device in which to add a clock + * @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 a input clock to device @dev as a clock named @name. + * This adds a child<> property. + * The callback will be called with @dev as opaque parameter. + */ +ClockIn *qdev_init_clock_in(DeviceState *dev, const char *name, + ClockCallback *callback, void *opaque); + +/** + * qdev_init_clock_out: + * @dev: the device to add a clock to + * @name: the name of the clock (can't be NULL). + * @callback: optional callback to be called on update or NULL. + * @returns: a pointer to the newly added clock + * + * Add a output clock to device @dev as a clock named @name. + * This adds a child<> property. + */ +ClockOut *qdev_init_clock_out(DeviceState *dev, const char *name); + +/** + * qdev_pass_clock: + * @dev: the device to forward the clock to + * @name: the name of the clock to be added (can't be NULL) + * @container: the device which already has the clock + * @cont_name: the name of the clock in the container device + * + * Add a clock @name to @dev which forward to the clock @cont_name in @container + */ +void qdev_pass_clock(DeviceState *dev, const char *name, + DeviceState *container, const char *cont_name); + +/** + * qdev_connect_clock: + * @dev: the drived clock device. + * @name: the drived clock name. + * @driver: the driving clock device. + * @driver_name: the driving clock name. + * @errp: error report + * + * Setup @driver_name output clock of @driver to drive @name input clock of + * @dev. Errors are trigerred if clock does not exists + */ +void qdev_connect_clock(DeviceState *dev, const char *name, + DeviceState *driver, const char *driver_name, + Error **errp); + +#endif /* QDEV_CLOCK_H */ diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index f1fd0f8736..e6014d3a41 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -127,6 +127,19 @@ struct NamedGPIOList { QLIST_ENTRY(NamedGPIOList) node; }; +typedef struct NamedClockList NamedClockList; + +typedef struct ClockIn ClockIn; +typedef struct ClockOut ClockOut; + +struct NamedClockList { + char *name; + bool forward; + ClockIn *in; + ClockOut *out; + QLIST_ENTRY(NamedClockList) node; +}; + /** * DeviceState: * @realized: Indicates whether the device has been fully constructed. @@ -147,6 +160,7 @@ struct DeviceState { int hotplugged; 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/include/hw/qdev.h b/include/hw/qdev.h index 5cb8b080a6..b031da7b41 100644 --- a/include/hw/qdev.h +++ b/include/hw/qdev.h @@ -4,5 +4,6 @@ #include "hw/hw.h" #include "hw/qdev-core.h" #include "hw/qdev-properties.h" +#include "hw/qdev-clock.h" #endif diff --git a/hw/core/qdev-clock.c b/hw/core/qdev-clock.c new file mode 100644 index 0000000000..f0e4839aed --- /dev/null +++ b/hw/core/qdev-clock.c @@ -0,0 +1,140 @@ +/* + * Device's clock + * + * Copyright GreenSocs 2016-2018 + * + * 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 "qom/object.h" +#include "hw/qdev-clock.h" +#include "qapi/error.h" + +static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char *name, + bool forward) +{ + NamedClockList *ncl; + + /* + * The clock path will be computed by the device's realize function call. + * This is required to ensure the clock's canonical path is right and log + * messages are meaningfull. + */ + assert(name); + assert(!dev->realized); + + ncl = g_malloc0(sizeof(*ncl)); + ncl->name = g_strdup(name); + ncl->forward = forward; + + QLIST_INSERT_HEAD(&dev->clocks, ncl, node); + return ncl; +} + +ClockOut *qdev_init_clock_out(DeviceState *dev, const char *name) +{ + NamedClockList *ncl; + Object *clk; + + ncl = qdev_init_clocklist(dev, name, false); + + clk = object_new(TYPE_CLOCK_OUT); + + /* will fail if name already exists */ + object_property_add_child(OBJECT(dev), name, clk, &error_abort); + object_unref(clk); /* remove the initial ref made by object_new */ + + ncl->out = CLOCK_OUT(clk); + return ncl->out; +} + +ClockIn *qdev_init_clock_in(DeviceState *dev, const char *name, + ClockCallback *callback, void *opaque) +{ + NamedClockList *ncl; + Object *clk; + + ncl = qdev_init_clocklist(dev, name, false); + + clk = object_new(TYPE_CLOCK_IN); + /* + * the ref initialized by object_new will be cleared during dev finalize. + * It allows us to safely remove the callback. + */ + + /* will fail if name already exists */ + object_property_add_child(OBJECT(dev), name, clk, &error_abort); + + ncl->in = CLOCK_IN(clk); + if (callback) { + clock_set_callback(ncl->in, callback, opaque); + } + return ncl->in; +} + +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; + } + } + + return NULL; +} + +void qdev_pass_clock(DeviceState *dev, const char *name, + DeviceState *container, const char *cont_name) +{ + NamedClockList *original_ncl, *ncl; + Object **clk; + + assert(container && cont_name); + + original_ncl = qdev_get_clocklist(container, cont_name); + assert(original_ncl); /* clock must exist in origin */ + + ncl = qdev_init_clocklist(dev, name, true); + + if (ncl->out) { + clk = (Object **)&ncl->out; + } else { + clk = (Object **)&ncl->in; + } + + /* will fail if name already exists */ + object_property_add_link(OBJECT(dev), name, object_get_typename(*clk), + clk, NULL, OBJ_PROP_LINK_STRONG, &error_abort); +} + +void qdev_connect_clock(DeviceState *dev, const char *name, + DeviceState *driver, const char *driver_name, + Error **errp) +{ + NamedClockList *ncl, *drv_ncl; + + assert(dev && name); + assert(driver && driver_name); + + ncl = qdev_get_clocklist(dev, name); + if (!ncl || !ncl->in) { + error_setg(errp, "no input clock '%s' in device", name); + return; + } + + drv_ncl = qdev_get_clocklist(driver, driver_name); + if (!drv_ncl || !drv_ncl->out) { + error_setg(errp, "no output clock '%s' in driver", driver_name); + return; + } + + clock_connect(ncl->in , drv_ncl->out); +} diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 529b82de18..c48edf180f 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -790,6 +790,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp) DeviceClass *dc = DEVICE_GET_CLASS(dev); HotplugHandler *hotplug_ctrl; BusState *bus; + NamedClockList *clk; Error *local_err = NULL; bool unattached_parent = false; static int unattached_count; @@ -846,6 +847,15 @@ 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(clk, &dev->clocks, node) { + if (clk->forward) { + continue; + } else if (clk->in != NULL) { + clock_in_setup_canonical_path(clk->in); + } else { + clock_out_setup_canonical_path(clk->out); + } + } if (qdev_get_vmsd(dev)) { if (vmstate_register_with_alias_id(dev, -1, qdev_get_vmsd(dev), dev, @@ -972,6 +982,7 @@ static void device_initfn(Object *obj) (Object **)&dev->parent_bus, NULL, 0, &error_abort); QLIST_INIT(&dev->gpios); + QLIST_INIT(&dev->clocks); } static void device_post_init(Object *obj) @@ -983,6 +994,7 @@ static void device_post_init(Object *obj) static void device_finalize(Object *obj) { NamedGPIOList *ngl, *next; + NamedClockList *clk, *clk_next; DeviceState *dev = DEVICE(obj); @@ -996,6 +1008,23 @@ static void device_finalize(Object *obj) */ } + QLIST_FOREACH_SAFE(clk, &dev->clocks, node, clk_next) { + QLIST_REMOVE(clk, node); + if (!clk->forward && clk->in) { + /* + * if this clock is not forwarded, clk->in & clk->out are child of + * dev. + * At this point the properties and associated reference are + * already deleted but we kept a ref on clk->in to ensure we + * don't have a lost callback to a deleted device somewhere. + */ + clock_clear_callback(clk->in); + object_unref(OBJECT(clk->in)); + } + g_free(clk->name); + g_free(clk); + } + /* 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 f7102121f4..fc0505e716 100644 --- a/hw/core/Makefile.objs +++ b/hw/core/Makefile.objs @@ -1,5 +1,5 @@ # core qdev-related obj files, also used by *-user: -common-obj-y += qdev.o qdev-properties.o +common-obj-y += qdev.o qdev-properties.o qdev-clock.o common-obj-y += bus.o reset.o common-obj-$(CONFIG_SOFTMMU) += qdev-fw.o common-obj-$(CONFIG_SOFTMMU) += fw-path-provider.o