From 4bd22ce63076df5456296c228938f1ba78609e87 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 12 Jun 2026 15:20:53 +0200 Subject: [PATCH 1/9] block: partitions: of: Skip child nodes without reg property Child nodes of a fixed-partitions node are not necessarily partition entries, for example an nvmem-layout node has no reg property. The current code passes a NULL reg pointer and uninitialized len to the length check, which can result in a kernel panic or silent failure to register any partitions. Fix validate_of_partition() to return a skip indicator when no reg property is present. Guard add_of_partition() with a reg property check for the same reason. Signed-off-by: Loic Poulain Reviewed-by: Bartosz Golaszewski --- block/partitions/of.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/block/partitions/of.c b/block/partitions/of.c index c22b606610981..534e02a9d85f6 100644 --- a/block/partitions/of.c +++ b/block/partitions/of.c @@ -15,6 +15,10 @@ static int validate_of_partition(struct device_node *np, int slot) int a_cells = of_n_addr_cells(np); int s_cells = of_n_size_cells(np); + /* Skip nodes without a reg property (e.g. nvmem-layout) */ + if (!reg) + return 1; + /* Make sure reg len match the expected addr and size cells */ if (len / sizeof(*reg) != a_cells + s_cells) return -EINVAL; @@ -80,14 +84,15 @@ int of_partition(struct parsed_partitions *state) slot = 1; /* Validate parition offset and size */ for_each_child_of_node(partitions_np, np) { - if (validate_of_partition(np, slot)) { + int err = validate_of_partition(np, slot); + + if (err < 0) { of_node_put(np); of_node_put(partitions_np); - return -1; } - - slot++; + if (!err) + slot++; } slot = 1; @@ -97,9 +102,10 @@ int of_partition(struct parsed_partitions *state) break; } - add_of_partition(state, slot, np); - - slot++; + if (of_property_present(np, "reg")) { + add_of_partition(state, slot, np); + slot++; + } } seq_buf_puts(&state->pp_buf, "\n"); From 0c6954ab63bae5608045756d18f0a7eaedd4d0f4 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 12 Jun 2026 15:20:54 +0200 Subject: [PATCH 2/9] dt-bindings: mmc: Document support for nvmem-layout 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. Reviewed-by: Rob Herring (Arm) Reviewed-by: Bartosz Golaszewski Signed-off-by: Loic Poulain --- .../devicetree/bindings/mmc/mmc-card.yaml | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/mmc-card.yaml b/Documentation/devicetree/bindings/mmc/mmc-card.yaml index a61d6c96df759..ca907ad730959 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,32 @@ examples: read-only; }; }; + + partitions-boot2 { + compatible = "fixed-partitions"; + + #address-cells = <1>; + #size-cells = <1>; + + 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>; + }; + }; + }; }; }; From bfc1145d6951c205e58aab7a2e040a16a5ff55c7 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 12 Jun 2026 15:20:55 +0200 Subject: [PATCH 3/9] dt-bindings: net: wireless: qcom,ath10k: Document NVMEM cells Document the NVMEM cells supported by the ath10k driver, the mac-address, pre-calibration data, and calibration data. Since such data may also originate from chipset OTP or be supplied via other device tree structures. All of these cells are optional and can be provided independently, in any combination. Reviewed-by: Bartosz Golaszewski Reviewed-by: Krzysztof Kozlowski Signed-off-by: Loic Poulain --- .../bindings/net/wireless/qcom,ath10k.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml index c21d66c7cd558..878c5d833a9cb 100644 --- a/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml +++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml @@ -92,6 +92,22 @@ properties: ieee80211-freq-limit: true + nvmem-cells: + minItems: 1 + maxItems: 3 + description: + References to nvmem cells for MAC address and/or calibration data. + Supported cell names are mac-address, calibration, and pre-calibration. + + nvmem-cell-names: + minItems: 1 + maxItems: 3 + items: + enum: + - mac-address + - calibration + - pre-calibration + qcom,calibration-data: $ref: /schemas/types.yaml#/definitions/uint8-array description: From 71bbe5d8b75503c3015803cab554a6d001992731 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 12 Jun 2026 15:20:56 +0200 Subject: [PATCH 4/9] dt-bindings: bluetooth: qcom: Add NVMEM BD address cell 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) Reviewed-by: Bartosz Golaszewski Signed-off-by: Loic Poulain --- .../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 c8e9c55c1afb4..7cb28f30c9af0 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 From 323fb61ce48ebaf3f11caa60bcd2aaf7cbd5ad7e Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Fri, 12 Jun 2026 15:20:57 +0200 Subject: [PATCH 5/9] block: implement NVMEM provider 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. For now, NVMEM is only registered for the whole disk block device, as the OF node is currently only associated to it. Signed-off-by: Daniel Golle Co-developed-by: Loic Poulain Signed-off-by: Loic Poulain --- block/Kconfig | 9 ++++ block/Makefile | 1 + block/blk-nvmem.c | 109 ++++++++++++++++++++++++++++++++++++++ block/blk.h | 8 +++ block/genhd.c | 4 ++ include/linux/blk_types.h | 3 ++ include/linux/blkdev.h | 1 + 7 files changed, 135 insertions(+) create mode 100644 block/blk-nvmem.c diff --git a/block/Kconfig b/block/Kconfig index 15027963472d7..0b33747e16dc3 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 7dce2e44276c4..d7ac389e71902 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 0000000000000..c005f059d9fe5 --- /dev/null +++ b/block/blk-nvmem.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * block device NVMEM provider + * + * Copyright (c) 2024 Daniel Golle + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * Useful on devices using a partition on an eMMC for MAC addresses or + * Wi-Fi calibration EEPROM data. + */ + +#include +#include +#include +#include +#include +#include + +#include "blk.h" + +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; + dev_t devt = (dev_t)(uintptr_t)priv; + size_t bytes_left = bytes; + loff_t pos = from; + int ret = 0; + + struct file *bdev_file __free(fput) = bdev_file_open_by_dev(devt, mode, priv, NULL); + if (IS_ERR(bdev_file)) + return PTR_ERR(bdev_file); + + while (bytes_left) { + pgoff_t f_index = pos >> PAGE_SHIFT; + struct folio *folio; + size_t folio_off; + size_t to_read; + + folio = read_mapping_folio(bdev_file->f_mapping, f_index, NULL); + if (IS_ERR(folio)) { + ret = PTR_ERR(folio); + break; + } + + folio_off = offset_in_folio(folio, pos); + to_read = min(bytes_left, folio_size(folio) - folio_off); + memcpy_from_folio(val, folio, folio_off, to_read); + pos += to_read; + bytes_left -= to_read; + val += to_read; + folio_put(folio); + } + + return ret; +} + +void blk_nvmem_add(struct block_device *bdev) +{ + struct device *dev = &bdev->bd_device; + struct nvmem_config config = {}; + + /* skip devices which do not have a device tree node */ + if (!dev_of_node(dev)) + return; + + /* skip devices without an nvmem layout defined */ + struct device_node *child __free(device_node) = + of_get_child_by_name(dev_of_node(dev), "nvmem-layout"); + if (!child) + return; + + /* + * skip block device too large to be represented as NVMEM devices, + * the NVMEM reg_read callback uses an unsigned int offset + */ + if (bdev_nr_bytes(bdev) > UINT_MAX) { + dev_warn(dev, "block device too large to be an NVMEM provider\n"); + return; + } + + config.id = NVMEM_DEVID_NONE; + config.dev = dev; + config.name = dev_name(dev); + config.owner = THIS_MODULE; + config.priv = (void *)(uintptr_t)dev->devt; + 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); + + bdev->bd_nvmem = nvmem_register(&config); + if (IS_ERR(bdev->bd_nvmem)) { + dev_err_probe(dev, PTR_ERR(bdev->bd_nvmem), + "Failed to register NVMEM device\n"); + bdev->bd_nvmem = NULL; + } +} + +void blk_nvmem_del(struct block_device *bdev) +{ + if (bdev->bd_nvmem) + nvmem_unregister(bdev->bd_nvmem); + + bdev->bd_nvmem = NULL; +} diff --git a/block/blk.h b/block/blk.h index b998a7761faf3..a3e6eecacce72 100644 --- a/block/blk.h +++ b/block/blk.h @@ -778,4 +778,12 @@ static inline void blk_debugfs_unlock(struct request_queue *q, memalloc_noio_restore(memflags); } +#ifdef CONFIG_BLK_NVMEM +void blk_nvmem_add(struct block_device *bdev); +void blk_nvmem_del(struct block_device *bdev); +#else +static inline void blk_nvmem_add(struct block_device *bdev) {} +static inline void blk_nvmem_del(struct block_device *bdev) {} +#endif + #endif /* BLK_INTERNAL_H */ diff --git a/block/genhd.c b/block/genhd.c index 7d6854fd28e95..1b2382de6fb30 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -421,6 +421,8 @@ static void add_disk_final(struct gendisk *disk) */ dev_set_uevent_suppress(ddev, 0); disk_uevent(disk, KOBJ_ADD); + + blk_nvmem_add(disk->part0); } blk_apply_bdi_limits(disk->bdi, &disk->queue->limits); @@ -704,6 +706,8 @@ static void __del_gendisk(struct gendisk *disk) disk_del_events(disk); + blk_nvmem_del(disk->part0); + /* * Prevent new openers by unlinked the bdev inode. */ diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index 8808ee76e73c0..ace6f59b860d0 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -73,6 +73,9 @@ struct block_device { int bd_writers; #ifdef CONFIG_SECURITY void *bd_security; +#endif +#ifdef CONFIG_BLK_NVMEM + struct nvmem_device *bd_nvmem; #endif /* * keep this out-of-line as it's both big and not needed in the fast diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 890128cdea1ce..f15d2b5bf9e4f 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -30,6 +30,7 @@ struct module; struct request_queue; +struct nvmem_device; struct elevator_queue; struct blk_trace; struct request; From 5ef4c1a85c7c15d8f6f639c83b7313d29398ecbc Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 12 Jun 2026 15:20:58 +0200 Subject: [PATCH 6/9] 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. 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. Reviewed-by: Bartosz Golaszewski Signed-off-by: Loic Poulain --- include/linux/of_net.h | 7 ++++++ net/core/of_net.c | 49 +++++++++++++++++++++++++++++++----------- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/include/linux/of_net.h b/include/linux/of_net.h index d88715a0b3a52..7854ba555d9a5 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 93ea425b9248a..11c1acca15126 100644 --- a/net/core/of_net.c +++ b/net/core/of_net.c @@ -61,9 +61,7 @@ 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; + u8 mac[ETH_ALEN] __aligned(sizeof(u16)); int ret; /* Try lookup by device first, there might be a nvmem_cell_lookup @@ -75,27 +73,54 @@ 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", mac); + if (ret) + return ret; + + if (!is_valid_ether_addr(mac)) + return -EINVAL; + + ether_addr_copy(addr, mac); + 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() From 9c853379862fa8fa18733a1855bceb88ff85cb64 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 12 Jun 2026 15:20:59 +0200 Subject: [PATCH 7/9] Bluetooth: hci_sync: Add NVMEM-backed BD address retrieval 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. Reviewed-by: Bartosz Golaszewski Signed-off-by: Loic Poulain --- 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 572b1c620c5d6..7686466d11092 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 df23245d6ccda..4cd2c2f8f929a 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -7,6 +7,7 @@ */ #include +#include #include #include @@ -3593,6 +3594,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 @@ -5050,12 +5082,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); From 626e7a8527c86d358b4313dd26dac38d1f20413b Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 12 Jun 2026 15:21:00 +0200 Subject: [PATCH 8/9] Bluetooth: qca: Set NVMEM BD address quirks when address is invalid 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. Reviewed-by: Bartosz Golaszewski Signed-off-by: Loic Poulain --- 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 dda76365726f0..df33eacfd29fa 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); From 48d53e9caa2e3cf4160d34cb451f3e88f9df219f Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 12 Jun 2026 15:21:01 +0200 Subject: [PATCH 9/9] arm64: dts: qcom: arduino-imola: Describe NVMEM layout for WiFi/BT addresses 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. Reviewed-by: Konrad Dybcio Reviewed-by: Bartosz Golaszewski Signed-off-by: Loic Poulain --- .../boot/dts/qcom/qrb2210-arduino-imola.dts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts b/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts index bf088fa9807f0..128c7a7e76b5b 100644 --- a/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts +++ b/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts @@ -409,7 +409,40 @@ no-sdio; no-sd; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + card@0 { + compatible = "mmc-card"; + reg = <0>; + + partitions-boot1 { + compatible = "fixed-partitions"; + + #address-cells = <1>; + #size-cells = <1>; + + 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 +545,9 @@ 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 +593,9 @@ qcom,ath10k-calibration-variant = "ArduinoImola"; firmware-name = "qcm2290"; + nvmem-cells = <&wifi_mac_addr 0>; + nvmem-cell-names = "mac-address"; + status = "okay"; };