Received: by 2002:a89:d88:0:b0:1fa:5c73:8e2d with SMTP id eb8csp2150561lqb; Mon, 27 May 2024 09:20:55 -0700 (PDT) X-Forwarded-Encrypted: i=3; AJvYcCXgpr8gEoLf2Sb85Ic1R0FgVfsc6o10gOSH+CfKhYH1oEY6lEjSRj+JGMHZDj73kvp9gXXSqUYmISHVoXRoGKv/COeLHeoR3It6PUWHbQ== X-Google-Smtp-Source: AGHT+IGw+fzfSY2RQ04gmOfibT1nOlOtALkPSHwNkQOKelvFKVn9hpnlj+mFXRDy2jH4qKlFgPsT X-Received: by 2002:a17:906:3481:b0:a5c:df8f:7233 with SMTP id a640c23a62f3a-a62642eb633mr635406866b.33.1716826854993; Mon, 27 May 2024 09:20:54 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1716826854; cv=pass; d=google.com; s=arc-20160816; b=PgxTpDAvKd9HZiAKaTarVEajHa0mmrRD2IT1Xu4VkXQ1xM85DEu1f0ePK9UwabT/iU NVGA3dbnAo6ykMss8KSoVZv95rAw1Izh2XXPpgKEmRZ1rhTsPK4Z84vGrzfyZEOmaGqG XkGzlRxKtpxrk7VtHzeApgZD8qdAhIbv9iU9aE8eM9xRMq+obFFv9HWEqjjn061n9uvv YvdRwSUlvhua/kp81aMGYL/9ITYSgPjVHQ43+m1VspEEwV9d84JpBsiTYvn7QP7NYaTM eUfluO5Rdfm6Kg8ysfaSxRVwaVpB4OyojeNDs4xT7W0eZCe18GgMtWs3iR2/+MkS3ws/ PNyg== 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=W9+p1hma003opriOOEox9fT6iYMjtITIcW1ZIQ2QLNo=; fh=FgwDDXx9Ad8vmEmzzXm7uCBC9EwkDp3k7U93srafrH4=; b=Ybx+IuF63mfOGl7JvynrHJNjL0+PSbqVO9dSaLW+1OwAaIJWO/1OFbUpEwBYHBPkiQ wufyzdkJJjvNqfzEM8GTEETiEepBuV7/pZisbEPPW4NTZhizb57wF5c1dziUDDg/fR9/ C+McrnGeDuBWKEDOtjjlUGfDWCcZYbFIaLTU44CTjSjWdB6pqPGuTJuxO7P0Qh9uDXUW XJSmBFJnJIS224YqoGI2XnzSTn9kQrJ0w0UScy2FxI0CYFOHYfAv1ixiInxxqd/7kikA clTk/gh71WivXls3tlr5z7aexGpl9sjJz/b+C0RYxaNIvNPhKd5QxckyQyDvRmSCPJ9Z MFLA==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@bootlin.com header.s=gm1 header.b=UbbkYHxY; 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-191212-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) smtp.mailfrom="linux-kernel+bounces-191212-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=bootlin.com Return-Path: Received: from am.mirrors.kernel.org (am.mirrors.kernel.org. [147.75.80.249]) by mx.google.com with ESMTPS id a640c23a62f3a-a626c80533fsi408890266b.41.2024.05.27.09.20.54 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 27 May 2024 09:20:54 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel+bounces-191212-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) client-ip=147.75.80.249; Authentication-Results: mx.google.com; dkim=pass header.i=@bootlin.com header.s=gm1 header.b=UbbkYHxY; 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-191212-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) smtp.mailfrom="linux-kernel+bounces-191212-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 am.mirrors.kernel.org (Postfix) with ESMTPS id 932811F22432 for ; Mon, 27 May 2024 16:20:54 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 51A4C15FD1E; Mon, 27 May 2024 16:15:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="UbbkYHxY" Received: from relay9-d.mail.gandi.net (relay9-d.mail.gandi.net [217.70.183.199]) (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 C3B4C15F30A; Mon, 27 May 2024 16:15:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.199 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716826505; cv=none; b=cgpw1J/EYqFzZk0J3mzLu0g+aV/pgtuiIMPE/eTvWJtm1gIXJdDAgKzlvzntofusJqyXty3kUoEX5ddjPmQZzuXVVr5iR7bYS7W8XkqPVubjSVJjtAloeSs71enpDPsmpvvHT2642HPyRfBTkgPFF8IFXKGXMae2fqs/C/PDTjo= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1716826505; c=relaxed/simple; bh=0M1Hns7CDOYJ2aLM9KSawz/vJcM90IBbHl4CqMegdlE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Dsa9AiA6NrSyZLTYAyhddrLRg8uJigHZ6cBBrX8P+nC9ZAZNbFKRPsJNtGyiImDxuxojGaReH4V2N8yl+U/0ABOENGgbXV7oc6pcKsp+EY51eFjL/tPqzkykR6I3dV8AZWoZlNsng4O0VV7zna5ufnvRbJpRWtZHC7qLs87t/kQ= 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=UbbkYHxY; arc=none smtp.client-ip=217.70.183.199 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 6E42AFF811; Mon, 27 May 2024 16:14:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1716826500; 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=W9+p1hma003opriOOEox9fT6iYMjtITIcW1ZIQ2QLNo=; b=UbbkYHxYIA3ApPt5oZI7rO4HRwWoAo+RNwHf495UokGST04QM/XPmwZtNfGcvXqNkTLLfv zPUvOe+3SPZsP9KT2IJxAesVe4FI9LV4C2BZTsojIVpNNUeFoesg2zWL8OJEA8+nRMAOHN K+llDN/wjyKrld3B3chDtl9+p+esUWB+dB8aE9FtPOBBFiQg0um64VbuBdlxOWQtqvk3OM W0JAOMp5UrAlcivoZaqFJ2Ggh8DZV8bwlkDnUzSnTShUs503r9tGo7Of+ahmFZI3fGyWJG U5aa5lsr0s8+nuyELHEYKPojqVP9Am8/bPDFC0Q/FPlAZ32lf/kjwEoYKm4y8A== From: Herve Codina To: Simon Horman , Sai Krishna Gajula , 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 v2 01/19] mfd: syscon: Add reference counting and device managed support Date: Mon, 27 May 2024 18:14:28 +0200 Message-ID: <20240527161450.326615-2-herve.codina@bootlin.com> X-Mailer: git-send-email 2.45.0 In-Reply-To: <20240527161450.326615-1-herve.codina@bootlin.com> References: <20240527161450.326615-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..f742d865a37a 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 inline 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.45.0