The series was merged for Linux v6.9, so move it to backports.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
--- /dev/null
+From 25d88bfd35bac3196eafa666e3b05033b46ffa21 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 19 Dec 2023 02:32:00 +0000
+Subject: [PATCH 1/8] dt-bindings: mtd: add basic bindings for UBI
+
+Add basic bindings for UBI devices and volumes.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ .../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 95b113222b5164ac0887eb5c514ff3970a0136f0 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 19 Dec 2023 02:32:11 +0000
+Subject: [PATCH 2/8] 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>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ .../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 2bba1cdcfcd2907d0696cc0139f1bd078d36ee81 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 19 Dec 2023 02:32:35 +0000
+Subject: [PATCH 3/8] 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>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ drivers/mtd/ubi/block.c | 136 ++++++++++++++++++++--------------------
+ drivers/mtd/ubi/kapi.c | 54 +++++++++++-----
+ drivers/mtd/ubi/ubi.h | 1 +
+ 3 files changed, 106 insertions(+), 85 deletions(-)
+
+--- a/drivers/mtd/ubi/block.c
++++ b/drivers/mtd/ubi/block.c
+@@ -65,10 +65,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;
+@@ -532,6 +532,70 @@ 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, cur_ubi_num, cur_vol_id;
++
++ if (ubi_num == -1) {
++ /* No ubi num, name must be a vol device path */
++ err = ubi_get_num_by_path(name, &cur_ubi_num, &cur_vol_id);
++ if (err || vi->ubi_num != cur_ubi_num || vi->vol_id != cur_vol_id)
++ return false;
++
++ return true;
++ }
++
++ if (vol_id == -1) {
++ /* Got ubi_num, but no vol_id, name must be volume name */
++ 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)
+ {
+@@ -539,10 +603,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);
+@@ -568,56 +629,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;
+@@ -643,18 +654,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;
+--- a/drivers/mtd/ubi/kapi.c
++++ b/drivers/mtd/ubi/kapi.c
+@@ -280,6 +280,41 @@ struct ubi_volume_desc *ubi_open_volume_
+ EXPORT_SYMBOL_GPL(ubi_open_volume_nm);
+
+ /**
++ * ubi_get_num_by_path - get UBI device and volume number from device path
++ * @pathname: volume character device node path
++ * @ubi_num: pointer to UBI device number to be set
++ * @vol_id: pointer to UBI volume ID to be set
++ *
++ * Returns 0 on success and sets ubi_num and vol_id, returns error otherwise.
++ */
++int ubi_get_num_by_path(const char *pathname, int *ubi_num, int *vol_id)
++{
++ int error;
++ struct path path;
++ struct kstat stat;
++
++ error = kern_path(pathname, LOOKUP_FOLLOW, &path);
++ if (error)
++ return error;
++
++ error = vfs_getattr(&path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT);
++ path_put(&path);
++ if (error)
++ return error;
++
++ if (!S_ISCHR(stat.mode))
++ return -EINVAL;
++
++ *ubi_num = ubi_major2num(MAJOR(stat.rdev));
++ *vol_id = MINOR(stat.rdev) - 1;
++
++ if (*vol_id < 0 || *ubi_num < 0)
++ return -ENODEV;
++
++ return 0;
++}
++
++/**
+ * ubi_open_volume_path - open UBI volume by its character device node path.
+ * @pathname: volume character device node path
+ * @mode: open mode
+@@ -290,32 +325,17 @@ EXPORT_SYMBOL_GPL(ubi_open_volume_nm);
+ struct ubi_volume_desc *ubi_open_volume_path(const char *pathname, int mode)
+ {
+ int error, ubi_num, vol_id;
+- struct path path;
+- struct kstat stat;
+
+ dbg_gen("open volume %s, mode %d", pathname, mode);
+
+ if (!pathname || !*pathname)
+ return ERR_PTR(-EINVAL);
+
+- error = kern_path(pathname, LOOKUP_FOLLOW, &path);
+- if (error)
+- return ERR_PTR(error);
+-
+- error = vfs_getattr(&path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT);
+- path_put(&path);
++ error = ubi_get_num_by_path(pathname, &ubi_num, &vol_id);
+ if (error)
+ return ERR_PTR(error);
+
+- if (!S_ISCHR(stat.mode))
+- return ERR_PTR(-EINVAL);
+-
+- ubi_num = ubi_major2num(MAJOR(stat.rdev));
+- vol_id = MINOR(stat.rdev) - 1;
+-
+- if (vol_id >= 0 && ubi_num >= 0)
+- return ubi_open_volume(ubi_num, vol_id, mode);
+- return ERR_PTR(-ENODEV);
++ return ubi_open_volume(ubi_num, vol_id, mode);
+ }
+ EXPORT_SYMBOL_GPL(ubi_open_volume_path);
+
+--- a/drivers/mtd/ubi/ubi.h
++++ b/drivers/mtd/ubi/ubi.h
+@@ -956,6 +956,7 @@ void ubi_free_internal_volumes(struct ub
+ void ubi_do_get_device_info(struct ubi_device *ubi, struct ubi_device_info *di);
+ void ubi_do_get_volume_info(struct ubi_device *ubi, struct ubi_volume *vol,
+ struct ubi_volume_info *vi);
++int ubi_get_num_by_path(const char *pathname, int *ubi_num, int *vol_id);
+ /* scan.c */
+ int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb,
+ int pnum, const struct ubi_vid_hdr *vid_hdr);
--- /dev/null
+From 6e331888643887ce85657527bc03f97d46235e71 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 19 Dec 2023 02:33:14 +0000
+Subject: [PATCH 4/8] 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>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ drivers/mtd/ubi/build.c | 135 ++++++++++++++++++++++++++++------------
+ 1 file changed, 96 insertions(+), 39 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"
+@@ -1214,43 +1215,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)
++{
++ /* do nothing for now */
++}
+
+- 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++) {
+@@ -1298,25 +1299,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);
++ 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:
+@@ -1326,13 +1381,15 @@ 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]) {
--- /dev/null
+From 924731fbed3247e3b82b8ab17db587ee28c2e781 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 19 Dec 2023 02:33:24 +0000
+Subject: [PATCH 5/8] 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>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ drivers/mtd/ubi/build.c | 19 ++++++++++++++-----
+ drivers/mtd/ubi/kapi.c | 2 +-
+ drivers/mtd/ubi/ubi.h | 2 ++
+ drivers/mtd/ubi/vmt.c | 17 +++++++++++++++--
+ include/linux/mtd/ubi.h | 2 ++
+ 5 files changed, 34 insertions(+), 8 deletions(-)
+
+--- a/drivers/mtd/ubi/build.c
++++ b/drivers/mtd/ubi/build.c
+@@ -91,7 +91,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/' */
+@@ -259,6 +259,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;
+@@ -296,7 +299,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);
+@@ -325,7 +328,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;
+ }
+@@ -512,7 +515,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,7 +1097,6 @@ 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) {
+@@ -1105,6 +1107,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 1c54542170819e36baa43c17ca55bb3d7da89a53 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 19 Dec 2023 02:33:38 +0000
+Subject: [PATCH 6/8] 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>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ 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 15fc7dc926c91c871f6c0305b2938dbdeb14203b Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 19 Dec 2023 02:33:48 +0000
+Subject: [PATCH 7/8] 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>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ 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 04231c61dcd51db0f12061e49bb761b197109f2f Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 29 Feb 2024 03:47:24 +0000
+Subject: [PATCH 8/8] mtd: ubi: fix NVMEM over UBI volumes on 32-bit systems
+
+A compiler warning related to sizeof(int) != 8 when calling do_div()
+is triggered when building on 32-bit platforms.
+Address this by using integer types having a well-defined size.
+
+Fixes: 3ce485803da1 ("mtd: ubi: provide NVMEM layer over UBI volumes")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Zhihao Cheng <chengzhihao1@huawei.com>
+Tested-by: Randy Dunlap <rdunlap@infradead.org>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ drivers/mtd/ubi/nvmem.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/drivers/mtd/ubi/nvmem.c
++++ b/drivers/mtd/ubi/nvmem.c
+@@ -23,9 +23,12 @@ struct ubi_nvmem {
+ 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;
++ size_t to_read, bytes_left = bytes;
+ struct ubi_nvmem *unv = priv;
+ struct ubi_volume_desc *desc;
++ uint32_t offs;
++ uint64_t lnum = from;
++ int err = 0;
+
+ desc = ubi_open_volume(unv->ubi_num, unv->vol_id, UBI_READONLY);
+ if (IS_ERR(desc))
+++ /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/blkdev.h>
-@@ -65,10 +66,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;
-@@ -469,7 +470,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;
-@@ -532,6 +533,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)
- {
-@@ -539,10 +619,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);
-@@ -568,56 +645,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;
-@@ -643,18 +670,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"
-@@ -1072,6 +1073,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
-@@ -1081,7 +1083,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;
-
-@@ -1137,7 +1139,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;
- }
-@@ -1214,43 +1220,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++) {
-@@ -1298,25 +1304,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:
-@@ -1326,18 +1386,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
-@@ -533,6 +533,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)
- {
-@@ -624,6 +647,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
-@@ -91,7 +91,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/' */
-@@ -259,6 +259,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;
-@@ -296,7 +299,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);
-@@ -325,7 +328,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;
- }
-@@ -512,7 +515,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]);
-@@ -1095,10 +1098,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;
- }
-@@ -1106,6 +1109,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,191 @@
-+// 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)
-+{
-+ uint32_t offs, to_read, bytes_left;
-+ struct ubi_nvmem *unv = priv;
-+ struct ubi_volume_desc *desc;
-+ uint64_t lnum = from;
-+ int err = 0;
-+
-+ desc = ubi_open_volume(unv->ubi_num, unv->vol_id, UBI_READONLY);
-+ if (IS_ERR(desc))
-+ return PTR_ERR(desc);
-+
-+ bytes_left = bytes;
-+ 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");
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
-@@ -1264,6 +1264,74 @@ static struct mtd_notifier ubi_mtd_notif
+@@ -1258,6 +1258,74 @@ static struct mtd_notifier ubi_mtd_notif
.remove = ubi_notify_remove,
};
static int __init ubi_init_attach(void)
{
int err, i, k;
-@@ -1314,6 +1382,12 @@ static int __init ubi_init_attach(void)
+@@ -1308,6 +1376,12 @@ static int __init ubi_init_attach(void)
}
}
--- a/drivers/mtd/ubi/block.c
+++ b/drivers/mtd/ubi/block.c
-@@ -609,10 +609,47 @@ match_volume_desc(struct ubi_volume_info
+@@ -570,10 +570,47 @@ match_volume_desc(struct ubi_volume_info
return true;
}
struct ubiblock_param *p;
/*
-@@ -625,6 +662,7 @@ ubiblock_create_from_param(struct ubi_vo
+@@ -586,6 +623,7 @@ ubiblock_create_from_param(struct ubi_vo
if (!match_volume_desc(vi, p->name, p->ubi_num, p->vol_id))
continue;
ret = ubiblock_create(vi);
if (ret) {
pr_err(
-@@ -633,6 +671,10 @@ ubiblock_create_from_param(struct ubi_vo
+@@ -594,6 +632,10 @@ ubiblock_create_from_param(struct ubi_vo
}
break;
}
--- a/drivers/mtd/ubi/block.c
+++ b/drivers/mtd/ubi/block.c
-@@ -42,6 +42,7 @@
+@@ -41,6 +41,7 @@
#include <linux/scatterlist.h>
#include <linux/idr.h>
#include <asm/div64.h>
#include "ubi-media.h"
#include "ubi.h"
-@@ -429,6 +430,15 @@ int ubiblock_create(struct ubi_volume_in
+@@ -428,6 +429,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);
set_capacity(gd, ((u64)new->size * tr->blksize) >> 9);
--- a/drivers/mtd/ubi/block.c
+++ b/drivers/mtd/ubi/block.c
-@@ -411,7 +411,9 @@ int ubiblock_create(struct ubi_volume_in
+@@ -410,7 +410,9 @@ int ubiblock_create(struct ubi_volume_in
ret = -ENODEV;
goto out_cleanup_disk;
}