Re: [PATCH v6 5/9] block: implement NVMEM provider
Bartosz Golaszewski <[email protected]> Tue, 30 Jun 2026 07:59:07 +0000
| Newsgroups | org.infradead.lists.ath10k,org.kernel.vger.linux-arm-msm,org.kernel.vger.linux-block,org.kernel.vger.linux-bluetooth,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel,org.kernel.vger.linux-mmc,org.kernel.vger.linux-wireless,org.kernel.vger.netdev |
|---|---|
| Message-ID | <CAMRc=McvGOyMrK8vMU=6CZi1-p3=aVy8S3UbN5etJ9uKdNEP5g@mail.gmail.com> |
On Mon, 29 Jun 2026 10:55:24 +0200, Loic Poulain <[email protected]> said: > From: Daniel Golle <[email protected]> > > 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 <[email protected]> > Co-developed-by: Loic Poulain <[email protected]> > Signed-off-by: Loic Poulain <[email protected]> > --- > block/Kconfig | 9 ++++ > block/Makefile | 1 + > block/blk-nvmem.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++ > block/blk.h | 8 ++++ > block/genhd.c | 4 ++ > include/linux/blk_types.h | 3 ++ > include/linux/blkdev.h | 1 + > 7 files changed, 137 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..4b35a74255059320ef0cbd3c0003f1510bae5733 > --- /dev/null > +++ b/block/blk-nvmem.c > @@ -0,0 +1,111 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * block device NVMEM provider > + * > + * Copyright (c) 2024 Daniel Golle <[email protected]> > + * 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. > + */ > + Add linux/cleanup.h for __free() and linux/device.h for dev_err_probe(). > +#include <linux/file.h> > +#include <linux/nvmem-provider.h> > +#include <linux/nvmem-consumer.h> > +#include <linux/of.h> > +#include <linux/pagemap.h> > +#include <linux/property.h> > + > +#include "blk.h" > + > +static int blk_nvmem_reg_read(void *priv, unsigned int from, void *val, size_t bytes) > +{ > + 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, BLK_OPEN_READ, NULL, 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; > +} > + > +int 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 0; > + > + /* 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 0; > + > + /* > + * skip block device too large to be represented as NVMEM devices, > + * nvmem_config.size is a signed int > + */ > + if (bdev_nr_bytes(bdev) > INT_MAX) { > + dev_warn(dev, "block device too large to be an NVMEM provider\n"); > + return 0; > + } > + > + 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)) { > + int ret = PTR_ERR(bdev->bd_nvmem); > + > + bdev->bd_nvmem = NULL; > + dev_err_probe(dev, ret, "Failed to register NVMEM device\n"); > + return ret; Just do return dev_err_probe(). > + } > + > + return 0; > +} > + > +void blk_nvmem_del(struct block_device *bdev) > +{ > + nvmem_unregister(bdev->bd_nvmem); > + bdev->bd_nvmem = NULL; > +} > diff --git a/block/blk.h b/block/blk.h > index ec4674cdf2ead4fd259ff5fc42401f591e684ee9..ed0c10168ba7be10855509637f824a9cea2b9ccb 100644 > --- a/block/blk.h > +++ b/block/blk.h > @@ -757,4 +757,12 @@ static inline void blk_debugfs_unlock(struct request_queue *q, > memalloc_noio_restore(memflags); > } > > +#ifdef CONFIG_BLK_NVMEM > +int blk_nvmem_add(struct block_device *bdev); > +void blk_nvmem_del(struct block_device *bdev); > +#else > +static inline int blk_nvmem_add(struct block_device *bdev) { return 0; } > +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 7d6854fd28e95ae9134309679a7c6a937f5b7db8..1b2382de6fb30c1e5f60f45c04dc03ed3bf5d5f2 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 8808ee76e73c09e0ceaac41ba59e86fb0c4efc64..ace6f59b860d0813665b2f62a1c03a1f4be94059 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 890128cdea1ce66863c5baa36f3b336ec4550807..f15d2b5bf9e4fd2368b8a70416a978e22c0d4333 100644 > --- a/include/linux/blkdev.h > +++ b/include/linux/blkdev.h > @@ -30,6 +30,7 @@ > > struct module; > struct request_queue; > +struct nvmem_device; Why is the forward declaration here and not in blk_types.h where it's needed? > struct elevator_queue; > struct blk_trace; > struct request; > > -- > 2.34.1 > > With the above nits addressed, LGTM: Reviewed-by: Bartosz Golaszewski <[email protected]> Bart