* [PATCH v2 1/8] dt-bindings: mmc: Document support for nvmem-layout
2026-05-07 15:24 [PATCH v2 0/8] Support for block device NVMEM providers Loic Poulain
@ 2026-05-07 15:24 ` Loic Poulain
2026-05-07 15:24 ` [PATCH v2 2/8] dt-bindings: net: wireless: qcom,ath10k: Add NVMEM MAC address cell Loic Poulain
` (6 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Loic Poulain @ 2026-05-07 15:24 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain
Add support for an nvmem-layout subnode under an eMMC hardware
partition. This allows the partition to be exposed as an NVMEM
provider and its internal layout to be described. For example,
an eMMC boot partition can be used to store device-specific
information such as a WiFi MAC address.
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
.../devicetree/bindings/mmc/mmc-card.yaml | 24 ++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/Documentation/devicetree/bindings/mmc/mmc-card.yaml b/Documentation/devicetree/bindings/mmc/mmc-card.yaml
index a61d6c96df759102f9c1fbfd548b026a77921cae..b21426a49cf1d9aae5b4e8e447b5be11b08c96bf 100644
--- a/Documentation/devicetree/bindings/mmc/mmc-card.yaml
+++ b/Documentation/devicetree/bindings/mmc/mmc-card.yaml
@@ -40,6 +40,9 @@ patternProperties:
contains:
const: fixed-partitions
+ nvmem-layout:
+ $ref: /schemas/nvmem/layouts/nvmem-layout.yaml
+
required:
- compatible
- reg
@@ -86,6 +89,27 @@ examples:
read-only;
};
};
+
+ partitions-boot2 {
+ nvmem-layout {
+ compatible = "fixed-layout";
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ mac-addr@4400 {
+ compatible = "mac-base";
+ reg = <0x4400 0x6>;
+ #nvmem-cell-cells = <1>;
+ };
+
+ bd-addr@5400 {
+ compatible = "mac-base";
+ reg = <0x5400 0x6>;
+ #nvmem-cell-cells = <1>;
+ };
+ };
+ };
};
};
--
2.34.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 2/8] dt-bindings: net: wireless: qcom,ath10k: Add NVMEM MAC address cell
2026-05-07 15:24 [PATCH v2 0/8] Support for block device NVMEM providers Loic Poulain
2026-05-07 15:24 ` [PATCH v2 1/8] dt-bindings: mmc: Document support for nvmem-layout Loic Poulain
@ 2026-05-07 15:24 ` Loic Poulain
2026-05-08 18:20 ` sashiko-bot
2026-05-07 15:24 ` [PATCH v2 3/8] dt-bindings: bluetooth: qcom: Add NVMEM BD " Loic Poulain
` (5 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Loic Poulain @ 2026-05-07 15:24 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain
Add support for an NVMEM cell provider with the standard "mac-address"
cell name. This allows the ath10k device to retrieve its MAC address
from non-volatile storage such as an EEPROM or an eMMC partition.
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml
index c21d66c7cd558ab792524be9afec8b79272d1c87..96e025cd1e3acacf3da270ed43955b0d6acdb7de 100644
--- a/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml
+++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml
@@ -92,6 +92,15 @@ properties:
ieee80211-freq-limit: true
+ nvmem-cells:
+ maxItems: 1
+ description:
+ Nvmem data cell that contains a 6 byte MAC address with the most
+ significant byte first (big-endian).
+
+ nvmem-cell-names:
+ const: mac-address
+
qcom,calibration-data:
$ref: /schemas/types.yaml#/definitions/uint8-array
description:
--
2.34.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v2 2/8] dt-bindings: net: wireless: qcom,ath10k: Add NVMEM MAC address cell
2026-05-07 15:24 ` [PATCH v2 2/8] dt-bindings: net: wireless: qcom,ath10k: Add NVMEM MAC address cell Loic Poulain
@ 2026-05-08 18:20 ` sashiko-bot
0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-05-08 18:20 UTC (permalink / raw)
To: Loic Poulain; +Cc: robh, krzk+dt, devicetree, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The patch defines `nvmem-cells: maxItems: 1` and `nvmem-cell-names: const: mac-address`, which explicitly invalidates and prevents valid device tree configurations that use NVMEM for calibration data.
--
commit fb9ea9036c91c7c939b039f47a6a36361ae2f2fe
Author: Loic Poulain <loic.poulain@oss.qualcomm.com>
dt-bindings: net: wireless: qcom,ath10k: Add NVMEM MAC address cell
This commit adds support for an NVMEM cell provider with the standard
mac-address cell name to allow ath10k to retrieve its MAC address from
non-volatile storage.
> diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml
> --- a/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml
> +++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml
[ ... ]
> @@ -92,6 +92,15 @@ properties:
>
> ieee80211-freq-limit: true
>
> + nvmem-cells:
> + maxItems: 1
> + description:
> + Nvmem data cell that contains a 6 byte MAC address with the most
> + significant byte first (big-endian).
> +
> + nvmem-cell-names:
> + const: mac-address
Does this restrict the binding to only allow a single NVMEM cell, and
force its name to be exactly mac-address?
The ath10k driver natively fetches NVMEM calibration data via cells named
calibration and pre-calibration (see ath10k_download_cal_nvmem() in
drivers/net/wireless/ath/ath10k/core.c).
Existing upstream device trees, such as
arch/arm/boot/dts/qcom/qcom-ipq4018-ap120c-ac.dtsi, actively define
nvmem-cell-names = "pre-calibration" for ath10k nodes.
Would setting maxItems to 1 and restricting the name to mac-address
prevent a device from defining both a MAC address and calibration data
simultaneously, and cause dtbs_check validation failures for existing
hardware configurations?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260507-block-as-nvmem-v2-0-bf17edd5134e@oss.qualcomm.com?part=2
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2 3/8] dt-bindings: bluetooth: qcom: Add NVMEM BD address cell
2026-05-07 15:24 [PATCH v2 0/8] Support for block device NVMEM providers Loic Poulain
2026-05-07 15:24 ` [PATCH v2 1/8] dt-bindings: mmc: Document support for nvmem-layout Loic Poulain
2026-05-07 15:24 ` [PATCH v2 2/8] dt-bindings: net: wireless: qcom,ath10k: Add NVMEM MAC address cell Loic Poulain
@ 2026-05-07 15:24 ` Loic Poulain
2026-05-07 15:24 ` [PATCH v2 4/8] block: implement NVMEM provider Loic Poulain
` (4 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Loic Poulain @ 2026-05-07 15:24 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain
Add support for an NVMEM cell provider for "local-bd-address",
allowing the Bluetooth stack to retrieve controller's BD address
from non-volatile storage such as an EEPROM or an eMMC partition.
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
.../devicetree/bindings/net/bluetooth/qcom,bluetooth-common.yaml | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,bluetooth-common.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,bluetooth-common.yaml
index c8e9c55c1afb4c8e05ba2dae41ce2db4194b4a0f..7cb28f30c9af032082f23311f2fc89a32f266f17 100644
--- a/Documentation/devicetree/bindings/net/bluetooth/qcom,bluetooth-common.yaml
+++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,bluetooth-common.yaml
@@ -22,4 +22,13 @@ properties:
description:
boot firmware is incorrectly passing the address in big-endian order
+ nvmem-cells:
+ maxItems: 1
+ description:
+ Nvmem data cell that contains a 6 byte BD address with the most
+ significant byte first (big-endian).
+
+ nvmem-cell-names:
+ const: local-bd-address
+
additionalProperties: true
--
2.34.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 4/8] block: implement NVMEM provider
2026-05-07 15:24 [PATCH v2 0/8] Support for block device NVMEM providers Loic Poulain
` (2 preceding siblings ...)
2026-05-07 15:24 ` [PATCH v2 3/8] dt-bindings: bluetooth: qcom: Add NVMEM BD " Loic Poulain
@ 2026-05-07 15:24 ` Loic Poulain
2026-05-08 18:20 ` sashiko-bot
2026-05-07 15:24 ` [PATCH v2 5/8] net: of_net: Add of_get_nvmem_eui48() helper for EUI-48 lookup Loic Poulain
` (3 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Loic Poulain @ 2026-05-07 15:24 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain
From: Daniel Golle <daniel@makrotopia.org>
On embedded devices using an eMMC it is common that one or more partitions
on the eMMC are used to store MAC addresses and Wi-Fi calibration EEPROM
data. Allow referencing the partition in device tree for the kernel and
Wi-Fi drivers accessing it via the NVMEM layer.
To safely defer the freeing of the provider private data until all
consumers have released their cells, a nvmem_dev() accessor is added to
the NVMEM core to expose the struct device embedded in struct nvmem_device.
This allows registering a devm action on the nvmem device itself, ensuring
the private data outlives any active cell references.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Co-developed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
block/Kconfig | 9 ++
block/Makefile | 1 +
block/blk-nvmem.c | 188 +++++++++++++++++++++++++++++++++++++++++
drivers/nvmem/core.c | 13 +++
include/linux/nvmem-consumer.h | 6 ++
5 files changed, 217 insertions(+)
diff --git a/block/Kconfig b/block/Kconfig
index 15027963472d7b40e27b9097a5993c457b5b3054..0b33747e16dc33473683706f75c92bdf8b648f7c 100644
--- a/block/Kconfig
+++ b/block/Kconfig
@@ -209,6 +209,15 @@ config BLK_INLINE_ENCRYPTION_FALLBACK
by falling back to the kernel crypto API when inline
encryption hardware is not present.
+config BLK_NVMEM
+ bool "Block device NVMEM provider"
+ depends on OF
+ depends on NVMEM
+ help
+ Allow block devices (or partitions) to act as NVMEM providers,
+ typically used with eMMC to store MAC addresses or Wi-Fi
+ calibration data on embedded devices.
+
source "block/partitions/Kconfig"
config BLK_PM
diff --git a/block/Makefile b/block/Makefile
index 7dce2e44276c4274c11a0a61121c83d9c43d6e0c..d7ac389e71902bc091a8800ea266190a43b3e63d 100644
--- a/block/Makefile
+++ b/block/Makefile
@@ -36,3 +36,4 @@ obj-$(CONFIG_BLK_INLINE_ENCRYPTION) += blk-crypto.o blk-crypto-profile.o \
blk-crypto-sysfs.o
obj-$(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) += blk-crypto-fallback.o
obj-$(CONFIG_BLOCK_HOLDER_DEPRECATED) += holder.o
+obj-$(CONFIG_BLK_NVMEM) += blk-nvmem.o
diff --git a/block/blk-nvmem.c b/block/blk-nvmem.c
new file mode 100644
index 0000000000000000000000000000000000000000..96c0ffc51b1862a75644f3f94add35d59577d86b
--- /dev/null
+++ b/block/blk-nvmem.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * block device NVMEM provider
+ *
+ * Copyright (c) 2024 Daniel Golle <daniel@makrotopia.org>
+ *
+ * Useful on devices using a partition on an eMMC for MAC addresses or
+ * Wi-Fi calibration EEPROM data.
+ */
+
+#include "blk.h"
+#include <linux/nvmem-provider.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/pagemap.h>
+#include <linux/property.h>
+
+static void blk_nvmem_free(void *data)
+{
+ kfree(data);
+}
+
+/* List of all NVMEM devices */
+static LIST_HEAD(nvmem_devices);
+static DEFINE_MUTEX(devices_mutex);
+
+struct blk_nvmem {
+ struct nvmem_device *nvmem;
+ dev_t devt;
+ bool removed;
+ struct list_head list;
+};
+
+static int blk_nvmem_reg_read(void *priv, unsigned int from,
+ void *val, size_t bytes)
+{
+ blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_RESTRICT_WRITES;
+ unsigned long offs = from & ~PAGE_MASK, to_read;
+ pgoff_t f_index = from >> PAGE_SHIFT;
+ struct blk_nvmem *bnv = priv;
+ size_t bytes_left = bytes;
+ struct file *bdev_file;
+ struct folio *folio;
+ void *p;
+ int ret = 0;
+
+ if (bnv->removed)
+ return -ENODEV;
+
+ bdev_file = bdev_file_open_by_dev(bnv->devt, mode, priv, NULL);
+ if (!bdev_file)
+ return -ENODEV;
+
+ if (IS_ERR(bdev_file))
+ return PTR_ERR(bdev_file);
+
+ while (bytes_left) {
+ folio = read_mapping_folio(bdev_file->f_mapping, f_index++, NULL);
+ if (IS_ERR(folio)) {
+ ret = PTR_ERR(folio);
+ goto err_release_bdev;
+ }
+ to_read = min_t(unsigned long, bytes_left, PAGE_SIZE - offs);
+ p = folio_address(folio) + offset_in_folio(folio, offs);
+ memcpy(val, p, to_read);
+ offs = 0;
+ bytes_left -= to_read;
+ val += to_read;
+ folio_put(folio);
+ }
+
+err_release_bdev:
+ fput(bdev_file);
+
+ return ret;
+}
+
+static int blk_nvmem_register(struct device *dev)
+{
+ struct device_node *np = dev_of_node(dev);
+ struct block_device *bdev = dev_to_bdev(dev);
+ struct nvmem_config config = {};
+ struct blk_nvmem *bnv;
+
+ /* skip devices which do not have a device tree node */
+ if (!np)
+ return 0;
+
+ /* skip devices without an nvmem layout defined */
+ if (!of_get_child_by_name(np, "nvmem-layout"))
+ return 0;
+
+ /*
+ * skip block device too large to be represented as NVMEM devices
+ * which are using an 'int' as address
+ */
+ if (bdev_nr_bytes(bdev) > INT_MAX)
+ return -EFBIG;
+
+ bnv = kzalloc_obj(*bnv);
+ if (!bnv)
+ return -ENOMEM;
+
+ config.id = NVMEM_DEVID_NONE;
+ config.dev = &bdev->bd_device;
+ config.name = dev_name(&bdev->bd_device);
+ config.owner = THIS_MODULE;
+ config.priv = bnv;
+ config.reg_read = blk_nvmem_reg_read;
+ config.size = bdev_nr_bytes(bdev);
+ config.word_size = 1;
+ config.stride = 1;
+ config.read_only = true;
+ config.root_only = true;
+ config.ignore_wp = true;
+ config.of_node = to_of_node(dev->fwnode);
+
+ bnv->devt = bdev->bd_device.devt;
+ bnv->nvmem = nvmem_register(&config);
+ if (IS_ERR(bnv->nvmem)) {
+ dev_err_probe(&bdev->bd_device, PTR_ERR(bnv->nvmem),
+ "Failed to register NVMEM device\n");
+
+ kfree(bnv);
+ return PTR_ERR(bnv->nvmem);
+ }
+
+ /*
+ * Free bnv only when the nvmem device is fully released (i.e. when
+ * its kref hits zero), not at unregister time. This prevents a
+ * use-after-free if a consumer still holds an nvmem_cell reference
+ * when the block device is removed: nvmem_unregister() only does a
+ * kref_put(), so reg_read could still be called with bnv as priv
+ * until the last consumer drops its cell.
+ */
+ if (devm_add_action(nvmem_dev(bnv->nvmem), blk_nvmem_free, bnv)) {
+ nvmem_unregister(bnv->nvmem);
+ kfree(bnv);
+ return -ENOMEM;
+ }
+
+ mutex_lock(&devices_mutex);
+ list_add_tail(&bnv->list, &nvmem_devices);
+ mutex_unlock(&devices_mutex);
+
+ return 0;
+}
+
+static void blk_nvmem_unregister(struct device *dev)
+{
+ struct blk_nvmem *bnv_c, *bnv = NULL;
+
+ mutex_lock(&devices_mutex);
+ list_for_each_entry(bnv_c, &nvmem_devices, list) {
+ if (bnv_c->devt == dev_to_bdev(dev)->bd_device.devt) {
+ bnv = bnv_c;
+ break;
+ }
+ }
+
+ if (!bnv) {
+ mutex_unlock(&devices_mutex);
+ return;
+ }
+
+ list_del(&bnv->list);
+ mutex_unlock(&devices_mutex);
+ bnv->removed = true;
+ nvmem_unregister(bnv->nvmem);
+}
+
+static struct class_interface blk_nvmem_bus_interface __refdata = {
+ .class = &block_class,
+ .add_dev = &blk_nvmem_register,
+ .remove_dev = &blk_nvmem_unregister,
+};
+
+static int __init blk_nvmem_init(void)
+{
+ int ret;
+
+ ret = class_interface_register(&blk_nvmem_bus_interface);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+device_initcall(blk_nvmem_init);
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 311cb2e5a5c02d2c6979d7c9bbb7f94abdfbdad1..ee3481229c20b3063c86d0dd66aabbf6b5e29169 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -2154,6 +2154,19 @@ const char *nvmem_dev_name(struct nvmem_device *nvmem)
}
EXPORT_SYMBOL_GPL(nvmem_dev_name);
+/**
+ * nvmem_dev() - Get the struct device of a given nvmem device.
+ *
+ * @nvmem: nvmem device.
+ *
+ * Return: pointer to the struct device of the nvmem device.
+ */
+struct device *nvmem_dev(struct nvmem_device *nvmem)
+{
+ return &nvmem->dev;
+}
+EXPORT_SYMBOL_GPL(nvmem_dev);
+
/**
* nvmem_dev_size() - Get the size of a given nvmem device.
*
diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h
index 34c0e58dfa26636d2804fcc7e0bc4a875ee73dae..ce387c89dc8e4bc1241f3b6f36be8c6c95e282ed 100644
--- a/include/linux/nvmem-consumer.h
+++ b/include/linux/nvmem-consumer.h
@@ -82,6 +82,7 @@ int nvmem_device_cell_write(struct nvmem_device *nvmem,
const char *nvmem_dev_name(struct nvmem_device *nvmem);
size_t nvmem_dev_size(struct nvmem_device *nvmem);
+struct device *nvmem_dev(struct nvmem_device *nvmem);
void nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries,
size_t nentries);
@@ -220,6 +221,11 @@ static inline const char *nvmem_dev_name(struct nvmem_device *nvmem)
return NULL;
}
+static inline struct device *nvmem_dev(struct nvmem_device *nvmem)
+{
+ return NULL;
+}
+
static inline void
nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) {}
static inline void
--
2.34.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v2 4/8] block: implement NVMEM provider
2026-05-07 15:24 ` [PATCH v2 4/8] block: implement NVMEM provider Loic Poulain
@ 2026-05-08 18:20 ` sashiko-bot
0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-05-08 18:20 UTC (permalink / raw)
To: Loic Poulain; +Cc: conor+dt, robh, devicetree, krzk+dt
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] Incorrect page offset calculation and iteration logic for large folios causes severe data corruption.
- [High] Dereferencing NULL when reading from a block device page cache allocated in high memory.
- [High] Use-after-free of provider private data `bnv` on the `devm_add_action` error path.
- [Medium] Leaked device node reference in `blk_nvmem_register` when checking for the layout node.
- [Medium] A Time-of-Check to Time-of-Use (TOCTOU) race allows cross-device arbitrary data reads due to `devt` reallocation.
--
commit a3bbd821536b45bed34dfa8815ddf9b55f9bb8b9
Author: Daniel Golle <daniel@makrotopia.org>
Subject: block: implement NVMEM provider
This commit adds an NVMEM provider for block devices, allowing partitions on
embedded devices to be accessed via the NVMEM layer. It introduces an
nvmem_dev() accessor to safely defer freeing provider private data by adding
a devm action to the nvmem device.
> diff --git a/block/blk-nvmem.c b/block/blk-nvmem.c
> new file mode 100644
> index 0000000000000..96c0ffc51b186
> --- /dev/null
> +++ b/block/blk-nvmem.c
> @@ -0,0 +1,188 @@
[ ... ]
> +static int blk_nvmem_reg_read(void *priv, unsigned int from,
> + void *val, size_t bytes)
> +{
> + blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_RESTRICT_WRITES;
> + unsigned long offs = from & ~PAGE_MASK, to_read;
> + pgoff_t f_index = from >> PAGE_SHIFT;
> + struct blk_nvmem *bnv = priv;
> + size_t bytes_left = bytes;
> + struct file *bdev_file;
> + struct folio *folio;
> + void *p;
> + int ret = 0;
> +
> + if (bnv->removed)
> + return -ENODEV;
> +
> + bdev_file = bdev_file_open_by_dev(bnv->devt, mode, priv, NULL);
Is there a race condition between the removed check and opening the device?
The check for bnv->removed is performed without locks. If
blk_nvmem_unregister() runs concurrently and the original block device is
hot-removed, its devt identifier could be freed and reassigned to a newly
plugged-in, unrelated block device.
If this thread is preempted after the lockless check, it could open the new
block device using the recycled devt and read data from the wrong disk.
> + if (!bdev_file)
> + return -ENODEV;
> +
> + if (IS_ERR(bdev_file))
> + return PTR_ERR(bdev_file);
> +
> + while (bytes_left) {
> + folio = read_mapping_folio(bdev_file->f_mapping, f_index++, NULL);
> + if (IS_ERR(folio)) {
> + ret = PTR_ERR(folio);
> + goto err_release_bdev;
> + }
> + to_read = min_t(unsigned long, bytes_left, PAGE_SIZE - offs);
> + p = folio_address(folio) + offset_in_folio(folio, offs);
Does this logic handle large folios correctly?
If read_mapping_folio() returns a large folio, it will return the same head
folio for multiple sequential f_index values.
Because offs is repeatedly reset to 0 in subsequent loop iterations,
offset_in_folio(folio, 0) will evaluate to 0, causing p to repeatedly point
to the start of the head page instead of advancing through the subpages of
the large folio.
Additionally, can this cause a NULL pointer dereference on 32-bit platforms
with HIGHMEM?
Block device page cache mappings can allocate folios from the HIGHMEM zone,
and folio_address() relies on page_address(), which returns NULL for
unmapped high memory pages.
Adding an offset to this NULL pointer and attempting to read from it might
crash the kernel. Mapping the folio safely with kmap_local_folio() or
memcpy_from_folio() might be needed here.
> + memcpy(val, p, to_read);
> + offs = 0;
> + bytes_left -= to_read;
> + val += to_read;
> + folio_put(folio);
> + }
> +
> +err_release_bdev:
> + fput(bdev_file);
> +
> + return ret;
> +}
> +
> +static int blk_nvmem_register(struct device *dev)
> +{
> + struct device_node *np = dev_of_node(dev);
> + struct block_device *bdev = dev_to_bdev(dev);
> + struct nvmem_config config = {};
> + struct blk_nvmem *bnv;
> +
> + /* skip devices which do not have a device tree node */
> + if (!np)
> + return 0;
> +
> + /* skip devices without an nvmem layout defined */
> + if (!of_get_child_by_name(np, "nvmem-layout"))
> + return 0;
Does this leak the device node reference? of_get_child_by_name() increments
the reference count of the returned child device node, but since the pointer
is not saved, of_node_put() can never be called to release it.
> +
> + /*
> + * skip block device too large to be represented as NVMEM devices
> + * which are using an 'int' as address
> + */
> + if (bdev_nr_bytes(bdev) > INT_MAX)
> + return -EFBIG;
> +
> + bnv = kzalloc_obj(*bnv);
> + if (!bnv)
> + return -ENOMEM;
> +
> + config.id = NVMEM_DEVID_NONE;
> + config.dev = &bdev->bd_device;
> + config.name = dev_name(&bdev->bd_device);
> + config.owner = THIS_MODULE;
> + config.priv = bnv;
> + config.reg_read = blk_nvmem_reg_read;
> + config.size = bdev_nr_bytes(bdev);
> + config.word_size = 1;
> + config.stride = 1;
> + config.read_only = true;
> + config.root_only = true;
> + config.ignore_wp = true;
> + config.of_node = to_of_node(dev->fwnode);
> +
> + bnv->devt = bdev->bd_device.devt;
> + bnv->nvmem = nvmem_register(&config);
> + if (IS_ERR(bnv->nvmem)) {
> + dev_err_probe(&bdev->bd_device, PTR_ERR(bnv->nvmem),
> + "Failed to register NVMEM device\n");
> +
> + kfree(bnv);
> + return PTR_ERR(bnv->nvmem);
> + }
> +
> + /*
> + * Free bnv only when the nvmem device is fully released (i.e. when
> + * its kref hits zero), not at unregister time. This prevents a
> + * use-after-free if a consumer still holds an nvmem_cell reference
> + * when the block device is removed: nvmem_unregister() only does a
> + * kref_put(), so reg_read could still be called with bnv as priv
> + * until the last consumer drops its cell.
> + */
> + if (devm_add_action(nvmem_dev(bnv->nvmem), blk_nvmem_free, bnv)) {
> + nvmem_unregister(bnv->nvmem);
> + kfree(bnv);
Could this result in a use-after-free if a consumer acquires a reference
before devm_add_action() fails?
nvmem_register() makes the NVMEM device globally visible. If
nvmem_unregister() is called on the error path, it only drops the
provider's reference.
If a consumer has already obtained a reference, calling kfree(bnv)
immediately will free the provider data while the consumer might still
invoke blk_nvmem_reg_read().
> + return -ENOMEM;
> + }
> +
> + mutex_lock(&devices_mutex);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260507-block-as-nvmem-v2-0-bf17edd5134e@oss.qualcomm.com?part=4
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2 5/8] net: of_net: Add of_get_nvmem_eui48() helper for EUI-48 lookup
2026-05-07 15:24 [PATCH v2 0/8] Support for block device NVMEM providers Loic Poulain
` (3 preceding siblings ...)
2026-05-07 15:24 ` [PATCH v2 4/8] block: implement NVMEM provider Loic Poulain
@ 2026-05-07 15:24 ` Loic Poulain
2026-05-08 18:20 ` sashiko-bot
2026-05-07 15:24 ` [PATCH v2 6/8] Bluetooth: hci_sync: Add NVMEM-backed BD address retrieval Loic Poulain
` (2 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Loic Poulain @ 2026-05-07 15:24 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain
Factor out the common NVMEM EUI-48 retrieval logic from
of_get_mac_address_nvmem() into a new of_get_nvmem_eui48() helper that
accepts the NVMEM cell name as a parameter. This allows other subsystems
(e.g. Bluetooth) to reuse the same lookup-validate-copy pattern with a
different cell name, without duplicating code.
of_get_mac_address_nvmem() is updated to call of_get_nvmem_eui48() with
"mac-address", preserving its existing behavior.
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
include/linux/of_net.h | 7 +++++++
net/core/of_net.c | 47 +++++++++++++++++++++++++++++++++++------------
2 files changed, 42 insertions(+), 12 deletions(-)
diff --git a/include/linux/of_net.h b/include/linux/of_net.h
index d88715a0b3a52f87af23d47791bea3baf5be5200..7854ba555d9a55f3d020a37fe00a27ae52e0e5dc 100644
--- a/include/linux/of_net.h
+++ b/include/linux/of_net.h
@@ -15,6 +15,7 @@ struct net_device;
extern int of_get_phy_mode(struct device_node *np, phy_interface_t *interface);
extern int of_get_mac_address(struct device_node *np, u8 *mac);
extern int of_get_mac_address_nvmem(struct device_node *np, u8 *mac);
+int of_get_nvmem_eui48(struct device_node *np, const char *cell_name, u8 *addr);
int of_get_ethdev_address(struct device_node *np, struct net_device *dev);
extern struct net_device *of_find_net_device_by_node(struct device_node *np);
#else
@@ -34,6 +35,12 @@ static inline int of_get_mac_address_nvmem(struct device_node *np, u8 *mac)
return -ENODEV;
}
+static inline int of_get_nvmem_eui48(struct device_node *np,
+ const char *cell_name, u8 *addr)
+{
+ return -ENODEV;
+}
+
static inline int of_get_ethdev_address(struct device_node *np, struct net_device *dev)
{
return -ENODEV;
diff --git a/net/core/of_net.c b/net/core/of_net.c
index 93ea425b9248a23f4f95a336e9cdbf0053248e32..79b289de0f16aa5f8724e84d6f2300648c25b6c4 100644
--- a/net/core/of_net.c
+++ b/net/core/of_net.c
@@ -61,9 +61,6 @@ static int of_get_mac_addr(struct device_node *np, const char *name, u8 *addr)
int of_get_mac_address_nvmem(struct device_node *np, u8 *addr)
{
struct platform_device *pdev = of_find_device_by_node(np);
- struct nvmem_cell *cell;
- const void *mac;
- size_t len;
int ret;
/* Try lookup by device first, there might be a nvmem_cell_lookup
@@ -75,27 +72,53 @@ int of_get_mac_address_nvmem(struct device_node *np, u8 *addr)
return ret;
}
- cell = of_nvmem_cell_get(np, "mac-address");
+ ret = of_get_nvmem_eui48(np, "mac-address", addr);
+ if (ret)
+ return ret;
+
+ if (!is_valid_ether_addr(addr))
+ return -EINVAL;
+
+ return 0;
+}
+EXPORT_SYMBOL(of_get_mac_address_nvmem);
+
+/**
+ * of_get_nvmem_eui48 - Read a 6-byte EUI-48 address from a named NVMEM cell.
+ * @np: Device node to look up the NVMEM cell from.
+ * @cell_name: Name of the NVMEM cell (e.g. "mac-address", "local-bd-address").
+ * @addr: Output buffer for the 6-byte address.
+ *
+ * Reads the named NVMEM cell and validates that it contains a non-zero 6-byte
+ * address. Returns 0 on success, negative errno on failure.
+ */
+int of_get_nvmem_eui48(struct device_node *np, const char *cell_name, u8 *addr)
+{
+ struct nvmem_cell *cell;
+ const void *eui48;
+ size_t len;
+
+ cell = of_nvmem_cell_get(np, cell_name);
if (IS_ERR(cell))
return PTR_ERR(cell);
- mac = nvmem_cell_read(cell, &len);
+ eui48 = nvmem_cell_read(cell, &len);
nvmem_cell_put(cell);
- if (IS_ERR(mac))
- return PTR_ERR(mac);
+ if (IS_ERR(eui48))
+ return PTR_ERR(eui48);
- if (len != ETH_ALEN || !is_valid_ether_addr(mac)) {
- kfree(mac);
+ if (len != ETH_ALEN || !memchr_inv(eui48, 0, ETH_ALEN)) {
+ kfree(eui48);
return -EINVAL;
}
- memcpy(addr, mac, ETH_ALEN);
- kfree(mac);
+ memcpy(addr, eui48, ETH_ALEN);
+ kfree(eui48);
return 0;
}
-EXPORT_SYMBOL(of_get_mac_address_nvmem);
+EXPORT_SYMBOL_GPL(of_get_nvmem_eui48);
/**
* of_get_mac_address()
--
2.34.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v2 5/8] net: of_net: Add of_get_nvmem_eui48() helper for EUI-48 lookup
2026-05-07 15:24 ` [PATCH v2 5/8] net: of_net: Add of_get_nvmem_eui48() helper for EUI-48 lookup Loic Poulain
@ 2026-05-08 18:20 ` sashiko-bot
0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-05-08 18:20 UTC (permalink / raw)
To: Loic Poulain; +Cc: robh, krzk+dt, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] of_get_mac_address_nvmem() corrupts the output buffer on failure, causing network drivers to use invalid MAC addresses.
--
commit dfaa504d49a935a1aa8c630022d2ce8a6b303cce
Author: Loic Poulain <loic.poulain@oss.qualcomm.com>
net: of_net: Add of_get_nvmem_eui48() helper for EUI-48 lookup
Factor out the common NVMEM EUI-48 retrieval logic from
of_get_mac_address_nvmem() into a new of_get_nvmem_eui48() helper that
accepts the NVMEM cell name as a parameter.
> diff --git a/net/core/of_net.c b/net/core/of_net.c
> index 93ea425b9248a..79b289de0f16a 100644
> --- a/net/core/of_net.c
> +++ b/net/core/of_net.c
[ ... ]
> @@ -75,27 +72,53 @@ int of_get_mac_address_nvmem(struct device_node *np, u8 *addr)
> return ret;
> }
>
> - cell = of_nvmem_cell_get(np, "mac-address");
> + ret = of_get_nvmem_eui48(np, "mac-address", addr);
> + if (ret)
> + return ret;
> +
> + if (!is_valid_ether_addr(addr))
> + return -EINVAL;
Does this change inadvertently clobber the caller's output buffer when an
invalid MAC address is read?
Before this patch, of_get_mac_address_nvmem() only modified the output
buffer if the NVMEM-provided MAC address passed is_valid_ether_addr().
Now, it calls of_get_nvmem_eui48() which unconditionally copies any non-zero
6-byte value into the caller's buffer, and only then checks
is_valid_ether_addr(addr). If the MAC is invalid (e.g. multicast or
broadcast), it returns -EINVAL but leaves the invalid MAC in the caller's
buffer.
This can break existing callers that rely on the buffer remaining unmodified
on error. For example, in drivers/net/ethernet/freescale/enetc/enetc_pf_common.c,
enetc_setup_mac_address() initializes the buffer to zeroes, ignores non-defer
errors, and falls back to a hardware or random MAC if the buffer is still
all-zeroes. Because the buffer is clobbered with a non-zero invalid MAC,
the fallback is bypassed and enetc_set_si_hw_addr() programs the hardware
with the invalid MAC.
Similarly, in drivers/net/ethernet/renesas/sh_eth.c, read_mac_address()
checks if the buffer is non-zero to decide whether to read the MAC from
hardware registers. Clobbering the buffer causes the driver to skip the
valid hardware MAC and instead fall back to a random MAC.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260507-block-as-nvmem-v2-0-bf17edd5134e@oss.qualcomm.com?part=5
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2 6/8] Bluetooth: hci_sync: Add NVMEM-backed BD address retrieval
2026-05-07 15:24 [PATCH v2 0/8] Support for block device NVMEM providers Loic Poulain
` (4 preceding siblings ...)
2026-05-07 15:24 ` [PATCH v2 5/8] net: of_net: Add of_get_nvmem_eui48() helper for EUI-48 lookup Loic Poulain
@ 2026-05-07 15:24 ` Loic Poulain
2026-05-08 18:20 ` sashiko-bot
2026-05-07 15:24 ` [PATCH v2 7/8] Bluetooth: qca: Set NVMEM BD address quirks when address is invalid Loic Poulain
2026-05-07 15:24 ` [PATCH v2 8/8] arm64: dts: qcom: arduino-imola: Describe NVMEM layout for WiFi/BT addresses Loic Poulain
7 siblings, 1 reply; 13+ messages in thread
From: Loic Poulain @ 2026-05-07 15:24 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain
Some devices store the Bluetooth BD address in non-volatile
memory, which can be accessed through the NVMEM framework.
Similar to Ethernet or WiFi MAC addresses, add support for
reading the BD address from a 'local-bd-address' NVMEM cell.
As with the device-tree provided BD address, add a quirk to
indicate whether a device or platform should attempt to read
the address from NVMEM when no valid in-chip address is present.
Also add a quirk to indicate if the address is stored in
big-endian byte order.
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
include/net/bluetooth/hci.h | 18 ++++++++++++++++++
net/bluetooth/hci_sync.c | 39 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 56 insertions(+), 1 deletion(-)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 572b1c620c5d653a1fe10b26c1b0ba33e8f4968f..7686466d1109253b0d75edeb5f6a99fb98ce4cc6 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -164,6 +164,24 @@ enum {
*/
HCI_QUIRK_BDADDR_PROPERTY_BROKEN,
+ /* When this quirk is set, the public Bluetooth address
+ * initially reported by HCI Read BD Address command
+ * is considered invalid. The public BD Address can be
+ * retrieved via a 'local-bd-address' NVMEM cell.
+ *
+ * This quirk can be set before hci_register_dev is called or
+ * during the hdev->setup vendor callback.
+ */
+ HCI_QUIRK_USE_BDADDR_NVMEM,
+
+ /* When this quirk is set, the Bluetooth Device Address provided by
+ * the 'local-bd-address' NVMEM is stored in big-endian order.
+ *
+ * This quirk can be set before hci_register_dev is called or
+ * during the hdev->setup vendor callback.
+ */
+ HCI_QUIRK_BDADDR_NVMEM_BE,
+
/* When this quirk is set, the duplicate filtering during
* scanning is based on Bluetooth devices addresses. To allow
* RSSI based updates, restart scanning if needed.
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index fd3aacdea512a37c22b9a2be90c89ddca4b4d99f..589ccdfa26c1281d6eb979370523fff0d7920302 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -7,6 +7,7 @@
*/
#include <linux/property.h>
+#include <linux/of_net.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -3588,6 +3589,37 @@ int hci_powered_update_sync(struct hci_dev *hdev)
return 0;
}
+/**
+ * hci_dev_get_bd_addr_from_nvmem - Get the Bluetooth Device Address
+ * (BD_ADDR) for a HCI device from
+ * an NVMEM cell.
+ * @hdev: The HCI device
+ *
+ * Search for 'local-bd-address' NVMEM cell in the device firmware node.
+ *
+ * All-zero BD addresses are rejected (unprovisioned).
+ */
+static int hci_dev_get_bd_addr_from_nvmem(struct hci_dev *hdev)
+{
+ struct device_node *np = dev_of_node(hdev->dev.parent);
+ u8 ba[sizeof(bdaddr_t)];
+ int err;
+
+ if (!np)
+ return -ENODEV;
+
+ err = of_get_nvmem_eui48(np, "local-bd-address", ba);
+ if (err)
+ return err;
+
+ if (hci_test_quirk(hdev, HCI_QUIRK_BDADDR_NVMEM_BE))
+ baswap(&hdev->public_addr, (bdaddr_t *)ba);
+ else
+ bacpy(&hdev->public_addr, (bdaddr_t *)ba);
+
+ return 0;
+}
+
/**
* hci_dev_get_bd_addr_from_property - Get the Bluetooth Device Address
* (BD_ADDR) for a HCI device from
@@ -5042,12 +5074,17 @@ static int hci_dev_setup_sync(struct hci_dev *hdev)
* its setup callback.
*/
invalid_bdaddr = hci_test_quirk(hdev, HCI_QUIRK_INVALID_BDADDR) ||
- hci_test_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY);
+ hci_test_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY) ||
+ hci_test_quirk(hdev, HCI_QUIRK_USE_BDADDR_NVMEM);
if (!ret) {
if (hci_test_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY) &&
!bacmp(&hdev->public_addr, BDADDR_ANY))
hci_dev_get_bd_addr_from_property(hdev);
+ if (hci_test_quirk(hdev, HCI_QUIRK_USE_BDADDR_NVMEM) &&
+ !bacmp(&hdev->public_addr, BDADDR_ANY))
+ hci_dev_get_bd_addr_from_nvmem(hdev);
+
if (invalid_bdaddr && bacmp(&hdev->public_addr, BDADDR_ANY) &&
hdev->set_bdaddr) {
ret = hdev->set_bdaddr(hdev, &hdev->public_addr);
--
2.34.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v2 6/8] Bluetooth: hci_sync: Add NVMEM-backed BD address retrieval
2026-05-07 15:24 ` [PATCH v2 6/8] Bluetooth: hci_sync: Add NVMEM-backed BD address retrieval Loic Poulain
@ 2026-05-08 18:20 ` sashiko-bot
0 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-05-08 18:20 UTC (permalink / raw)
To: Loic Poulain; +Cc: conor+dt, devicetree, robh, krzk+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Retrieval of NVMEM data during `hci_dev_setup_sync()` bypasses the probe deferral mechanism and ignores errors, causing silent initialization failures.
--
commit 543fb62fd4ba7de331b6ec2a7642078d5eebdac4
Author: Loic Poulain <loic.poulain@oss.qualcomm.com>
Bluetooth: hci_sync: Add NVMEM-backed BD address retrieval
This commit adds support for retrieving the Bluetooth BD address from an
NVMEM cell using the NVMEM framework. It introduces quirks to indicate when
the address should be read from NVMEM and whether the address is stored in
big-endian byte order.
> diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
> index fd3aacdea512a..589ccdfa26c12 100644
> --- a/net/bluetooth/hci_sync.c
> +++ b/net/bluetooth/hci_sync.c
[ ... ]
> @@ -3588,6 +3589,37 @@ int hci_powered_update_sync(struct hci_dev *hdev)
> return 0;
> }
>
> +/**
> + * hci_dev_get_bd_addr_from_nvmem - Get the Bluetooth Device Address
[ ... ]
> +static int hci_dev_get_bd_addr_from_nvmem(struct hci_dev *hdev)
> +{
> + struct device_node *np = dev_of_node(hdev->dev.parent);
> + u8 ba[sizeof(bdaddr_t)];
> + int err;
> +
> + if (!np)
> + return -ENODEV;
> +
> + err = of_get_nvmem_eui48(np, "local-bd-address", ba);
> + if (err)
> + return err;
> +
> + if (hci_test_quirk(hdev, HCI_QUIRK_BDADDR_NVMEM_BE))
> + baswap(&hdev->public_addr, (bdaddr_t *)ba);
> + else
> + bacpy(&hdev->public_addr, (bdaddr_t *)ba);
> +
> + return 0;
> +}
[ ... ]
> @@ -5042,12 +5074,17 @@ static int hci_dev_setup_sync(struct hci_dev *hdev)
[ ... ]
> if (hci_test_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY) &&
> !bacmp(&hdev->public_addr, BDADDR_ANY))
> hci_dev_get_bd_addr_from_property(hdev);
>
> + if (hci_test_quirk(hdev, HCI_QUIRK_USE_BDADDR_NVMEM) &&
> + !bacmp(&hdev->public_addr, BDADDR_ANY))
> + hci_dev_get_bd_addr_from_nvmem(hdev);
Are errors from reading the NVMEM cell intentionally ignored here?
If of_get_nvmem_eui48() fails, the error is lost and the controller might
be initialized with an all-zero BD address.
Also, if the NVMEM provider is not yet probed, of_get_nvmem_eui48() will
return -EPROBE_DEFER. Because this is called from hci_dev_setup_sync()
rather than the transport driver's probe path, will this bypass the kernel's
probe deferral mechanism?
Could this leave the device stuck in the HCI_UNCONFIGURED state instead of
retrying later? To properly support probe deferral, should NVMEM resources
be queried in the transport driver's probe function before
hci_register_dev() is called?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260507-block-as-nvmem-v2-0-bf17edd5134e@oss.qualcomm.com?part=6
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2 7/8] Bluetooth: qca: Set NVMEM BD address quirks when address is invalid
2026-05-07 15:24 [PATCH v2 0/8] Support for block device NVMEM providers Loic Poulain
` (5 preceding siblings ...)
2026-05-07 15:24 ` [PATCH v2 6/8] Bluetooth: hci_sync: Add NVMEM-backed BD address retrieval Loic Poulain
@ 2026-05-07 15:24 ` Loic Poulain
2026-05-07 15:24 ` [PATCH v2 8/8] arm64: dts: qcom: arduino-imola: Describe NVMEM layout for WiFi/BT addresses Loic Poulain
7 siblings, 0 replies; 13+ messages in thread
From: Loic Poulain @ 2026-05-07 15:24 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain
When the controller BD address is invalid (zero or default),
set the NVMEM quirks to allow retrieving the address from a
'local-bd-address' NVMEM cell. The BD address is often stored
alongside the WiFi MAC address in big-endian format, so also
set the big-endian quirk.
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
drivers/bluetooth/btqca.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index dda76365726f0bfe0e80e05fe04859fa4f0592e1..df33eacfd29fa680f393f90215150743e6001d5b 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -721,8 +721,11 @@ static int qca_check_bdaddr(struct hci_dev *hdev, const struct qca_fw_config *co
}
bda = (struct hci_rp_read_bd_addr *)skb->data;
- if (!bacmp(&bda->bdaddr, &config->bdaddr))
+ if (!bacmp(&bda->bdaddr, &config->bdaddr)) {
hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY);
+ hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_NVMEM);
+ hci_set_quirk(hdev, HCI_QUIRK_BDADDR_NVMEM_BE);
+ }
kfree_skb(skb);
--
2.34.1
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 8/8] arm64: dts: qcom: arduino-imola: Describe NVMEM layout for WiFi/BT addresses
2026-05-07 15:24 [PATCH v2 0/8] Support for block device NVMEM providers Loic Poulain
` (6 preceding siblings ...)
2026-05-07 15:24 ` [PATCH v2 7/8] Bluetooth: qca: Set NVMEM BD address quirks when address is invalid Loic Poulain
@ 2026-05-07 15:24 ` Loic Poulain
7 siblings, 0 replies; 13+ messages in thread
From: Loic Poulain @ 2026-05-07 15:24 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain, Konrad Dybcio
On Arduino Uno-Q, the eMMC boot1 partition is factory provisioned
with device-specific information such as the WiFi MAC address
and the Bluetooth BD address. This partition can serve as an
alternative to additional non-volatile memory, such as a
dedicated EEPROM.
The eMMC boot partitions are typically good candidates, as they
are relatively small, read-only by default (and can be enforced
as hardware read-only), and are not affected by board reflashing
procedures, which generally target the eMMC user or GP partitions.
Describe the corresponding nvmem-layout for the WiFi and Bluetooth
addresses, and point the WiFi and Bluetooth nodes to the appropriate
NVMEM cells to retrieve them.
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts | 34 ++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts b/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts
index bf088fa9807f040f0c8f405f9111b01790b09377..6ed91cccae2fbf0723629a4db12d2724312d50b2 100644
--- a/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts
+++ b/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts
@@ -409,7 +409,35 @@ &sdhc_1 {
no-sdio;
no-sd;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
status = "okay";
+
+ card@0 {
+ compatible = "mmc-card";
+ reg = <0>;
+
+ partitions-boot1 {
+ nvmem-layout {
+ compatible = "fixed-layout";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ wifi_mac_addr: mac-addr@4400 {
+ compatible = "mac-base";
+ reg = <0x4400 0x6>;
+ #nvmem-cell-cells = <1>;
+ };
+
+ bd_addr: bd-addr@5400 {
+ compatible = "mac-base";
+ reg = <0x5400 0x6>;
+ #nvmem-cell-cells = <1>;
+ };
+ };
+ };
+ };
};
&spi5 {
@@ -512,6 +540,9 @@ bluetooth {
vddch0-supply = <&pm4125_l22>;
enable-gpios = <&tlmm 87 GPIO_ACTIVE_HIGH>;
max-speed = <3000000>;
+
+ nvmem-cells = <&bd_addr 0>;
+ nvmem-cell-names = "local-bd-address";
};
};
@@ -557,6 +588,9 @@ &wifi {
qcom,ath10k-calibration-variant = "ArduinoImola";
firmware-name = "qcm2290";
+ nvmem-cells = <&wifi_mac_addr 0>;
+ nvmem-cell-names = "mac-address";
+
status = "okay";
};
--
2.34.1
^ permalink raw reply related [flat|nested] 13+ messages in thread