From nobody Thu Sep 11 20:40:51 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1AB71C0015E for ; Sat, 5 Aug 2023 16:27:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229964AbjHEQ1B (ORCPT ); Sat, 5 Aug 2023 12:27:01 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40058 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229917AbjHEQ05 (ORCPT ); Sat, 5 Aug 2023 12:26:57 -0400 Received: from pidgin.makrotopia.org (pidgin.makrotopia.org [185.142.180.65]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 740D94218 for ; Sat, 5 Aug 2023 09:26:44 -0700 (PDT) Received: from local by pidgin.makrotopia.org with esmtpsa (TLS1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.96) (envelope-from ) id 1qSK6y-0000fe-35; Sat, 05 Aug 2023 16:26:41 +0000 Date: Sat, 5 Aug 2023 17:26:33 +0100 From: Daniel Golle To: Randy Dunlap , Richard Weinberger , Miquel Raynal , Vignesh Raghavendra , linux-kernel@vger.kernel.org, linux-mtd@lists.infradead.org Subject: [PATCH v2 7/7] mtd: ubi: provide NVMEM layer over UBI volumes Message-ID: <3df11df592be6a8f681fc612217f75c2a04e8186.1691252291.git.daniel@makrotopia.org> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- drivers/mtd/ubi/Kconfig | 12 +++ drivers/mtd/ubi/Makefile | 1 + drivers/mtd/ubi/nvmem.c | 189 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 202 insertions(+) create mode 100644 drivers/mtd/ubi/nvmem.c diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig index 184118f9a2969..fb15d4549d55f 100644 --- a/drivers/mtd/ubi/Kconfig +++ b/drivers/mtd/ubi/Kconfig @@ -104,4 +104,16 @@ config MTD_UBI_BLOCK =20 If in doubt, say "N". =20 +config MTD_UBI_NVMEM + tristate "UBI virtual NVMEM" + default n + depends on NVMEM + help + This option enables 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 diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile index 543673605ca72..4b51aaf00d1a2 100644 --- a/drivers/mtd/ubi/Makefile +++ b/drivers/mtd/ubi/Makefile @@ -7,3 +7,4 @@ ubi-$(CONFIG_MTD_UBI_FASTMAP) +=3D fastmap.o ubi-$(CONFIG_MTD_UBI_BLOCK) +=3D block.o =20 obj-$(CONFIG_MTD_UBI_GLUEBI) +=3D gluebi.o +obj-$(CONFIG_MTD_UBI_NVMEM) +=3D nvmem.o diff --git a/drivers/mtd/ubi/nvmem.c b/drivers/mtd/ubi/nvmem.c new file mode 100644 index 0000000000000..dd7cc6afb8d00 --- /dev/null +++ b/drivers/mtd/ubi/nvmem.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Daniel Golle + */ + +/* UBI NVMEM provider */ +#include "ubi.h" +#include + +/* 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) +{ + struct ubi_nvmem *unv =3D priv; + struct ubi_volume_desc *desc; + int err =3D 0, lnum, offs, bytes_left; + size_t to_read; + + desc =3D ubi_open_volume(unv->ubi_num, unv->vol_id, UBI_READONLY); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + lnum =3D div_u64_rem(from, unv->usable_leb_size, &offs); + bytes_left =3D bytes; + while (bytes_left) { + to_read =3D unv->usable_leb_size - offs; + + if (to_read > bytes_left) + to_read =3D bytes_left; + + err =3D ubi_read(desc, lnum, val, offs, to_read); + if (err) + break; + + lnum +=3D 1; + offs =3D 0; + bytes_left -=3D to_read; + val +=3D to_read; + } + ubi_close_volume(desc); + + if (err) + return err; + + return bytes_left =3D=3D 0 ? 0 : -EIO; +} + +static int ubi_nvmem_add(struct ubi_volume_info *vi) +{ + struct nvmem_config config =3D {}; + struct ubi_nvmem *unv; + int ret; + + if (!device_is_compatible(vi->dev, "nvmem-cells")) + return 0; + + unv =3D kzalloc(sizeof(struct ubi_nvmem), GFP_KERNEL); + if (!unv) + return -ENOMEM; + + config.id =3D NVMEM_DEVID_NONE; + config.dev =3D vi->dev; + config.name =3D dev_name(vi->dev); + config.owner =3D THIS_MODULE; + config.priv =3D unv; + config.reg_read =3D ubi_nvmem_reg_read; + config.size =3D vi->usable_leb_size * vi->size; + config.word_size =3D 1; + config.stride =3D 1; + config.read_only =3D true; + config.root_only =3D true; + config.ignore_wp =3D true; + config.of_node =3D dev_of_node(vi->dev); + + if (!config.of_node) + config.no_of_node =3D true; + + unv->ubi_num =3D vi->ubi_num; + unv->vol_id =3D vi->vol_id; + unv->usable_leb_size =3D vi->usable_leb_size; + unv->nvmem =3D nvmem_register(&config); + if (IS_ERR(unv->nvmem)) { + /* Just ignore if there is no NVMEM support in the kernel */ + if (PTR_ERR(unv->nvmem) =3D=3D -EOPNOTSUPP) + ret =3D 0; + else + ret =3D 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 =3D NULL; + + mutex_lock(&devices_mutex); + list_for_each_entry(unv_c, &nvmem_devices, list) + if (unv_c->ubi_num =3D=3D vi->ubi_num && unv_c->vol_id =3D=3D vi->vol_id= ) { + unv =3D 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 =3D 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 =3D { + .notifier_call =3D 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"); --=20 2.41.0