Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Daniel Golle and committed by
Richard Weinberger
3ce48580 51932f9f

+202
+13
drivers/mtd/ubi/Kconfig
··· 113 113 testing purposes. 114 114 115 115 If in doubt, say "N". 116 + 117 + config MTD_UBI_NVMEM 118 + tristate "UBI virtual NVMEM" 119 + default n 120 + depends on NVMEM 121 + help 122 + This option enabled an additional driver exposing UBI volumes as NVMEM 123 + providers, intended for platforms where UBI is part of the firmware 124 + specification and used to store also e.g. MAC addresses or board- 125 + specific Wi-Fi calibration data. 126 + 127 + If in doubt, say "N". 128 + 116 129 endif # MTD_UBI
+1
drivers/mtd/ubi/Makefile
··· 7 7 ubi-$(CONFIG_MTD_UBI_BLOCK) += block.o 8 8 9 9 obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o 10 + obj-$(CONFIG_MTD_UBI_NVMEM) += nvmem.o
+188
drivers/mtd/ubi/nvmem.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Copyright (c) 2023 Daniel Golle <daniel@makrotopia.org> 4 + */ 5 + 6 + /* UBI NVMEM provider */ 7 + #include "ubi.h" 8 + #include <linux/nvmem-provider.h> 9 + #include <asm/div64.h> 10 + 11 + /* List of all NVMEM devices */ 12 + static LIST_HEAD(nvmem_devices); 13 + static DEFINE_MUTEX(devices_mutex); 14 + 15 + struct ubi_nvmem { 16 + struct nvmem_device *nvmem; 17 + int ubi_num; 18 + int vol_id; 19 + int usable_leb_size; 20 + struct list_head list; 21 + }; 22 + 23 + static int ubi_nvmem_reg_read(void *priv, unsigned int from, 24 + void *val, size_t bytes) 25 + { 26 + int err = 0, lnum = from, offs, bytes_left = bytes, to_read; 27 + struct ubi_nvmem *unv = priv; 28 + struct ubi_volume_desc *desc; 29 + 30 + desc = ubi_open_volume(unv->ubi_num, unv->vol_id, UBI_READONLY); 31 + if (IS_ERR(desc)) 32 + return PTR_ERR(desc); 33 + 34 + offs = do_div(lnum, unv->usable_leb_size); 35 + while (bytes_left) { 36 + to_read = unv->usable_leb_size - offs; 37 + 38 + if (to_read > bytes_left) 39 + to_read = bytes_left; 40 + 41 + err = ubi_read(desc, lnum, val, offs, to_read); 42 + if (err) 43 + break; 44 + 45 + lnum += 1; 46 + offs = 0; 47 + bytes_left -= to_read; 48 + val += to_read; 49 + } 50 + ubi_close_volume(desc); 51 + 52 + if (err) 53 + return err; 54 + 55 + return bytes_left == 0 ? 0 : -EIO; 56 + } 57 + 58 + static int ubi_nvmem_add(struct ubi_volume_info *vi) 59 + { 60 + struct device_node *np = dev_of_node(vi->dev); 61 + struct nvmem_config config = {}; 62 + struct ubi_nvmem *unv; 63 + int ret; 64 + 65 + if (!np) 66 + return 0; 67 + 68 + if (!of_get_child_by_name(np, "nvmem-layout")) 69 + return 0; 70 + 71 + if (WARN_ON_ONCE(vi->usable_leb_size <= 0) || 72 + WARN_ON_ONCE(vi->size <= 0)) 73 + return -EINVAL; 74 + 75 + unv = kzalloc(sizeof(struct ubi_nvmem), GFP_KERNEL); 76 + if (!unv) 77 + return -ENOMEM; 78 + 79 + config.id = NVMEM_DEVID_NONE; 80 + config.dev = vi->dev; 81 + config.name = dev_name(vi->dev); 82 + config.owner = THIS_MODULE; 83 + config.priv = unv; 84 + config.reg_read = ubi_nvmem_reg_read; 85 + config.size = vi->usable_leb_size * vi->size; 86 + config.word_size = 1; 87 + config.stride = 1; 88 + config.read_only = true; 89 + config.root_only = true; 90 + config.ignore_wp = true; 91 + config.of_node = np; 92 + 93 + unv->ubi_num = vi->ubi_num; 94 + unv->vol_id = vi->vol_id; 95 + unv->usable_leb_size = vi->usable_leb_size; 96 + unv->nvmem = nvmem_register(&config); 97 + if (IS_ERR(unv->nvmem)) { 98 + ret = dev_err_probe(vi->dev, PTR_ERR(unv->nvmem), 99 + "Failed to register NVMEM device\n"); 100 + kfree(unv); 101 + return ret; 102 + } 103 + 104 + mutex_lock(&devices_mutex); 105 + list_add_tail(&unv->list, &nvmem_devices); 106 + mutex_unlock(&devices_mutex); 107 + 108 + return 0; 109 + } 110 + 111 + static void ubi_nvmem_remove(struct ubi_volume_info *vi) 112 + { 113 + struct ubi_nvmem *unv_c, *unv = NULL; 114 + 115 + mutex_lock(&devices_mutex); 116 + list_for_each_entry(unv_c, &nvmem_devices, list) 117 + if (unv_c->ubi_num == vi->ubi_num && unv_c->vol_id == vi->vol_id) { 118 + unv = unv_c; 119 + break; 120 + } 121 + 122 + if (!unv) { 123 + mutex_unlock(&devices_mutex); 124 + return; 125 + } 126 + 127 + list_del(&unv->list); 128 + mutex_unlock(&devices_mutex); 129 + nvmem_unregister(unv->nvmem); 130 + kfree(unv); 131 + } 132 + 133 + /** 134 + * nvmem_notify - UBI notification handler. 135 + * @nb: registered notifier block 136 + * @l: notification type 137 + * @ns_ptr: pointer to the &struct ubi_notification object 138 + */ 139 + static int nvmem_notify(struct notifier_block *nb, unsigned long l, 140 + void *ns_ptr) 141 + { 142 + struct ubi_notification *nt = ns_ptr; 143 + 144 + switch (l) { 145 + case UBI_VOLUME_RESIZED: 146 + ubi_nvmem_remove(&nt->vi); 147 + fallthrough; 148 + case UBI_VOLUME_ADDED: 149 + ubi_nvmem_add(&nt->vi); 150 + break; 151 + case UBI_VOLUME_SHUTDOWN: 152 + ubi_nvmem_remove(&nt->vi); 153 + break; 154 + default: 155 + break; 156 + } 157 + return NOTIFY_OK; 158 + } 159 + 160 + static struct notifier_block nvmem_notifier = { 161 + .notifier_call = nvmem_notify, 162 + }; 163 + 164 + static int __init ubi_nvmem_init(void) 165 + { 166 + return ubi_register_volume_notifier(&nvmem_notifier, 0); 167 + } 168 + 169 + static void __exit ubi_nvmem_exit(void) 170 + { 171 + struct ubi_nvmem *unv, *tmp; 172 + 173 + mutex_lock(&devices_mutex); 174 + list_for_each_entry_safe(unv, tmp, &nvmem_devices, list) { 175 + nvmem_unregister(unv->nvmem); 176 + list_del(&unv->list); 177 + kfree(unv); 178 + } 179 + mutex_unlock(&devices_mutex); 180 + 181 + ubi_unregister_volume_notifier(&nvmem_notifier); 182 + } 183 + 184 + module_init(ubi_nvmem_init); 185 + module_exit(ubi_nvmem_exit); 186 + MODULE_DESCRIPTION("NVMEM layer over UBI volumes"); 187 + MODULE_AUTHOR("Daniel Golle"); 188 + MODULE_LICENSE("GPL");