The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH 00/14] nvmem: updates for 7.3
@ 2026-07-29  9:46 srini
  2026-07-29  9:46 ` [PATCH 01/14] nvmem: remove unused field from struct nvmem_device srini
                   ` (13 more replies)
  0 siblings, 14 replies; 15+ messages in thread
From: srini @ 2026-07-29  9:46 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, Srinivas Kandagatla

From: Srinivas Kandagatla <srini@kernel.org>

Hi Greg,

This series collects the nvmem changes that can be queued for 7.3.

Highlights:

 - Core: split the reg_read/write() callbacks out of struct nvmem_device
   into a separate ops structure and protect nvmem_device::ops with
   SRCU. After nvmem_unregister() returns, the device stops accepting
   consumer calls (they get -ENODEV) so we no longer reach into the
   provider's memory once it has gone. Cell entries are now destroyed
   in .release() so outstanding nvmem_cell handles remain valid until
   the last reference drops.

 - Core cleanups leading up to the SRCU conversion: return -EOPNOTSUPP
   to in-kernel users when the read/write callback is missing, simplify
   locking with guard(), drop __nvmem_device_put() and the duplicated
   reference counting, simplify nvmem_sysfs_remove_compat(), check the
   return value of gpiod_set_value_cansleep(), and remove an unused
   field from struct nvmem_device.

 - Move the nvmem-based EEPROM drivers (at24, at25, ee1004,
   eeprom_93xx46, m24lr) from drivers/misc/eeprom/ to drivers/nvmem/
   so the review path and get_maintainer.pl output match the subsystem
   they actually belong to.

 - brcm_nvram: reject empty NVRAM partitions and fix an out-of-bounds
   access on malformed flash data where header->len sat between the
   allocated data buffer size and the full partition size.

 - airoha: add an ARM64 dependency.

 - dt-bindings: document qcom,qfprom compatibles for ipq5210 and
   ipq9650.

These patches are rebased on top of char-misc-next:

  https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git/log/?h=char-misc-next

and depend on the nvmem fixes series I sent earlier:

  https://lkml.org/lkml/2026/7/24/2035

Thanks,
srini

Arnd Bergmann (1):
  nvmem: airoha: add ARM64 dependency

Bartosz Golaszewski (10):
  nvmem: remove unused field from struct nvmem_device
  nvmem: return -EOPNOTSUPP to in-kernel users on missing callbacks
  nvmem: check the return value of gpiod_set_value_cansleep()
  nvmem: simplify locking with guard()
  nvmem: remove unneeded __nvmem_device_put()
  nvmem: split out the reg_read/write() callbacks out of struct
    nvmem_device
  nvmem: simplify nvmem_sysfs_remove_compat()
  nvmem: remove duplicated reference counting
  nvmem: protect nvmem_device::ops with SRCU
  eeprom: move nvmem EEPROM drivers to drivers/nvmem/

Rosen Penev (2):
  nvmem: brcm_nvram: reject empty NVRAM partition
  nvmem: brcm_nvram: fix out-of-bounds access on malformed flash data

Varadarajan Narayanan (1):
  dt-bindings: nvmem: qfprom: Add ipq5210 & ipq9650 compatible

 .../bindings/nvmem/qcom,qfprom.yaml           |   2 +
 MAINTAINERS                                   |   4 +-
 drivers/misc/eeprom/Kconfig                   |  93 ------
 drivers/misc/eeprom/Makefile                  |   5 -
 drivers/nvmem/Kconfig                         |  88 +++++
 drivers/nvmem/Makefile                        |   5 +
 drivers/{misc/eeprom => nvmem}/at24.c         |   0
 drivers/{misc/eeprom => nvmem}/at25.c         |   0
 drivers/nvmem/brcm_nvram.c                    |  15 +-
 drivers/nvmem/core.c                          | 311 ++++++++++--------
 drivers/{misc/eeprom => nvmem}/ee1004.c       |   0
 .../{misc/eeprom => nvmem}/eeprom_93xx46.c    |   0
 drivers/nvmem/internals.h                     |  13 +-
 drivers/{misc/eeprom => nvmem}/m24lr.c        |   0
 14 files changed, 284 insertions(+), 252 deletions(-)
 rename drivers/{misc/eeprom => nvmem}/at24.c (100%)
 rename drivers/{misc/eeprom => nvmem}/at25.c (100%)
 rename drivers/{misc/eeprom => nvmem}/ee1004.c (100%)
 rename drivers/{misc/eeprom => nvmem}/eeprom_93xx46.c (100%)
 rename drivers/{misc/eeprom => nvmem}/m24lr.c (100%)

-- 
2.53.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH 01/14] nvmem: remove unused field from struct nvmem_device
  2026-07-29  9:46 [PATCH 00/14] nvmem: updates for 7.3 srini
@ 2026-07-29  9:46 ` srini
  2026-07-29  9:46 ` [PATCH 02/14] nvmem: return -EOPNOTSUPP to in-kernel users on missing callbacks srini
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: srini @ 2026-07-29  9:46 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, Bartosz Golaszewski, Loic Poulain,
	Srinivas Kandagatla

From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

The node list_head in struct nvmem_device was added accidentally by
commit ec9c08a1cb8d ("nvmem: Create a header for internal sharing") and
is unused so remove it.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Tested-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Signed-off-by: Srinivas Kandagatla <srini@kernel.org>
---
 drivers/nvmem/internals.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/nvmem/internals.h b/drivers/nvmem/internals.h
index f6d452b6a28f..05d49c4f631f 100644
--- a/drivers/nvmem/internals.h
+++ b/drivers/nvmem/internals.h
@@ -10,7 +10,6 @@
 struct nvmem_device {
 	struct module		*owner;
 	struct device		dev;
-	struct list_head	node;
 	int			stride;
 	int			word_size;
 	int			id;
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 02/14] nvmem: return -EOPNOTSUPP to in-kernel users on missing callbacks
  2026-07-29  9:46 [PATCH 00/14] nvmem: updates for 7.3 srini
  2026-07-29  9:46 ` [PATCH 01/14] nvmem: remove unused field from struct nvmem_device srini
@ 2026-07-29  9:46 ` srini
  2026-07-29  9:46 ` [PATCH 03/14] nvmem: check the return value of gpiod_set_value_cansleep() srini
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: srini @ 2026-07-29  9:46 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, Bartosz Golaszewski, Loic Poulain,
	Srinivas Kandagatla

From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

__nvmem_reg_read/write() currently return -EINVAL if the relevant
callback is not present. User-space helpers again check the presence
of the callbacks to see if they should return -EPERM.

Ahead of adding SRCU synchronization: change the error code returned to
in-kernel users to -EOPNOTSUPP which is more indicative of the actual
reason for the failure.

Remove the checks from the sysfs attribute callbacks as these are not
visible without the required callbacks.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Tested-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Signed-off-by: Srinivas Kandagatla <srini@kernel.org>
---
 drivers/nvmem/core.c | 37 +++++++++++++++++++------------------
 1 file changed, 19 insertions(+), 18 deletions(-)

diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 703f760ad35e..ea17374209ba 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -55,10 +55,10 @@ static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
 static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
 			    void *val, size_t bytes)
 {
-	if (nvmem->reg_read)
-		return nvmem->reg_read(nvmem->priv, offset, val, bytes);
+	if (!nvmem->reg_read)
+		return -EOPNOTSUPP;
 
-	return -EINVAL;
+	return nvmem->reg_read(nvmem->priv, offset, val, bytes);
 }
 
 static int __nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
@@ -66,14 +66,14 @@ static int __nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
 {
 	int ret;
 
-	if (nvmem->reg_write) {
-		gpiod_set_value_cansleep(nvmem->wp_gpio, 0);
-		ret = nvmem->reg_write(nvmem->priv, offset, val, bytes);
-		gpiod_set_value_cansleep(nvmem->wp_gpio, 1);
-		return ret;
-	}
+	if (!nvmem->reg_write)
+		return -EOPNOTSUPP;
+
+	gpiod_set_value_cansleep(nvmem->wp_gpio, 0);
+	ret = nvmem->reg_write(nvmem->priv, offset, val, bytes);
+	gpiod_set_value_cansleep(nvmem->wp_gpio, 1);
 
-	return -EINVAL;
+	return ret;
 }
 
 static int nvmem_access_with_keepouts(struct nvmem_device *nvmem,
@@ -231,13 +231,12 @@ static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj,
 
 	count = round_down(count, nvmem->word_size);
 
-	if (!nvmem->reg_read)
-		return -EPERM;
-
 	rc = nvmem_reg_read(nvmem, pos, buf, count);
-
-	if (rc)
+	if (rc) {
+		if (rc == -EOPNOTSUPP)
+			return -EPERM;
 		return rc;
+	}
 
 	return count;
 }
@@ -264,13 +263,15 @@ static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj,
 
 	count = round_down(count, nvmem->word_size);
 
-	if (!nvmem->reg_write || nvmem->read_only)
+	if (nvmem->read_only)
 		return -EPERM;
 
 	rc = nvmem_reg_write(nvmem, pos, buf, count);
-
-	if (rc)
+	if (rc) {
+		if (rc == -EOPNOTSUPP)
+			return -EPERM;
 		return rc;
+	}
 
 	return count;
 }
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 03/14] nvmem: check the return value of gpiod_set_value_cansleep()
  2026-07-29  9:46 [PATCH 00/14] nvmem: updates for 7.3 srini
  2026-07-29  9:46 ` [PATCH 01/14] nvmem: remove unused field from struct nvmem_device srini
  2026-07-29  9:46 ` [PATCH 02/14] nvmem: return -EOPNOTSUPP to in-kernel users on missing callbacks srini
@ 2026-07-29  9:46 ` srini
  2026-07-29  9:46 ` [PATCH 04/14] nvmem: simplify locking with guard() srini
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: srini @ 2026-07-29  9:46 UTC (permalink / raw)
  To: gregkh
  Cc: linux-kernel, Bartosz Golaszewski, Johan Hovold, Loic Poulain,
	Srinivas Kandagatla

From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

GPIO setters now return integer values and can indicate failures in
lower abstraction layers. Check the return values of
gpiod_set_value_cansleep() calls in nvmem core.

Reviewed-by: Johan Hovold <johan@kernel.org>
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Tested-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Signed-off-by: Srinivas Kandagatla <srini@kernel.org>
---
 drivers/nvmem/core.c | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index ea17374209ba..a7e58bceb7ab 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -64,16 +64,22 @@ static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
 static int __nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
 			     void *val, size_t bytes)
 {
-	int ret;
+	int ret, wr_ok;
 
 	if (!nvmem->reg_write)
 		return -EOPNOTSUPP;
 
-	gpiod_set_value_cansleep(nvmem->wp_gpio, 0);
-	ret = nvmem->reg_write(nvmem->priv, offset, val, bytes);
-	gpiod_set_value_cansleep(nvmem->wp_gpio, 1);
+	ret = gpiod_set_value_cansleep(nvmem->wp_gpio, 0);
+	if (ret)
+		return ret;
 
-	return ret;
+	wr_ok = nvmem->reg_write(nvmem->priv, offset, val, bytes);
+
+	ret = gpiod_set_value_cansleep(nvmem->wp_gpio, 1);
+	if (ret)
+		return ret;
+
+	return wr_ok;
 }
 
 static int nvmem_access_with_keepouts(struct nvmem_device *nvmem,
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 04/14] nvmem: simplify locking with guard()
  2026-07-29  9:46 [PATCH 00/14] nvmem: updates for 7.3 srini
                   ` (2 preceding siblings ...)
  2026-07-29  9:46 ` [PATCH 03/14] nvmem: check the return value of gpiod_set_value_cansleep() srini
@ 2026-07-29  9:46 ` srini
  2026-07-29  9:46 ` [PATCH 05/14] nvmem: remove unneeded __nvmem_device_put() srini
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: srini @ 2026-07-29  9:46 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, Bartosz Golaszewski, Loic Poulain,
	Srinivas Kandagatla

From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

Use lock guards from cleanup.h to simplify locking. While at it: add the
missing mutex.h include.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Tested-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Signed-off-by: Srinivas Kandagatla <srini@kernel.org>
---
 drivers/nvmem/core.c | 88 +++++++++++++++++++-------------------------
 1 file changed, 37 insertions(+), 51 deletions(-)

diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index a7e58bceb7ab..0a011b6b5837 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -6,6 +6,7 @@
  * Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
  */
 
+#include <linux/cleanup.h>
 #include <linux/device.h>
 #include <linux/export.h>
 #include <linux/fs.h>
@@ -13,6 +14,7 @@
 #include <linux/init.h>
 #include <linux/kref.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 #include <linux/nvmem-consumer.h>
 #include <linux/nvmem-provider.h>
 #include <linux/gpio/consumer.h>
@@ -468,27 +470,23 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem)
 	const struct bin_attribute **pattrs;
 	struct bin_attribute *attrs;
 	unsigned int ncells = 0, i = 0;
-	int ret = 0;
+	int ret;
 
-	mutex_lock(&nvmem_mutex);
+	guard(mutex)(&nvmem_mutex);
 
 	if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated)
-		goto unlock_mutex;
+		return 0;
 
 	/* Allocate an array of attributes with a sentinel */
 	ncells = list_count_nodes(&nvmem->cells);
 	pattrs = devm_kcalloc(&nvmem->dev, ncells + 1,
 			      sizeof(struct bin_attribute *), GFP_KERNEL);
-	if (!pattrs) {
-		ret = -ENOMEM;
-		goto unlock_mutex;
-	}
+	if (!pattrs)
+		return -ENOMEM;
 
 	attrs = devm_kcalloc(&nvmem->dev, ncells, sizeof(struct bin_attribute), GFP_KERNEL);
-	if (!attrs) {
-		ret = -ENOMEM;
-		goto unlock_mutex;
-	}
+	if (!attrs)
+		return -ENOMEM;
 
 	/* Initialize each attribute to take the name and size of the cell */
 	list_for_each_entry(entry, &nvmem->cells, node) {
@@ -501,10 +499,8 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem)
 		attrs[i].size = entry->bytes;
 		attrs[i].read = &nvmem_cell_attr_read;
 		attrs[i].private = entry;
-		if (!attrs[i].attr.name) {
-			ret = -ENOMEM;
-			goto unlock_mutex;
-		}
+		if (!attrs[i].attr.name)
+			return -ENOMEM;
 
 		pattrs[i] = &attrs[i];
 		i++;
@@ -514,13 +510,10 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem)
 
 	ret = device_add_group(&nvmem->dev, &group);
 	if (ret)
-		goto unlock_mutex;
+		return ret;
 
 	nvmem->sysfs_cells_populated = true;
 
-unlock_mutex:
-	mutex_unlock(&nvmem_mutex);
-
 	return ret;
 }
 
@@ -558,9 +551,8 @@ static const struct bus_type nvmem_bus_type = {
 static void nvmem_cell_entry_drop(struct nvmem_cell_entry *cell)
 {
 	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_REMOVE, cell);
-	mutex_lock(&nvmem_mutex);
-	list_del(&cell->node);
-	mutex_unlock(&nvmem_mutex);
+	scoped_guard(mutex, &nvmem_mutex)
+		list_del(&cell->node);
 	of_node_put(cell->np);
 	kfree_const(cell->name);
 	kfree(cell);
@@ -576,9 +568,8 @@ static void nvmem_device_remove_all_cells(const struct nvmem_device *nvmem)
 
 static void nvmem_cell_entry_add(struct nvmem_cell_entry *cell)
 {
-	mutex_lock(&nvmem_mutex);
-	list_add_tail(&cell->node, &cell->nvmem->cells);
-	mutex_unlock(&nvmem_mutex);
+	scoped_guard(mutex, &nvmem_mutex)
+		list_add_tail(&cell->node, &cell->nvmem->cells);
 	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_ADD, cell);
 }
 
@@ -728,14 +719,14 @@ nvmem_find_cell_entry_by_name(struct nvmem_device *nvmem, const char *cell_id)
 {
 	struct nvmem_cell_entry *iter, *cell = NULL;
 
-	mutex_lock(&nvmem_mutex);
+	guard(mutex)(&nvmem_mutex);
+
 	list_for_each_entry(iter, &nvmem->cells, node) {
 		if (strcmp(cell_id, iter->name) == 0) {
 			cell = iter;
 			break;
 		}
 	}
-	mutex_unlock(&nvmem_mutex);
 
 	return cell;
 }
@@ -1105,11 +1096,11 @@ static struct nvmem_device *__nvmem_device_get(void *data,
 	struct nvmem_device *nvmem = NULL;
 	struct device *dev;
 
-	mutex_lock(&nvmem_mutex);
-	dev = bus_find_device(&nvmem_bus_type, NULL, data, match);
-	if (dev)
-		nvmem = to_nvmem_device(dev);
-	mutex_unlock(&nvmem_mutex);
+	scoped_guard(mutex, &nvmem_mutex) {
+		dev = bus_find_device(&nvmem_bus_type, NULL, data, match);
+		if (dev)
+			nvmem = to_nvmem_device(dev);
+	}
 	if (!nvmem)
 		return ERR_PTR(-EPROBE_DEFER);
 
@@ -1319,7 +1310,7 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id)
 
 	dev_id = dev_name(dev);
 
-	mutex_lock(&nvmem_lookup_mutex);
+	guard(mutex)(&nvmem_lookup_mutex);
 
 	list_for_each_entry(lookup, &nvmem_lookup_list, node) {
 		if ((strcmp(lookup->dev_id, dev_id) == 0) &&
@@ -1327,11 +1318,9 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id)
 			/* This is the right entry. */
 			nvmem = __nvmem_device_get((void *)lookup->nvmem_name,
 						   device_match_name);
-			if (IS_ERR(nvmem)) {
+			if (IS_ERR(nvmem))
 				/* Provider may not be registered yet. */
-				cell = ERR_CAST(nvmem);
-				break;
-			}
+				return ERR_CAST(nvmem);
 
 			cell_entry = nvmem_find_cell_entry_by_name(nvmem,
 								   lookup->cell_name);
@@ -1347,7 +1336,6 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id)
 		}
 	}
 
-	mutex_unlock(&nvmem_lookup_mutex);
 	return cell;
 }
 
@@ -1361,18 +1349,16 @@ static void nvmem_layout_module_put(struct nvmem_device *nvmem)
 static struct nvmem_cell_entry *
 nvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np)
 {
-	struct nvmem_cell_entry *iter, *cell = NULL;
+	struct nvmem_cell_entry *cell;
 
-	mutex_lock(&nvmem_mutex);
-	list_for_each_entry(iter, &nvmem->cells, node) {
-		if (np == iter->np) {
-			cell = iter;
-			break;
-		}
+	guard(mutex)(&nvmem_mutex);
+
+	list_for_each_entry(cell, &nvmem->cells, node) {
+		if (np == cell->np)
+			return cell;
 	}
-	mutex_unlock(&nvmem_mutex);
 
-	return cell;
+	return NULL;
 }
 
 static int nvmem_layout_module_get_optional(struct nvmem_device *nvmem)
@@ -2105,10 +2091,10 @@ void nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries)
 {
 	int i;
 
-	mutex_lock(&nvmem_lookup_mutex);
+	guard(mutex)(&nvmem_lookup_mutex);
+
 	for (i = 0; i < nentries; i++)
 		list_add_tail(&entries[i].node, &nvmem_lookup_list);
-	mutex_unlock(&nvmem_lookup_mutex);
 }
 EXPORT_SYMBOL_GPL(nvmem_add_cell_lookups);
 
@@ -2123,10 +2109,10 @@ void nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries)
 {
 	int i;
 
-	mutex_lock(&nvmem_lookup_mutex);
+	guard(mutex)(&nvmem_lookup_mutex);
+
 	for (i = 0; i < nentries; i++)
 		list_del(&entries[i].node);
-	mutex_unlock(&nvmem_lookup_mutex);
 }
 EXPORT_SYMBOL_GPL(nvmem_del_cell_lookups);
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 05/14] nvmem: remove unneeded __nvmem_device_put()
  2026-07-29  9:46 [PATCH 00/14] nvmem: updates for 7.3 srini
                   ` (3 preceding siblings ...)
  2026-07-29  9:46 ` [PATCH 04/14] nvmem: simplify locking with guard() srini
@ 2026-07-29  9:46 ` srini
  2026-07-29  9:46 ` [PATCH 06/14] nvmem: split out the reg_read/write() callbacks out of struct nvmem_device srini
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: srini @ 2026-07-29  9:46 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, Bartosz Golaszewski, Loic Poulain,
	Srinivas Kandagatla

From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

__nvmem_device_put() is wrapped by nvmem_device_put() but there's no
extra functionality offered by the latter so just fold one into the
other. There's still the corresponding __nvmem_device_get() so in order
to keep things symmetrical: rename it to nvmem_device_match() to better
reflect its functionality and not confuse users by its presence in the
absence of the similarly prefixed put() counterpart.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Tested-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Signed-off-by: Srinivas Kandagatla <srini@kernel.org>
---
 drivers/nvmem/core.c | 35 +++++++++++++++--------------------
 1 file changed, 15 insertions(+), 20 deletions(-)

diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 0a011b6b5837..b536ac7ffa11 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -1090,7 +1090,7 @@ struct nvmem_device *devm_nvmem_register(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(devm_nvmem_register);
 
-static struct nvmem_device *__nvmem_device_get(void *data,
+static struct nvmem_device *nvmem_device_match(void *data,
 			int (*match)(struct device *dev, const void *data))
 {
 	struct nvmem_device *nvmem = NULL;
@@ -1118,13 +1118,6 @@ static struct nvmem_device *__nvmem_device_get(void *data,
 	return nvmem;
 }
 
-static void __nvmem_device_put(struct nvmem_device *nvmem)
-{
-	put_device(&nvmem->dev);
-	module_put(nvmem->owner);
-	kref_put(&nvmem->refcnt, nvmem_device_release);
-}
-
 #if IS_ENABLED(CONFIG_OF)
 /**
  * of_nvmem_device_get() - Get nvmem device from a given id
@@ -1149,7 +1142,7 @@ struct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *id)
 	if (!nvmem_np)
 		return ERR_PTR(-ENOENT);
 
-	nvmem = __nvmem_device_get(nvmem_np, device_match_of_node);
+	nvmem = nvmem_device_match(nvmem_np, device_match_of_node);
 	of_node_put(nvmem_np);
 	return nvmem;
 }
@@ -1177,7 +1170,7 @@ struct nvmem_device *nvmem_device_get(struct device *dev, const char *dev_name)
 
 	}
 
-	return __nvmem_device_get((void *)dev_name, device_match_name);
+	return nvmem_device_match((void *)dev_name, device_match_name);
 }
 EXPORT_SYMBOL_GPL(nvmem_device_get);
 
@@ -1193,7 +1186,7 @@ EXPORT_SYMBOL_GPL(nvmem_device_get);
 struct nvmem_device *nvmem_device_find(void *data,
 			int (*match)(struct device *dev, const void *data))
 {
-	return __nvmem_device_get(data, match);
+	return nvmem_device_match(data, match);
 }
 EXPORT_SYMBOL_GPL(nvmem_device_find);
 
@@ -1237,7 +1230,9 @@ EXPORT_SYMBOL_GPL(devm_nvmem_device_put);
  */
 void nvmem_device_put(struct nvmem_device *nvmem)
 {
-	__nvmem_device_put(nvmem);
+	put_device(&nvmem->dev);
+	module_put(nvmem->owner);
+	kref_put(&nvmem->refcnt, nvmem_device_release);
 }
 EXPORT_SYMBOL_GPL(nvmem_device_put);
 
@@ -1316,7 +1311,7 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id)
 		if ((strcmp(lookup->dev_id, dev_id) == 0) &&
 		    (strcmp(lookup->con_id, con_id) == 0)) {
 			/* This is the right entry. */
-			nvmem = __nvmem_device_get((void *)lookup->nvmem_name,
+			nvmem = nvmem_device_match((void *)lookup->nvmem_name,
 						   device_match_name);
 			if (IS_ERR(nvmem))
 				/* Provider may not be registered yet. */
@@ -1325,12 +1320,12 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id)
 			cell_entry = nvmem_find_cell_entry_by_name(nvmem,
 								   lookup->cell_name);
 			if (!cell_entry) {
-				__nvmem_device_put(nvmem);
+				nvmem_device_put(nvmem);
 				cell = ERR_PTR(-ENOENT);
 			} else {
 				cell = nvmem_create_cell(cell_entry, con_id, 0);
 				if (IS_ERR(cell))
-					__nvmem_device_put(nvmem);
+					nvmem_device_put(nvmem);
 			}
 			break;
 		}
@@ -1428,7 +1423,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id)
 		}
 	}
 
-	nvmem = __nvmem_device_get(nvmem_np, device_match_of_node);
+	nvmem = nvmem_device_match(nvmem_np, device_match_of_node);
 	of_node_put(nvmem_np);
 	if (IS_ERR(nvmem)) {
 		of_node_put(cell_np);
@@ -1438,7 +1433,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id)
 	ret = nvmem_layout_module_get_optional(nvmem);
 	if (ret) {
 		of_node_put(cell_np);
-		__nvmem_device_put(nvmem);
+		nvmem_device_put(nvmem);
 		return ERR_PTR(ret);
 	}
 
@@ -1447,14 +1442,14 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id)
 	if (!cell_entry) {
 		nvmem_layout_module_put(nvmem);
 		ret = nvmem->layout ? -EPROBE_DEFER : -ENOENT;
-		__nvmem_device_put(nvmem);
+		nvmem_device_put(nvmem);
 		return ERR_PTR(ret);
 	}
 
 	cell = nvmem_create_cell(cell_entry, id, cell_index);
 	if (IS_ERR(cell)) {
 		nvmem_layout_module_put(nvmem);
-		__nvmem_device_put(nvmem);
+		nvmem_device_put(nvmem);
 	}
 
 	return cell;
@@ -1569,7 +1564,7 @@ void nvmem_cell_put(struct nvmem_cell *cell)
 
 	kfree(cell);
 	nvmem_layout_module_put(nvmem);
-	__nvmem_device_put(nvmem);
+	nvmem_device_put(nvmem);
 }
 EXPORT_SYMBOL_GPL(nvmem_cell_put);
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 06/14] nvmem: split out the reg_read/write() callbacks out of struct nvmem_device
  2026-07-29  9:46 [PATCH 00/14] nvmem: updates for 7.3 srini
                   ` (4 preceding siblings ...)
  2026-07-29  9:46 ` [PATCH 05/14] nvmem: remove unneeded __nvmem_device_put() srini
@ 2026-07-29  9:46 ` srini
  2026-07-29  9:46 ` [PATCH 07/14] nvmem: simplify nvmem_sysfs_remove_compat() srini
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: srini @ 2026-07-29  9:46 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, Bartosz Golaszewski, Loic Poulain,
	Srinivas Kandagatla

From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

The reg_read/write() fields of struct nvmem_device point to memory owned
by the nvmem provider. They must not be dereferenced after the provider
is unregistered. Ahead of protecting against accesses to invalid memory
with SRCU, move the callbacks into a separate structure the address of
which is stored in nvmem_device.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Tested-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Signed-off-by: Srinivas Kandagatla <srini@kernel.org>
---
 drivers/nvmem/core.c      | 37 +++++++++++++++++++++++++++----------
 drivers/nvmem/internals.h |  9 +++++++--
 2 files changed, 34 insertions(+), 12 deletions(-)

diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index b536ac7ffa11..45ff4ceca1b9 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -57,25 +57,28 @@ static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
 static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
 			    void *val, size_t bytes)
 {
-	if (!nvmem->reg_read)
+	struct nvmem_operations *ops = nvmem->ops;
+
+	if (!ops->reg_read)
 		return -EOPNOTSUPP;
 
-	return nvmem->reg_read(nvmem->priv, offset, val, bytes);
+	return ops->reg_read(nvmem->priv, offset, val, bytes);
 }
 
 static int __nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
 			     void *val, size_t bytes)
 {
+	struct nvmem_operations *ops = nvmem->ops;
 	int ret, wr_ok;
 
-	if (!nvmem->reg_write)
+	if (!ops->reg_write)
 		return -EOPNOTSUPP;
 
 	ret = gpiod_set_value_cansleep(nvmem->wp_gpio, 0);
 	if (ret)
 		return ret;
 
-	wr_ok = nvmem->reg_write(nvmem->priv, offset, val, bytes);
+	wr_ok = ops->reg_write(nvmem->priv, offset, val, bytes);
 
 	ret = gpiod_set_value_cansleep(nvmem->wp_gpio, 1);
 	if (ret)
@@ -286,6 +289,8 @@ static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj,
 
 static umode_t nvmem_bin_attr_get_umode(struct nvmem_device *nvmem)
 {
+	struct nvmem_operations *ops = nvmem->ops;
+
 	umode_t mode = 0400;
 
 	if (!nvmem->root_only)
@@ -294,10 +299,10 @@ static umode_t nvmem_bin_attr_get_umode(struct nvmem_device *nvmem)
 	if (!nvmem->read_only)
 		mode |= 0200;
 
-	if (!nvmem->reg_write)
+	if (!ops->reg_write)
 		mode &= ~0200;
 
-	if (!nvmem->reg_read)
+	if (!ops->reg_read)
 		mode &= ~0444;
 
 	return mode;
@@ -328,6 +333,7 @@ static umode_t nvmem_attr_is_visible(struct kobject *kobj,
 {
 	struct device *dev = kobj_to_dev(kobj);
 	struct nvmem_device *nvmem = to_nvmem_device(dev);
+	struct nvmem_operations *ops = nvmem->ops;
 
 	/*
 	 * If the device has no .reg_write operation, do not allow
@@ -336,7 +342,7 @@ static umode_t nvmem_attr_is_visible(struct kobject *kobj,
 	 * can be forced into read-write mode using the 'force_ro'
 	 * attribute.
 	 */
-	if (attr == &dev_attr_force_ro.attr && !nvmem->reg_write)
+	if (attr == &dev_attr_force_ro.attr && !ops->reg_write)
 		return 0;	/* Attribute not visible */
 
 	return attr->mode;
@@ -537,6 +543,7 @@ static void nvmem_release(struct device *dev)
 
 	ida_free(&nvmem_ida, nvmem->id);
 	gpiod_put(nvmem->wp_gpio);
+	kfree(nvmem->ops);
 	kfree(nvmem);
 }
 
@@ -881,6 +888,7 @@ EXPORT_SYMBOL_GPL(nvmem_layout_unregister);
 
 struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 {
+	struct nvmem_operations *ops;
 	struct nvmem_device *nvmem;
 	int rval;
 
@@ -894,8 +902,15 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 	if (!nvmem)
 		return ERR_PTR(-ENOMEM);
 
+	ops = kzalloc_obj(*ops);
+	if (!ops) {
+		kfree(nvmem);
+		return ERR_PTR(-ENOMEM);
+	}
+
 	rval = ida_alloc(&nvmem_ida, GFP_KERNEL);
 	if (rval < 0) {
+		kfree(ops);
 		kfree(nvmem);
 		return ERR_PTR(rval);
 	}
@@ -905,6 +920,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 	nvmem->dev.type = &nvmem_provider_type;
 	nvmem->dev.bus = &nvmem_bus_type;
 	nvmem->dev.parent = config->dev;
+	nvmem->ops = ops;
 
 	device_initialize(&nvmem->dev);
 
@@ -921,6 +937,9 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 	INIT_LIST_HEAD(&nvmem->cells);
 	nvmem->fixup_dt_cell_info = config->fixup_dt_cell_info;
 
+	ops->reg_read = config->reg_read;
+	ops->reg_write = config->reg_write;
+
 	nvmem->owner = config->owner;
 	if (!nvmem->owner && config->dev->driver)
 		nvmem->owner = config->dev->driver->owner;
@@ -930,8 +949,6 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 	nvmem->root_only = config->root_only;
 	nvmem->priv = config->priv;
 	nvmem->type = config->type;
-	nvmem->reg_read = config->reg_read;
-	nvmem->reg_write = config->reg_write;
 	nvmem->keepout = config->keepout;
 	nvmem->nkeepout = config->nkeepout;
 	if (config->of_node)
@@ -957,7 +974,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 		goto err_put_device;
 
 	nvmem->read_only = device_property_present(config->dev, "read-only") ||
-			   config->read_only || !nvmem->reg_write;
+			   config->read_only || !ops->reg_write;
 
 #ifdef CONFIG_NVMEM_SYSFS
 	nvmem->dev.groups = nvmem_dev_groups;
diff --git a/drivers/nvmem/internals.h b/drivers/nvmem/internals.h
index 05d49c4f631f..4e610deeaa7b 100644
--- a/drivers/nvmem/internals.h
+++ b/drivers/nvmem/internals.h
@@ -7,6 +7,12 @@
 #include <linux/nvmem-consumer.h>
 #include <linux/nvmem-provider.h>
 
+/* Hold pointers to callbacks owned by the nvmem provider module. */
+struct nvmem_operations {
+	nvmem_reg_read_t	reg_read;
+	nvmem_reg_write_t	reg_write;
+};
+
 struct nvmem_device {
 	struct module		*owner;
 	struct device		dev;
@@ -26,10 +32,9 @@ struct nvmem_device {
 				   struct nvmem_cell_info *cell);
 	const struct nvmem_keepout *keepout;
 	unsigned int		nkeepout;
-	nvmem_reg_read_t	reg_read;
-	nvmem_reg_write_t	reg_write;
 	struct gpio_desc	*wp_gpio;
 	struct nvmem_layout	*layout;
+	struct nvmem_operations	*ops;
 	void *priv;
 	bool			sysfs_cells_populated;
 };
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 07/14] nvmem: simplify nvmem_sysfs_remove_compat()
  2026-07-29  9:46 [PATCH 00/14] nvmem: updates for 7.3 srini
                   ` (5 preceding siblings ...)
  2026-07-29  9:46 ` [PATCH 06/14] nvmem: split out the reg_read/write() callbacks out of struct nvmem_device srini
@ 2026-07-29  9:46 ` srini
  2026-07-29  9:46 ` [PATCH 08/14] nvmem: remove duplicated reference counting srini
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: srini @ 2026-07-29  9:46 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, Bartosz Golaszewski, Loic Poulain,
	Srinivas Kandagatla

From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

There's no need for the config argument in nvmem_sysfs_remove_compat().
Once the compat attribute is registered, that information is carried in
nvmem_device::flags. Rework the code to always query that field and drop
the second argument.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Tested-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Signed-off-by: Srinivas Kandagatla <srini@kernel.org>
---
 drivers/nvmem/core.c | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 45ff4ceca1b9..0556d140170a 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -460,10 +460,9 @@ static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem,
 	return 0;
 }
 
-static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem,
-			      const struct nvmem_config *config)
+static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem)
 {
-	if (config->compat)
+	if (nvmem->flags & FLAG_COMPAT)
 		device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
 }
 
@@ -530,8 +529,7 @@ static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem,
 {
 	return -ENOSYS;
 }
-static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem,
-				      const struct nvmem_config *config)
+static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem)
 {
 }
 
@@ -1036,8 +1034,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 	device_del(&nvmem->dev);
 err_remove_cells:
 	nvmem_device_remove_all_cells(nvmem);
-	if (config->compat)
-		nvmem_sysfs_remove_compat(nvmem, config);
+	nvmem_sysfs_remove_compat(nvmem);
 err_put_device:
 	put_device(&nvmem->dev);
 
@@ -1053,8 +1050,7 @@ static void nvmem_device_release(struct kref *kref)
 
 	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_REMOVE, nvmem);
 
-	if (nvmem->flags & FLAG_COMPAT)
-		device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
+	nvmem_sysfs_remove_compat(nvmem);
 
 	nvmem_device_remove_all_cells(nvmem);
 	nvmem_destroy_layout(nvmem);
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 08/14] nvmem: remove duplicated reference counting
  2026-07-29  9:46 [PATCH 00/14] nvmem: updates for 7.3 srini
                   ` (6 preceding siblings ...)
  2026-07-29  9:46 ` [PATCH 07/14] nvmem: simplify nvmem_sysfs_remove_compat() srini
@ 2026-07-29  9:46 ` srini
  2026-07-29  9:46 ` [PATCH 09/14] nvmem: protect nvmem_device::ops with SRCU srini
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: srini @ 2026-07-29  9:46 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, Bartosz Golaszewski, Loic Poulain,
	Srinivas Kandagatla

From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

Commit c1de7f43bd84 ("nvmem: use kref") introduced reference counting
with kref to an already reference counted nvmem_device structure. We
only need one refcount so use the one provded by device's kobject and
drop the kref field from struct nvmem_device.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Tested-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Signed-off-by: Srinivas Kandagatla <srini@kernel.org>
---
 drivers/nvmem/core.c      | 80 +++++++++++++++++----------------------
 drivers/nvmem/internals.h |  1 -
 2 files changed, 34 insertions(+), 47 deletions(-)

diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 0556d140170a..8b227db91923 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -535,24 +535,6 @@ static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem)
 
 #endif /* CONFIG_NVMEM_SYSFS */
 
-static void nvmem_release(struct device *dev)
-{
-	struct nvmem_device *nvmem = to_nvmem_device(dev);
-
-	ida_free(&nvmem_ida, nvmem->id);
-	gpiod_put(nvmem->wp_gpio);
-	kfree(nvmem->ops);
-	kfree(nvmem);
-}
-
-static const struct device_type nvmem_provider_type = {
-	.release	= nvmem_release,
-};
-
-static const struct bus_type nvmem_bus_type = {
-	.name		= "nvmem",
-};
-
 static void nvmem_cell_entry_drop(struct nvmem_cell_entry *cell)
 {
 	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_REMOVE, cell);
@@ -571,6 +553,25 @@ static void nvmem_device_remove_all_cells(const struct nvmem_device *nvmem)
 		nvmem_cell_entry_drop(cell);
 }
 
+static void nvmem_release(struct device *dev)
+{
+	struct nvmem_device *nvmem = to_nvmem_device(dev);
+
+	gpiod_put(nvmem->wp_gpio);
+	nvmem_device_remove_all_cells(nvmem);
+	ida_free(&nvmem_ida, nvmem->id);
+	kfree(nvmem->ops);
+	kfree(nvmem);
+}
+
+static const struct device_type nvmem_provider_type = {
+	.release	= nvmem_release,
+};
+
+static const struct bus_type nvmem_bus_type = {
+	.name		= "nvmem",
+};
+
 static void nvmem_cell_entry_add(struct nvmem_cell_entry *cell)
 {
 	scoped_guard(mutex, &nvmem_mutex)
@@ -918,6 +919,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 	nvmem->dev.type = &nvmem_provider_type;
 	nvmem->dev.bus = &nvmem_bus_type;
 	nvmem->dev.parent = config->dev;
+	INIT_LIST_HEAD(&nvmem->cells);
 	nvmem->ops = ops;
 
 	device_initialize(&nvmem->dev);
@@ -931,8 +933,6 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 		goto err_put_device;
 	}
 
-	kref_init(&nvmem->refcnt);
-	INIT_LIST_HEAD(&nvmem->cells);
 	nvmem->fixup_dt_cell_info = config->fixup_dt_cell_info;
 
 	ops->reg_read = config->reg_read;
@@ -993,20 +993,20 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 	if (config->cells) {
 		rval = nvmem_add_cells(nvmem, config->cells, config->ncells);
 		if (rval)
-			goto err_remove_cells;
+			goto err_remove_compat;
 	}
 
 	if (config->add_legacy_fixed_of_cells) {
 		rval = nvmem_add_cells_from_legacy_of(nvmem);
 		if (rval)
-			goto err_remove_cells;
+			goto err_remove_compat;
 	}
 
 	dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
 
 	rval = device_add(&nvmem->dev);
 	if (rval)
-		goto err_remove_cells;
+		goto err_remove_compat;
 
 	rval = nvmem_populate_layout(nvmem);
 	if (rval)
@@ -1032,8 +1032,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 #endif
 err_remove_dev:
 	device_del(&nvmem->dev);
-err_remove_cells:
-	nvmem_device_remove_all_cells(nvmem);
+err_remove_compat:
 	nvmem_sysfs_remove_compat(nvmem);
 err_put_device:
 	put_device(&nvmem->dev);
@@ -1042,21 +1041,6 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 }
 EXPORT_SYMBOL_GPL(nvmem_register);
 
-static void nvmem_device_release(struct kref *kref)
-{
-	struct nvmem_device *nvmem;
-
-	nvmem = container_of(kref, struct nvmem_device, refcnt);
-
-	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_REMOVE, nvmem);
-
-	nvmem_sysfs_remove_compat(nvmem);
-
-	nvmem_device_remove_all_cells(nvmem);
-	nvmem_destroy_layout(nvmem);
-	device_unregister(&nvmem->dev);
-}
-
 /**
  * nvmem_unregister() - Unregister previously registered nvmem device
  *
@@ -1064,8 +1048,15 @@ static void nvmem_device_release(struct kref *kref)
  */
 void nvmem_unregister(struct nvmem_device *nvmem)
 {
-	if (nvmem)
-		kref_put(&nvmem->refcnt, nvmem_device_release);
+	if (!nvmem)
+		return;
+
+	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_REMOVE, nvmem);
+
+	nvmem_sysfs_remove_compat(nvmem);
+	nvmem_destroy_layout(nvmem);
+
+	device_unregister(&nvmem->dev);
 }
 EXPORT_SYMBOL_GPL(nvmem_unregister);
 
@@ -1126,8 +1117,6 @@ static struct nvmem_device *nvmem_device_match(void *data,
 		return ERR_PTR(-EINVAL);
 	}
 
-	kref_get(&nvmem->refcnt);
-
 	return nvmem;
 }
 
@@ -1243,9 +1232,8 @@ EXPORT_SYMBOL_GPL(devm_nvmem_device_put);
  */
 void nvmem_device_put(struct nvmem_device *nvmem)
 {
-	put_device(&nvmem->dev);
 	module_put(nvmem->owner);
-	kref_put(&nvmem->refcnt, nvmem_device_release);
+	put_device(&nvmem->dev);
 }
 EXPORT_SYMBOL_GPL(nvmem_device_put);
 
diff --git a/drivers/nvmem/internals.h b/drivers/nvmem/internals.h
index 4e610deeaa7b..2c3645a27272 100644
--- a/drivers/nvmem/internals.h
+++ b/drivers/nvmem/internals.h
@@ -19,7 +19,6 @@ struct nvmem_device {
 	int			stride;
 	int			word_size;
 	int			id;
-	struct kref		refcnt;
 	size_t			size;
 	bool			read_only;
 	bool			root_only;
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 09/14] nvmem: protect nvmem_device::ops with SRCU
  2026-07-29  9:46 [PATCH 00/14] nvmem: updates for 7.3 srini
                   ` (7 preceding siblings ...)
  2026-07-29  9:46 ` [PATCH 08/14] nvmem: remove duplicated reference counting srini
@ 2026-07-29  9:46 ` srini
  2026-07-29  9:46 ` [PATCH 10/14] eeprom: move nvmem EEPROM drivers to drivers/nvmem/ srini
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: srini @ 2026-07-29  9:46 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, Bartosz Golaszewski, Loic Poulain,
	Srinivas Kandagatla

From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

With the provider-owned data split out into a separate 'ops' structure,
we can now protect it with SRCU.

Protect all dereferences of nvmem->ops with an SRCU read lock.
Synchronize SRCU in nvmem_unregister() after setting the implementation
pointer to NULL. This has the effect of numbing down the device after
nvmem_unregister() returns - it will no longer accept any consumer calls
and return -ENODEV. The actual device will live on for as long as there
are references to it but we will no longer reach into the consumer's
memory which may be gone by this time.

Nvmem cell entries are destroyed in .release() now as they may be still
dereferenced via the nvmem_cell handles after nvmem_release(). The
actual calls will still go through SRCU and fail with -ENODEV if the
provider is gone.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Tested-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Signed-off-by: Srinivas Kandagatla <srini@kernel.org>
---
 drivers/nvmem/core.c      | 44 +++++++++++++++++++++++++++++++++------
 drivers/nvmem/internals.h |  4 +++-
 2 files changed, 41 insertions(+), 7 deletions(-)

diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 8b227db91923..f65aebe7837b 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -57,7 +57,12 @@ static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
 static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
 			    void *val, size_t bytes)
 {
-	struct nvmem_operations *ops = nvmem->ops;
+	struct nvmem_operations *ops;
+
+	guard(srcu)(&nvmem->srcu);
+	ops = srcu_dereference(nvmem->ops, &nvmem->srcu);
+	if (!ops)
+		return -ENODEV;
 
 	if (!ops->reg_read)
 		return -EOPNOTSUPP;
@@ -68,9 +73,14 @@ static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
 static int __nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
 			     void *val, size_t bytes)
 {
-	struct nvmem_operations *ops = nvmem->ops;
+	struct nvmem_operations *ops;
 	int ret, wr_ok;
 
+	guard(srcu)(&nvmem->srcu);
+	ops = srcu_dereference(nvmem->ops, &nvmem->srcu);
+	if (!ops)
+		return -ENODEV;
+
 	if (!ops->reg_write)
 		return -EOPNOTSUPP;
 
@@ -289,7 +299,7 @@ static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj,
 
 static umode_t nvmem_bin_attr_get_umode(struct nvmem_device *nvmem)
 {
-	struct nvmem_operations *ops = nvmem->ops;
+	struct nvmem_operations *ops = rcu_dereference_raw(nvmem->ops);
 
 	umode_t mode = 0400;
 
@@ -333,7 +343,7 @@ static umode_t nvmem_attr_is_visible(struct kobject *kobj,
 {
 	struct device *dev = kobj_to_dev(kobj);
 	struct nvmem_device *nvmem = to_nvmem_device(dev);
-	struct nvmem_operations *ops = nvmem->ops;
+	struct nvmem_operations *ops = rcu_dereference_raw(nvmem->ops);
 
 	/*
 	 * If the device has no .reg_write operation, do not allow
@@ -560,7 +570,7 @@ static void nvmem_release(struct device *dev)
 	gpiod_put(nvmem->wp_gpio);
 	nvmem_device_remove_all_cells(nvmem);
 	ida_free(&nvmem_ida, nvmem->id);
-	kfree(nvmem->ops);
+	cleanup_srcu_struct(&nvmem->srcu);
 	kfree(nvmem);
 }
 
@@ -920,7 +930,20 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 	nvmem->dev.bus = &nvmem_bus_type;
 	nvmem->dev.parent = config->dev;
 	INIT_LIST_HEAD(&nvmem->cells);
-	nvmem->ops = ops;
+
+	/*
+	 * Must happen before we assign the release() callback in
+	 * device_initialize().
+	 */
+	rval = init_srcu_struct(&nvmem->srcu);
+	if (rval) {
+		ida_free(&nvmem_ida, nvmem->id);
+		kfree(ops);
+		kfree(nvmem);
+		return ERR_PTR(rval);
+	}
+
+	rcu_assign_pointer(nvmem->ops, ops);
 
 	device_initialize(&nvmem->dev);
 
@@ -1035,7 +1058,10 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 err_remove_compat:
 	nvmem_sysfs_remove_compat(nvmem);
 err_put_device:
+	ops = rcu_replace_pointer(nvmem->ops, NULL, true);
+	synchronize_srcu(&nvmem->srcu);
 	put_device(&nvmem->dev);
+	kfree(ops);
 
 	return ERR_PTR(rval);
 }
@@ -1048,13 +1074,19 @@ EXPORT_SYMBOL_GPL(nvmem_register);
  */
 void nvmem_unregister(struct nvmem_device *nvmem)
 {
+	struct nvmem_operations *ops;
+
 	if (!nvmem)
 		return;
 
 	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_REMOVE, nvmem);
 
+	ops = rcu_replace_pointer(nvmem->ops, NULL, true);
+	synchronize_srcu(&nvmem->srcu);
+
 	nvmem_sysfs_remove_compat(nvmem);
 	nvmem_destroy_layout(nvmem);
+	kfree(ops);
 
 	device_unregister(&nvmem->dev);
 }
diff --git a/drivers/nvmem/internals.h b/drivers/nvmem/internals.h
index 2c3645a27272..bc7a99f5aefb 100644
--- a/drivers/nvmem/internals.h
+++ b/drivers/nvmem/internals.h
@@ -6,6 +6,7 @@
 #include <linux/device.h>
 #include <linux/nvmem-consumer.h>
 #include <linux/nvmem-provider.h>
+#include <linux/srcu.h>
 
 /* Hold pointers to callbacks owned by the nvmem provider module. */
 struct nvmem_operations {
@@ -16,6 +17,7 @@ struct nvmem_operations {
 struct nvmem_device {
 	struct module		*owner;
 	struct device		dev;
+	struct srcu_struct	srcu;
 	int			stride;
 	int			word_size;
 	int			id;
@@ -33,7 +35,7 @@ struct nvmem_device {
 	unsigned int		nkeepout;
 	struct gpio_desc	*wp_gpio;
 	struct nvmem_layout	*layout;
-	struct nvmem_operations	*ops;
+	struct nvmem_operations __rcu *ops;
 	void *priv;
 	bool			sysfs_cells_populated;
 };
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 10/14] eeprom: move nvmem EEPROM drivers to drivers/nvmem/
  2026-07-29  9:46 [PATCH 00/14] nvmem: updates for 7.3 srini
                   ` (8 preceding siblings ...)
  2026-07-29  9:46 ` [PATCH 09/14] nvmem: protect nvmem_device::ops with SRCU srini
@ 2026-07-29  9:46 ` srini
  2026-07-29  9:46 ` [PATCH 11/14] nvmem: airoha: add ARM64 dependency srini
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: srini @ 2026-07-29  9:46 UTC (permalink / raw)
  To: gregkh
  Cc: linux-kernel, Bartosz Golaszewski, Wolfram Sang, Arnd Bergmann,
	Srinivas Kandagatla

From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

For historical reasons EEPROM drivers have lived under
drivers/misc/eeprom/. Also for historical reasons changes to most of
them would go through the char-misc tree while some would be queued
through the I2C tree. Over the years some of them have also been
converted to using nvmem - the dedicated subsystem for non-volatile
memory - while get_maintainer.pl does not Cc the maintainer of nvmem on
patches changing them.

Move the EEPROM drivers using nvmem under drivers/nvmem/ for
consistency of the review process and path upstream.

Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Srinivas Kandagatla <srini@kernel.org>
---
 MAINTAINERS                                   |  4 +-
 drivers/misc/eeprom/Kconfig                   | 93 -------------------
 drivers/misc/eeprom/Makefile                  |  5 -
 drivers/nvmem/Kconfig                         | 87 +++++++++++++++++
 drivers/nvmem/Makefile                        |  5 +
 drivers/{misc/eeprom => nvmem}/at24.c         |  0
 drivers/{misc/eeprom => nvmem}/at25.c         |  0
 drivers/{misc/eeprom => nvmem}/ee1004.c       |  0
 .../{misc/eeprom => nvmem}/eeprom_93xx46.c    |  0
 drivers/{misc/eeprom => nvmem}/m24lr.c        |  0
 10 files changed, 94 insertions(+), 100 deletions(-)
 rename drivers/{misc/eeprom => nvmem}/at24.c (100%)
 rename drivers/{misc/eeprom => nvmem}/at25.c (100%)
 rename drivers/{misc/eeprom => nvmem}/ee1004.c (100%)
 rename drivers/{misc/eeprom => nvmem}/eeprom_93xx46.c (100%)
 rename drivers/{misc/eeprom => nvmem}/m24lr.c (100%)

diff --git a/MAINTAINERS b/MAINTAINERS
index 9ecc4963ea9d..dd08f2c94e63 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4168,11 +4168,11 @@ F:	include/linux/async_tx.h
 
 AT24 EEPROM DRIVER
 M:	Bartosz Golaszewski <brgl@kernel.org>
-L:	linux-i2c@vger.kernel.org
+L:	linux-kernel@vger.kernel.org
 S:	Maintained
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git
 F:	Documentation/devicetree/bindings/eeprom/at24.yaml
-F:	drivers/misc/eeprom/at24.c
+F:	drivers/nvmem/at24.c
 
 ATA OVER ETHERNET (AOE) DRIVER
 M:	"Justin Sanders" <justin@coraid.com>
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index 4d0ce47aa282..d6af02588c66 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -1,52 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 menu "EEPROM support"
 
-config EEPROM_AT24
-	tristate "I2C EEPROMs / RAMs / ROMs from most vendors"
-	depends on I2C && SYSFS
-	select NVMEM
-	select NVMEM_SYSFS
-	select REGMAP
-	select REGMAP_I2C
-	help
-	  Enable this driver to get read/write support to most I2C EEPROMs
-	  and compatible devices like FRAMs, SRAMs, ROMs etc. After you
-	  configure the driver to know about each chip on your target
-	  board.  Use these generic chip names, instead of vendor-specific
-	  ones like at24c64, 24lc02 or fm24c04:
-
-	     24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08,
-	     24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024, 24c2048
-
-	  Unless you like data loss puzzles, always be sure that any chip
-	  you configure as a 24c32 (32 kbit) or larger is NOT really a
-	  24c16 (16 kbit) or smaller, and vice versa. Marking the chip
-	  as read-only won't help recover from this. Also, if your chip
-	  has any software write-protect mechanism you may want to review the
-	  code to make sure this driver won't turn it on by accident.
-
-	  If you use this with an SMBus adapter instead of an I2C adapter,
-	  full functionality is not available.  Only smaller devices are
-	  supported (24c16 and below, max 4 kByte).
-
-	  This driver can also be built as a module.  If so, the module
-	  will be called at24.
-
-config EEPROM_AT25
-	tristate "SPI EEPROMs (FRAMs) from most vendors"
-	depends on SPI && SYSFS
-	select NVMEM
-	select NVMEM_SYSFS
-	select SPI_MEM
-	help
-	  Enable this driver to get read/write support to most SPI EEPROMs
-	  and Cypress FRAMs,
-	  after you configure the board init code to know about each eeprom
-	  on your target board.
-
-	  This driver can also be built as a module.  If so, the module
-	  will be called at25.
-
 config EEPROM_MAX6875
 	tristate "Maxim MAX6874/5 power supply supervisor"
 	depends on I2C
@@ -69,22 +23,6 @@ config EEPROM_93CX6
 
 	  If unsure, say N.
 
-config EEPROM_93XX46
-	tristate "Microwire EEPROM 93XX46 support"
-	depends on SPI && SYSFS
-	select REGMAP
-	select NVMEM
-	select NVMEM_SYSFS
-	help
-	  Driver for the microwire EEPROM chipsets 93xx46x. The driver
-	  supports both read and write commands and also the command to
-	  erase the whole EEPROM.
-
-	  This driver can also be built as a module.  If so, the module
-	  will be called eeprom_93xx46.
-
-	  If unsure, say N.
-
 config EEPROM_DIGSY_MTC_CFG
 	bool "DigsyMTC display configuration EEPROMs device"
 	depends on GPIO_MPC5200 && SPI_GPIO
@@ -107,35 +45,4 @@ config EEPROM_IDT_89HPESX
 	  This driver can also be built as a module. If so, the module
 	  will be called idt_89hpesx.
 
-config EEPROM_EE1004
-	tristate "SPD EEPROMs on DDR4 memory modules"
-	depends on I2C && SYSFS
-	select NVMEM
-	select NVMEM_SYSFS
-	help
-	  Enable this driver to get read support to SPD EEPROMs following
-	  the JEDEC EE1004 standard. These are typically found on DDR4
-	  SDRAM memory modules.
-
-	  This driver can also be built as a module.  If so, the module
-	  will be called ee1004.
-
-config EEPROM_M24LR
-	tristate "STMicroelectronics M24LR RFID/NFC EEPROM support"
-	depends on I2C && SYSFS
-	select REGMAP_I2C
-	select NVMEM
-	select NVMEM_SYSFS
-	help
-	  This enables support for STMicroelectronics M24LR RFID/NFC EEPROM
-	  chips. These dual-interface devices expose two I2C addresses:
-	  one for EEPROM memory access and another for control and system
-	  configuration (e.g. UID, password handling).
-
-	  This driver provides a sysfs interface for control functions and
-	  integrates with the nvmem subsystem for EEPROM access.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called m24lr.
-
 endmenu
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
index 8f311fd6a4ce..44a20cf025a1 100644
--- a/drivers/misc/eeprom/Makefile
+++ b/drivers/misc/eeprom/Makefile
@@ -1,10 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_EEPROM_AT24)	+= at24.o
-obj-$(CONFIG_EEPROM_AT25)	+= at25.o
 obj-$(CONFIG_EEPROM_MAX6875)	+= max6875.o
 obj-$(CONFIG_EEPROM_93CX6)	+= eeprom_93cx6.o
-obj-$(CONFIG_EEPROM_93XX46)	+= eeprom_93xx46.o
 obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o
 obj-$(CONFIG_EEPROM_IDT_89HPESX) += idt_89hpesx.o
-obj-$(CONFIG_EEPROM_EE1004)	+= ee1004.o
-obj-$(CONFIG_EEPROM_M24LR) += m24lr.o
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
index e10f7ff725ff..b0044c4c63c6 100644
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -28,6 +28,93 @@ source "drivers/nvmem/layouts/Kconfig"
 
 # Devices
 
+if NVMEM_SYSFS
+
+config EEPROM_AT24
+	tristate "I2C EEPROMs / RAMs / ROMs from most vendors"
+	depends on I2C && SYSFS
+	select REGMAP
+	select REGMAP_I2C
+	help
+	  Enable this driver to get read/write support to most I2C EEPROMs
+	  and compatible devices like FRAMs, SRAMs, ROMs etc. After you
+	  configure the driver to know about each chip on your target
+	  board.  Use these generic chip names, instead of vendor-specific
+	  ones like at24c64, 24lc02 or fm24c04:
+
+	     24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08,
+	     24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024, 24c2048
+
+	  Unless you like data loss puzzles, always be sure that any chip
+	  you configure as a 24c32 (32 kbit) or larger is NOT really a
+	  24c16 (16 kbit) or smaller, and vice versa. Marking the chip
+	  as read-only won't help recover from this. Also, if your chip
+	  has any software write-protect mechanism you may want to review the
+	  code to make sure this driver won't turn it on by accident.
+
+	  If you use this with an SMBus adapter instead of an I2C adapter,
+	  full functionality is not available.  Only smaller devices are
+	  supported (24c16 and below, max 4 kByte).
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called at24.
+
+config EEPROM_AT25
+	tristate "SPI EEPROMs (FRAMs) from most vendors"
+	depends on SPI && SYSFS
+	select SPI_MEM
+	help
+	  Enable this driver to get read/write support to most SPI EEPROMs
+	  and Cypress FRAMs,
+	  after you configure the board init code to know about each eeprom
+	  on your target board.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called at25.
+
+config EEPROM_93XX46
+	tristate "Microwire EEPROM 93XX46 support"
+	depends on SPI && SYSFS
+	select REGMAP
+	help
+	  Driver for the microwire EEPROM chipsets 93xx46x. The driver
+	  supports both read and write commands and also the command to
+	  erase the whole EEPROM.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called eeprom_93xx46.
+
+	  If unsure, say N.
+
+config EEPROM_EE1004
+	tristate "SPD EEPROMs on DDR4 memory modules"
+	depends on I2C && SYSFS
+	help
+	  Enable this driver to get read support to SPD EEPROMs following
+	  the JEDEC EE1004 standard. These are typically found on DDR4
+	  SDRAM memory modules.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called ee1004.
+
+config EEPROM_M24LR
+	tristate "STMicroelectronics M24LR RFID/NFC EEPROM support"
+	depends on I2C && SYSFS
+	select REGMAP_I2C
+	help
+	  This enables support for STMicroelectronics M24LR RFID/NFC EEPROM
+	  chips. These dual-interface devices expose two I2C addresses:
+	  one for EEPROM memory access and another for control and system
+	  configuration (e.g. UID, password handling).
+
+	  This driver provides a sysfs interface for control functions and
+	  integrates with the nvmem subsystem for EEPROM access.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called m24lr.
+
+endif # NVMEM_SYSFS
+
 config NVMEM_AIROHA_SMC_EFUSES
 	tristate "Airoha SMC eFuse support"
 	depends on ARCH_AIROHA || COMPILE_TEST
diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
index f6f2bc51dee1..2bbfb9ff1885 100644
--- a/drivers/nvmem/Makefile
+++ b/drivers/nvmem/Makefile
@@ -10,6 +10,11 @@ nvmem_layouts-y			:= layouts.o
 obj-y				+= layouts/
 
 # Devices
+obj-$(CONFIG_EEPROM_AT24)		+= at24.o
+obj-$(CONFIG_EEPROM_AT25)		+= at25.o
+obj-$(CONFIG_EEPROM_93XX46)		+= eeprom_93xx46.o
+obj-$(CONFIG_EEPROM_EE1004)		+= ee1004.o
+obj-$(CONFIG_EEPROM_M24LR)		+= m24lr.o
 obj-$(CONFIG_NVMEM_AIROHA_SMC_EFUSES)	+= nvmem-airoha-smc-efuses.o
 nvmem-airoha-smc-efuses-y 		:= airoha-smc-efuses.o
 obj-$(CONFIG_NVMEM_AN8855_EFUSE)	+= nvmem-an8855-efuse.o
diff --git a/drivers/misc/eeprom/at24.c b/drivers/nvmem/at24.c
similarity index 100%
rename from drivers/misc/eeprom/at24.c
rename to drivers/nvmem/at24.c
diff --git a/drivers/misc/eeprom/at25.c b/drivers/nvmem/at25.c
similarity index 100%
rename from drivers/misc/eeprom/at25.c
rename to drivers/nvmem/at25.c
diff --git a/drivers/misc/eeprom/ee1004.c b/drivers/nvmem/ee1004.c
similarity index 100%
rename from drivers/misc/eeprom/ee1004.c
rename to drivers/nvmem/ee1004.c
diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/nvmem/eeprom_93xx46.c
similarity index 100%
rename from drivers/misc/eeprom/eeprom_93xx46.c
rename to drivers/nvmem/eeprom_93xx46.c
diff --git a/drivers/misc/eeprom/m24lr.c b/drivers/nvmem/m24lr.c
similarity index 100%
rename from drivers/misc/eeprom/m24lr.c
rename to drivers/nvmem/m24lr.c
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 11/14] nvmem: airoha: add ARM64 dependency
  2026-07-29  9:46 [PATCH 00/14] nvmem: updates for 7.3 srini
                   ` (9 preceding siblings ...)
  2026-07-29  9:46 ` [PATCH 10/14] eeprom: move nvmem EEPROM drivers to drivers/nvmem/ srini
@ 2026-07-29  9:46 ` srini
  2026-07-29  9:46 ` [PATCH 12/14] nvmem: brcm_nvram: reject empty NVRAM partition srini
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: srini @ 2026-07-29  9:46 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, Arnd Bergmann, Christian Marangi,
	Srinivas Kandagatla

From: Arnd Bergmann <arnd@arndb.de>

The driver already depends on HAVE_ARM_SMCCC and ARCH_AIROHA, but both
are available for 32-bit and 64-bit targets. However, the smccc invocation
fails on thumb2 builds with clang:

drivers/nvmem/airoha-smc-efuses.c:38:2: error: write to reserved register 'R7'
   38 |         arm_smccc_1_1_invoke(AIROHA_SMC_EFUSE_FID,
      |         ^
note: (skipping 1 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
arch/arm/include/asm/opcodes.h:215:2: note: expanded from macro '__inst_arm_thumb32'
  215 |         __inst_thumb32(thumb_opcode)
      |         ^

Since the driver is only used on the 64-bit an7581 soc, avoid this
problem with a stricter dependency.

Fixes: b7846af2e6ca ("nvmem: airoha: Add support for SMC eFUSE")
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Reviewed-by: Christian Marangi <ansuelsmth@gmail.com>
Signed-off-by: Srinivas Kandagatla <srini@kernel.org>
---
 drivers/nvmem/Kconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
index b0044c4c63c6..c36c2a4c2a0b 100644
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -117,6 +117,7 @@ endif # NVMEM_SYSFS
 
 config NVMEM_AIROHA_SMC_EFUSES
 	tristate "Airoha SMC eFuse support"
+	depends on ARM64
 	depends on ARCH_AIROHA || COMPILE_TEST
 	depends on HAVE_ARM_SMCCC
 	default ARCH_AIROHA
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 12/14] nvmem: brcm_nvram: reject empty NVRAM partition
  2026-07-29  9:46 [PATCH 00/14] nvmem: updates for 7.3 srini
                   ` (10 preceding siblings ...)
  2026-07-29  9:46 ` [PATCH 11/14] nvmem: airoha: add ARM64 dependency srini
@ 2026-07-29  9:46 ` srini
  2026-07-29  9:46 ` [PATCH 13/14] nvmem: brcm_nvram: fix out-of-bounds access on malformed flash data srini
  2026-07-29  9:46 ` [PATCH 14/14] dt-bindings: nvmem: qfprom: Add ipq5210 & ipq9650 compatible srini
  13 siblings, 0 replies; 15+ messages in thread
From: srini @ 2026-07-29  9:46 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, Rosen Penev, Srinivas Kandagatla

From: Rosen Penev <rosenp@gmail.com>

If the partition is completely erased (all padding bytes), the trimming
loop reduces data_len to 0. devm_kzalloc(dev, 0, GFP_KERNEL) returns
ZERO_SIZE_PTR ((void *)16), which is non-NULL and bypasses the NULL
check. The subsequent cast of priv->data to struct brcm_nvram_header *
and dereference of header->magic causes a page fault on address 0x10.

Reject data_len smaller than the header before allocating.

Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
Signed-off-by: Srinivas Kandagatla <srini@kernel.org>
---
 drivers/nvmem/brcm_nvram.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/nvmem/brcm_nvram.c b/drivers/nvmem/brcm_nvram.c
index aaa6537798bf..c3b4282aa164 100644
--- a/drivers/nvmem/brcm_nvram.c
+++ b/drivers/nvmem/brcm_nvram.c
@@ -84,6 +84,11 @@ static int brcm_nvram_copy_data(struct brcm_nvram *priv, struct platform_device
 	}
 	WARN(priv->data_len > SZ_128K, "Unexpected (big) NVRAM size: %zu B\n", priv->data_len);
 
+	if (priv->data_len < sizeof(struct brcm_nvram_header)) {
+		dev_err(priv->dev, "NVRAM data too small (%zu)\n", priv->data_len);
+		return -EINVAL;
+	}
+
 	priv->data = devm_kzalloc(priv->dev, priv->data_len, GFP_KERNEL);
 	if (!priv->data)
 		return -ENOMEM;
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 13/14] nvmem: brcm_nvram: fix out-of-bounds access on malformed flash data
  2026-07-29  9:46 [PATCH 00/14] nvmem: updates for 7.3 srini
                   ` (11 preceding siblings ...)
  2026-07-29  9:46 ` [PATCH 12/14] nvmem: brcm_nvram: reject empty NVRAM partition srini
@ 2026-07-29  9:46 ` srini
  2026-07-29  9:46 ` [PATCH 14/14] dt-bindings: nvmem: qfprom: Add ipq5210 & ipq9650 compatible srini
  13 siblings, 0 replies; 15+ messages in thread
From: srini @ 2026-07-29  9:46 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, Rosen Penev, Srinivas Kandagatla

From: Rosen Penev <rosenp@gmail.com>

The length check in brcm_nvram_parse() validated header->len against
priv->nvmem_size (the full partition size) instead of priv->data_len
(the actual allocated data buffer). A malformed flash partition with
header->len between the two would pass the check, causing
brcm_nvram_add_cells() to read and write priv->data[len - 1] beyond
the heap allocation.

Also add a minimum bound: len < sizeof(*header) could underflow the
data[len - 1] access.

Fix both bounds by rejecting len outside [sizeof(*header), priv->data_len].

Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
Signed-off-by: Srinivas Kandagatla <srini@kernel.org>
---
 drivers/nvmem/brcm_nvram.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/drivers/nvmem/brcm_nvram.c b/drivers/nvmem/brcm_nvram.c
index c3b4282aa164..9f77aee37121 100644
--- a/drivers/nvmem/brcm_nvram.c
+++ b/drivers/nvmem/brcm_nvram.c
@@ -192,9 +192,13 @@ static int brcm_nvram_parse(struct brcm_nvram *priv)
 	}
 
 	len = le32_to_cpu(header->len);
-	if (len > priv->nvmem_size) {
-		dev_err(dev, "NVRAM length (%zd) exceeds mapped size (%zd)\n", len,
-			priv->nvmem_size);
+	if (len < sizeof(*header)) {
+		dev_err(dev, "NVRAM length (%zd) too small\n", len);
+		return -EINVAL;
+	}
+	if (len > priv->data_len) {
+		dev_err(dev, "NVRAM length (%zd) exceeds data size (%zd)\n", len,
+			priv->data_len);
 		return -EINVAL;
 	}
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 14/14] dt-bindings: nvmem: qfprom: Add ipq5210 & ipq9650 compatible
  2026-07-29  9:46 [PATCH 00/14] nvmem: updates for 7.3 srini
                   ` (12 preceding siblings ...)
  2026-07-29  9:46 ` [PATCH 13/14] nvmem: brcm_nvram: fix out-of-bounds access on malformed flash data srini
@ 2026-07-29  9:46 ` srini
  13 siblings, 0 replies; 15+ messages in thread
From: srini @ 2026-07-29  9:46 UTC (permalink / raw)
  To: gregkh
  Cc: linux-kernel, Varadarajan Narayanan, Krzysztof Kozlowski,
	Srinivas Kandagatla

From: Varadarajan Narayanan <varadarajan.narayanan@oss.qualcomm.com>

Document compatible string for the QFPROM on ipq5210 & ipq9650 platforms.

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Varadarajan Narayanan <varadarajan.narayanan@oss.qualcomm.com>
Signed-off-by: Srinivas Kandagatla <srini@kernel.org>
---
 Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml
index 8134ddb54e13..bc0a0898b9e4 100644
--- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml
+++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml
@@ -22,12 +22,14 @@ properties:
           - qcom,glymur-qfprom
           - qcom,hawi-qfprom
           - qcom,ipq5018-qfprom
+          - qcom,ipq5210-qfprom
           - qcom,ipq5332-qfprom
           - qcom,ipq5424-qfprom
           - qcom,ipq6018-qfprom
           - qcom,ipq8064-qfprom
           - qcom,ipq8074-qfprom
           - qcom,ipq9574-qfprom
+          - qcom,ipq9650-qfprom
           - qcom,kaanapali-qfprom
           - qcom,milos-qfprom
           - qcom,msm8226-qfprom
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-07-29  9:47 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-29  9:46 [PATCH 00/14] nvmem: updates for 7.3 srini
2026-07-29  9:46 ` [PATCH 01/14] nvmem: remove unused field from struct nvmem_device srini
2026-07-29  9:46 ` [PATCH 02/14] nvmem: return -EOPNOTSUPP to in-kernel users on missing callbacks srini
2026-07-29  9:46 ` [PATCH 03/14] nvmem: check the return value of gpiod_set_value_cansleep() srini
2026-07-29  9:46 ` [PATCH 04/14] nvmem: simplify locking with guard() srini
2026-07-29  9:46 ` [PATCH 05/14] nvmem: remove unneeded __nvmem_device_put() srini
2026-07-29  9:46 ` [PATCH 06/14] nvmem: split out the reg_read/write() callbacks out of struct nvmem_device srini
2026-07-29  9:46 ` [PATCH 07/14] nvmem: simplify nvmem_sysfs_remove_compat() srini
2026-07-29  9:46 ` [PATCH 08/14] nvmem: remove duplicated reference counting srini
2026-07-29  9:46 ` [PATCH 09/14] nvmem: protect nvmem_device::ops with SRCU srini
2026-07-29  9:46 ` [PATCH 10/14] eeprom: move nvmem EEPROM drivers to drivers/nvmem/ srini
2026-07-29  9:46 ` [PATCH 11/14] nvmem: airoha: add ARM64 dependency srini
2026-07-29  9:46 ` [PATCH 12/14] nvmem: brcm_nvram: reject empty NVRAM partition srini
2026-07-29  9:46 ` [PATCH 13/14] nvmem: brcm_nvram: fix out-of-bounds access on malformed flash data srini
2026-07-29  9:46 ` [PATCH 14/14] dt-bindings: nvmem: qfprom: Add ipq5210 & ipq9650 compatible srini

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox