From: James Hilliard <james.hilliard1@gmail.com>
To: Lee Jones <lee@kernel.org>, Arnd Bergmann <arnd@arndb.de>,
Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>,
James Hilliard <james.hilliard1@gmail.com>
Cc: mfd@lists.linux.dev, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: [PATCH v6 2/3] mfd: syscon: Add managed registration for external regmaps
Date: Tue, 11 Aug 2026 02:27:24 -0600 [thread overview]
Message-ID: <20260811-submit-ac200-mfd-v6-2-c5b1292c8498@gmail.com> (raw)
In-Reply-To: <20260811-submit-ac200-mfd-v6-0-c5b1292c8498@gmail.com>
of_syscon_register_regmap() publishes an externally owned regmap in the
global syscon list, but provides no way for a removable driver to
withdraw it. Registering a devm-managed regmap from such a driver would
therefore leave a stale pointer after unbind.
Factor external registration through an internal helper and add
devm_of_syscon_register_regmap(). The managed action removes the entry
under the syscon list lock before later devres actions release the
regmap. Hold a device-node reference for the lifetime of every
externally registered entry so its lookup key also remains valid.
Consumers of a removable provider remain responsible for ordering
teardown, for example with a managed device link.
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/mfd/syscon.c | 87 ++++++++++++++++++++++++++++++++++------------
include/linux/mfd/syscon.h | 12 +++++++
2 files changed, 77 insertions(+), 22 deletions(-)
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
index 21a7fcdd2737..448422fe4f21 100644
--- a/drivers/mfd/syscon.c
+++ b/drivers/mfd/syscon.c
@@ -10,6 +10,7 @@
#include <linux/cleanup.h>
#include <linux/clk.h>
+#include <linux/device.h>
#include <linux/err.h>
#include <linux/hwspinlock.h>
#include <linux/list.h>
@@ -193,53 +194,95 @@ static struct regmap *device_node_get_regmap(struct device_node *np,
return syscon->regmap;
}
-/**
- * of_syscon_register_regmap() - Register regmap for specified device node
- * @np: Device tree node
- * @regmap: Pointer to regmap object
- *
- * Register an externally created regmap object with syscon for the specified
- * device tree node. This regmap will then be returned to client drivers using
- * the syscon_regmap_lookup_by_phandle() API.
- *
- * Return: 0 on success, negative error code on failure.
- */
-int of_syscon_register_regmap(struct device_node *np, struct regmap *regmap)
+static struct syscon *
+of_syscon_register_regmap_internal(struct device_node *np,
+ struct regmap *regmap)
{
struct syscon *entry, *syscon = NULL;
- int ret;
if (!np || !regmap)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
syscon = kzalloc_obj(*syscon);
if (!syscon)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
/* check if syscon entry already exists */
mutex_lock(&syscon_list_lock);
list_for_each_entry(entry, &syscon_list, list)
if (entry->np == np) {
- ret = -EEXIST;
- goto err_unlock;
+ mutex_unlock(&syscon_list_lock);
+ kfree(syscon);
+ return ERR_PTR(-EEXIST);
}
syscon->regmap = regmap;
- syscon->np = np;
+ syscon->np = of_node_get(np);
/* register the regmap in syscon list */
list_add_tail(&syscon->list, &syscon_list);
mutex_unlock(&syscon_list_lock);
- return 0;
+ return syscon;
+}
+
+/**
+ * of_syscon_register_regmap() - Register regmap for specified device node
+ * @np: Device tree node
+ * @regmap: Pointer to regmap object
+ *
+ * Register an externally created regmap object with syscon for the specified
+ * device tree node. This regmap will then be returned to client drivers using
+ * the syscon_regmap_lookup_by_phandle() API.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int of_syscon_register_regmap(struct device_node *np, struct regmap *regmap)
+{
+ return PTR_ERR_OR_ZERO(of_syscon_register_regmap_internal(np, regmap));
+}
+EXPORT_SYMBOL_GPL(of_syscon_register_regmap);
+
+static void devm_of_syscon_register_regmap_release(void *data)
+{
+ struct syscon *syscon = data;
-err_unlock:
+ mutex_lock(&syscon_list_lock);
+ list_del(&syscon->list);
mutex_unlock(&syscon_list_lock);
+
+ of_node_put(syscon->np);
kfree(syscon);
- return ret;
}
-EXPORT_SYMBOL_GPL(of_syscon_register_regmap);
+
+/**
+ * devm_of_syscon_register_regmap() - Register a managed external syscon regmap
+ * @dev: Device that owns the regmap
+ * @np: Device tree node associated with the regmap
+ * @regmap: Pointer to the externally created regmap
+ *
+ * Register an externally created regmap object with syscon and remove it when
+ * @dev is unbound. Consumers must stop using the regmap before the provider is
+ * unbound, for example by establishing a managed device link to @dev.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int devm_of_syscon_register_regmap(struct device *dev,
+ struct device_node *np,
+ struct regmap *regmap)
+{
+ struct syscon *syscon;
+
+ syscon = of_syscon_register_regmap_internal(np, regmap);
+ if (IS_ERR(syscon))
+ return PTR_ERR(syscon);
+
+ return devm_add_action_or_reset(dev,
+ devm_of_syscon_register_regmap_release,
+ syscon);
+}
+EXPORT_SYMBOL_GPL(devm_of_syscon_register_regmap);
/**
* device_node_to_regmap() - Get or create a regmap for specified device node
diff --git a/include/linux/mfd/syscon.h b/include/linux/mfd/syscon.h
index aad9c6b50463..3e804fc1b03e 100644
--- a/include/linux/mfd/syscon.h
+++ b/include/linux/mfd/syscon.h
@@ -15,6 +15,7 @@
#include <linux/errno.h>
struct device_node;
+struct device;
#ifdef CONFIG_MFD_SYSCON
struct regmap *device_node_to_regmap(struct device_node *np);
@@ -30,6 +31,9 @@ struct regmap *syscon_regmap_lookup_by_phandle_optional(struct device_node *np,
const char *property);
int of_syscon_register_regmap(struct device_node *np,
struct regmap *regmap);
+int devm_of_syscon_register_regmap(struct device *dev,
+ struct device_node *np,
+ struct regmap *regmap);
#else
static inline struct regmap *device_node_to_regmap(struct device_node *np)
{
@@ -75,6 +79,14 @@ static inline int of_syscon_register_regmap(struct device_node *np,
return -EOPNOTSUPP;
}
+static inline int
+devm_of_syscon_register_regmap(struct device *dev,
+ struct device_node *np,
+ struct regmap *regmap)
+{
+ return -EOPNOTSUPP;
+}
+
#endif
#endif /* __LINUX_MFD_SYSCON_H__ */
--
2.53.0
next prev parent reply other threads:[~2026-08-11 8:27 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-11 8:27 [PATCH v6 0/3] mfd: add X-Powers AC200 support James Hilliard
2026-08-11 8:27 ` [PATCH v6 1/3] dt-bindings: mfd: x-powers: Add AC200 James Hilliard
2026-08-11 8:27 ` James Hilliard [this message]
2026-08-11 8:46 ` [PATCH v6 2/3] mfd: syscon: Add managed registration for external regmaps Arnd Bergmann
2026-08-11 9:17 ` James Hilliard
2026-08-11 11:03 ` Arnd Bergmann
2026-08-11 16:56 ` James Hilliard
2026-08-11 8:27 ` [PATCH v6 3/3] mfd: ac200: Add X-Powers AC200 support James Hilliard
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260811-submit-ac200-mfd-v6-2-c5b1292c8498@gmail.com \
--to=james.hilliard1@gmail.com \
--cc=arnd@arndb.de \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=krzk+dt@kernel.org \
--cc=lee@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mfd@lists.linux.dev \
--cc=robh@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox