static inline int mmc_blk_part_switch(struct mmc_card *card,
unsigned int part_type);
static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
-@@ -3040,6 +3047,8 @@ static int mmc_blk_probe(struct mmc_card
+@@ -3049,6 +3056,8 @@ static int mmc_blk_probe(struct mmc_card
{
struct mmc_blk_data *md;
int ret = 0;
/*
* Check that the card supports the command class(es) we need.
-@@ -3047,7 +3056,16 @@ static int mmc_blk_probe(struct mmc_card
+@@ -3056,7 +3065,16 @@ static int mmc_blk_probe(struct mmc_card
if (!(card->csd.cmdclass & CCC_BLOCK_READ))
return -ENODEV;
card->complete_wq = alloc_workqueue("mmc_complete",
WQ_MEM_RECLAIM | WQ_HIGHPRI, 0);
-@@ -3062,6 +3080,17 @@ static int mmc_blk_probe(struct mmc_card
+@@ -3071,6 +3089,17 @@ static int mmc_blk_probe(struct mmc_card
goto out_free;
}
# CONFIG_BLK_DEV_VIA82CXXX is not set
# CONFIG_BLK_DEV_ZONED is not set
# CONFIG_BLK_INLINE_ENCRYPTION is not set
+# CONFIG_BLK_NVMEM is not set
# CONFIG_BLK_SED_OPAL is not set
# CONFIG_BLK_WBT is not set
CONFIG_BLOCK=y
# CONFIG_MTD_UBI is not set
# CONFIG_MTD_UBI_FASTMAP is not set
# CONFIG_MTD_UBI_GLUEBI is not set
+# CONFIG_MTD_UBI_NVMEM is not set
# CONFIG_MTD_UIMAGE_SPLIT is not set
# CONFIG_MTD_VIRT_CONCAT is not set
# CONFIG_MTK_DEVAPC is not set
--- /dev/null
+From ffbbe7d66872ff8957dad2136133e28a1fd5d437 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Mon, 7 Aug 2023 22:51:05 +0100
+Subject: [PATCH 01/15] dt-bindings: mtd: add basic bindings for UBI
+
+Add basic bindings for UBI devices and volumes.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ .../bindings/mtd/partitions/linux,ubi.yaml | 65 +++++++++++++++++++
+ .../bindings/mtd/partitions/ubi-volume.yaml | 35 ++++++++++
+ 2 files changed, 100 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml
+ create mode 100644 Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml
+@@ -0,0 +1,65 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/mtd/partitions/linux,ubi.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Unsorted Block Images
++
++description: |
++ UBI ("Unsorted Block Images") is a volume management system for raw
++ flash devices which manages multiple logical volumes on a single
++ physical flash device and spreads the I/O load (i.e wear-leveling)
++ across the whole flash chip.
++
++maintainers:
++ - Daniel Golle <daniel@makrotopia.org>
++
++allOf:
++ - $ref: partition.yaml#
++
++properties:
++ compatible:
++ const: linux,ubi
++
++ volumes:
++ type: object
++ description: UBI Volumes
++
++ patternProperties:
++ "^ubi-volume-.*$":
++ $ref: /schemas/mtd/partitions/ubi-volume.yaml#
++
++ unevaluatedProperties: false
++
++required:
++ - compatible
++
++unevaluatedProperties: false
++
++examples:
++ - |
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ partition@0 {
++ reg = <0x0 0x100000>;
++ label = "bootloader";
++ read-only;
++ };
++
++ partition@100000 {
++ reg = <0x100000 0x1ff00000>;
++ label = "ubi";
++ compatible = "linux,ubi";
++
++ volumes {
++ ubi-volume-caldata {
++ volid = <2>;
++ volname = "rf";
++ };
++ };
++ };
++ };
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml
+@@ -0,0 +1,35 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/mtd/partitions/ubi-volume.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: UBI volume
++
++description: |
++ This binding describes a single UBI volume. Volumes can be matches either
++ by their ID or their name, or both.
++
++maintainers:
++ - Daniel Golle <daniel@makrotopia.org>
++
++properties:
++ volid:
++ $ref: "/schemas/types.yaml#/definitions/uint32"
++ description:
++ Match UBI volume ID
++
++ volname:
++ $ref: "/schemas/types.yaml#/definitions/string"
++ description:
++ Match UBI volume ID
++
++anyOf:
++ - required:
++ - volid
++
++ - required:
++ - volname
++
++# This is a generic file other binding inherit from and extend
++additionalProperties: true
--- /dev/null
+From e4dad3aa5c3ab9c553555dd23c0b85f725f2eb51 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Mon, 7 Aug 2023 22:53:01 +0100
+Subject: [PATCH 02/15] dt-bindings: mtd: ubi-volume: allow UBI volumes to
+ provide NVMEM
+
+UBI volumes may be used to contain NVMEM bits, typically device MAC
+addresses or wireless radio calibration data.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ .../devicetree/bindings/mtd/partitions/linux,ubi.yaml | 10 ++++++++++
+ .../devicetree/bindings/mtd/partitions/ubi-volume.yaml | 5 +++++
+ 2 files changed, 15 insertions(+)
+
+--- a/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml
++++ b/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml
+@@ -59,6 +59,16 @@ examples:
+ ubi-volume-caldata {
+ volid = <2>;
+ volname = "rf";
++
++ nvmem-layout {
++ compatible = "fixed-layout";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ eeprom@0 {
++ reg = <0x0 0x1000>;
++ };
++ };
+ };
+ };
+ };
+--- a/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml
++++ b/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml
+@@ -24,6 +24,11 @@ properties:
+ description:
+ Match UBI volume ID
+
++ nvmem-layout:
++ $ref: /schemas/nvmem/layouts/nvmem-layout.yaml#
++ description:
++ This container may reference an NVMEM layout parser.
++
+ anyOf:
+ - required:
+ - volid
--- /dev/null
+From e5cf19bd8204925f3bd2067df9e867313eac388b Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Mon, 1 May 2023 11:57:51 +0100
+Subject: [PATCH 03/15] mtd: ubi: block: use notifier to create ubiblock from
+ parameter
+
+Use UBI_VOLUME_ADDED notification to create ubiblock device specified
+on kernel cmdline or module parameter.
+This makes thing more simple and has the advantage that ubiblock devices
+on volumes which are not present at the time the ubi module is probed
+will still be created.
+
+Suggested-by: Zhihao Cheng <chengzhihao1@huawei.com>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mtd/ubi/block.c | 154 ++++++++++++++++++++++------------------
+ 1 file changed, 85 insertions(+), 69 deletions(-)
+
+--- a/drivers/mtd/ubi/block.c
++++ b/drivers/mtd/ubi/block.c
+@@ -33,6 +33,7 @@
+ #include <linux/kernel.h>
+ #include <linux/list.h>
+ #include <linux/mutex.h>
++#include <linux/namei.h>
+ #include <linux/slab.h>
+ #include <linux/mtd/ubi.h>
+ #include <linux/workqueue.h>
+@@ -67,10 +68,10 @@ struct ubiblock_pdu {
+ };
+
+ /* Numbers of elements set in the @ubiblock_param array */
+-static int ubiblock_devs __initdata;
++static int ubiblock_devs;
+
+ /* MTD devices specification parameters */
+-static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES] __initdata;
++static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES];
+
+ struct ubiblock {
+ struct ubi_volume_desc *desc;
+@@ -504,7 +505,7 @@ int ubiblock_remove(struct ubi_volume_in
+ }
+
+ /* Found a device, let's lock it so we can check if it's busy */
+- mutex_lock(&dev->dev_mutex);
++ mutex_lock_nested(&dev->dev_mutex, SINGLE_DEPTH_NESTING);
+ if (dev->refcnt > 0) {
+ ret = -EBUSY;
+ goto out_unlock_dev;
+@@ -567,6 +568,85 @@ static int ubiblock_resize(struct ubi_vo
+ return 0;
+ }
+
++static bool
++match_volume_desc(struct ubi_volume_info *vi, const char *name, int ubi_num, int vol_id)
++{
++ int err, len;
++ struct path path;
++ struct kstat stat;
++
++ if (ubi_num == -1) {
++ /* No ubi num, name must be a vol device path */
++ err = kern_path(name, LOOKUP_FOLLOW, &path);
++ if (err)
++ return false;
++
++ err = vfs_getattr(&path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT);
++ path_put(&path);
++ if (err)
++ return false;
++
++ if (!S_ISCHR(stat.mode))
++ return false;
++
++ if (vi->ubi_num != ubi_major2num(MAJOR(stat.rdev)))
++ return false;
++
++ if (vi->vol_id != MINOR(stat.rdev) - 1)
++ return false;
++
++ return true;
++ }
++
++ if (vol_id == -1) {
++ if (vi->ubi_num != ubi_num)
++ return false;
++
++ len = strnlen(name, UBI_VOL_NAME_MAX + 1);
++ if (len < 1 || vi->name_len != len)
++ return false;
++
++ if (strcmp(name, vi->name))
++ return false;
++
++ return true;
++ }
++
++ if (vi->ubi_num != ubi_num)
++ return false;
++
++ if (vi->vol_id != vol_id)
++ return false;
++
++ return true;
++}
++
++static void
++ubiblock_create_from_param(struct ubi_volume_info *vi)
++{
++ int i, ret = 0;
++ struct ubiblock_param *p;
++
++ /*
++ * Iterate over ubiblock cmdline parameters. If a parameter matches the
++ * newly added volume create the ubiblock device for it.
++ */
++ for (i = 0; i < ubiblock_devs; i++) {
++ p = &ubiblock_param[i];
++
++ if (!match_volume_desc(vi, p->name, p->ubi_num, p->vol_id))
++ continue;
++
++ ret = ubiblock_create(vi);
++ if (ret) {
++ pr_err(
++ "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n",
++ vi->name, p->ubi_num, p->vol_id, ret);
++ }
++ break;
++ }
++}
++
+ static int ubiblock_notify(struct notifier_block *nb,
+ unsigned long notification_type, void *ns_ptr)
+ {
+@@ -574,10 +654,7 @@ static int ubiblock_notify(struct notifi
+
+ switch (notification_type) {
+ case UBI_VOLUME_ADDED:
+- /*
+- * We want to enforce explicit block device creation for
+- * volumes, so when a volume is added we do nothing.
+- */
++ ubiblock_create_from_param(&nt->vi);
+ break;
+ case UBI_VOLUME_REMOVED:
+ ubiblock_remove(&nt->vi);
+@@ -603,56 +680,6 @@ static struct notifier_block ubiblock_no
+ .notifier_call = ubiblock_notify,
+ };
+
+-static struct ubi_volume_desc * __init
+-open_volume_desc(const char *name, int ubi_num, int vol_id)
+-{
+- if (ubi_num == -1)
+- /* No ubi num, name must be a vol device path */
+- return ubi_open_volume_path(name, UBI_READONLY);
+- else if (vol_id == -1)
+- /* No vol_id, must be vol_name */
+- return ubi_open_volume_nm(ubi_num, name, UBI_READONLY);
+- else
+- return ubi_open_volume(ubi_num, vol_id, UBI_READONLY);
+-}
+-
+-static void __init ubiblock_create_from_param(void)
+-{
+- int i, ret = 0;
+- struct ubiblock_param *p;
+- struct ubi_volume_desc *desc;
+- struct ubi_volume_info vi;
+-
+- /*
+- * If there is an error creating one of the ubiblocks, continue on to
+- * create the following ubiblocks. This helps in a circumstance where
+- * the kernel command-line specifies multiple block devices and some
+- * may be broken, but we still want the working ones to come up.
+- */
+- for (i = 0; i < ubiblock_devs; i++) {
+- p = &ubiblock_param[i];
+-
+- desc = open_volume_desc(p->name, p->ubi_num, p->vol_id);
+- if (IS_ERR(desc)) {
+- pr_err(
+- "UBI: block: can't open volume on ubi%d_%d, err=%ld\n",
+- p->ubi_num, p->vol_id, PTR_ERR(desc));
+- continue;
+- }
+-
+- ubi_get_volume_info(desc, &vi);
+- ubi_close_volume(desc);
+-
+- ret = ubiblock_create(&vi);
+- if (ret) {
+- pr_err(
+- "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n",
+- vi.name, p->ubi_num, p->vol_id, ret);
+- continue;
+- }
+- }
+-}
+-
+ static void ubiblock_remove_all(void)
+ {
+ struct ubiblock *next;
+@@ -678,18 +705,7 @@ int __init ubiblock_init(void)
+ if (ubiblock_major < 0)
+ return ubiblock_major;
+
+- /*
+- * Attach block devices from 'block=' module param.
+- * Even if one block device in the param list fails to come up,
+- * still allow the module to load and leave any others up.
+- */
+- ubiblock_create_from_param();
+-
+- /*
+- * Block devices are only created upon user requests, so we ignore
+- * existing volumes.
+- */
+- ret = ubi_register_volume_notifier(&ubiblock_notifier, 1);
++ ret = ubi_register_volume_notifier(&ubiblock_notifier, 0);
+ if (ret)
+ goto err_unreg;
+ return 0;
--- /dev/null
+From 471a17d8d1b838092d1a76e48cdce8b5b67ff809 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Mon, 27 Nov 2023 01:54:28 +0000
+Subject: [PATCH 04/15] mtd: ubi: attach from device tree
+
+Introduce device tree compatible 'linux,ubi' and attach compatible MTD
+devices using the MTD add notifier. This is needed for a UBI device to
+be available early at boot (and not only after late_initcall), so
+volumes on them can be used eg. as NVMEM providers for other drivers.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mtd/ubi/build.c | 146 ++++++++++++++++++++++++++++------------
+ drivers/mtd/ubi/cdev.c | 2 +-
+ drivers/mtd/ubi/ubi.h | 2 +-
+ 3 files changed, 106 insertions(+), 44 deletions(-)
+
+--- a/drivers/mtd/ubi/build.c
++++ b/drivers/mtd/ubi/build.c
+@@ -27,6 +27,7 @@
+ #include <linux/log2.h>
+ #include <linux/kthread.h>
+ #include <linux/kernel.h>
++#include <linux/of.h>
+ #include <linux/slab.h>
+ #include <linux/major.h>
+ #include "ubi.h"
+@@ -1071,6 +1072,7 @@ out_free:
+ * ubi_detach_mtd_dev - detach an MTD device.
+ * @ubi_num: UBI device number to detach from
+ * @anyway: detach MTD even if device reference count is not zero
++ * @have_lock: called by MTD notifier holding mtd_table_mutex
+ *
+ * This function destroys an UBI device number @ubi_num and detaches the
+ * underlying MTD device. Returns zero in case of success and %-EBUSY if the
+@@ -1080,7 +1082,7 @@ out_free:
+ * Note, the invocations of this function has to be serialized by the
+ * @ubi_devices_mutex.
+ */
+-int ubi_detach_mtd_dev(int ubi_num, int anyway)
++int ubi_detach_mtd_dev(int ubi_num, int anyway, bool have_lock)
+ {
+ struct ubi_device *ubi;
+
+@@ -1136,7 +1138,11 @@ int ubi_detach_mtd_dev(int ubi_num, int
+ vfree(ubi->peb_buf);
+ vfree(ubi->fm_buf);
+ ubi_msg(ubi, "mtd%d is detached", ubi->mtd->index);
+- put_mtd_device(ubi->mtd);
++ if (have_lock)
++ __put_mtd_device(ubi->mtd);
++ else
++ put_mtd_device(ubi->mtd);
++
+ put_device(&ubi->dev);
+ return 0;
+ }
+@@ -1213,43 +1219,43 @@ static struct mtd_info * __init open_mtd
+ return mtd;
+ }
+
+-static int __init ubi_init(void)
++static void ubi_notify_add(struct mtd_info *mtd)
+ {
+- int err, i, k;
++ struct device_node *np = mtd_get_of_node(mtd);
++ int err;
+
+- /* Ensure that EC and VID headers have correct size */
+- BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64);
+- BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64);
++ if (!of_device_is_compatible(np, "linux,ubi"))
++ return;
+
+- if (mtd_devs > UBI_MAX_DEVICES) {
+- pr_err("UBI error: too many MTD devices, maximum is %d\n",
+- UBI_MAX_DEVICES);
+- return -EINVAL;
+- }
++ /*
++ * we are already holding &mtd_table_mutex, but still need
++ * to bump refcount
++ */
++ err = __get_mtd_device(mtd);
++ if (err)
++ return;
+
+- /* Create base sysfs directory and sysfs files */
+- err = class_register(&ubi_class);
++ /* called while holding mtd_table_mutex */
++ mutex_lock_nested(&ubi_devices_mutex, SINGLE_DEPTH_NESTING);
++ err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, 0, 0, false);
++ mutex_unlock(&ubi_devices_mutex);
+ if (err < 0)
+- return err;
+-
+- err = misc_register(&ubi_ctrl_cdev);
+- if (err) {
+- pr_err("UBI error: cannot register device\n");
+- goto out;
+- }
++ __put_mtd_device(mtd);
++}
+
+- ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab",
+- sizeof(struct ubi_wl_entry),
+- 0, 0, NULL);
+- if (!ubi_wl_entry_slab) {
+- err = -ENOMEM;
+- goto out_dev_unreg;
+- }
++static void ubi_notify_remove(struct mtd_info *mtd)
++{
++ WARN(1, "mtd%d removed despite UBI still being attached", mtd->index);
++}
+
+- err = ubi_debugfs_init();
+- if (err)
+- goto out_slab;
++static struct mtd_notifier ubi_mtd_notifier = {
++ .add = ubi_notify_add,
++ .remove = ubi_notify_remove,
++};
+
++static int __init ubi_init_attach(void)
++{
++ int err, i, k;
+
+ /* Attach MTD devices */
+ for (i = 0; i < mtd_devs; i++) {
+@@ -1297,25 +1303,79 @@ static int __init ubi_init(void)
+ }
+ }
+
++ return 0;
++
++out_detach:
++ for (k = 0; k < i; k++)
++ if (ubi_devices[k]) {
++ mutex_lock(&ubi_devices_mutex);
++ ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1, false);
++ mutex_unlock(&ubi_devices_mutex);
++ }
++ return err;
++}
++#ifndef CONFIG_MTD_UBI_MODULE
++late_initcall(ubi_init_attach);
++#endif
++
++static int __init ubi_init(void)
++{
++ int err;
++
++ /* Ensure that EC and VID headers have correct size */
++ BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64);
++ BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64);
++
++ if (mtd_devs > UBI_MAX_DEVICES) {
++ pr_err("UBI error: too many MTD devices, maximum is %d\n",
++ UBI_MAX_DEVICES);
++ return -EINVAL;
++ }
++
++ /* Create base sysfs directory and sysfs files */
++ err = class_register(&ubi_class);
++ if (err < 0)
++ return err;
++
++ err = misc_register(&ubi_ctrl_cdev);
++ if (err) {
++ pr_err("UBI error: cannot register device\n");
++ goto out;
++ }
++
++ ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab",
++ sizeof(struct ubi_wl_entry),
++ 0, 0, NULL);
++ if (!ubi_wl_entry_slab) {
++ err = -ENOMEM;
++ goto out_dev_unreg;
++ }
++
++ err = ubi_debugfs_init();
++ if (err)
++ goto out_slab;
++
+ err = ubiblock_init();
+ if (err) {
+ pr_err("UBI error: block: cannot initialize, error %d\n", err);
+
+ /* See comment above re-ubi_is_module(). */
+ if (ubi_is_module())
+- goto out_detach;
++ goto out_slab;
++ }
++
++ register_mtd_user(&ubi_mtd_notifier);
++
++ if (ubi_is_module()) {
++ err = ubi_init_attach();
++ if (err)
++ goto out_mtd_notifier;
+ }
+
+ return 0;
+
+-out_detach:
+- for (k = 0; k < i; k++)
+- if (ubi_devices[k]) {
+- mutex_lock(&ubi_devices_mutex);
+- ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1);
+- mutex_unlock(&ubi_devices_mutex);
+- }
+- ubi_debugfs_exit();
++out_mtd_notifier:
++ unregister_mtd_user(&ubi_mtd_notifier);
+ out_slab:
+ kmem_cache_destroy(ubi_wl_entry_slab);
+ out_dev_unreg:
+@@ -1325,18 +1385,20 @@ out:
+ pr_err("UBI error: cannot initialize UBI, error %d\n", err);
+ return err;
+ }
+-late_initcall(ubi_init);
++device_initcall(ubi_init);
++
+
+ static void __exit ubi_exit(void)
+ {
+ int i;
+
+ ubiblock_exit();
++ unregister_mtd_user(&ubi_mtd_notifier);
+
+ for (i = 0; i < UBI_MAX_DEVICES; i++)
+ if (ubi_devices[i]) {
+ mutex_lock(&ubi_devices_mutex);
+- ubi_detach_mtd_dev(ubi_devices[i]->ubi_num, 1);
++ ubi_detach_mtd_dev(ubi_devices[i]->ubi_num, 1, false);
+ mutex_unlock(&ubi_devices_mutex);
+ }
+ ubi_debugfs_exit();
+--- a/drivers/mtd/ubi/cdev.c
++++ b/drivers/mtd/ubi/cdev.c
+@@ -1065,7 +1065,7 @@ static long ctrl_cdev_ioctl(struct file
+ }
+
+ mutex_lock(&ubi_devices_mutex);
+- err = ubi_detach_mtd_dev(ubi_num, 0);
++ err = ubi_detach_mtd_dev(ubi_num, 0, false);
+ mutex_unlock(&ubi_devices_mutex);
+ break;
+ }
+--- a/drivers/mtd/ubi/ubi.h
++++ b/drivers/mtd/ubi/ubi.h
+@@ -939,7 +939,7 @@ int ubi_io_write_vid_hdr(struct ubi_devi
+ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
+ int vid_hdr_offset, int max_beb_per1024,
+ bool disable_fm);
+-int ubi_detach_mtd_dev(int ubi_num, int anyway);
++int ubi_detach_mtd_dev(int ubi_num, int anyway, bool have_lock);
+ struct ubi_device *ubi_get_device(int ubi_num);
+ void ubi_put_device(struct ubi_device *ubi);
+ struct ubi_device *ubi_get_by_major(int major);
--- /dev/null
+From 2d664266cfdd114cc7a1fa28dd64275e99222455 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 8 Jun 2023 17:18:09 +0100
+Subject: [PATCH 05/15] mtd: ubi: introduce pre-removal notification for UBI
+ volumes
+
+Introduce a new notification type UBI_VOLUME_SHUTDOWN to inform users
+that a volume is just about to be removed.
+This is needed because users (such as the NVMEM subsystem) expect that
+at the time their removal function is called, the parenting device is
+still available (for removal of sysfs nodes, for example, in case of
+NVMEM which otherwise WARNs on volume removal).
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mtd/ubi/block.c | 26 ++++++++++++++++++++++++++
+ drivers/mtd/ubi/build.c | 20 +++++++++++++++-----
+ drivers/mtd/ubi/kapi.c | 2 +-
+ drivers/mtd/ubi/ubi.h | 2 ++
+ drivers/mtd/ubi/vmt.c | 17 +++++++++++++++--
+ include/linux/mtd/ubi.h | 2 ++
+ 6 files changed, 61 insertions(+), 8 deletions(-)
+
+--- a/drivers/mtd/ubi/block.c
++++ b/drivers/mtd/ubi/block.c
+@@ -568,6 +568,29 @@ static int ubiblock_resize(struct ubi_vo
+ return 0;
+ }
+
++static int ubiblock_shutdown(struct ubi_volume_info *vi)
++{
++ struct ubiblock *dev;
++ struct gendisk *disk;
++ int ret = 0;
++
++ mutex_lock(&devices_mutex);
++ dev = find_dev_nolock(vi->ubi_num, vi->vol_id);
++ if (!dev) {
++ ret = -ENODEV;
++ goto out_unlock;
++ }
++ disk = dev->gd;
++
++out_unlock:
++ mutex_unlock(&devices_mutex);
++
++ if (!ret)
++ blk_mark_disk_dead(disk);
++
++ return ret;
++};
++
+ static bool
+ match_volume_desc(struct ubi_volume_info *vi, const char *name, int ubi_num, int vol_id)
+ {
+@@ -659,6 +682,9 @@ static int ubiblock_notify(struct notifi
+ case UBI_VOLUME_REMOVED:
+ ubiblock_remove(&nt->vi);
+ break;
++ case UBI_VOLUME_SHUTDOWN:
++ ubiblock_shutdown(&nt->vi);
++ break;
+ case UBI_VOLUME_RESIZED:
+ ubiblock_resize(&nt->vi);
+ break;
+--- a/drivers/mtd/ubi/build.c
++++ b/drivers/mtd/ubi/build.c
+@@ -89,7 +89,7 @@ static struct ubi_device *ubi_devices[UB
+ /* Serializes UBI devices creations and removals */
+ DEFINE_MUTEX(ubi_devices_mutex);
+
+-/* Protects @ubi_devices and @ubi->ref_count */
++/* Protects @ubi_devices, @ubi->ref_count and @ubi->is_dead */
+ static DEFINE_SPINLOCK(ubi_devices_lock);
+
+ /* "Show" method for files in '/<sysfs>/class/ubi/' */
+@@ -258,6 +258,9 @@ struct ubi_device *ubi_get_device(int ub
+
+ spin_lock(&ubi_devices_lock);
+ ubi = ubi_devices[ubi_num];
++ if (ubi && ubi->is_dead)
++ ubi = NULL;
++
+ if (ubi) {
+ ubi_assert(ubi->ref_count >= 0);
+ ubi->ref_count += 1;
+@@ -295,7 +298,7 @@ struct ubi_device *ubi_get_by_major(int
+ spin_lock(&ubi_devices_lock);
+ for (i = 0; i < UBI_MAX_DEVICES; i++) {
+ ubi = ubi_devices[i];
+- if (ubi && MAJOR(ubi->cdev.dev) == major) {
++ if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) {
+ ubi_assert(ubi->ref_count >= 0);
+ ubi->ref_count += 1;
+ get_device(&ubi->dev);
+@@ -324,7 +327,7 @@ int ubi_major2num(int major)
+ for (i = 0; i < UBI_MAX_DEVICES; i++) {
+ struct ubi_device *ubi = ubi_devices[i];
+
+- if (ubi && MAJOR(ubi->cdev.dev) == major) {
++ if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) {
+ ubi_num = ubi->ubi_num;
+ break;
+ }
+@@ -511,7 +514,7 @@ static void ubi_free_volumes_from(struct
+ int i;
+
+ for (i = from; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
+- if (!ubi->volumes[i])
++ if (!ubi->volumes[i] || ubi->volumes[i]->is_dead)
+ continue;
+ ubi_eba_replace_table(ubi->volumes[i], NULL);
+ ubi_fastmap_destroy_checkmap(ubi->volumes[i]);
+@@ -1094,10 +1097,10 @@ int ubi_detach_mtd_dev(int ubi_num, int
+ return -EINVAL;
+
+ spin_lock(&ubi_devices_lock);
+- put_device(&ubi->dev);
+ ubi->ref_count -= 1;
+ if (ubi->ref_count) {
+ if (!anyway) {
++ ubi->ref_count += 1;
+ spin_unlock(&ubi_devices_lock);
+ return -EBUSY;
+ }
+@@ -1105,6 +1108,13 @@ int ubi_detach_mtd_dev(int ubi_num, int
+ ubi_err(ubi, "%s reference count %d, destroy anyway",
+ ubi->ubi_name, ubi->ref_count);
+ }
++ ubi->is_dead = true;
++ spin_unlock(&ubi_devices_lock);
++
++ ubi_notify_all(ubi, UBI_VOLUME_SHUTDOWN, NULL);
++
++ spin_lock(&ubi_devices_lock);
++ put_device(&ubi->dev);
+ ubi_devices[ubi_num] = NULL;
+ spin_unlock(&ubi_devices_lock);
+
+--- a/drivers/mtd/ubi/kapi.c
++++ b/drivers/mtd/ubi/kapi.c
+@@ -152,7 +152,7 @@ struct ubi_volume_desc *ubi_open_volume(
+
+ spin_lock(&ubi->volumes_lock);
+ vol = ubi->volumes[vol_id];
+- if (!vol)
++ if (!vol || vol->is_dead)
+ goto out_unlock;
+
+ err = -EBUSY;
+--- a/drivers/mtd/ubi/ubi.h
++++ b/drivers/mtd/ubi/ubi.h
+@@ -345,6 +345,7 @@ struct ubi_volume {
+ int writers;
+ int exclusive;
+ int metaonly;
++ bool is_dead;
+
+ int reserved_pebs;
+ int vol_type;
+@@ -564,6 +565,7 @@ struct ubi_device {
+ spinlock_t volumes_lock;
+ int ref_count;
+ int image_seq;
++ bool is_dead;
+
+ int rsvd_pebs;
+ int avail_pebs;
+--- a/drivers/mtd/ubi/vmt.c
++++ b/drivers/mtd/ubi/vmt.c
+@@ -59,7 +59,7 @@ static ssize_t vol_attribute_show(struct
+ struct ubi_device *ubi = vol->ubi;
+
+ spin_lock(&ubi->volumes_lock);
+- if (!ubi->volumes[vol->vol_id]) {
++ if (!ubi->volumes[vol->vol_id] || ubi->volumes[vol->vol_id]->is_dead) {
+ spin_unlock(&ubi->volumes_lock);
+ return -ENODEV;
+ }
+@@ -189,7 +189,7 @@ int ubi_create_volume(struct ubi_device
+
+ /* Ensure that the name is unique */
+ for (i = 0; i < ubi->vtbl_slots; i++)
+- if (ubi->volumes[i] &&
++ if (ubi->volumes[i] && !ubi->volumes[i]->is_dead &&
+ ubi->volumes[i]->name_len == req->name_len &&
+ !strcmp(ubi->volumes[i]->name, req->name)) {
+ ubi_err(ubi, "volume \"%s\" exists (ID %d)",
+@@ -352,6 +352,19 @@ int ubi_remove_volume(struct ubi_volume_
+ err = -EBUSY;
+ goto out_unlock;
+ }
++
++ /*
++ * Mark volume as dead at this point to prevent that anyone
++ * can take a reference to the volume from now on.
++ * This is necessary as we have to release the spinlock before
++ * calling ubi_volume_notify.
++ */
++ vol->is_dead = true;
++ spin_unlock(&ubi->volumes_lock);
++
++ ubi_volume_notify(ubi, vol, UBI_VOLUME_SHUTDOWN);
++
++ spin_lock(&ubi->volumes_lock);
+ ubi->volumes[vol_id] = NULL;
+ spin_unlock(&ubi->volumes_lock);
+
+--- a/include/linux/mtd/ubi.h
++++ b/include/linux/mtd/ubi.h
+@@ -192,6 +192,7 @@ struct ubi_device_info {
+ * or a volume was removed)
+ * @UBI_VOLUME_RESIZED: a volume has been re-sized
+ * @UBI_VOLUME_RENAMED: a volume has been re-named
++ * @UBI_VOLUME_SHUTDOWN: a volume is going to removed, shutdown users
+ * @UBI_VOLUME_UPDATED: data has been written to a volume
+ *
+ * These constants define which type of event has happened when a volume
+@@ -202,6 +203,7 @@ enum {
+ UBI_VOLUME_REMOVED,
+ UBI_VOLUME_RESIZED,
+ UBI_VOLUME_RENAMED,
++ UBI_VOLUME_SHUTDOWN,
+ UBI_VOLUME_UPDATED,
+ };
+
--- /dev/null
+From 3a041ee543cdf2e707a1dd72946cd6a583509b28 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Fri, 21 Jul 2023 19:26:37 +0100
+Subject: [PATCH 06/15] mtd: ubi: populate ubi volume fwnode
+
+Look for the 'volumes' subnode of an MTD partition attached to a UBI
+device and attach matching child nodes to UBI volumes.
+This allows UBI volumes to be referenced in device tree, e.g. for use
+as NVMEM providers.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mtd/ubi/vmt.c | 27 +++++++++++++++++++++++++++
+ 1 file changed, 27 insertions(+)
+
+--- a/drivers/mtd/ubi/vmt.c
++++ b/drivers/mtd/ubi/vmt.c
+@@ -124,6 +124,31 @@ static void vol_release(struct device *d
+ kfree(vol);
+ }
+
++static struct fwnode_handle *find_volume_fwnode(struct ubi_volume *vol)
++{
++ struct fwnode_handle *fw_vols, *fw_vol;
++ const char *volname;
++ u32 volid;
++
++ fw_vols = device_get_named_child_node(vol->dev.parent->parent, "volumes");
++ if (!fw_vols)
++ return NULL;
++
++ fwnode_for_each_child_node(fw_vols, fw_vol) {
++ if (!fwnode_property_read_string(fw_vol, "volname", &volname) &&
++ strncmp(volname, vol->name, vol->name_len))
++ continue;
++
++ if (!fwnode_property_read_u32(fw_vol, "volid", &volid) &&
++ vol->vol_id != volid)
++ continue;
++
++ return fw_vol;
++ }
++
++ return NULL;
++}
++
+ /**
+ * ubi_create_volume - create volume.
+ * @ubi: UBI device description object
+@@ -223,6 +248,7 @@ int ubi_create_volume(struct ubi_device
+ vol->name_len = req->name_len;
+ memcpy(vol->name, req->name, vol->name_len);
+ vol->ubi = ubi;
++ device_set_node(&vol->dev, find_volume_fwnode(vol));
+
+ /*
+ * Finish all pending erases because there may be some LEBs belonging
+@@ -605,6 +631,7 @@ int ubi_add_volume(struct ubi_device *ub
+ vol->dev.class = &ubi_class;
+ vol->dev.groups = volume_dev_groups;
+ dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id);
++ device_set_node(&vol->dev, find_volume_fwnode(vol));
+ err = device_register(&vol->dev);
+ if (err) {
+ cdev_del(&vol->cdev);
--- /dev/null
+From 7eb6666348f3f2d1f7308c712fa5903cbe189401 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 8 Jun 2023 17:22:04 +0100
+Subject: [PATCH 07/15] mtd: ubi: provide NVMEM layer over UBI volumes
+
+In an ideal world we would like UBI to be used where ever possible on a
+NAND chip. And with UBI support in ARM Trusted Firmware and U-Boot it
+is possible to achieve an (almost-)all-UBI flash layout. Hence the need
+for a way to also use UBI volumes to store board-level constants, such
+as MAC addresses and calibration data of wireless interfaces.
+
+Add UBI volume NVMEM driver module exposing UBI volumes as NVMEM
+providers. Allow UBI devices to have a "volumes" firmware subnode with
+volumes which may be compatible with "nvmem-cells".
+Access to UBI volumes via the NVMEM interface at this point is
+read-only, and it is slow, opening and closing the UBI volume for each
+access due to limitations of the NVMEM provider API.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mtd/ubi/Kconfig | 12 +++
+ drivers/mtd/ubi/Makefile | 1 +
+ drivers/mtd/ubi/nvmem.c | 188 +++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 201 insertions(+)
+ create mode 100644 drivers/mtd/ubi/nvmem.c
+
+--- a/drivers/mtd/ubi/Kconfig
++++ b/drivers/mtd/ubi/Kconfig
+@@ -104,4 +104,16 @@ config MTD_UBI_BLOCK
+
+ If in doubt, say "N".
+
++config MTD_UBI_NVMEM
++ tristate "UBI virtual NVMEM"
++ default n
++ depends on NVMEM
++ help
++ This option enabled an additional driver exposing UBI volumes as NVMEM
++ providers, intended for platforms where UBI is part of the firmware
++ specification and used to store also e.g. MAC addresses or board-
++ specific Wi-Fi calibration data.
++
++ If in doubt, say "N".
++
+ endif # MTD_UBI
+--- a/drivers/mtd/ubi/Makefile
++++ b/drivers/mtd/ubi/Makefile
+@@ -7,3 +7,4 @@ ubi-$(CONFIG_MTD_UBI_FASTMAP) += fastmap
+ ubi-$(CONFIG_MTD_UBI_BLOCK) += block.o
+
+ obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
++obj-$(CONFIG_MTD_UBI_NVMEM) += nvmem.o
+--- /dev/null
++++ b/drivers/mtd/ubi/nvmem.c
+@@ -0,0 +1,188 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (c) 2023 Daniel Golle <daniel@makrotopia.org>
++ */
++
++/* UBI NVMEM provider */
++#include "ubi.h"
++#include <linux/nvmem-provider.h>
++#include <asm/div64.h>
++
++/* List of all NVMEM devices */
++static LIST_HEAD(nvmem_devices);
++static DEFINE_MUTEX(devices_mutex);
++
++struct ubi_nvmem {
++ struct nvmem_device *nvmem;
++ int ubi_num;
++ int vol_id;
++ int usable_leb_size;
++ struct list_head list;
++};
++
++static int ubi_nvmem_reg_read(void *priv, unsigned int from,
++ void *val, size_t bytes)
++{
++ int err = 0, lnum = from, offs, bytes_left = bytes, to_read;
++ struct ubi_nvmem *unv = priv;
++ struct ubi_volume_desc *desc;
++
++ desc = ubi_open_volume(unv->ubi_num, unv->vol_id, UBI_READONLY);
++ if (IS_ERR(desc))
++ return PTR_ERR(desc);
++
++ offs = do_div(lnum, unv->usable_leb_size);
++ while (bytes_left) {
++ to_read = unv->usable_leb_size - offs;
++
++ if (to_read > bytes_left)
++ to_read = bytes_left;
++
++ err = ubi_read(desc, lnum, val, offs, to_read);
++ if (err)
++ break;
++
++ lnum += 1;
++ offs = 0;
++ bytes_left -= to_read;
++ val += to_read;
++ }
++ ubi_close_volume(desc);
++
++ if (err)
++ return err;
++
++ return bytes_left == 0 ? 0 : -EIO;
++}
++
++static int ubi_nvmem_add(struct ubi_volume_info *vi)
++{
++ struct device_node *np = dev_of_node(vi->dev);
++ struct nvmem_config config = {};
++ struct ubi_nvmem *unv;
++ int ret;
++
++ if (!np)
++ return 0;
++
++ if (!of_get_child_by_name(np, "nvmem-layout"))
++ return 0;
++
++ if (WARN_ON_ONCE(vi->usable_leb_size <= 0) ||
++ WARN_ON_ONCE(vi->size <= 0))
++ return -EINVAL;
++
++ unv = kzalloc(sizeof(struct ubi_nvmem), GFP_KERNEL);
++ if (!unv)
++ return -ENOMEM;
++
++ config.id = NVMEM_DEVID_NONE;
++ config.dev = vi->dev;
++ config.name = dev_name(vi->dev);
++ config.owner = THIS_MODULE;
++ config.priv = unv;
++ config.reg_read = ubi_nvmem_reg_read;
++ config.size = vi->usable_leb_size * vi->size;
++ config.word_size = 1;
++ config.stride = 1;
++ config.read_only = true;
++ config.root_only = true;
++ config.ignore_wp = true;
++ config.of_node = np;
++
++ unv->ubi_num = vi->ubi_num;
++ unv->vol_id = vi->vol_id;
++ unv->usable_leb_size = vi->usable_leb_size;
++ unv->nvmem = nvmem_register(&config);
++ if (IS_ERR(unv->nvmem)) {
++ ret = dev_err_probe(vi->dev, PTR_ERR(unv->nvmem),
++ "Failed to register NVMEM device\n");
++ kfree(unv);
++ return ret;
++ }
++
++ mutex_lock(&devices_mutex);
++ list_add_tail(&unv->list, &nvmem_devices);
++ mutex_unlock(&devices_mutex);
++
++ return 0;
++}
++
++static void ubi_nvmem_remove(struct ubi_volume_info *vi)
++{
++ struct ubi_nvmem *unv_c, *unv = NULL;
++
++ mutex_lock(&devices_mutex);
++ list_for_each_entry(unv_c, &nvmem_devices, list)
++ if (unv_c->ubi_num == vi->ubi_num && unv_c->vol_id == vi->vol_id) {
++ unv = unv_c;
++ break;
++ }
++
++ if (!unv) {
++ mutex_unlock(&devices_mutex);
++ return;
++ }
++
++ list_del(&unv->list);
++ mutex_unlock(&devices_mutex);
++ nvmem_unregister(unv->nvmem);
++ kfree(unv);
++}
++
++/**
++ * nvmem_notify - UBI notification handler.
++ * @nb: registered notifier block
++ * @l: notification type
++ * @ns_ptr: pointer to the &struct ubi_notification object
++ */
++static int nvmem_notify(struct notifier_block *nb, unsigned long l,
++ void *ns_ptr)
++{
++ struct ubi_notification *nt = ns_ptr;
++
++ switch (l) {
++ case UBI_VOLUME_RESIZED:
++ ubi_nvmem_remove(&nt->vi);
++ fallthrough;
++ case UBI_VOLUME_ADDED:
++ ubi_nvmem_add(&nt->vi);
++ break;
++ case UBI_VOLUME_SHUTDOWN:
++ ubi_nvmem_remove(&nt->vi);
++ break;
++ default:
++ break;
++ }
++ return NOTIFY_OK;
++}
++
++static struct notifier_block nvmem_notifier = {
++ .notifier_call = nvmem_notify,
++};
++
++static int __init ubi_nvmem_init(void)
++{
++ return ubi_register_volume_notifier(&nvmem_notifier, 0);
++}
++
++static void __exit ubi_nvmem_exit(void)
++{
++ struct ubi_nvmem *unv, *tmp;
++
++ mutex_lock(&devices_mutex);
++ list_for_each_entry_safe(unv, tmp, &nvmem_devices, list) {
++ nvmem_unregister(unv->nvmem);
++ list_del(&unv->list);
++ kfree(unv);
++ }
++ mutex_unlock(&devices_mutex);
++
++ ubi_unregister_volume_notifier(&nvmem_notifier);
++}
++
++module_init(ubi_nvmem_init);
++module_exit(ubi_nvmem_exit);
++MODULE_DESCRIPTION("NVMEM layer over UBI volumes");
++MODULE_AUTHOR("Daniel Golle");
++MODULE_LICENSE("GPL");
--- /dev/null
+From 9ffc1d7d73609a89eb264d6066340f8b7b3b0ebe Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Mon, 7 Aug 2023 21:19:45 +0100
+Subject: [PATCH 08/15] dt-bindings: block: add basic bindings for block
+ devices
+
+Add bindings for block devices which are used to allow referencing
+nvmem bits on them.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ .../bindings/block/block-device.yaml | 22 ++++++++
+ .../devicetree/bindings/block/partition.yaml | 50 +++++++++++++++++++
+ .../devicetree/bindings/block/partitions.yaml | 20 ++++++++
+ 3 files changed, 92 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
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/block/block-device.yaml
+@@ -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 <daniel@makrotopia.org>
++
++properties:
++ partitions:
++ $ref: /schemas/block/partitions.yaml
++
++ nvmem-layout:
++ $ref: /schemas/nvmem/layouts/nvmem-layout.yaml#
++
++unevaluatedProperties: false
+--- /dev/null
++++ b/Documentation/devicetree/bindings/block/partition.yaml
+@@ -0,0 +1,50 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/block/partition.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Partition on a block device
++
++description: |
++ This binding describes a partition on a block device.
++ Partitions may be matched by a combination of partition number, name,
++ and UUID.
++
++maintainers:
++ - Daniel Golle <daniel@makrotopia.org>
++
++properties:
++ $nodename:
++ pattern: '^block-partition-.+$'
++
++ partnum:
++ description:
++ Matches partition by number if present.
++
++ partname:
++ "$ref": "/schemas/types.yaml#/definitions/string"
++ description:
++ Matches partition by PARTNAME if present.
++
++ uuid:
++ "$ref": "/schemas/types.yaml#/definitions/string"
++ description:
++ Matches partition by PARTUUID if present.
++
++ nvmem-layout:
++ $ref: /schemas/nvmem/layouts/nvmem-layout.yaml#
++ description:
++ This container may reference an NVMEM layout parser.
++
++anyOf:
++ - required:
++ - partnum
++
++ - required:
++ - partname
++
++ - required:
++ - uuid
++
++unevaluatedProperties: false
+--- /dev/null
++++ b/Documentation/devicetree/bindings/block/partitions.yaml
+@@ -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 <daniel@makrotopia.org>
++
++patternProperties:
++ "^block-partition-.+$":
++ $ref: partition.yaml
++
++unevaluatedProperties: false
--- /dev/null
+From 614f4f6fdda09e30ecf7ef6c8091579db15018cb Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Fri, 21 Jul 2023 17:51:03 +0100
+Subject: [PATCH 09/15] block: partitions: populate fwnode
+
+Let block partitions to be represented by a firmware node and hence
+allow them to being referenced e.g. for use with blk-nvmem.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ block/partitions/core.c | 41 +++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 41 insertions(+)
+
+--- a/block/partitions/core.c
++++ b/block/partitions/core.c
+@@ -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 (*check_part[])(struct parsed_partitions *) = {
+@@ -298,6 +300,43 @@ static ssize_t whole_disk_show(struct de
+ }
+ static 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->bd_partno != 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.
+@@ -380,6 +419,8 @@ static struct block_device *add_partitio
+ 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);
--- /dev/null
+From 65f3ff9672ccd5ee78937047e7a2fc696eee1c8f Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 13 Jul 2023 04:07:16 +0100
+Subject: [PATCH 10/15] block: add new genhd flag GENHD_FL_NVMEM
+
+Add new flag to destinguish block devices which may act as an NVMEM
+provider.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ include/linux/blkdev.h | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/include/linux/blkdev.h
++++ b/include/linux/blkdev.h
+@@ -87,11 +87,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 {
--- /dev/null
+From b9936aa8a3775c2027f655d91a206d0e6e1c7ec0 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 11 Jul 2023 00:17:31 +0100
+Subject: [PATCH 11/15] 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.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ block/Kconfig | 9 +++
+ block/Makefile | 1 +
+ block/blk-nvmem.c | 186 ++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 196 insertions(+)
+ create mode 100644 block/blk-nvmem.c
+
+--- a/block/Kconfig
++++ b/block/Kconfig
+@@ -203,6 +203,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 prodivers,
++ typically used with eMMC to store MAC addresses or Wi-Fi
++ calibration data on embedded devices.
++
+ source "block/partitions/Kconfig"
+
+ config BLOCK_COMPAT
+--- a/block/Makefile
++++ b/block/Makefile
+@@ -35,6 +35,7 @@ obj-$(CONFIG_BLK_DEV_ZONED) += blk-zoned
+ obj-$(CONFIG_BLK_WBT) += blk-wbt.o
+ obj-$(CONFIG_BLK_DEBUG_FS) += blk-mq-debugfs.o
+ obj-$(CONFIG_BLK_DEBUG_FS_ZONED)+= blk-mq-debugfs-zoned.o
++obj-$(CONFIG_BLK_NVMEM) += blk-nvmem.o
+ obj-$(CONFIG_BLK_SED_OPAL) += sed-opal.o
+ obj-$(CONFIG_BLK_PM) += blk-pm.o
+ obj-$(CONFIG_BLK_INLINE_ENCRYPTION) += blk-crypto.o blk-crypto-profile.o \
+--- /dev/null
++++ b/block/blk-nvmem.c
+@@ -0,0 +1,186 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * block device NVMEM provider
++ *
++ * Copyright (c) 2023 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/of.h>
++#include <linux/pagemap.h>
++#include <linux/property.h>
++
++/* List of all NVMEM devices */
++static LIST_HEAD(nvmem_devices);
++static DEFINE_MUTEX(devices_mutex);
++
++struct blk_nvmem {
++ struct nvmem_device *nvmem;
++ struct block_device *bdev;
++ struct list_head list;
++};
++
++static int blk_nvmem_reg_read(void *priv, unsigned int from,
++ void *val, size_t bytes)
++{
++ unsigned long offs = from & ~PAGE_MASK, to_read;
++ pgoff_t f_index = from >> PAGE_SHIFT;
++ struct address_space *mapping;
++ struct blk_nvmem *bnv = priv;
++ size_t bytes_left = bytes;
++ struct folio *folio;
++ void *p;
++ int ret;
++
++ if (!bnv->bdev)
++ return -ENODEV;
++
++ if (!bnv->bdev->bd_disk)
++ return -EINVAL;
++
++ if (!bnv->bdev->bd_disk->fops)
++ return -EIO;
++
++ if (!bnv->bdev->bd_disk->fops->open)
++ return -EIO;
++
++ ret = bnv->bdev->bd_disk->fops->open(bnv->bdev, FMODE_READ);
++ if (ret)
++ return ret;
++
++ mapping = bnv->bdev->bd_inode->i_mapping;
++
++ while (bytes_left) {
++ folio = read_mapping_folio(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:
++ bnv->bdev->bd_disk->fops->release(bnv->bdev->bd_disk, FMODE_READ);
++
++ return ret;
++}
++
++static int blk_nvmem_register(struct device *dev, struct class_interface *iface)
++{
++ 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 devices which don't have GENHD_FL_NVMEM set
++ *
++ * This flag is used for mtdblock and ubiblock devices because
++ * both, MTD and UBI already implement their own NVMEM provider.
++ * To avoid registering multiple NVMEM providers for the same
++ * device node, don't register the block NVMEM provider for them.
++ */
++ if (!(bdev->bd_disk->flags & GENHD_FL_NVMEM))
++ 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(sizeof(struct blk_nvmem), GFP_KERNEL);
++ 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->bdev = bdev;
++ 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);
++ }
++
++ 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 class_interface *iface)
++{
++ struct block_device *bdev = dev_to_bdev(dev);
++ struct blk_nvmem *bnv_c, *bnv = NULL;
++
++ mutex_lock(&devices_mutex);
++ list_for_each_entry(bnv_c, &nvmem_devices, list) {
++ if (bnv_c->bdev == bdev) {
++ bnv = bnv_c;
++ break;
++ }
++ }
++
++ if (!bnv) {
++ mutex_unlock(&devices_mutex);
++ return;
++ }
++
++ list_del(&bnv->list);
++ mutex_unlock(&devices_mutex);
++ nvmem_unregister(bnv->nvmem);
++ kfree(bnv);
++}
++
++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);
--- /dev/null
+From 86864bf8f40e84dc881c197ef470a88668329dbf Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Mon, 7 Aug 2023 21:21:45 +0100
+Subject: [PATCH 12/15] dt-bindings: mmc: mmc-card: add block device nodes
+
+Add nodes representing the block devices exposed by an MMC device
+including an example involving nvmem-cells.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ .../devicetree/bindings/mmc/mmc-card.yaml | 45 +++++++++++++++++++
+ 1 file changed, 45 insertions(+)
+
+--- a/Documentation/devicetree/bindings/mmc/mmc-card.yaml
++++ b/Documentation/devicetree/bindings/mmc/mmc-card.yaml
+@@ -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>;
++ };
++ };
++ };
+ };
+ };
+
--- /dev/null
+From 644942a31719de674e2aa68f83d66bd8ae7e4fb7 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 13 Jul 2023 04:12:21 +0100
+Subject: [PATCH 13/15] mmc: core: set card fwnode_handle
+
+Set fwnode in case it isn't set yet and of_node is present.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mmc/core/bus.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/mmc/core/bus.c
++++ b/drivers/mmc/core/bus.c
+@@ -363,6 +363,8 @@ int mmc_add_card(struct mmc_card *card)
+ mmc_add_card_debugfs(card);
+ #endif
+ 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);
+
--- /dev/null
+From d9143f86330dd038fc48878558dd287ceee5d3d4 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 13 Jul 2023 04:13:04 +0100
+Subject: [PATCH 14/15] mmc: block: set fwnode of disk devices
+
+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.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mmc/core/block.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/mmc/core/block.c
++++ b/drivers/mmc/core/block.c
+@@ -2484,6 +2484,8 @@ static struct mmc_blk_data *mmc_blk_allo
+ int area_type,
+ unsigned int part_type)
+ {
++ struct fwnode_handle *fwnode;
++ struct device *ddev;
+ struct mmc_blk_data *md;
+ int devidx, ret;
+ char cap_str[10];
+@@ -2580,6 +2582,12 @@ static struct mmc_blk_data *mmc_blk_allo
+
+ blk_queue_write_cache(md->queue.queue, cache_enabled, fua_enabled);
+
++ ddev = disk_to_dev(md->disk);
++ fwnode = device_get_named_child_node(subname ? md->parent->parent :
++ md->parent,
++ subname ? subname : "block");
++ ddev->fwnode = fwnode;
++
+ string_get_size((u64)size, 512, STRING_UNITS_2,
+ cap_str, sizeof(cap_str));
+ pr_info("%s: %s %s %s %s\n",
--- /dev/null
+From 322035ab2b0113d98b6c0ea788d971e0df2952a4 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 20 Jul 2023 17:36:44 +0100
+Subject: [PATCH 15/15] mmc: block: set GENHD_FL_NVMEM
+
+Set flag to consider MMC block devices as NVMEM providers.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mmc/core/block.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/mmc/core/block.c
++++ b/drivers/mmc/core/block.c
+@@ -2538,6 +2538,7 @@ static struct mmc_blk_data *mmc_blk_allo
+ 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;
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
-@@ -1213,6 +1213,73 @@ static struct mtd_info * __init open_mtd
- return mtd;
- }
+@@ -1263,6 +1263,74 @@ static struct mtd_notifier ubi_mtd_notif
+ .remove = ubi_notify_remove,
+ };
++
+/*
+ * This function tries attaching mtd partitions named either "ubi" or "data"
+ * during boot.
+ put_mtd_device(mtd);
+}
+
- static int __init ubi_init(void)
+ static int __init ubi_init_attach(void)
{
int err, i, k;
-@@ -1297,6 +1364,12 @@ static int __init ubi_init(void)
+@@ -1313,6 +1381,12 @@ static int __init ubi_init_attach(void)
}
}
+ !ubi_is_module() && !mtd_devs)
+ ubi_auto_attach();
+
- err = ubiblock_init();
- if (err) {
- pr_err("UBI error: block: cannot initialize, error %d\n", err);
+ return 0;
+
+ out_detach:
--- a/drivers/mtd/ubi/block.c
+++ b/drivers/mtd/ubi/block.c
-@@ -653,6 +653,47 @@ static void __init ubiblock_create_from_
- }
+@@ -644,10 +644,47 @@ match_volume_desc(struct ubi_volume_info
+ return true;
}
+#define UBIFS_NODE_MAGIC 0x06101831
+ return magic == UBIFS_NODE_MAGIC;
+}
+
-+static void __init ubiblock_create_auto_rootfs(void)
++static void __init ubiblock_create_auto_rootfs(struct ubi_volume_info *vi)
+{
-+ int ubi_num, ret, is_ubifs;
++ int ret, is_ubifs;
+ struct ubi_volume_desc *desc;
-+ struct ubi_volume_info vi;
+
-+ for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++) {
-+ desc = ubi_open_volume_nm(ubi_num, "rootfs", UBI_READONLY);
-+ if (IS_ERR(desc))
-+ desc = ubi_open_volume_nm(ubi_num, "fit", UBI_READONLY);;
++ if (strcmp(vi->name, "rootfs") &&
++ strcmp(vi->name, "fit"))
++ return;
+
-+ if (IS_ERR(desc))
-+ continue;
++ desc = ubi_open_volume(vi->ubi_num, vi->vol_id, UBI_READONLY);
++ if (IS_ERR(desc))
++ return;
+
-+ ubi_get_volume_info(desc, &vi);
-+ is_ubifs = ubi_vol_is_ubifs(desc);
-+ ubi_close_volume(desc);
-+ if (is_ubifs)
-+ break;
++ is_ubifs = ubi_vol_is_ubifs(desc);
++ ubi_close_volume(desc);
++ if (is_ubifs)
++ return;
+
-+ ret = ubiblock_create(&vi);
-+ if (ret)
-+ pr_err("UBI error: block: can't add '%s' volume, err=%d\n",
-+ vi.name, ret);
-+ /* always break if we get here */
-+ break;
-+ }
++ ret = ubiblock_create(vi);
++ if (ret)
++ pr_err("UBI error: block: can't add '%s' volume, err=%d\n",
++ vi->name, ret);
+}
+
- static void ubiblock_remove_all(void)
+ static void
+ ubiblock_create_from_param(struct ubi_volume_info *vi)
{
- struct ubiblock *next;
-@@ -685,6 +726,10 @@ int __init ubiblock_init(void)
- */
- ubiblock_create_from_param();
+ int i, ret = 0;
++ bool got_param = false;
+ struct ubiblock_param *p;
-+ /* auto-attach "rootfs" volume if existing and non-ubifs */
-+ if (IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV))
-+ ubiblock_create_auto_rootfs();
-+
/*
- * Block devices are only created upon user requests, so we ignore
- * existing volumes.
+@@ -660,6 +697,7 @@ ubiblock_create_from_param(struct ubi_vo
+ if (!match_volume_desc(vi, p->name, p->ubi_num, p->vol_id))
+ continue;
+
++ got_param = true;
+ ret = ubiblock_create(vi);
+ if (ret) {
+ pr_err(
+@@ -668,6 +706,10 @@ ubiblock_create_from_param(struct ubi_vo
+ }
+ break;
+ }
++
++ /* auto-attach "rootfs" volume if existing and non-ubifs */
++ if (!got_param && IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV))
++ ubiblock_create_auto_rootfs(vi);
+ }
+
+ static int ubiblock_notify(struct notifier_block *nb,
--- a/drivers/mtd/ubi/block.c
+++ b/drivers/mtd/ubi/block.c
-@@ -42,6 +42,7 @@
+@@ -43,6 +43,7 @@
#include <linux/scatterlist.h>
#include <linux/idr.h>
#include <asm/div64.h>
#include "ubi-media.h"
#include "ubi.h"
-@@ -459,6 +460,15 @@ int ubiblock_create(struct ubi_volume_in
+@@ -460,6 +461,15 @@ int ubiblock_create(struct ubi_volume_in
dev_info(disk_to_dev(dev->gd), "created from ubi%d:%d(%s)",
dev->ubi_num, dev->vol_id, vi->name);
mutex_unlock(&devices_mutex);
break;
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
-@@ -778,6 +778,7 @@ struct ubi_attach_info {
+@@ -780,6 +780,7 @@ struct ubi_attach_info {
int mean_ec;
uint64_t ec_sum;
int ec_count;
+int parse_fit_partitions(struct parsed_partitions *state, u64 start_sector, u64 nr_sectors, int *slot, int add_remain);
--- a/block/partitions/core.c
+++ b/block/partitions/core.c
-@@ -10,6 +10,10 @@
- #include <linux/ctype.h>
+@@ -11,6 +11,9 @@
#include <linux/vmalloc.h>
#include <linux/raid/detect.h>
+ #include <linux/property.h>
+#ifdef CONFIG_FIT_PARTITION
+#include <linux/root_dev.h>
+#endif
-+
+
#include "check.h"
- static int (*check_part[])(struct parsed_partitions *) = {
-@@ -46,6 +50,9 @@ static int (*check_part[])(struct parsed
+@@ -48,6 +51,9 @@ static int (*check_part[])(struct parsed
#ifdef CONFIG_EFI_PARTITION
efi_partition, /* this must come before msdos */
#endif
#ifdef CONFIG_SGI_PARTITION
sgi_partition,
#endif
-@@ -398,6 +405,11 @@ static struct block_device *add_partitio
+@@ -439,6 +445,11 @@ static struct block_device *add_partitio
goto out_del;
}
/* everything is up and running, commence */
err = xa_insert(&disk->part_tbl, partno, bdev, GFP_KERNEL);
if (err)
-@@ -590,6 +602,11 @@ static bool blk_add_partition(struct gen
+@@ -631,6 +642,11 @@ static bool blk_add_partition(struct gen
(state->parts[p].flags & ADDPART_FLAG_RAID))
md_autodetect_dev(part->bd_dev);
set_capacity(gd, ((u64)new->size * tr->blksize) >> 9);
--- a/drivers/mtd/ubi/block.c
+++ b/drivers/mtd/ubi/block.c
-@@ -431,7 +431,9 @@ int ubiblock_create(struct ubi_volume_in
+@@ -432,7 +432,9 @@ int ubiblock_create(struct ubi_volume_in
ret = -ENODEV;
goto out_cleanup_disk;
}