Skip to contentSkip to conversation

nvmem: implement block NVMEM provider

PATCH Superseded Daniel Golle 2 participants 11 messages May 30, 2024 – Jun 13, 2024

Subsystems (4)

v2 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

v2 cover nvmem: implement block NVMEM provider

On embedded devices using an eMMC it is common that one or more (hw/sw) partitions on the eMMC are used to store MAC addresses and Wi-Fi calibration EEPROM data.

Implement an NVMEM provider backed by a block device as typically the NVMEM framework is used to have kernel drivers read and use binary data from EEPROMs, efuses, flash memory (MTD), ...

In order to be able to reference hardware partitions on an eMMC, add code to bind each hardware partition to a specific firmware subnode.

Overall, this enables uniform handling across practially all flash storage types used for this purpose (MTD, UBI, and now also MMC or and in future may also other block devices).

As part of this series it was necessary to define a device tree schema for block devices and partitions on them, which (similar to how it now works also for UBI volumes) can be matched by one or more properties.

---
This series has previously been submitted as RFC on July 19th 2023[1] and most of the basic idea did not change since. Another round of RFC was submitted on March 5th 2024[2].

Changes since v1 sent on March 21st 2024 [3]:
- introduce notifications for block device addition and removal for in-kernel users. This allows the nvmem driver to be built as a module and avoids using class_interface and block subsystem internals as suggested in https://patchwork.kernel.org/comment/25771998/ and
https://patchwork.kernel.org/comment/25770441/

[1]: https://patchwork.kernel.org/project/linux-block/list/?series=767565
[2]: https://patchwork.kernel.org/project/linux-block/list/?series=832705
[3]: https://patchwork.kernel.org/project/linux-block/list/?series=837150&archive=both

Daniel Golle (9):
dt-bindings: block: add basic bindings for block devices
block: partitions: populate fwnode
block: add support for notifications
block: add new genhd flag GENHD_FL_NVMEM
nvmem: implement block NVMEM provider
dt-bindings: mmc: mmc-card: add block device nodes
mmc: core: set card fwnode_handle
mmc: block: set fwnode of disk devices
mmc: block: set GENHD_FL_NVMEM

 .../bindings/block/block-device.yaml          |  22 ++
 .../devicetree/bindings/block/partition.yaml  |  51 +++++
 .../devicetree/bindings/block/partitions.yaml |  20 ++
 .../devicetree/bindings/mmc/mmc-card.yaml     |  45 ++++
 block/Kconfig                                 |   6 +
 block/Makefile                                |   1 +
 block/blk-notify.c                            |  88 ++++++++
 block/partitions/core.c                       |  41 ++++
 drivers/mmc/core/block.c                      |   8 +
 drivers/mmc/core/bus.c                        |   2 +
 drivers/nvmem/Kconfig                         |  11 +
 drivers/nvmem/Makefile                        |   2 +
 drivers/nvmem/block.c                         | 198 ++++++++++++++++++
 include/linux/blkdev.h                        |  10 +
 14 files changed, 505 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/block/block-device.yaml
 create mode 100644 Documentation/devicetree/bindings/block/partition.yaml
 create mode 100644 Documentation/devicetree/bindings/block/partitions.yaml
 create mode 100644 block/blk-notify.c
 create mode 100644 drivers/nvmem/block.c
Signature · 1 line
2.45.1
#1 lore raw

PATCH v2 1/9 dt-bindings: block: add basic bindings for block devices

Add bindings for block devices which are used to allow referencing nvmem bits on them.

---
 .../bindings/block/block-device.yaml          | 22 ++++++++
 .../devicetree/bindings/block/partition.yaml  | 51 +++++++++++++++++++
 .../devicetree/bindings/block/partitions.yaml | 20 ++++++++
 3 files changed, 93 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/block/block-device.yaml
 create mode 100644 Documentation/devicetree/bindings/block/partition.yaml
 create mode 100644 Documentation/devicetree/bindings/block/partitions.yaml
Documentation/devicetree/bindings/block/block-device.yaml new file +22 -0
new file mode 100644
index 000000000000..c83ea525650b
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/block/block-device.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: block storage device
+
+description: |
+  This binding is generic and describes a block-oriented storage device.
+
+maintainers:
+  - Daniel Golle <[email protected]>
+
+properties:
+  partitions:
+    $ref: /schemas/block/partitions.yaml
+
+  nvmem-layout:
+    $ref: /schemas/nvmem/layouts/nvmem-layout.yaml#
+
+unevaluatedProperties: false
Documentation/devicetree/bindings/block/partition.yaml new file +51 -0

51 lines not on this page · raw message

Documentation/devicetree/bindings/block/partitions.yaml new file +20 -0
new file mode 100644
index 000000000000..fd84c3ba8493
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/block/partitions.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Partitions on block devices
+
+description: |
+  This binding is generic and describes the content of the partitions container
+  node.
+
+maintainers:
+  - Daniel Golle <[email protected]>
+
+patternProperties:
+  "^block-partition-.+$":
+    $ref: partition.yaml
+
+unevaluatedProperties: false
Signature · 1 line
2.45.1
#2 lore raw

PATCH v2 2/9 block: partitions: populate fwnode

In reply to Daniel Golle

Let block partitions to be represented by a firmware node and hence allow them to being referenced e.g. for use with blk-nvmem.

---
 block/partitions/core.c | 41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)
block/partitions/core.c +41 -0
index ab76e64f0f6c..93d109409905 100644
@@ -10,6 +10,8 @@
 #include <linux/ctype.h>
 #include <linux/vmalloc.h>
 #include <linux/raid/detect.h>
+#include <linux/property.h>
+
 #include "check.h"
 
 static int (*const check_part[])(struct parsed_partitions *) = {
@@ -281,6 +283,43 @@ static ssize_t whole_disk_show(struct device *dev,
 }
 static const DEVICE_ATTR(whole_disk, 0444, whole_disk_show, NULL);
 
+static struct fwnode_handle *find_partition_fwnode(struct block_device *bdev)
+{
+	struct fwnode_handle *fw_parts, *fw_part;
+	struct device *ddev = disk_to_dev(bdev->bd_disk);
+	const char *partname, *uuid;
+	u32 partno;
+
+	fw_parts = device_get_named_child_node(ddev, "partitions");
+	if (!fw_parts)
+		fw_parts = device_get_named_child_node(ddev->parent, "partitions");
+
+	if (!fw_parts)
+		return NULL;
+
+	fwnode_for_each_child_node(fw_parts, fw_part) {
+		if (!fwnode_property_read_string(fw_part, "uuid", &uuid) &&
+		    (!bdev->bd_meta_info || strncmp(uuid,
+						    bdev->bd_meta_info->uuid,
+						    PARTITION_META_INFO_UUIDLTH)))
+			continue;
+
+		if (!fwnode_property_read_string(fw_part, "partname", &partname) &&
+		    (!bdev->bd_meta_info || strncmp(partname,
+						    bdev->bd_meta_info->volname,
+						    PARTITION_META_INFO_VOLNAMELTH)))
+			continue;
+
+		if (!fwnode_property_read_u32(fw_part, "partno", &partno) &&
+		    bdev_partno(bdev) != partno)
+			continue;
+
+		return fw_part;
+	}
+
+	return NULL;
+}
+
 /*
  * Must be called either with open_mutex held, before a disk can be opened or
  * after all disk users are gone.
@@ -355,6 +394,8 @@ static struct block_device *add_partition(struct gendisk *disk, int partno,
 			goto out_put;
 	}
 
+	device_set_node(pdev, find_partition_fwnode(bdev));
+
 	/* delay uevent until 'holders' subdir is created */
 	dev_set_uevent_suppress(pdev, 1);
 	err = device_add(pdev);
Signature · 1 line
2.45.1
#3 lore raw

Re: [PATCH v2 2/9] block: partitions: populate fwnode

Hi Daniel,

Quoted 25 lines · Daniel Golle

Let block partitions to be represented by a firmware node and hence allow them to being referenced e.g. for use with blk-nvmem.

---
  block/partitions/core.c | 41 +++++++++++++++++++++++++++++++++++++++++
  1 file changed, 41 insertions(+)
block/partitions/core.c +2 -0
index ab76e64f0f6c..93d109409905 100644
@@ -10,6 +10,8 @@
  #include <linux/ctype.h>
  #include <linux/vmalloc.h>
  #include <linux/raid/detect.h>
+#include <linux/property.h>
+
  #include "check.h"
 
  static int (*const check_part[])(struct parsed_partitions *) = {
@@ -281,6 +283,43 @@ static ssize_t whole_disk_show(struct device *dev,
  }
  static const DEVICE_ATTR(whole_disk, 0444, whole_disk_show, NULL);
 

block/partitions/core.c static ssize_t whole_disk_show(struct device *dev, · line 295

Quoted 10 lines
block/partitions/core.c +10 -0
+static struct fwnode_handle *find_partition_fwnode(struct block_device *bdev)
+{
+	struct fwnode_handle *fw_parts, *fw_part;
+	struct device *ddev = disk_to_dev(bdev->bd_disk);
+	const char *partname, *uuid;
+	u32 partno;
+
+	fw_parts = device_get_named_child_node(ddev, "partitions");
+	if (!fw_parts)
+		fw_parts = device_get_named_child_node(ddev->parent, "partitions");

Could you please explain why you try to get the node from the device and the parent when it is not available? I think this should go into the commit description.

block/partitions/core.c static ssize_t whole_disk_show(struct device *dev, · line 304

Quoted 9 lines
block/partitions/core.c +9 -0
+
+	if (!fw_parts)
+		return NULL;
+
+	fwnode_for_each_child_node(fw_parts, fw_part) {
+		if (!fwnode_property_read_string(fw_part, "uuid", &uuid) &&
+		    (!bdev->bd_meta_info || strncmp(uuid,
+						    bdev->bd_meta_info->uuid,
+						    PARTITION_META_INFO_UUIDLTH)))

I think you should check that the uuid provided in the device tree is not longer than PARTITION_META_INFO_UUIDLTH. Same for the volume name.

Quoted 22 lines
block/partitions/core.c +18 -0
+			continue;
+
+		if (!fwnode_property_read_string(fw_part, "partname", &partname) &&
+		    (!bdev->bd_meta_info || strncmp(partname,
+						    bdev->bd_meta_info->volname,
+						    PARTITION_META_INFO_VOLNAMELTH)))
+			continue;
+
+		if (!fwnode_property_read_u32(fw_part, "partno", &partno) &&
+		    bdev_partno(bdev) != partno)
+			continue;
+
+		return fw_part;
+	}
+
+	return NULL;
+}
+
  /*
   * Must be called either with open_mutex held, before a disk can be opened or
   * after all disk users are gone.

block/partitions/core.c static struct block_device *add_partition(struct gendisk *disk, int partno, · line 401

Quoted 8 lines
block/partitions/core.c +2 -0
@@ -355,6 +394,8 @@ static struct block_device *add_partition(struct gendisk *disk, int partno,
  			goto out_put;
  	}
 
+	device_set_node(pdev, find_partition_fwnode(bdev));
+
  	/* delay uevent until 'holders' subdir is created */
  	dev_set_uevent_suppress(pdev, 1);
  	err = device_add(pdev);

Hauke

#4 lore raw

PATCH v2 3/9 block: add support for notifications

In reply to Daniel Golle

Add notifier block to notify other subsystems about the addition or removal of block devices.

---
 block/Kconfig          |  6 +++
 block/Makefile         |  1 +
 block/blk-notify.c     | 88 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/blkdev.h |  8 ++++
 4 files changed, 103 insertions(+)
 create mode 100644 block/blk-notify.c
block/Kconfig +6 -0
index dc12af58dbae..b9a28ecd408f 100644
@@ -213,6 +213,12 @@ config BLK_INLINE_ENCRYPTION_FALLBACK
 	  by falling back to the kernel crypto API when inline
 	  encryption hardware is not present.
 
+config BLOCK_NOTIFIERS
+	bool "Enable support for notifications in block layer"
+	help
+	  Enable this option to provide notifiers for other subsystems
+	  upon addition or removal of block devices.
+
 source "block/partitions/Kconfig"
 
 config BLK_MQ_PCI
block/Makefile +1 -0
index 168150b9c510..54ff8aded85e 100644
@@ -39,3 +39,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_BLOCK_NOTIFIERS) += blk-notify.o
block/blk-notify.c new file +88 -0

88 lines not on this page · raw message

include/linux/blkdev.h +8 -0
index aefdda9f4ec7..0a8c3478a462 100644
@@ -1604,4 +1604,12 @@ struct io_comp_batch {
 
 #define DEFINE_IO_COMP_BATCH(name)	struct io_comp_batch name = { }
 
+
+#ifdef CONFIG_BLOCK_NOTIFIERS
+#define BLK_DEVICE_ADD		1
+#define BLK_DEVICE_REMOVE	2
+void blk_register_notify(struct notifier_block *nb);
+void blk_unregister_notify(struct notifier_block *nb);
+#endif
+
 #endif /* _LINUX_BLKDEV_H */
Signature · 1 line
2.45.1
#5 lore raw

PATCH v2 4/9 block: add new genhd flag GENHD_FL_NVMEM

In reply to Daniel Golle

Add new flag to destinguish block devices which may act as an NVMEM provider.

---
 include/linux/blkdev.h | 2 ++
 1 file changed, 2 insertions(+)
include/linux/blkdev.h +2 -0
index 0a8c3478a462..c3365856e67b 100644
@@ -81,11 +81,13 @@ struct partition_meta_info {
  * ``GENHD_FL_NO_PART``: partition support is disabled.  The kernel will not
  * scan for partitions from add_disk, and users can't add partitions manually.
  *
+ * ``GENHD_FL_NVMEM``: the block device should be considered as NVMEM provider.
  */
 enum {
 	GENHD_FL_REMOVABLE			= 1 << 0,
 	GENHD_FL_HIDDEN				= 1 << 1,
 	GENHD_FL_NO_PART			= 1 << 2,
+	GENHD_FL_NVMEM				= 1 << 3,
 };
 
 enum {
Signature · 1 line
2.45.1
#6 lore raw

PATCH v2 5/9 nvmem: implement block NVMEM provider

In reply to Daniel Golle

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 any block device or partition in Device Tree to allow e.g. Ethernet and Wi-Fi drivers accessing them via the NVMEM layer.

---
 drivers/nvmem/Kconfig  |  11 +++
 drivers/nvmem/Makefile |   2 +
 drivers/nvmem/block.c  | 198 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 211 insertions(+)
 create mode 100644 drivers/nvmem/block.c
drivers/nvmem/Kconfig +11 -0
index 283134498fbc..1d6df20301a4 100644
@@ -40,6 +40,17 @@ config NVMEM_APPLE_EFUSES
 	  This driver can also be built as a module. If so, the module will
 	  be called nvmem-apple-efuses.
 
+config NVMEM_BLOCK
+	tristate "Block device NVMEM provider"
+	depends on BLOCK
+	depends on OF
+	depends on NVMEM
+	select BLOCK_NOTIFIERS
+	help
+	  Allow block devices (or partitions) to act as NVMEM prodivers,
+	  typically used with eMMC to store MAC addresses or Wi-Fi
+	  calibration data on embedded devices.
+
 config NVMEM_BCM_OCOTP
 	tristate "Broadcom On-Chip OTP Controller support"
 	depends on ARCH_BCM_IPROC || COMPILE_TEST
drivers/nvmem/Makefile +2 -0
index cdd01fbf1313..68fcf845f417 100644
@@ -14,6 +14,8 @@ obj-$(CONFIG_NVMEM_APPLE_EFUSES)	+= nvmem-apple-efuses.o
 nvmem-apple-efuses-y 			:= apple-efuses.o
 obj-$(CONFIG_NVMEM_BCM_OCOTP)		+= nvmem-bcm-ocotp.o
 nvmem-bcm-ocotp-y			:= bcm-ocotp.o
+obj-$(CONFIG_NVMEM_BLOCK)		+= nvmem-block.o
+nvmem-block-y				:= block.o
 obj-$(CONFIG_NVMEM_BRCM_NVRAM)		+= nvmem_brcm_nvram.o
 nvmem_brcm_nvram-y			:= brcm_nvram.o
 obj-$(CONFIG_NVMEM_IMX_IIM)		+= nvmem-imx-iim.o
drivers/nvmem/block.c new file +197 -0

197 lines not on this page · raw message

Signature · 1 line
2.45.1
#7 lore raw

PATCH v2 6/9 dt-bindings: mmc: mmc-card: add block device nodes

In reply to Daniel Golle

Add nodes representing the block devices exposed by an MMC device including an example involving nvmem-cells.

---
 .../devicetree/bindings/mmc/mmc-card.yaml     | 45 +++++++++++++++++++
 1 file changed, 45 insertions(+)
Documentation/devicetree/bindings/mmc/mmc-card.yaml +45 -0
index fd347126449a..95ccbda871d2 100644
@@ -26,6 +26,18 @@ properties:
       Use this to indicate that the mmc-card has a broken hpi
       implementation, and that hpi should not be used.
 
+  block:
+    $ref: /schemas/block/block-device.yaml#
+    description:
+      Represents the block storage provided by an SD card or the
+      main hardware partition of an eMMC.
+
+patternProperties:
+  '^boot[0-9]+':
+    $ref: /schemas/block/block-device.yaml#
+    description:
+      Represents a boot hardware partition on an eMMC.
+
 required:
   - compatible
   - reg
@@ -42,6 +54,39 @@ examples:
             compatible = "mmc-card";
             reg = <0>;
             broken-hpi;
+
+            block {
+                partitions {
+                    cal_data: block-partition-rf {
+                        partnum = <3>;
+                        partname = "rf";
+
+                        nvmem-layout {
+                            compatible = "fixed-layout";
+                            #address-cells = <1>;
+                            #size-cells = <1>;
+
+                            eeprom@0 {
+                                reg = <0x0 0x1000>;
+                            };
+                        };
+                    };
+                };
+            };
+
+            boot1 {
+                nvmem-layout {
+                    compatible = "fixed-layout";
+                    #address-cells = <1>;
+                    #size-cells = <1>;
+
+                    macaddr: macaddr@a {
+                        compatible = "mac-base";
+                        reg = <0xa 0x6>;
+                        #nvmem-cell-cells = <1>;
+                    };
+                };
+            };
         };
     };
 
Signature · 1 line
2.45.1
#8 lore raw

PATCH v2 7/9 mmc: core: set card fwnode_handle

In reply to Daniel Golle

Set fwnode in case it isn't set yet and of_node is present.

---
 drivers/mmc/core/bus.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/mmc/core/bus.c +2 -0
index 0ddaee0eae54..e1c5fc1b3ce4 100644
@@ -364,6 +364,8 @@ int mmc_add_card(struct mmc_card *card)
 
 	mmc_add_card_debugfs(card);
 	card->dev.of_node = mmc_of_find_child_device(card->host, 0);
+	if (card->dev.of_node && !card->dev.fwnode)
+		card->dev.fwnode = &card->dev.of_node->fwnode;
 
 	device_enable_async_suspend(&card->dev);
 
Signature · 1 line
2.45.1
#9 lore raw

PATCH v2 8/9 mmc: block: set fwnode of disk devices

In reply to Daniel Golle

Set fwnode of disk devices to 'block', 'boot0' and 'boot1' subnodes of the mmc-card. This is done in preparation for having the eMMC act as NVMEM provider.

---
 drivers/mmc/core/block.c | 7 +++++++
 1 file changed, 7 insertions(+)
drivers/mmc/core/block.c +7 -0
index 367509b5b646..cd3ee6d69aaa 100644
@@ -2463,6 +2463,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
 					      int area_type,
 					      unsigned int part_type)
 {
+	struct fwnode_handle *fwnode;
 	struct mmc_blk_data *md;
 	int devidx, ret;
 	char cap_str[10];
@@ -2559,6 +2560,12 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
 
 	blk_queue_write_cache(md->queue.queue, cache_enabled, fua_enabled);
 
+	fwnode = device_get_named_child_node(subname ? md->parent->parent :
+						       md->parent,
+					     subname ? subname : "block");
+	if (fwnode)
+		device_set_node(disk_to_dev(md->disk), fwnode);
+
 	string_get_size((u64)size, 512, STRING_UNITS_2,
 			cap_str, sizeof(cap_str));
 	pr_info("%s: %s %s %s%s\n",
Signature · 1 line
2.45.1
#10 lore raw

PATCH v2 9/9 mmc: block: set GENHD_FL_NVMEM

In reply to Daniel Golle

Set flag to consider MMC block devices as NVMEM providers.

---
 drivers/mmc/core/block.c | 1 +
 1 file changed, 1 insertion(+)
drivers/mmc/core/block.c +1 -0
index cd3ee6d69aaa..577e7ae5df14 100644
@@ -2516,6 +2516,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
 	md->disk->major	= MMC_BLOCK_MAJOR;
 	md->disk->minors = perdev_minors;
 	md->disk->first_minor = devidx * perdev_minors;
+	md->disk->flags = GENHD_FL_NVMEM;
 	md->disk->fops = &mmc_bdops;
 	md->disk->private_data = md;
 	md->parent = parent;
Signature · 1 line
2.45.1