From patchwork Fri Dec 11 15:41:58 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 7830811 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 249A09F387 for ; Fri, 11 Dec 2015 15:44:20 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 03B972057E for ; Fri, 11 Dec 2015 15:44:19 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id DEBF920592 for ; Fri, 11 Dec 2015 15:44:17 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1a7PqG-00073E-3U; Fri, 11 Dec 2015 15:42:44 +0000 Received: from mx1.redhat.com ([209.132.183.28]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1a7Ppz-0006el-Vo for linux-arm-kernel@lists.infradead.org; Fri, 11 Dec 2015 15:42:29 +0000 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (Postfix) with ESMTPS id D955E8E763; Fri, 11 Dec 2015 15:42:06 +0000 (UTC) Received: from shalem.localdomain.com (unused [10.10.51.125] (may be forged)) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id tBBFg29u015798; Fri, 11 Dec 2015 10:42:02 -0500 From: Hans de Goede To: Philipp Zabel , Josh Triplett , Rashika Kheria , Stephen Warren , Maxime Ripard , Greg Kroah-Hartman Subject: [PATCH v2 1/3] reset: Add shared reset_control_[de]assert variants Date: Fri, 11 Dec 2015 16:41:58 +0100 Message-Id: <1449848520-27379-1-git-send-email-hdegoede@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.22 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20151211_074228_120672_BD69D357 X-CRM114-Status: GOOD ( 20.07 ) X-Spam-Score: -6.9 (------) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: devicetree , Florian Fainelli , linux-usb , Chen-Yu Tsai , Tony Prisk , Hans de Goede , linux-sunxi@googlegroups.com, Alan Stern , linux-arm-kernel@lists.infradead.org, Roger Quadros MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add reset_control_deassert_shared / reset_control_assert_shared functions which are intended for use by drivers for hw blocks which (may) share a reset line with another driver / hw block. Unlike the regular reset_control_[de]assert functions these functions keep track of how often deassert_shared / assert_shared have been called and keep the line deasserted as long as deassert has been called more times than assert. Signed-off-by: Hans de Goede --- Changes in v2: -This is a new patch in v2 of this patch-set --- drivers/reset/core.c | 121 ++++++++++++++++++++++++++++++++++++--- include/linux/reset-controller.h | 2 + include/linux/reset.h | 2 + 3 files changed, 116 insertions(+), 9 deletions(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 9ab9290..8c3436c 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -22,16 +22,29 @@ static DEFINE_MUTEX(reset_controller_list_mutex); static LIST_HEAD(reset_controller_list); /** + * struct reset_line - a reset line + * @list: list entry for the reset controllers reset line list + * @id: ID of the reset line in the reset controller device + * @refcnt: Number of reset_control structs referencing this device + * @deassert_cnt: Number of times this reset line has been deasserted + */ +struct reset_line { + struct list_head list; + unsigned int id; + unsigned int refcnt; + unsigned int deassert_cnt; +}; + +/** * struct reset_control - a reset control * @rcdev: a pointer to the reset controller device * this reset control belongs to - * @id: ID of the reset controller in the reset - * controller device + * @line: reset line for this reset control */ struct reset_control { struct reset_controller_dev *rcdev; + struct reset_line *line; struct device *dev; - unsigned int id; }; /** @@ -66,6 +79,8 @@ int reset_controller_register(struct reset_controller_dev *rcdev) rcdev->of_xlate = of_reset_simple_xlate; } + INIT_LIST_HEAD(&rcdev->reset_line_head); + mutex_lock(&reset_controller_list_mutex); list_add(&rcdev->list, &reset_controller_list); mutex_unlock(&reset_controller_list_mutex); @@ -93,7 +108,7 @@ EXPORT_SYMBOL_GPL(reset_controller_unregister); int reset_control_reset(struct reset_control *rstc) { if (rstc->rcdev->ops->reset) - return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id); + return rstc->rcdev->ops->reset(rstc->rcdev, rstc->line->id); return -ENOTSUPP; } @@ -106,7 +121,7 @@ EXPORT_SYMBOL_GPL(reset_control_reset); int reset_control_assert(struct reset_control *rstc) { if (rstc->rcdev->ops->assert) - return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id); + return rstc->rcdev->ops->assert(rstc->rcdev, rstc->line->id); return -ENOTSUPP; } @@ -119,13 +134,55 @@ EXPORT_SYMBOL_GPL(reset_control_assert); int reset_control_deassert(struct reset_control *rstc) { if (rstc->rcdev->ops->deassert) - return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); + return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->line->id); return -ENOTSUPP; } EXPORT_SYMBOL_GPL(reset_control_deassert); /** + * reset_control_assert_shared - asserts a shared reset line + * @rstc: reset controller + * + * Assert a shared reset line, this functions decreases the deassert count + * of the line by one and asserts it if, and only if, the deassert count + * reaches 0. + */ +int reset_control_assert_shared(struct reset_control *rstc) +{ + if (!rstc->rcdev->ops->assert) + return -ENOTSUPP; + + rstc->line->deassert_cnt--; + if (rstc->line->deassert_cnt) + return 0; + + return rstc->rcdev->ops->assert(rstc->rcdev, rstc->line->id); +} +EXPORT_SYMBOL_GPL(reset_control_assert_shared); + +/** + * reset_control_deassert_shared - deasserts a shared reset line + * @rstc: reset controller + * + * Assert a shared reset line, this functions increases the deassert count + * of the line by one and deasserts the reset line (if it was not already + * deasserted). + */ +int reset_control_deassert_shared(struct reset_control *rstc) +{ + if (!rstc->rcdev->ops->deassert) + return -ENOTSUPP; + + rstc->line->deassert_cnt++; + if (rstc->line->deassert_cnt != 1) + return 0; + + return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->line->id); +} +EXPORT_SYMBOL_GPL(reset_control_deassert_shared); + +/** * reset_control_status - returns a negative errno if not supported, a * positive value if the reset line is asserted, or zero if the reset * line is not asserted. @@ -134,12 +191,47 @@ EXPORT_SYMBOL_GPL(reset_control_deassert); int reset_control_status(struct reset_control *rstc) { if (rstc->rcdev->ops->status) - return rstc->rcdev->ops->status(rstc->rcdev, rstc->id); + return rstc->rcdev->ops->status(rstc->rcdev, rstc->line->id); return -ENOTSUPP; } EXPORT_SYMBOL_GPL(reset_control_status); +static struct reset_line *reset_line_get(struct reset_controller_dev *rcdev, + unsigned int index) +{ + struct reset_line *line; + + list_for_each_entry(line, &rcdev->reset_line_head, list) { + if (line->id == index) { + line->refcnt++; + return line; + } + } + + line = kzalloc(sizeof(*line), GFP_KERNEL); + if (!line) + return NULL; + + list_add(&line->list, &rcdev->reset_line_head); + line->id = index; + line->refcnt = 1; + + return line; +} + +static void reset_line_put(struct reset_line *line) +{ + if (!line) + return; + + if (--line->refcnt) + return; + + list_del(&line->list); + kfree(line); +} + /** * of_reset_control_get_by_index - Lookup and obtain a reference to a reset * controller by index. @@ -155,6 +247,7 @@ struct reset_control *of_reset_control_get_by_index(struct device_node *node, { struct reset_control *rstc = ERR_PTR(-EPROBE_DEFER); struct reset_controller_dev *r, *rcdev; + struct reset_line *line; struct of_phandle_args args; int rstc_id; int ret; @@ -186,16 +279,22 @@ struct reset_control *of_reset_control_get_by_index(struct device_node *node, } try_module_get(rcdev->owner); + + /* reset_controller_list_mutex also protects the reset_line list */ + line = reset_line_get(rcdev, rstc_id); + mutex_unlock(&reset_controller_list_mutex); rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); - if (!rstc) { + if (!line || !rstc) { + kfree(rstc); + reset_line_put(line); module_put(rcdev->owner); return ERR_PTR(-ENOMEM); } rstc->rcdev = rcdev; - rstc->id = rstc_id; + rstc->line = line; return rstc; } @@ -259,6 +358,10 @@ void reset_control_put(struct reset_control *rstc) if (IS_ERR(rstc)) return; + mutex_lock(&reset_controller_list_mutex); + reset_line_put(rstc->line); + mutex_unlock(&reset_controller_list_mutex); + module_put(rstc->rcdev->owner); kfree(rstc); } diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h index ce6b962..7f2cbd1 100644 --- a/include/linux/reset-controller.h +++ b/include/linux/reset-controller.h @@ -31,6 +31,7 @@ struct of_phandle_args; * @ops: a pointer to device specific struct reset_control_ops * @owner: kernel module of the reset controller driver * @list: internal list of reset controller devices + * @reset_line_head: head of internal list of reset lines * @of_node: corresponding device tree node as phandle target * @of_reset_n_cells: number of cells in reset line specifiers * @of_xlate: translation function to translate from specifier as found in the @@ -41,6 +42,7 @@ struct reset_controller_dev { struct reset_control_ops *ops; struct module *owner; struct list_head list; + struct list_head reset_line_head; struct device_node *of_node; int of_reset_n_cells; int (*of_xlate)(struct reset_controller_dev *rcdev, diff --git a/include/linux/reset.h b/include/linux/reset.h index c4c097d..1cca8ce 100644 --- a/include/linux/reset.h +++ b/include/linux/reset.h @@ -11,6 +11,8 @@ int reset_control_reset(struct reset_control *rstc); int reset_control_assert(struct reset_control *rstc); int reset_control_deassert(struct reset_control *rstc); int reset_control_status(struct reset_control *rstc); +int reset_control_assert_shared(struct reset_control *rstc); +int reset_control_deassert_shared(struct reset_control *rstc); struct reset_control *reset_control_get(struct device *dev, const char *id); void reset_control_put(struct reset_control *rstc);