Received: by 2002:a05:7208:9594:b0:7e:5202:c8b4 with SMTP id gs20csp1033039rbb; Sun, 25 Feb 2024 16:24:38 -0800 (PST) X-Forwarded-Encrypted: i=3; AJvYcCVD6oMvVa0rKhPobSP2FQOf9MqjtlqUDceTlY4lzSkNT9xNzNIVAIdvXOB3MjtDI6L/gk7SXty9hVErjJbDQx4fE4qBgbGCFBpCj7Ppig== X-Google-Smtp-Source: AGHT+IHYYFYQgSZzKxFZTpRXW168BS0PuGAp8zIPscc/AqrjiQpFaUn643Km4GRP/ahDMl+yg89V X-Received: by 2002:a2e:a783:0:b0:2d2:50ba:e5b6 with SMTP id c3-20020a2ea783000000b002d250bae5b6mr3712924ljf.10.1708907077431; Sun, 25 Feb 2024 16:24:37 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1708907077; cv=pass; d=google.com; s=arc-20160816; b=e/32NV+b243QP6DKTNjpkI6SmIhI2ikliEx1a/aSitbiVQvpRnWpgv9x6PYFmWJERY 0b5Vi/gcbpsuHqpt/4KiOktPrbnsalW/gZ3r/2/4PrfkDvF1aPunmKVZXf8JLEMSXqhQ E/poYGI6JfB+AiSVwATugceBHwBxd6vAQ+G5t0b3fWsqj8IXfITSG2k/H1zlgyz4wZcK GWjOfbwa0tq0/zUmQmlfK0Z/W42a3Ql1A9afFi/rYpJep3/sC21ruvkLL9Xv4Hy1z6CU +S8TY7kXYkmQlvt4Pwnlw9S5Tf+1LJXspd4GQ7P5mXgXoiRjJhJbPLBaA8+hnWPCXoCi yf1Q== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=in-reply-to:content-disposition:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:references:message-id:subject:to :from:date; bh=V7gaaz/I3lVkdB2sJ76BXQ8iZfViqFCHVbhgfjEqsqo=; fh=iCY7pZACTeEM/aV1VSRdKj3GWTB74ZtqN+MwTbm3QUU=; b=JTfGjDKYOtmdUYEBmWkZ0Ymf3FGAHH4vMWQBye8bMpyF25mPTt+rhpilwCfJT0YkOX m8OVZoWEFSWt6jGuy4QXQSOCGtfDTZoHwoiFlfpJaeu+cGxzjZz3uqvp9X9cKDv6d7TQ PcRzFHuGfQwk9qGyjlfPKPegQNawHq2mXfI7NMwVe22Sgqzu2vPmtaSqwSDa+1B0ZSwj grqz5VI5tPLSW+Rm5/WnONT971jMZNwMHKPV/nOBPlZwldfmm6sWFo3L+/G96ulq7+Yr SJEFnQRkok3KLJoMXy81ss6zN9HR6nAzAYF3rO8DXs6ptmITcmfGc0vR8W1gqQHFRZM5 /GoQ==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; arc=pass (i=1 spf=pass spfdomain=makrotopia.org); spf=pass (google.com: domain of linux-kernel+bounces-80361-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:4601:e00::3 as permitted sender) smtp.mailfrom="linux-kernel+bounces-80361-linux.lists.archive=gmail.com@vger.kernel.org" Return-Path: Received: from am.mirrors.kernel.org (am.mirrors.kernel.org. [2604:1380:4601:e00::3]) by mx.google.com with ESMTPS id u10-20020a50eaca000000b00565dcc278fasi659338edp.540.2024.02.25.16.24.37 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 25 Feb 2024 16:24:37 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-80361-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:4601:e00::3 as permitted sender) client-ip=2604:1380:4601:e00::3; Authentication-Results: mx.google.com; arc=pass (i=1 spf=pass spfdomain=makrotopia.org); spf=pass (google.com: domain of linux-kernel+bounces-80361-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:4601:e00::3 as permitted sender) smtp.mailfrom="linux-kernel+bounces-80361-linux.lists.archive=gmail.com@vger.kernel.org" Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by am.mirrors.kernel.org (Postfix) with ESMTPS id 078A81F214A7 for ; Mon, 26 Feb 2024 00:24:37 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id D29AAD50F; Mon, 26 Feb 2024 00:23:54 +0000 (UTC) Received: from pidgin.makrotopia.org (pidgin.makrotopia.org [185.142.180.65]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 84B3F33EC; Mon, 26 Feb 2024 00:23:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.142.180.65 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708907034; cv=none; b=vE5rD4VYqdmhfBeQgX0LPZIoBrSeJBNES6qoDFAFrf3FWQlq/p7uXkv6fQzfo8Sc3C6kGNpSd0mCLPPIJC/VnoK0KT7jurm2jC224JlqxEkYDwYMaMr/NZ65IiPp8ybTZm2qaZhy9zmO7fmgU4M96T0ae7jqXRxMQ1bDSnk3SoM= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708907034; c=relaxed/simple; bh=CF56sxrMUbhfd9jdc++hHknqQjW6Pw4Hu7N6DnVeMhE=; h=Date:From:To:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=KeAHZH83r/HNzshF3XnT395FZkZlfLK8vbMLiImKsB2Go8oKmKY5jAxSxjk9ntsjfYkbt54CV8aVQHIyHs2Rs2cSEhiQtghqM0+UbZrP75/gNLQGWvSVMLlNYVbAjboMkSXHIvIueUdsRuu41arGN47HWBd3cgRsTBkGpWJt5cA= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=makrotopia.org; spf=pass smtp.mailfrom=makrotopia.org; arc=none smtp.client-ip=185.142.180.65 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=makrotopia.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=makrotopia.org Received: from local by pidgin.makrotopia.org with esmtpsa (TLS1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.96.2) (envelope-from ) id 1reOmW-0001qY-0d; Mon, 26 Feb 2024 00:23:44 +0000 Date: Mon, 26 Feb 2024 00:23:41 +0000 From: Daniel Golle To: Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Daniel Golle , linux-mtd@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v8 7/7] mtd: ubi: provide NVMEM layer over UBI volumes Message-ID: <3ac88da4adc342af428b359cd27037b1498f6c91.1708906456.git.daniel@makrotopia.org> References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: 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 | 13 +++ drivers/mtd/ubi/Makefile | 1 + drivers/mtd/ubi/nvmem.c | 191 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 drivers/mtd/ubi/nvmem.c diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig index 7499a540121e8..e28a3af83c0eb 100644 --- a/drivers/mtd/ubi/Kconfig +++ b/drivers/mtd/ubi/Kconfig @@ -113,4 +113,17 @@ config MTD_UBI_FAULT_INJECTION testing purposes. If in doubt, say "N". + +config MTD_UBI_NVMEM + tristate "UBI virtual NVMEM" + default n + depends on NVMEM + help + This option enabled 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) += fastmap.o ubi-$(CONFIG_MTD_UBI_BLOCK) += block.o obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o +obj-$(CONFIG_MTD_UBI_NVMEM) += nvmem.o diff --git a/drivers/mtd/ubi/nvmem.c b/drivers/mtd/ubi/nvmem.c new file mode 100644 index 0000000000000..5820a170d2512 --- /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 + */ + +/* UBI NVMEM provider */ +#include "ubi.h" +#include +#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) +{ + uint32_t bytes_left, offs, to_read; + struct ubi_nvmem *unv = priv; + struct ubi_volume_desc *desc; + 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); + + bytes_left = bytes; + 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"); -- 2.44.0