Received: by 2002:ab2:1689:0:b0:1f7:5705:b850 with SMTP id d9csp1876796lqa; Tue, 30 Apr 2024 01:39:43 -0700 (PDT) X-Forwarded-Encrypted: i=3; AJvYcCXWnqKOUMsOFfnW6QjGj64J6N2iUHZ1pux1Md2L4xfneevAINjGieGiomqM3W2Y+hs3WPovFF3T9uC8qbA0my0rFYcGD5uLpvSOmhiWnw== X-Google-Smtp-Source: AGHT+IF17F46U58sNV1ijeReQ5E3nrsV1H8J9PpdLzBKRIlntTX3c31rqKc3Kn9WZjSXEdRv9Ep6 X-Received: by 2002:a17:902:cec9:b0:1eb:992b:789b with SMTP id d9-20020a170902cec900b001eb992b789bmr8748065plg.2.1714466383375; Tue, 30 Apr 2024 01:39:43 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1714466383; cv=pass; d=google.com; s=arc-20160816; b=wkVJ0Kx4NctHao36iYtIvABJrul5pxmiNbe2O3CBVoyG84xcqzUZFJylizHdaI0Eyk unFkTO268of6Whdzt6elFlWC4t74DPfO7pH2LfkOB46xbGSkzt1WVDJcR4t6ZGsssR4V eFV+JS2CCMEiO9gr8nXEZAyziRAZ0JnS1xH4eF2pcclVKmnot/YV71VGWRbid8LBgT86 rjQr9voJP32Uc1aMhD5wx8T+bvev3O9V4nnej9x94FTRqBy216PLyzLc+3YpwRom3z1P lm+K43RBfhovmsB5yCGmH6UKxKrHdMw6O4j3kI9faIM4nAqoIBT6kanL4dMPRbziKh2K XRIw== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:references:in-reply-to:message-id :date:subject:cc:to:from:dkim-signature; bh=TKlAO4KsYqpHiNkyQhj+YDisqgQI3Psuq2ZrhzBKVbE=; fh=FZ8lJPkZghXo7cjjk5LlG/r+nFxaAY6dY38taoG8W1o=; b=AqHm0JNmTrePXrUg5tH0olaxS79EGFRqtxBHsXBIUFCowq0a6e3GNZXX7obCqlMxLh UKlWEmZHR9gsTBRfFCUlO4MpOsRgJUXnB0LgjdDsjg+PHOxATUTCYDvJ+fVmK3A2VD+i 4Cl6Uc+coZes9m6X0kQSX+F35Ba4u0eonCJGIhbZbu7/5fDmmv918BPsI30snTIhKkBO nO99vfi4N+llg43Wjy9FRUc+v01oG8TriaH1yFXks5RM2ejY4uH500+UN6x8JEba1LSE KRzT6wA+lMjZTnTVXq5KrbhDcKUlh1BpXg31DBZXaayZRIzZGzZsJi7lnXfTzhb+vo4e bazQ==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@bootlin.com header.s=gm1 header.b=k8zW+xUF; arc=pass (i=1 spf=pass spfdomain=bootlin.com dkim=pass dkdomain=bootlin.com dmarc=pass fromdomain=bootlin.com); spf=pass (google.com: domain of linux-kernel+bounces-163551-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:40f1:3f00::1 as permitted sender) smtp.mailfrom="linux-kernel+bounces-163551-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=bootlin.com Return-Path: Received: from sy.mirrors.kernel.org (sy.mirrors.kernel.org. [2604:1380:40f1:3f00::1]) by mx.google.com with ESMTPS id ma12-20020a170903094c00b001e675f9de65si21620599plb.304.2024.04.30.01.39.42 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 30 Apr 2024 01:39:43 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel+bounces-163551-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:40f1:3f00::1 as permitted sender) client-ip=2604:1380:40f1:3f00::1; Authentication-Results: mx.google.com; dkim=pass header.i=@bootlin.com header.s=gm1 header.b=k8zW+xUF; arc=pass (i=1 spf=pass spfdomain=bootlin.com dkim=pass dkdomain=bootlin.com dmarc=pass fromdomain=bootlin.com); spf=pass (google.com: domain of linux-kernel+bounces-163551-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:40f1:3f00::1 as permitted sender) smtp.mailfrom="linux-kernel+bounces-163551-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=bootlin.com Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sy.mirrors.kernel.org (Postfix) with ESMTPS id B5176B21A2A for ; Tue, 30 Apr 2024 08:39:39 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id BCCA5127B52; Tue, 30 Apr 2024 08:39:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="k8zW+xUF" Received: from relay7-d.mail.gandi.net (relay7-d.mail.gandi.net [217.70.183.200]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BB75B41C89; Tue, 30 Apr 2024 08:39:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.200 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714466354; cv=none; b=YAb7Qn/mJayr4IbjjwqdpOIT9yXPwBk/6MFfCpetxumpxiU2TbYggL0DW+3ApGd9CtEWUxKt2dFFp81mTrqh7dm+D/yRaO1CQYXhgRN+1SXhtGlzwOdqpLIjTFJmCTTaVGUJbyKDCdxubopdpQWYZsjUOGpryFtj0GNfo5YJ6D0= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714466354; c=relaxed/simple; bh=G1UAW0H9h1wFRSfJQIj8x5FIWwrzTgw72kS9F2yRFtE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=nD/RAravCiZUKjarFus8ODdyzHe7ciwXJUunbwpT+kSDWt9sBevDTmd2XmHCC563NxVyM1/G2Q+UMwxyDSlPfstqRTodG3WQvchnNiSw+jzSDWoGWIMctXoBq2WdiojCPgv6SUMinKJtQcdHKC+uB9WYvaPwGekJqc7y8+ntkXs= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=k8zW+xUF; arc=none smtp.client-ip=217.70.183.200 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Received: by mail.gandi.net (Postfix) with ESMTPA id AF2AA20005; Tue, 30 Apr 2024 08:39:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1714466350; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=TKlAO4KsYqpHiNkyQhj+YDisqgQI3Psuq2ZrhzBKVbE=; b=k8zW+xUF9Ig2N46QYNzzXAZWkk9CwpQGFVoL1lcxo7Ne2Tyt12nrZm4PxjG/LH00SmaMB/ NPpyw/hwPz60R0x13V+NbvA3m87UJYXm3oYZDpiWInwc1xVubDenz3jCXxFx5vKahNLwDd dulid62Pnia2byihDIgvvoUmmt6xXx8CoufsOP9pQHO/IJtJJYhBn11qWdqYHqB/N5NDVK ceBa8baD+QBXcS+ZOXnxIvY3iTSsk1egyBCEprHswV6oJsEBUG9+BKSkf8YnAvhM3wFdgG 3pvdYrxeGS5er10NdS6UNmQMHiMwPMhmkPNa6qdbGjxX4HivsFkGP6yKze3U2A== From: Herve Codina To: Herve Codina , Thomas Gleixner , Rob Herring , Krzysztof Kozlowski , Conor Dooley , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Lee Jones , Arnd Bergmann , Horatiu Vultur , UNGLinuxDriver@microchip.com, Andrew Lunn , Heiner Kallweit , Russell King , Saravana Kannan , Bjorn Helgaas , Philipp Zabel , Lars Povlsen , Steen Hegelund , Daniel Machon , Alexandre Belloni Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, netdev@vger.kernel.org, linux-pci@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Allan Nielsen , Steen Hegelund , Luca Ceresoli , Thomas Petazzoni , =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= Subject: [PATCH 01/17] mfd: syscon: Add reference counting and device managed support Date: Tue, 30 Apr 2024 10:37:10 +0200 Message-ID: <20240430083730.134918-2-herve.codina@bootlin.com> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240430083730.134918-1-herve.codina@bootlin.com> References: <20240430083730.134918-1-herve.codina@bootlin.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-GND-Sasl: herve.codina@bootlin.com From: Clément Léger Syscon releasing is not supported. Without release function, unbinding a driver that uses syscon whether explicitly or due to a module removal left the used syscon in a in-use state. For instance a syscon_node_to_regmap() call from a consumer retrieve a syscon regmap instance. Internally, syscon_node_to_regmap() can create syscon instance and add it to the existing syscon list. No API is available to release this syscon instance, remove it from the list and free it when it is not used anymore. Introduce reference counting in syscon in order to keep track of syscon usage using syscon_{get,put}() and add a device managed version of syscon_regmap_lookup_by_phandle(), to automatically release the syscon instance on the consumer removal. Signed-off-by: Clément Léger Signed-off-by: Herve Codina --- drivers/mfd/syscon.c | 145 ++++++++++++++++++++++++++++++++++--- include/linux/mfd/syscon.h | 18 +++++ 2 files changed, 154 insertions(+), 9 deletions(-) diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index 7d0e91164cba..86898831b842 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -34,6 +34,7 @@ struct syscon { struct regmap *regmap; struct reset_control *reset; struct list_head list; + struct kref refcount; }; static const struct regmap_config syscon_regmap_config = { @@ -147,6 +148,8 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_res) syscon->regmap = regmap; syscon->np = np; + of_node_get(syscon->np); + kref_init(&syscon->refcount); spin_lock(&syscon_list_slock); list_add_tail(&syscon->list, &syscon_list); @@ -168,7 +171,30 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_res) return ERR_PTR(ret); } -static struct regmap *device_node_get_regmap(struct device_node *np, +static void syscon_free(struct kref *kref) +{ + struct syscon *syscon = container_of(kref, struct syscon, refcount); + + spin_lock(&syscon_list_slock); + list_del(&syscon->list); + spin_unlock(&syscon_list_slock); + + regmap_exit(syscon->regmap); + of_node_put(syscon->np); + kfree(syscon); +} + +static void syscon_get(struct syscon *syscon) +{ + kref_get(&syscon->refcount); +} + +static void syscon_put(struct syscon *syscon) +{ + kref_put(&syscon->refcount, syscon_free); +} + +static struct syscon *device_node_get_syscon(struct device_node *np, bool check_res) { struct syscon *entry, *syscon = NULL; @@ -183,9 +209,23 @@ static struct regmap *device_node_get_regmap(struct device_node *np, spin_unlock(&syscon_list_slock); - if (!syscon) + if (!syscon) { syscon = of_syscon_register(np, check_res); + if (IS_ERR(syscon)) + return ERR_CAST(syscon); + } else { + syscon_get(syscon); + } + + return syscon; +} +static struct regmap *device_node_get_regmap(struct device_node *np, + bool check_res) +{ + struct syscon *syscon; + + syscon = device_node_get_syscon(np, check_res); if (IS_ERR(syscon)) return ERR_CAST(syscon); @@ -198,12 +238,23 @@ struct regmap *device_node_to_regmap(struct device_node *np) } EXPORT_SYMBOL_GPL(device_node_to_regmap); -struct regmap *syscon_node_to_regmap(struct device_node *np) +static struct syscon *syscon_node_to_syscon(struct device_node *np) { if (!of_device_is_compatible(np, "syscon")) return ERR_PTR(-EINVAL); - return device_node_get_regmap(np, true); + return device_node_get_syscon(np, true); +} + +struct regmap *syscon_node_to_regmap(struct device_node *np) +{ + struct syscon *syscon; + + syscon = syscon_node_to_syscon(np); + if (IS_ERR(syscon)) + return ERR_CAST(syscon); + + return syscon->regmap; } EXPORT_SYMBOL_GPL(syscon_node_to_regmap); @@ -223,11 +274,11 @@ struct regmap *syscon_regmap_lookup_by_compatible(const char *s) } EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible); -struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, - const char *property) +static struct syscon *syscon_lookup_by_phandle(struct device_node *np, + const char *property) { struct device_node *syscon_np; - struct regmap *regmap; + struct syscon *syscon; if (property) syscon_np = of_parse_phandle(np, property, 0); @@ -237,12 +288,24 @@ struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, if (!syscon_np) return ERR_PTR(-ENODEV); - regmap = syscon_node_to_regmap(syscon_np); + syscon = syscon_node_to_syscon(syscon_np); if (property) of_node_put(syscon_np); - return regmap; + return syscon; +} + +struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, + const char *property) +{ + struct syscon *syscon; + + syscon = syscon_lookup_by_phandle(np, property); + if (IS_ERR(syscon)) + return ERR_CAST(syscon); + + return syscon->regmap; } EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle); @@ -293,6 +356,70 @@ struct regmap *syscon_regmap_lookup_by_phandle_optional(struct device_node *np, } EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle_optional); +static struct syscon *syscon_from_regmap(struct regmap *regmap) +{ + struct syscon *entry, *syscon = NULL; + + spin_lock(&syscon_list_slock); + + list_for_each_entry(entry, &syscon_list, list) + if (entry->regmap == regmap) { + syscon = entry; + break; + } + + spin_unlock(&syscon_list_slock); + + return syscon; +} + +void syscon_put_regmap(struct regmap *regmap) +{ + struct syscon *syscon; + + syscon = syscon_from_regmap(regmap); + if (!syscon) + return; + + syscon_put(syscon); +} +EXPORT_SYMBOL_GPL(syscon_put_regmap); + +static void devm_syscon_release(struct device *dev, void *res) +{ + syscon_put(*(struct syscon **)res); +} + +static struct regmap *__devm_syscon_get(struct device *dev, + struct syscon *syscon) +{ + struct syscon **ptr; + + if (IS_ERR(syscon)) + return ERR_CAST(syscon); + + ptr = devres_alloc(devm_syscon_release, sizeof(struct syscon *), GFP_KERNEL); + if (!ptr) { + syscon_put(syscon); + return ERR_PTR(-ENOMEM); + } + + *ptr = syscon; + devres_add(dev, ptr); + + return syscon->regmap; +} + +struct regmap *devm_syscon_regmap_lookup_by_phandle(struct device *dev, + struct device_node *np, + const char *property) +{ + struct syscon *syscon = syscon_lookup_by_phandle(np, property); + + return __devm_syscon_get(dev, syscon); +} +EXPORT_SYMBOL_GPL(devm_syscon_regmap_lookup_by_phandle); + static int syscon_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; diff --git a/include/linux/mfd/syscon.h b/include/linux/mfd/syscon.h index c315903f6dab..164b9bcb49c3 100644 --- a/include/linux/mfd/syscon.h +++ b/include/linux/mfd/syscon.h @@ -15,6 +15,7 @@ #include struct device_node; +struct device; #ifdef CONFIG_MFD_SYSCON struct regmap *device_node_to_regmap(struct device_node *np); @@ -28,6 +29,11 @@ struct regmap *syscon_regmap_lookup_by_phandle_args(struct device_node *np, unsigned int *out_args); struct regmap *syscon_regmap_lookup_by_phandle_optional(struct device_node *np, const char *property); +void syscon_put_regmap(struct regmap *regmap); + +struct regmap *devm_syscon_regmap_lookup_by_phandle(struct device *dev, + struct device_node *np, + const char *property); #else static inline struct regmap *device_node_to_regmap(struct device_node *np) { @@ -67,6 +73,18 @@ static inline struct regmap *syscon_regmap_lookup_by_phandle_optional( return NULL; } +static intline void syscon_put_regmap(struct regmap *regmap) +{ +} + +static inline +struct regmap *devm_syscon_regmap_lookup_by_phandle(struct device *dev, + struct device_node *np, + const char *property) +{ + return NULL; +} + #endif #endif /* __LINUX_MFD_SYSCON_H__ */ -- 2.44.0