diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-03-22 01:09:29 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-03-22 01:09:29 +0300 |
commit | 85a79128c4f5723f812ab8d5ee465ec660e223f1 (patch) | |
tree | c7b658afff391c565023c5c02a16800aa93ee822 /drivers/mtd/ubi/nvmem.c | |
parent | cba9ffdb9913dfe6be29f049ce920ce451ce7cc4 (diff) | |
parent | b8a77b9a5f9c2ba313f2beef8440b6f9f69768e7 (diff) | |
download | linux-85a79128c4f5723f812ab8d5ee465ec660e223f1.tar.xz |
Merge tag 'ubifs-for-linus-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/ubifs
Pull UBI and UBIFS updates from Richard Weinberger:
"UBI:
- Add Zhihao Cheng as reviewer
- Attach via device tree
- Add NVMEM layer
- Various fastmap related fixes
UBIFS:
- Add Zhihao Cheng as reviewer
- Convert to folios
- Various fixes (memory leaks in error paths, function prototypes)"
* tag 'ubifs-for-linus-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/ubifs: (34 commits)
mtd: ubi: fix NVMEM over UBI volumes on 32-bit systems
mtd: ubi: provide NVMEM layer over UBI volumes
mtd: ubi: populate ubi volume fwnode
mtd: ubi: introduce pre-removal notification for UBI volumes
mtd: ubi: attach from device tree
mtd: ubi: block: use notifier to create ubiblock from parameter
dt-bindings: mtd: ubi-volume: allow UBI volumes to provide NVMEM
dt-bindings: mtd: add basic bindings for UBI
ubifs: Queue up space reservation tasks if retrying many times
ubifs: ubifs_symlink: Fix memleak of inode->i_link in error path
ubifs: dbg_check_idx_size: Fix kmemleak if loading znode failed
ubi: Correct the number of PEBs after a volume resize failure
ubi: fix slab-out-of-bounds in ubi_eba_get_ldesc+0xfb/0x130
ubi: correct the calculation of fastmap size
ubifs: Remove unreachable code in dbg_check_ltab_lnum
ubifs: fix function pointer cast warnings
ubifs: fix sort function prototype
ubi: Check for too small LEB size in VTBL code
MAINTAINERS: Add Zhihao Cheng as UBI/UBIFS reviewer
ubifs: Convert populate_page() to take a folio
...
Diffstat (limited to 'drivers/mtd/ubi/nvmem.c')
-rw-r--r-- | drivers/mtd/ubi/nvmem.c | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/drivers/mtd/ubi/nvmem.c b/drivers/mtd/ubi/nvmem.c new file mode 100644 index 000000000000..8aeb9c428e51 --- /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) +{ + 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)) + 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"); |