File Index
Files and directories
Sign in
FILE

nvmem.c

File preview with syntax highlighting.

linux-6.19.14/drivers/mtd/ubi/nvmem.c 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
 10/* List of all NVMEM devices */
 11static LIST_HEAD(nvmem_devices);
 12static DEFINE_MUTEX(devices_mutex);
 13
 14struct 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
 22static 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 0;
 59}
 60
 61static 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
114static 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 */
142static 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
163static struct notifier_block nvmem_notifier = {
164	.notifier_call = nvmem_notify,
165};
166
167static int __init ubi_nvmem_init(void)
168{
169	return ubi_register_volume_notifier(&nvmem_notifier, 0);
170}
171
172static 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
187module_init(ubi_nvmem_init);
188module_exit(ubi_nvmem_exit);
189MODULE_DESCRIPTION("NVMEM layer over UBI volumes");
190MODULE_AUTHOR("Daniel Golle");
191MODULE_LICENSE("GPL");
~/ Read-only file preview.