From patchwork Mon Sep 17 16:39:54 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Geert Uytterhoeven X-Patchwork-Id: 10603069 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 E334F13AD for ; Mon, 17 Sep 2018 16:40:12 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D4EB02A17C for ; Mon, 17 Sep 2018 16:40:12 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D2B072A1C8; Mon, 17 Sep 2018 16:40:12 +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.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DCBBE2A1C8 for ; Mon, 17 Sep 2018 16:40:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728801AbeIQWIS (ORCPT ); Mon, 17 Sep 2018 18:08:18 -0400 Received: from michel.telenet-ops.be ([195.130.137.88]:42718 "EHLO michel.telenet-ops.be" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728551AbeIQWIS (ORCPT ); Mon, 17 Sep 2018 18:08:18 -0400 Received: from ramsan.of.borg ([84.194.111.163]) by michel.telenet-ops.be with bizsmtp id cgg41y00Y3XaVaC06gg4HS; Mon, 17 Sep 2018 18:40:04 +0200 Received: from rox.of.borg ([192.168.97.57]) by ramsan.of.borg with esmtp (Exim 4.86_2) (envelope-from ) id 1g1wZA-0006bC-9h; Mon, 17 Sep 2018 18:40:04 +0200 Received: from geert by rox.of.borg with local (Exim 4.90_1) (envelope-from ) id 1g1wZA-0004xk-88; Mon, 17 Sep 2018 18:40:04 +0200 From: Geert Uytterhoeven To: Philipp Zabel , Eric Auger , Alex Williamson Cc: kvm@vger.kernel.org, devicetree@vger.kernel.org, linux-renesas-soc@vger.kernel.org, linux-kernel@vger.kernel.org, Geert Uytterhoeven Subject: [PATCH/RFC v4 1/2] reset: Add support for dedicated reset controls Date: Mon, 17 Sep 2018 18:39:54 +0200 Message-Id: <20180917163955.19023-2-geert+renesas@glider.be> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180917163955.19023-1-geert+renesas@glider.be> References: <20180917163955.19023-1-geert+renesas@glider.be> Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP In some SoCs multiple hardware blocks may share a reset control. The existing reset control API for shared resets will only assert such a reset when the drivers for all hardware blocks agree. The existing exclusive reset control API still allows to assert such a reset, but that impacts all other hardware blocks sharing the reset. Sometimes a driver needs to reset a specific hardware block, and be 100% sure it has no impact on other hardware blocks. This is e.g. the case for virtualization with device pass-through, where the host wants to reset any exported device before and after exporting it for use by the guest, for isolation. Hence a new flag for dedicated resets is added to the internal methods, with a new public reset_control_get_dedicated() method, to obtain an exclusive handle to a reset that is dedicated to one specific hardware block. This supports both DT-based and lookup-based reset controls. Signed-off-by: Geert Uytterhoeven --- v4: - New. Notes: - Dedicated lookup-based reset controls were not tested, - Several internal functions now take 3 boolean flags, and should probably be converted to take a bitmask instead, - I think __device_reset() should call __reset_control_get() with dedicated=true. However, that will impact existing users, - Should a different error than -EINVAL be returned on failure? --- drivers/reset/core.c | 76 ++++++++++++++++++++++++++++++++++++++----- include/linux/reset.h | 60 ++++++++++++++++++++++------------ 2 files changed, 107 insertions(+), 29 deletions(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 225e34c56b94a2e3..5bc4eeca70c0fcc2 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -459,9 +459,38 @@ static void __reset_control_put_internal(struct reset_control *rstc) kref_put(&rstc->refcnt, __reset_control_release); } +static bool __of_reset_is_dedicated(const struct device_node *node, + const struct of_phandle_args args) +{ + struct of_phandle_args args2; + struct device_node *node2; + int index, ret; + + for_each_node_with_property(node2, "resets") { + if (node == node2) + continue; + + for (index = 0; ; index++) { + ret = of_parse_phandle_with_args(node2, "resets", + "#reset-cells", index, + &args2); + if (ret) + break; + + if (args2.np == args.np && + args2.args_count == args.args_count && + !memcmp(args2.args, args.args, + args.args_count * sizeof(args.args[0]))) + return false; + } + } + + return true; +} + struct reset_control *__of_reset_control_get(struct device_node *node, const char *id, int index, bool shared, - bool optional) + bool optional, bool dedicated) { struct reset_control *rstc; struct reset_controller_dev *r, *rcdev; @@ -514,6 +543,11 @@ struct reset_control *__of_reset_control_get(struct device_node *node, return ERR_PTR(rstc_id); } + if (dedicated && !__of_reset_is_dedicated(node, args)) { + mutex_unlock(&reset_list_mutex); + return ERR_PTR(-EINVAL); + } + /* reset_list_mutex also protects the rcdev's reset_control list */ rstc = __reset_control_get_internal(rcdev, rstc_id, shared); @@ -541,9 +575,25 @@ __reset_controller_by_name(const char *name) return NULL; } +static bool __reset_is_dedicated(const struct reset_control_lookup *lookup) +{ + const struct reset_control_lookup *lookup2; + + list_for_each_entry(lookup, &reset_lookup_list, list) { + if (lookup2 == lookup) + continue; + + if (lookup2->provider == lookup->provider && + lookup2->index == lookup->index) + return false; + } + + return true; +} + static struct reset_control * __reset_control_get_from_lookup(struct device *dev, const char *con_id, - bool shared, bool optional) + bool shared, bool optional, bool dedicated) { const struct reset_control_lookup *lookup; struct reset_controller_dev *rcdev; @@ -562,6 +612,11 @@ __reset_control_get_from_lookup(struct device *dev, const char *con_id, if ((!con_id && !lookup->con_id) || ((con_id && lookup->con_id) && !strcmp(con_id, lookup->con_id))) { + if (dedicated && !__reset_is_dedicated(lookup)) { + mutex_unlock(&reset_lookup_mutex); + return ERR_PTR(-EINVAL); + } + mutex_lock(&reset_list_mutex); rcdev = __reset_controller_by_name(lookup->provider); if (!rcdev) { @@ -588,13 +643,15 @@ __reset_control_get_from_lookup(struct device *dev, const char *con_id, } struct reset_control *__reset_control_get(struct device *dev, const char *id, - int index, bool shared, bool optional) + int index, bool shared, + bool optional, bool dedicated) { if (dev->of_node) return __of_reset_control_get(dev->of_node, id, index, shared, - optional); + optional, dedicated); - return __reset_control_get_from_lookup(dev, id, shared, optional); + return __reset_control_get_from_lookup(dev, id, shared, optional, + dedicated); } EXPORT_SYMBOL_GPL(__reset_control_get); @@ -635,7 +692,7 @@ static void devm_reset_control_release(struct device *dev, void *res) struct reset_control *__devm_reset_control_get(struct device *dev, const char *id, int index, bool shared, - bool optional) + bool optional, bool dedicated) { struct reset_control **ptr, *rstc; @@ -644,7 +701,7 @@ struct reset_control *__devm_reset_control_get(struct device *dev, if (!ptr) return ERR_PTR(-ENOMEM); - rstc = __reset_control_get(dev, id, index, shared, optional); + rstc = __reset_control_get(dev, id, index, shared, optional, dedicated); if (!IS_ERR(rstc)) { *ptr = rstc; devres_add(dev, ptr); @@ -671,7 +728,7 @@ int __device_reset(struct device *dev, bool optional) struct reset_control *rstc; int ret; - rstc = __reset_control_get(dev, NULL, 0, 0, optional); + rstc = __reset_control_get(dev, NULL, 0, false, optional, false); if (IS_ERR(rstc)) return PTR_ERR(rstc); @@ -735,7 +792,8 @@ of_reset_control_array_get(struct device_node *np, bool shared, bool optional) return ERR_PTR(-ENOMEM); for (i = 0; i < num; i++) { - rstc = __of_reset_control_get(np, NULL, i, shared, optional); + rstc = __of_reset_control_get(np, NULL, i, shared, optional, + false); if (IS_ERR(rstc)) goto err_rst; resets->rstc[i] = rstc; diff --git a/include/linux/reset.h b/include/linux/reset.h index 09732c36f3515a1e..6ca6e108b612f923 100644 --- a/include/linux/reset.h +++ b/include/linux/reset.h @@ -17,15 +17,15 @@ int reset_control_status(struct reset_control *rstc); struct reset_control *__of_reset_control_get(struct device_node *node, const char *id, int index, bool shared, - bool optional); + bool optional, bool dedicated); struct reset_control *__reset_control_get(struct device *dev, const char *id, int index, bool shared, - bool optional); + bool optional, bool dedicated); void reset_control_put(struct reset_control *rstc); int __device_reset(struct device *dev, bool optional); struct reset_control *__devm_reset_control_get(struct device *dev, const char *id, int index, bool shared, - bool optional); + bool optional, bool dedicated); struct reset_control *devm_reset_control_array_get(struct device *dev, bool shared, bool optional); @@ -66,21 +66,23 @@ static inline int __device_reset(struct device *dev, bool optional) static inline struct reset_control *__of_reset_control_get( struct device_node *node, const char *id, int index, bool shared, - bool optional) + bool optional, bool dedicated) { return optional ? NULL : ERR_PTR(-ENOTSUPP); } static inline struct reset_control *__reset_control_get( struct device *dev, const char *id, - int index, bool shared, bool optional) + int index, bool shared, bool optional, + bool dedicated) { return optional ? NULL : ERR_PTR(-ENOTSUPP); } static inline struct reset_control *__devm_reset_control_get( struct device *dev, const char *id, - int index, bool shared, bool optional) + int index, bool shared, bool optional, + bool dedicated) { return optional ? NULL : ERR_PTR(-ENOTSUPP); } @@ -127,7 +129,25 @@ static inline int device_reset_optional(struct device *dev) static inline struct reset_control * __must_check reset_control_get_exclusive(struct device *dev, const char *id) { - return __reset_control_get(dev, id, 0, false, false); + return __reset_control_get(dev, id, 0, false, false, false); +} + +/** + * reset_control_get_dedicated - Lookup and obtain an exclusive reference + * to a dedicated reset controller. + * @dev: device to be reset by the controller + * @id: reset line name + * + * Returns a struct reset_control or IS_ERR() condition containing errno. + * If this function is called more than once for the same reset_control it will + * return -EBUSY. + * + * Use of id names is optional. + */ +static inline struct reset_control * +__must_check reset_control_get_dedicated(struct device *dev, const char *id) +{ + return __reset_control_get(dev, id, 0, false, false, true); } /** @@ -155,19 +175,19 @@ __must_check reset_control_get_exclusive(struct device *dev, const char *id) static inline struct reset_control *reset_control_get_shared( struct device *dev, const char *id) { - return __reset_control_get(dev, id, 0, true, false); + return __reset_control_get(dev, id, 0, true, false, false); } static inline struct reset_control *reset_control_get_optional_exclusive( struct device *dev, const char *id) { - return __reset_control_get(dev, id, 0, false, true); + return __reset_control_get(dev, id, 0, false, true, false); } static inline struct reset_control *reset_control_get_optional_shared( struct device *dev, const char *id) { - return __reset_control_get(dev, id, 0, true, true); + return __reset_control_get(dev, id, 0, true, true, false); } /** @@ -183,7 +203,7 @@ static inline struct reset_control *reset_control_get_optional_shared( static inline struct reset_control *of_reset_control_get_exclusive( struct device_node *node, const char *id) { - return __of_reset_control_get(node, id, 0, false, false); + return __of_reset_control_get(node, id, 0, false, false, false); } /** @@ -208,7 +228,7 @@ static inline struct reset_control *of_reset_control_get_exclusive( static inline struct reset_control *of_reset_control_get_shared( struct device_node *node, const char *id) { - return __of_reset_control_get(node, id, 0, true, false); + return __of_reset_control_get(node, id, 0, true, false, false); } /** @@ -225,7 +245,7 @@ static inline struct reset_control *of_reset_control_get_shared( static inline struct reset_control *of_reset_control_get_exclusive_by_index( struct device_node *node, int index) { - return __of_reset_control_get(node, NULL, index, false, false); + return __of_reset_control_get(node, NULL, index, false, false, false); } /** @@ -253,7 +273,7 @@ static inline struct reset_control *of_reset_control_get_exclusive_by_index( static inline struct reset_control *of_reset_control_get_shared_by_index( struct device_node *node, int index) { - return __of_reset_control_get(node, NULL, index, true, false); + return __of_reset_control_get(node, NULL, index, true, false, false); } /** @@ -272,7 +292,7 @@ static inline struct reset_control * __must_check devm_reset_control_get_exclusive(struct device *dev, const char *id) { - return __devm_reset_control_get(dev, id, 0, false, false); + return __devm_reset_control_get(dev, id, 0, false, false, false); } /** @@ -287,19 +307,19 @@ __must_check devm_reset_control_get_exclusive(struct device *dev, static inline struct reset_control *devm_reset_control_get_shared( struct device *dev, const char *id) { - return __devm_reset_control_get(dev, id, 0, true, false); + return __devm_reset_control_get(dev, id, 0, true, false, false); } static inline struct reset_control *devm_reset_control_get_optional_exclusive( struct device *dev, const char *id) { - return __devm_reset_control_get(dev, id, 0, false, true); + return __devm_reset_control_get(dev, id, 0, false, true, false); } static inline struct reset_control *devm_reset_control_get_optional_shared( struct device *dev, const char *id) { - return __devm_reset_control_get(dev, id, 0, true, true); + return __devm_reset_control_get(dev, id, 0, true, true, false); } /** @@ -317,7 +337,7 @@ static inline struct reset_control *devm_reset_control_get_optional_shared( static inline struct reset_control * devm_reset_control_get_exclusive_by_index(struct device *dev, int index) { - return __devm_reset_control_get(dev, NULL, index, false, false); + return __devm_reset_control_get(dev, NULL, index, false, false, false); } /** @@ -333,7 +353,7 @@ devm_reset_control_get_exclusive_by_index(struct device *dev, int index) static inline struct reset_control * devm_reset_control_get_shared_by_index(struct device *dev, int index) { - return __devm_reset_control_get(dev, NULL, index, true, false); + return __devm_reset_control_get(dev, NULL, index, true, false, false); } /* From patchwork Mon Sep 17 16:39:55 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Geert Uytterhoeven X-Patchwork-Id: 10603065 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 036E413AD for ; Mon, 17 Sep 2018 16:40:08 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EBBC32A1C3 for ; Mon, 17 Sep 2018 16:40:07 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DBDC029DC9; Mon, 17 Sep 2018 16:40:07 +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.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 49F1B2A1CD for ; Mon, 17 Sep 2018 16:40:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728724AbeIQWIN (ORCPT ); Mon, 17 Sep 2018 18:08:13 -0400 Received: from albert.telenet-ops.be ([195.130.137.90]:42692 "EHLO albert.telenet-ops.be" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727124AbeIQWIN (ORCPT ); Mon, 17 Sep 2018 18:08:13 -0400 Received: from ramsan.of.borg ([84.194.111.163]) by albert.telenet-ops.be with bizsmtp id cgg41y00o3XaVaC06gg4sy; Mon, 17 Sep 2018 18:40:04 +0200 Received: from rox.of.borg ([192.168.97.57]) by ramsan.of.borg with esmtp (Exim 4.86_2) (envelope-from ) id 1g1wZA-0006bE-AL; Mon, 17 Sep 2018 18:40:04 +0200 Received: from geert by rox.of.borg with local (Exim 4.90_1) (envelope-from ) id 1g1wZA-0004xm-9N; Mon, 17 Sep 2018 18:40:04 +0200 From: Geert Uytterhoeven To: Philipp Zabel , Eric Auger , Alex Williamson Cc: kvm@vger.kernel.org, devicetree@vger.kernel.org, linux-renesas-soc@vger.kernel.org, linux-kernel@vger.kernel.org, Geert Uytterhoeven Subject: [PATCH/RFC v4 2/2] vfio: platform: Add generic reset controller support Date: Mon, 17 Sep 2018 18:39:55 +0200 Message-Id: <20180917163955.19023-3-geert+renesas@glider.be> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180917163955.19023-1-geert+renesas@glider.be> References: <20180917163955.19023-1-geert+renesas@glider.be> Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Vfio-platform requires dedicated reset support, provided either by ACPI, or, on DT platforms, by a device-specific reset driver matching against the device's compatible value. On many SoCs, devices are connected to an SoC-internal reset controller. If the reset hierarchy is described in DT using "resets" properties, or in lookup tables in platform code, such devices can be reset in a generic way through the reset controller subsystem. Hence add support for this, avoiding the need to write device-specific reset drivers for each single device on affected SoCs. Devices that do require a more complex reset procedure can still provide a device-specific reset driver, as that takes precedence. Note that this functionality depends on CONFIG_RESET_CONTROLLER=y, and becomes a no-op (as in: "No reset function found for device") if reset controller support is disabled. Signed-off-by: Geert Uytterhoeven Reviewed-by: Philipp Zabel Reviewed-by: Simon Horman --- v4: - Add Reviewed-by, - Use new RFC reset_control_get_dedicated() instead of of_reset_control_get_exclusive(), to (a) make it more generic, and (b) make sure the reset returned is really a dedicated reset, and does not affect other devices, v3: - Add Reviewed-by, - Merge similar checks in vfio_platform_has_reset(), v2: - Don't store error values in vdev->reset_control, - Use of_reset_control_get_exclusive() instead of __of_reset_control_get(), - Improve description. --- drivers/vfio/platform/vfio_platform_common.c | 20 +++++++++++++++++-- drivers/vfio/platform/vfio_platform_private.h | 1 + 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/vfio/platform/vfio_platform_common.c b/drivers/vfio/platform/vfio_platform_common.c index c0cd824be2b767be..eb77fe87f3663e3e 100644 --- a/drivers/vfio/platform/vfio_platform_common.c +++ b/drivers/vfio/platform/vfio_platform_common.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -113,11 +114,13 @@ static bool vfio_platform_has_reset(struct vfio_platform_device *vdev) if (VFIO_PLATFORM_IS_ACPI(vdev)) return vfio_platform_acpi_has_reset(vdev); - return vdev->of_reset ? true : false; + return vdev->of_reset || vdev->reset_control; } static int vfio_platform_get_reset(struct vfio_platform_device *vdev) { + struct reset_control *rstc; + if (VFIO_PLATFORM_IS_ACPI(vdev)) return vfio_platform_acpi_has_reset(vdev) ? 0 : -ENOENT; @@ -128,8 +131,16 @@ static int vfio_platform_get_reset(struct vfio_platform_device *vdev) vdev->of_reset = vfio_platform_lookup_reset(vdev->compat, &vdev->reset_module); } + if (vdev->of_reset) + return 0; + + rstc = reset_control_get_dedicated(vdev->device, NULL); + if (!IS_ERR(rstc)) { + vdev->reset_control = rstc; + return 0; + } - return vdev->of_reset ? 0 : -ENOENT; + return PTR_ERR(rstc); } static void vfio_platform_put_reset(struct vfio_platform_device *vdev) @@ -139,6 +150,8 @@ static void vfio_platform_put_reset(struct vfio_platform_device *vdev) if (vdev->of_reset) module_put(vdev->reset_module); + + reset_control_put(vdev->reset_control); } static int vfio_platform_regions_init(struct vfio_platform_device *vdev) @@ -218,6 +231,9 @@ static int vfio_platform_call_reset(struct vfio_platform_device *vdev, } else if (vdev->of_reset) { dev_info(vdev->device, "reset\n"); return vdev->of_reset(vdev); + } else if (vdev->reset_control) { + dev_info(vdev->device, "reset\n"); + return reset_control_reset(vdev->reset_control); } dev_warn(vdev->device, "no reset function found!\n"); diff --git a/drivers/vfio/platform/vfio_platform_private.h b/drivers/vfio/platform/vfio_platform_private.h index 85ffe5d9d1abd94e..a56e80ae5986540b 100644 --- a/drivers/vfio/platform/vfio_platform_private.h +++ b/drivers/vfio/platform/vfio_platform_private.h @@ -60,6 +60,7 @@ struct vfio_platform_device { const char *compat; const char *acpihid; struct module *reset_module; + struct reset_control *reset_control; struct device *device; /*