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 10 /* List of all NVMEM devices */ 11 static LIST_HEAD(nvmem_devices); 12 static DEFINE_MUTEX(devices_mutex); 13 14 struct ubi_nvmem { 15 struct nvmem_device *nvmem; 16 int ubi_num; 17 int vol_id; 18 int usable_leb_size; 19 struct list_head list; 20 }; 21 22 static int ubi_nvmem_reg_read(void *priv, unsigned int from, 23 void *val, size_t bytes) 24 { 25 size_t to_read, bytes_left = bytes; 26 struct ubi_nvmem *unv = priv; 27 struct ubi_volume_desc *desc; 28 uint32_t offs; 29 uint32_t lnum; 30 int err = 0; 31 32 desc = ubi_open_volume(unv->ubi_num, unv->vol_id, UBI_READONLY); 33 if (IS_ERR(desc)) 34 return PTR_ERR(desc); 35 36 offs = from % unv->usable_leb_size; 37 lnum = from / unv->usable_leb_size; 38 while (bytes_left) { 39 to_read = unv->usable_leb_size - offs; 40 41 if (to_read > bytes_left) 42 to_read = bytes_left; 43 44 err = ubi_read(desc, lnum, val, offs, to_read); 45 if (err) 46 break; 47 48 lnum += 1; 49 offs = 0; 50 bytes_left -= to_read; 51 val += to_read; 52 } 53 ubi_close_volume(desc); 54 55 if (err) 56 return err; 57 58 return bytes_left == 0 ? 0 : -EIO; 59 } 60 61 static int ubi_nvmem_add(struct ubi_volume_info *vi) 62 { 63 struct device_node *np = dev_of_node(vi->dev); 64 struct nvmem_config config = {}; 65 struct ubi_nvmem *unv; 66 int ret; 67 68 if (!np) 69 return 0; 70 71 if (!of_get_child_by_name(np, "nvmem-layout")) 72 return 0; 73 74 if (WARN_ON_ONCE(vi->usable_leb_size <= 0) || 75 WARN_ON_ONCE(vi->size <= 0)) 76 return -EINVAL; 77 78 unv = kzalloc(sizeof(struct ubi_nvmem), GFP_KERNEL); 79 if (!unv) 80 return -ENOMEM; 81 82 config.id = NVMEM_DEVID_NONE; 83 config.dev = vi->dev; 84 config.name = dev_name(vi->dev); 85 config.owner = THIS_MODULE; 86 config.priv = unv; 87 config.reg_read = ubi_nvmem_reg_read; 88 config.size = vi->usable_leb_size * vi->size; 89 config.word_size = 1; 90 config.stride = 1; 91 config.read_only = true; 92 config.root_only = true; 93 config.ignore_wp = true; 94 config.of_node = np; 95 96 unv->ubi_num = vi->ubi_num; 97 unv->vol_id = vi->vol_id; 98 unv->usable_leb_size = vi->usable_leb_size; 99 unv->nvmem = nvmem_register(&config); 100 if (IS_ERR(unv->nvmem)) { 101 ret = dev_err_probe(vi->dev, PTR_ERR(unv->nvmem), 102 "Failed to register NVMEM device\n"); 103 kfree(unv); 104 return ret; 105 } 106 107 mutex_lock(&devices_mutex); 108 list_add_tail(&unv->list, &nvmem_devices); 109 mutex_unlock(&devices_mutex); 110 111 return 0; 112 } 113 114 static void ubi_nvmem_remove(struct ubi_volume_info *vi) 115 { 116 struct ubi_nvmem *unv_c, *unv = NULL; 117 118 mutex_lock(&devices_mutex); 119 list_for_each_entry(unv_c, &nvmem_devices, list) 120 if (unv_c->ubi_num == vi->ubi_num && unv_c->vol_id == vi->vol_id) { 121 unv = unv_c; 122 break; 123 } 124 125 if (!unv) { 126 mutex_unlock(&devices_mutex); 127 return; 128 } 129 130 list_del(&unv->list); 131 mutex_unlock(&devices_mutex); 132 nvmem_unregister(unv->nvmem); 133 kfree(unv); 134 } 135 136 /** 137 * nvmem_notify - UBI notification handler. 138 * @nb: registered notifier block 139 * @l: notification type 140 * @ns_ptr: pointer to the &struct ubi_notification object 141 */ 142 static int nvmem_notify(struct notifier_block *nb, unsigned long l, 143 void *ns_ptr) 144 { 145 struct ubi_notification *nt = ns_ptr; 146 147 switch (l) { 148 case UBI_VOLUME_RESIZED: 149 ubi_nvmem_remove(&nt->vi); 150 fallthrough; 151 case UBI_VOLUME_ADDED: 152 ubi_nvmem_add(&nt->vi); 153 break; 154 case UBI_VOLUME_SHUTDOWN: 155 ubi_nvmem_remove(&nt->vi); 156 break; 157 default: 158 break; 159 } 160 return NOTIFY_OK; 161 } 162 163 static struct notifier_block nvmem_notifier = { 164 .notifier_call = nvmem_notify, 165 }; 166 167 static int __init ubi_nvmem_init(void) 168 { 169 return ubi_register_volume_notifier(&nvmem_notifier, 0); 170 } 171 172 static void __exit ubi_nvmem_exit(void) 173 { 174 struct ubi_nvmem *unv, *tmp; 175 176 mutex_lock(&devices_mutex); 177 list_for_each_entry_safe(unv, tmp, &nvmem_devices, list) { 178 nvmem_unregister(unv->nvmem); 179 list_del(&unv->list); 180 kfree(unv); 181 } 182 mutex_unlock(&devices_mutex); 183 184 ubi_unregister_volume_notifier(&nvmem_notifier); 185 } 186 187 module_init(ubi_nvmem_init); 188 module_exit(ubi_nvmem_exit); 189 MODULE_DESCRIPTION("NVMEM layer over UBI volumes"); 190 MODULE_AUTHOR("Daniel Golle"); 191 MODULE_LICENSE("GPL"); 192