From f37af2760ea92cec3fbdcfceb6df7be57a618165 Mon Sep 17 00:00:00 2001 From: Abdellatif El Khlifi Date: Mon, 17 Apr 2023 10:11:52 +0100 Subject: [PATCH 1/7] drivers/mtd/nvmxip: introduce NVM XIP block storage emulation add block storage emulation for NVM XIP flash devices Some paltforms such as Corstone-1000 need to see NVM XIP raw flash as a block storage device with read only capability. Here NVM flash devices are devices with addressable memory (e.g: QSPI NOR flash). The implementation is generic and can be used by different platforms. Two drivers are provided as follows. nvmxip-blk : a generic block driver allowing to read from the XIP flash nvmxip Uclass driver : When a device is described in the DT and associated with UCLASS_NVMXIP, the Uclass creates a block device and binds it with the nvmxip-blk. Platforms can use multiple NVM XIP devices at the same time by defining a DT node for each one of them. Signed-off-by: Abdellatif El Khlifi --- MAINTAINERS | 6 ++ doc/develop/driver-model/index.rst | 1 + doc/develop/driver-model/nvmxip.rst | 48 +++++++++++ drivers/block/blk-uclass.c | 1 + drivers/mtd/Kconfig | 2 + drivers/mtd/Makefile | 1 + drivers/mtd/nvmxip/Kconfig | 13 +++ drivers/mtd/nvmxip/Makefile | 7 ++ drivers/mtd/nvmxip/nvmxip-uclass.c | 67 ++++++++++++++++ drivers/mtd/nvmxip/nvmxip.c | 119 ++++++++++++++++++++++++++++ drivers/mtd/nvmxip/nvmxip.h | 32 ++++++++ include/dm/uclass-id.h | 1 + 12 files changed, 298 insertions(+) create mode 100644 doc/develop/driver-model/nvmxip.rst create mode 100644 drivers/mtd/nvmxip/Kconfig create mode 100644 drivers/mtd/nvmxip/Makefile create mode 100644 drivers/mtd/nvmxip/nvmxip-uclass.c create mode 100644 drivers/mtd/nvmxip/nvmxip.c create mode 100644 drivers/mtd/nvmxip/nvmxip.h diff --git a/MAINTAINERS b/MAINTAINERS index 02a5a8682f8..fe87b39939f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1204,6 +1204,12 @@ F: cmd/nvme.c F: include/nvme.h F: doc/develop/driver-model/nvme.rst +NVMXIP +M: Abdellatif El Khlifi +S: Maintained +F: doc/develop/driver-model/nvmxip.rst +F: drivers/mtd/nvmxip/ + NVMEM M: Sean Anderson S: Maintained diff --git a/doc/develop/driver-model/index.rst b/doc/develop/driver-model/index.rst index 7366ef818c5..8e12bbd9366 100644 --- a/doc/develop/driver-model/index.rst +++ b/doc/develop/driver-model/index.rst @@ -20,6 +20,7 @@ subsystems livetree migration nvme + nvmxip of-plat pci-info pmic-framework diff --git a/doc/develop/driver-model/nvmxip.rst b/doc/develop/driver-model/nvmxip.rst new file mode 100644 index 00000000000..fe087b13d22 --- /dev/null +++ b/doc/develop/driver-model/nvmxip.rst @@ -0,0 +1,48 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +NVM XIP Block Storage Emulation Driver +======================================= + +Summary +------- + +Non-Volatile Memory devices with addressable memory (e.g: QSPI NOR flash) could +be used for block storage needs (e.g: parsing a GPT layout in a raw QSPI NOR flash). + +The NVMXIP Uclass provides this functionality and can be used for any 64-bit platform. + +The NVMXIP Uclass provides the following drivers: + + nvmxip-blk block driver: + + A generic block driver allowing to read from the XIP flash. + The driver belongs to UCLASS_BLK. + The driver implemented by drivers/mtd/nvmxip/nvmxip.c + + nvmxip Uclass driver: + + When a device is described in the DT and associated with UCLASS_NVMXIP, + the Uclass creates a block device and binds it with the nvmxip-blk. + The Uclass driver implemented by drivers/mtd/nvmxip/nvmxip-uclass.c + + The implementation is generic and can be used by different platforms. + +Supported hardware +-------------------------------- + +Any 64-bit plaform. + +Configuration +---------------------- + +config NVMXIP + This option allows the emulation of a block storage device + on top of a direct access non volatile memory XIP flash devices. + This support provides the read operation. + This option provides the block storage driver nvmxip-blk which + handles the read operation. This driver is HW agnostic and can support + multiple flash devices at the same time. + +Contributors +------------ + * Abdellatif El Khlifi diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c index cb73faaedaf..614b975e25c 100644 --- a/drivers/block/blk-uclass.c +++ b/drivers/block/blk-uclass.c @@ -28,6 +28,7 @@ static struct { { UCLASS_AHCI, "sata" }, { UCLASS_HOST, "host" }, { UCLASS_NVME, "nvme" }, + { UCLASS_NVMXIP, "nvmxip" }, { UCLASS_EFI_MEDIA, "efi" }, { UCLASS_EFI_LOADER, "efiloader" }, { UCLASS_VIRTIO, "virtio" }, diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index af45ef00dae..5fa88dae5f3 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -270,4 +270,6 @@ source "drivers/mtd/spi/Kconfig" source "drivers/mtd/ubi/Kconfig" +source "drivers/mtd/nvmxip/Kconfig" + endmenu diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 3a78590aaaa..c638980ea2b 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -25,6 +25,7 @@ obj-y += nand/ obj-y += onenand/ obj-y += spi/ obj-$(CONFIG_MTD_UBI) += ubi/ +obj-$(CONFIG_NVMXIP) += nvmxip/ #SPL/TPL build else diff --git a/drivers/mtd/nvmxip/Kconfig b/drivers/mtd/nvmxip/Kconfig new file mode 100644 index 00000000000..ef53fc3c790 --- /dev/null +++ b/drivers/mtd/nvmxip/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2023 Arm Limited and/or its affiliates +# Authors: +# Abdellatif El Khlifi + +config NVMXIP + bool "NVM XIP devices support" + select BLK + help + This option allows the emulation of a block storage device + on top of a direct access non volatile memory XIP flash devices. + This support provides the read operation. diff --git a/drivers/mtd/nvmxip/Makefile b/drivers/mtd/nvmxip/Makefile new file mode 100644 index 00000000000..07890982c7e --- /dev/null +++ b/drivers/mtd/nvmxip/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2023 Arm Limited and/or its affiliates +# Authors: +# Abdellatif El Khlifi + +obj-y += nvmxip-uclass.o nvmxip.o diff --git a/drivers/mtd/nvmxip/nvmxip-uclass.c b/drivers/mtd/nvmxip/nvmxip-uclass.c new file mode 100644 index 00000000000..9f96041e3d8 --- /dev/null +++ b/drivers/mtd/nvmxip/nvmxip-uclass.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi + */ + +#include +#include +#include +#include +#include "nvmxip.h" + +/* LBA Macros */ + +#define DEFAULT_LBA_SHIFT 10 /* 1024 bytes per block */ +#define DEFAULT_LBA_COUNT 1024 /* block count */ + +#define DEFAULT_LBA_SZ BIT(DEFAULT_LBA_SHIFT) + +/** + * nvmxip_post_bind() - post binding treatments + * @dev: the NVMXIP device + * + * Create and probe a child block device. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int nvmxip_post_bind(struct udevice *udev) +{ + int ret; + struct udevice *bdev = NULL; + char bdev_name[NVMXIP_BLKDEV_NAME_SZ + 1]; + int devnum; + + devnum = uclass_id_count(UCLASS_NVMXIP); + snprintf(bdev_name, NVMXIP_BLKDEV_NAME_SZ, "blk#%d", devnum); + + ret = blk_create_devicef(udev, NVMXIP_BLKDRV_NAME, bdev_name, UCLASS_NVMXIP, + devnum, DEFAULT_LBA_SZ, + DEFAULT_LBA_COUNT, &bdev); + if (ret) { + log_err("[%s]: failure during creation of the block device %s, error %d\n", + udev->name, bdev_name, ret); + return ret; + } + + ret = blk_probe_or_unbind(bdev); + if (ret) { + log_err("[%s]: failure during probing the block device %s, error %d\n", + udev->name, bdev_name, ret); + return ret; + } + + log_info("[%s]: the block device %s ready for use\n", udev->name, bdev_name); + + return 0; +} + +UCLASS_DRIVER(nvmxip) = { + .name = "nvmxip", + .id = UCLASS_NVMXIP, + .post_bind = nvmxip_post_bind, +}; diff --git a/drivers/mtd/nvmxip/nvmxip.c b/drivers/mtd/nvmxip/nvmxip.c new file mode 100644 index 00000000000..a359e3b4822 --- /dev/null +++ b/drivers/mtd/nvmxip/nvmxip.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi + */ + +#include +#include +#include +#include +#include +#include +#include +#include "nvmxip.h" + +/** + * nvmxip_mmio_rawread() - read from the XIP flash + * @address: address of the data + * @value: pointer to where storing the value read + * + * Read raw data from the XIP flash. + * + * Return: + * + * Always return 0. + */ +static int nvmxip_mmio_rawread(const phys_addr_t address, u64 *value) +{ + *value = readq(address); + return 0; +} + +/** + * nvmxip_blk_read() - block device read operation + * @dev: the block device + * @blknr: first block number to read from + * @blkcnt: number of blocks to read + * @buffer: destination buffer + * + * Read data from the block storage device. + * + * Return: + * + * number of blocks read on success. Otherwise, failure + */ +static ulong nvmxip_blk_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, void *buffer) +{ + struct nvmxip_plat *plat = dev_get_plat(dev->parent); + struct blk_desc *desc = dev_get_uclass_plat(dev); + /* number of the u64 words to read */ + u32 qwords = (blkcnt * desc->blksz) / sizeof(u64); + /* physical address of the first block to read */ + phys_addr_t blkaddr = plat->phys_base + blknr * desc->blksz; + u64 *virt_blkaddr; + u64 *pdst = buffer; + uint qdata_idx; + + if (!pdst) + return -EINVAL; + + log_debug("[%s]: reading from blknr: %lu , blkcnt: %lu\n", dev->name, blknr, blkcnt); + + virt_blkaddr = map_sysmem(blkaddr, 0); + + /* assumption: the data is virtually contiguous */ + + for (qdata_idx = 0 ; qdata_idx < qwords ; qdata_idx++) + nvmxip_mmio_rawread((phys_addr_t)(virt_blkaddr + qdata_idx), pdst++); + + log_debug("[%s]: src[0]: 0x%llx , dst[0]: 0x%llx , src[-1]: 0x%llx , dst[-1]: 0x%llx\n", + dev->name, + *virt_blkaddr, + *(u64 *)buffer, + *(u64 *)((u8 *)virt_blkaddr + desc->blksz * blkcnt - sizeof(u64)), + *(u64 *)((u8 *)buffer + desc->blksz * blkcnt - sizeof(u64))); + + unmap_sysmem(virt_blkaddr); + + return blkcnt; +} + +/** + * nvmxip_blk_probe() - block storage device probe + * @dev: the block storage device + * + * Initialize the block storage descriptor. + * + * Return: + * + * Always return 0. + */ +static int nvmxip_blk_probe(struct udevice *dev) +{ + struct nvmxip_plat *plat = dev_get_plat(dev->parent); + struct blk_desc *desc = dev_get_uclass_plat(dev); + + desc->lba = plat->lba; + desc->log2blksz = plat->lba_shift; + desc->blksz = BIT(plat->lba_shift); + desc->bdev = dev; + + log_debug("[%s]: block storage layout\n lbas: %lu , log2blksz: %d, blksz: %lu\n", + dev->name, desc->lba, desc->log2blksz, desc->blksz); + + return 0; +} + +static const struct blk_ops nvmxip_blk_ops = { + .read = nvmxip_blk_read, +}; + +U_BOOT_DRIVER(nvmxip_blk) = { + .name = NVMXIP_BLKDRV_NAME, + .id = UCLASS_BLK, + .probe = nvmxip_blk_probe, + .ops = &nvmxip_blk_ops, +}; diff --git a/drivers/mtd/nvmxip/nvmxip.h b/drivers/mtd/nvmxip/nvmxip.h new file mode 100644 index 00000000000..f4ef37725d2 --- /dev/null +++ b/drivers/mtd/nvmxip/nvmxip.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi + */ + +#ifndef __DRIVER_NVMXIP_H__ +#define __DRIVER_NVMXIP_H__ + +#include + +#define NVMXIP_BLKDRV_NAME "nvmxip-blk" +#define NVMXIP_BLKDEV_NAME_SZ 20 + +/** + * struct nvmxip_plat - the NVMXIP driver plat + * + * @phys_base: NVM XIP device base address + * @lba_shift: block size shift count + * @lba: number of blocks + * + * The NVMXIP information read from the DT. + */ +struct nvmxip_plat { + phys_addr_t phys_base; + u32 lba_shift; + lbaint_t lba; +}; + +#endif /* __DRIVER_NVMXIP_H__ */ diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 576237b9548..5386c3faf9f 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -89,6 +89,7 @@ enum uclass_id { UCLASS_NOP, /* No-op devices */ UCLASS_NORTHBRIDGE, /* Intel Northbridge / SDRAM controller */ UCLASS_NVME, /* NVM Express device */ + UCLASS_NVMXIP, /* NVM XIP devices */ UCLASS_P2SB, /* (x86) Primary-to-Sideband Bus */ UCLASS_PANEL, /* Display panel, such as an LCD */ UCLASS_PANEL_BACKLIGHT, /* Backlight controller for panel */ From 9e115ace358d9ed79844b27b06b3a0e8b23d49da Mon Sep 17 00:00:00 2001 From: Abdellatif El Khlifi Date: Mon, 17 Apr 2023 10:11:53 +0100 Subject: [PATCH 2/7] drivers/mtd/nvmxip: introduce QSPI XIP driver add nvmxip_qspi driver under UCLASS_NVMXIP The device associated with this driver is the parent of the blk# device nvmxip_qspi can be reused by other platforms. If the platform has custom settings to apply before using the flash, then the platform can provide its own parent driver belonging to UCLASS_NVMXIP and reuse nvmxip-blk driver. The custom driver can be implemented like nvmxip_qspi in addition to the platform custom settings. Platforms can use multiple NVM XIP devices at the same time by defining a DT node for each one of them. For more details please refer to doc/develop/driver-model/nvmxip_qspi.rst Signed-off-by: Abdellatif El Khlifi --- MAINTAINERS | 1 + doc/develop/driver-model/nvmxip.rst | 45 +++++++++++- .../nvmxip/nvmxip_qspi.txt | 56 +++++++++++++++ drivers/mtd/nvmxip/Kconfig | 6 ++ drivers/mtd/nvmxip/Makefile | 1 + drivers/mtd/nvmxip/nvmxip_qspi.c | 70 +++++++++++++++++++ 6 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 doc/device-tree-bindings/nvmxip/nvmxip_qspi.txt create mode 100644 drivers/mtd/nvmxip/nvmxip_qspi.c diff --git a/MAINTAINERS b/MAINTAINERS index fe87b39939f..fd155ca8788 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1208,6 +1208,7 @@ NVMXIP M: Abdellatif El Khlifi S: Maintained F: doc/develop/driver-model/nvmxip.rst +F: doc/device-tree-bindings/nvmxip/nvmxip_qspi.txt F: drivers/mtd/nvmxip/ NVMEM diff --git a/doc/develop/driver-model/nvmxip.rst b/doc/develop/driver-model/nvmxip.rst index fe087b13d22..09afdbcccf5 100644 --- a/doc/develop/driver-model/nvmxip.rst +++ b/doc/develop/driver-model/nvmxip.rst @@ -25,7 +25,33 @@ The NVMXIP Uclass provides the following drivers: the Uclass creates a block device and binds it with the nvmxip-blk. The Uclass driver implemented by drivers/mtd/nvmxip/nvmxip-uclass.c - The implementation is generic and can be used by different platforms. + nvmxip_qspi driver : + + The driver probed with the DT and is the parent of the blk# device. + nvmxip_qspi can be reused by other platforms. If the platform + has custom settings to apply before using the flash, then the platform + can provide its own parent driver belonging to UCLASS_NVMXIP and reuse + nvmxip-blk. The custom driver can be implemented like nvmxip_qspi in + addition to the platform custom settings. + The nvmxip_qspi driver belongs to UCLASS_NVMXIP. + The driver implemented by drivers/mtd/nvmxip/nvmxip_qspi.c + + For example, if we have two NVMXIP devices described in the DT + The devices hierarchy is as follows: + +:: + + => dm tree + + Class Index Probed Driver Name + ----------------------------------------------------------- + ... + nvmxip 0 [ + ] nvmxip_qspi |-- nvmxip-qspi1@08000000 + blk 3 [ + ] nvmxip-blk | `-- nvmxip-qspi1@08000000.blk#1 + nvmxip 1 [ + ] nvmxip_qspi |-- nvmxip-qspi2@08200000 + blk 4 [ + ] nvmxip-blk | `-- nvmxip-qspi2@08200000.blk#2 + +The implementation is generic and can be used by different platforms. Supported hardware -------------------------------- @@ -43,6 +69,23 @@ config NVMXIP handles the read operation. This driver is HW agnostic and can support multiple flash devices at the same time. +config NVMXIP_QSPI + This option allows the emulation of a block storage device on top of a QSPI XIP flash. + Any platform that needs to emulate one or multiple QSPI XIP flash devices can turn this + option on to enable the functionality. NVMXIP config is selected automatically. + Platforms that need to add custom treatments before accessing to the flash, can + write their own driver (same as nvmxip_qspi in addition to the custom settings). + +Device Tree nodes +-------------------- + +Multiple QSPI XIP flash devices can be used at the same time by describing them through DT +nodes. + +Please refer to the documentation of the DT binding at: + +doc/device-tree-bindings/nvmxip/nvmxip_qspi.txt + Contributors ------------ * Abdellatif El Khlifi diff --git a/doc/device-tree-bindings/nvmxip/nvmxip_qspi.txt b/doc/device-tree-bindings/nvmxip/nvmxip_qspi.txt new file mode 100644 index 00000000000..cc60e9efdcd --- /dev/null +++ b/doc/device-tree-bindings/nvmxip/nvmxip_qspi.txt @@ -0,0 +1,56 @@ +Specifying NVMXIP information for devices +====================================== + +QSPI XIP flash device nodes +--------------------------- + +Each flash device should have its own node. + +Each node must specify the following fields: + +1) + compatible = "nvmxip,qspi"; + +This allows to bind the flash device with the nvmxip_qspi driver +If a platform has its own driver, please provide your own compatible +string. + +2) + reg = <0x0 0x08000000 0x0 0x00200000>; + +The start address and size of the flash device. The values give here are an +example (when the cell size is 2). + +When cell size is 1, the reg field looks like this: + + reg = <0x08000000 0x00200000>; + +3) + + lba_shift = <9>; + +The number of bit shifts used to calculate the size in bytes of one block. +In this example the block size is 1 << 9 = 2 ^ 9 = 512 bytes + +4) + + lba = <4096>; + +The number of blocks. + +Example of multiple flash devices +---------------------------------------------------- + + nvmxip-qspi1@08000000 { + compatible = "nvmxip,qspi"; + reg = <0x0 0x08000000 0x0 0x00200000>; + lba_shift = <9>; + lba = <4096>; + }; + + nvmxip-qspi2@08200000 { + compatible = "nvmxip,qspi"; + reg = <0x0 0x08200000 0x0 0x00100000>; + lba_shift = <9>; + lba = <2048>; + }; diff --git a/drivers/mtd/nvmxip/Kconfig b/drivers/mtd/nvmxip/Kconfig index ef53fc3c790..3ef71050264 100644 --- a/drivers/mtd/nvmxip/Kconfig +++ b/drivers/mtd/nvmxip/Kconfig @@ -11,3 +11,9 @@ config NVMXIP This option allows the emulation of a block storage device on top of a direct access non volatile memory XIP flash devices. This support provides the read operation. + +config NVMXIP_QSPI + bool "QSPI XIP support" + select NVMXIP + help + This option allows the emulation of a block storage device on top of a QSPI XIP flash diff --git a/drivers/mtd/nvmxip/Makefile b/drivers/mtd/nvmxip/Makefile index 07890982c7e..54eacc102e6 100644 --- a/drivers/mtd/nvmxip/Makefile +++ b/drivers/mtd/nvmxip/Makefile @@ -5,3 +5,4 @@ # Abdellatif El Khlifi obj-y += nvmxip-uclass.o nvmxip.o +obj-$(CONFIG_NVMXIP_QSPI) += nvmxip_qspi.o diff --git a/drivers/mtd/nvmxip/nvmxip_qspi.c b/drivers/mtd/nvmxip/nvmxip_qspi.c new file mode 100644 index 00000000000..7221fd1cb46 --- /dev/null +++ b/drivers/mtd/nvmxip/nvmxip_qspi.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi + */ + +#include +#include +#include +#include +#include "nvmxip.h" + +#include +DECLARE_GLOBAL_DATA_PTR; + +#define NVMXIP_QSPI_DRV_NAME "nvmxip_qspi" + +/** + * nvmxip_qspi_of_to_plat() -read from DT + * @dev: the NVMXIP device + * + * Read from the DT the NVMXIP information. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int nvmxip_qspi_of_to_plat(struct udevice *dev) +{ + struct nvmxip_plat *plat = dev_get_plat(dev); + int ret; + + plat->phys_base = (phys_addr_t)dev_read_addr(dev); + if (plat->phys_base == FDT_ADDR_T_NONE) { + log_err("[%s]: can not get base address from device tree\n", dev->name); + return -EINVAL; + } + + ret = dev_read_u32(dev, "lba_shift", &plat->lba_shift); + if (ret) { + log_err("[%s]: can not get lba_shift from device tree\n", dev->name); + return -EINVAL; + } + + ret = dev_read_u32(dev, "lba", (u32 *)&plat->lba); + if (ret) { + log_err("[%s]: can not get lba from device tree\n", dev->name); + return -EINVAL; + } + + log_debug("[%s]: XIP device base addr: 0x%llx , lba_shift: %d , lbas: %lu\n", + dev->name, plat->phys_base, plat->lba_shift, plat->lba); + + return 0; +} + +static const struct udevice_id nvmxip_qspi_ids[] = { + { .compatible = "nvmxip,qspi" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(nvmxip_qspi) = { + .name = NVMXIP_QSPI_DRV_NAME, + .id = UCLASS_NVMXIP, + .of_match = nvmxip_qspi_ids, + .of_to_plat = nvmxip_qspi_of_to_plat, + .plat_auto = sizeof(struct nvmxip_plat), +}; From 4b6d114d6d30dafd18c27fb00608b2f6038ac5ff Mon Sep 17 00:00:00 2001 From: Abdellatif El Khlifi Date: Mon, 17 Apr 2023 10:11:54 +0100 Subject: [PATCH 3/7] sandbox64: fix: return unsigned long in readq() make readq return unsigned long readq should return 64-bit data Signed-off-by: Abdellatif El Khlifi Reviewed-by: Simon Glass --- arch/sandbox/cpu/cpu.c | 2 +- arch/sandbox/include/asm/io.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/sandbox/cpu/cpu.c b/arch/sandbox/cpu/cpu.c index 636d3545b95..248d17a85c8 100644 --- a/arch/sandbox/cpu/cpu.c +++ b/arch/sandbox/cpu/cpu.c @@ -230,7 +230,7 @@ phys_addr_t map_to_sysmem(const void *ptr) return mentry->tag; } -unsigned int sandbox_read(const void *addr, enum sandboxio_size_t size) +unsigned long sandbox_read(const void *addr, enum sandboxio_size_t size) { struct sandbox_state *state = state_get_current(); diff --git a/arch/sandbox/include/asm/io.h b/arch/sandbox/include/asm/io.h index ad6c29a4e26..31ab7289b4b 100644 --- a/arch/sandbox/include/asm/io.h +++ b/arch/sandbox/include/asm/io.h @@ -45,7 +45,7 @@ static inline void unmap_sysmem(const void *vaddr) /* Map from a pointer to our RAM buffer */ phys_addr_t map_to_sysmem(const void *ptr); -unsigned int sandbox_read(const void *addr, enum sandboxio_size_t size); +unsigned long sandbox_read(const void *addr, enum sandboxio_size_t size); void sandbox_write(void *addr, unsigned int val, enum sandboxio_size_t size); #define readb(addr) sandbox_read((const void *)addr, SB_SIZE_8) From cc89b7cf419223a746ab3cfc5741a19cd40ebabe Mon Sep 17 00:00:00 2001 From: Abdellatif El Khlifi Date: Mon, 17 Apr 2023 10:11:55 +0100 Subject: [PATCH 4/7] sandbox64: add support for NVMXIP QSPI enable NVMXIP QSPI for sandbox 64-bit Adding two NVM XIP QSPI storage devices. Signed-off-by: Abdellatif El Khlifi Reviewed-by: Simon Glass --- arch/sandbox/dts/sandbox64.dts | 13 +++++++++++++ arch/sandbox/dts/test.dts | 14 ++++++++++++++ configs/sandbox64_defconfig | 1 + doc/develop/driver-model/nvmxip.rst | 2 +- doc/device-tree-bindings/nvmxip/nvmxip_qspi.txt | 6 +++--- drivers/mtd/nvmxip/nvmxip-uclass.c | 7 +++++++ 6 files changed, 39 insertions(+), 4 deletions(-) diff --git a/arch/sandbox/dts/sandbox64.dts b/arch/sandbox/dts/sandbox64.dts index f21fc181f37..195365580a7 100644 --- a/arch/sandbox/dts/sandbox64.dts +++ b/arch/sandbox/dts/sandbox64.dts @@ -89,6 +89,19 @@ cs-gpios = <0>, <&gpio_a 0>; }; + nvmxip-qspi1@08000000 { + compatible = "nvmxip,qspi"; + reg = /bits/ 64 <0x08000000 0x00200000>; + lba_shift = <9>; + lba = <4096>; + }; + + nvmxip-qspi2@08200000 { + compatible = "nvmxip,qspi"; + reg = /bits/ 64 <0x08200000 0x00100000>; + lba_shift = <9>; + lba = <2048>; + }; }; #include "sandbox.dtsi" diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 7c1ee71cb7c..bcdea0b8e7b 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1802,6 +1802,20 @@ compatible = "u-boot,fwu-mdata-gpt"; fwu-mdata-store = <&mmc0>; }; + + nvmxip-qspi1@08000000 { + compatible = "nvmxip,qspi"; + reg = <0x08000000 0x00200000>; + lba_shift = <9>; + lba = <4096>; + }; + + nvmxip-qspi2@08200000 { + compatible = "nvmxip,qspi"; + reg = <0x08200000 0x00100000>; + lba_shift = <9>; + lba = <2048>; + }; }; #include "sandbox_pmic.dtsi" diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index af2c56ad4c6..bb877b6a587 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -260,3 +260,4 @@ CONFIG_FWU_MULTI_BANK_UPDATE=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_NVMXIP_QSPI=y diff --git a/doc/develop/driver-model/nvmxip.rst b/doc/develop/driver-model/nvmxip.rst index 09afdbcccf5..e85dc220b9c 100644 --- a/doc/develop/driver-model/nvmxip.rst +++ b/doc/develop/driver-model/nvmxip.rst @@ -56,7 +56,7 @@ The implementation is generic and can be used by different platforms. Supported hardware -------------------------------- -Any 64-bit plaform. +Any plaform supporting readq(). Configuration ---------------------- diff --git a/doc/device-tree-bindings/nvmxip/nvmxip_qspi.txt b/doc/device-tree-bindings/nvmxip/nvmxip_qspi.txt index cc60e9efdcd..882728d5413 100644 --- a/doc/device-tree-bindings/nvmxip/nvmxip_qspi.txt +++ b/doc/device-tree-bindings/nvmxip/nvmxip_qspi.txt @@ -16,7 +16,7 @@ If a platform has its own driver, please provide your own compatible string. 2) - reg = <0x0 0x08000000 0x0 0x00200000>; + reg = /bits/ 64 <0x08000000 0x00200000>; The start address and size of the flash device. The values give here are an example (when the cell size is 2). @@ -43,14 +43,14 @@ Example of multiple flash devices nvmxip-qspi1@08000000 { compatible = "nvmxip,qspi"; - reg = <0x0 0x08000000 0x0 0x00200000>; + reg = /bits/ 64 <0x08000000 0x00200000>; lba_shift = <9>; lba = <4096>; }; nvmxip-qspi2@08200000 { compatible = "nvmxip,qspi"; - reg = <0x0 0x08200000 0x0 0x00100000>; + reg = /bits/ 64 <0x08200000 0x00100000>; lba_shift = <9>; lba = <2048>; }; diff --git a/drivers/mtd/nvmxip/nvmxip-uclass.c b/drivers/mtd/nvmxip/nvmxip-uclass.c index 9f96041e3d8..6d8eb177b50 100644 --- a/drivers/mtd/nvmxip/nvmxip-uclass.c +++ b/drivers/mtd/nvmxip/nvmxip-uclass.c @@ -9,6 +9,9 @@ #include #include #include +#if CONFIG_IS_ENABLED(SANDBOX64) +#include +#endif #include #include "nvmxip.h" @@ -36,6 +39,10 @@ static int nvmxip_post_bind(struct udevice *udev) char bdev_name[NVMXIP_BLKDEV_NAME_SZ + 1]; int devnum; +#if CONFIG_IS_ENABLED(SANDBOX64) + sandbox_set_enable_memio(true); +#endif + devnum = uclass_id_count(UCLASS_NVMXIP); snprintf(bdev_name, NVMXIP_BLKDEV_NAME_SZ, "blk#%d", devnum); From da458bcae513ba2de5bb7f6383909ceb3a5a5448 Mon Sep 17 00:00:00 2001 From: Abdellatif El Khlifi Date: Mon, 17 Apr 2023 10:11:56 +0100 Subject: [PATCH 5/7] corstone1000: add NVM XIP QSPI device tree node add QSPI flash device node for block storage access Signed-off-by: Abdellatif El Khlifi --- arch/arm/dts/corstone1000.dtsi | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/arm/dts/corstone1000.dtsi b/arch/arm/dts/corstone1000.dtsi index 4e46826f883..533dfdf8e1c 100644 --- a/arch/arm/dts/corstone1000.dtsi +++ b/arch/arm/dts/corstone1000.dtsi @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 or MIT /* - * Copyright (c) 2022, Arm Limited. All rights reserved. + * Copyright 2022-2023 Arm Limited and/or its affiliates * Copyright (c) 2022, Linaro Limited. All rights reserved. * */ @@ -38,6 +38,13 @@ reg = <0x88200000 0x77e00000>; }; + nvmxip-qspi@08000000 { + compatible = "nvmxip,qspi"; + reg = <0x08000000 0x2000000>; + lba_shift = <9>; + lba = <65536>; + }; + gic: interrupt-controller@1c000000 { compatible = "arm,gic-400"; #interrupt-cells = <3>; From a6d7f1958ac402a2a099bd8c809cd061cd531c69 Mon Sep 17 00:00:00 2001 From: Abdellatif El Khlifi Date: Mon, 17 Apr 2023 10:11:57 +0100 Subject: [PATCH 6/7] corstone1000: enable NVM XIP QSPI flash add the QSPI flash device with block storage capability Signed-off-by: Abdellatif El Khlifi --- configs/corstone1000_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index 383317fefee..35f763596ad 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -52,3 +52,4 @@ CONFIG_DM_SERIAL=y CONFIG_USB=y CONFIG_USB_ISP1760=y CONFIG_ERRNO_STR=y +CONFIG_NVMXIP_QSPI=y From 56722fafd9b2a017930030e9b50c5784f64545db Mon Sep 17 00:00:00 2001 From: Abdellatif El Khlifi Date: Mon, 17 Apr 2023 10:11:58 +0100 Subject: [PATCH 7/7] sandbox64: add a test case for UCLASS_NVMXIP provide a test for NVM XIP devices The test case allows to make sure of the following: - The NVM XIP QSPI devices are probed - The DT entries are read correctly - the data read from the flash by the NVMXIP block driver is correct Signed-off-by: Abdellatif El Khlifi --- MAINTAINERS | 1 + test/dm/Makefile | 5 ++ test/dm/nvmxip.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 test/dm/nvmxip.c diff --git a/MAINTAINERS b/MAINTAINERS index fd155ca8788..0257526bc82 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1210,6 +1210,7 @@ S: Maintained F: doc/develop/driver-model/nvmxip.rst F: doc/device-tree-bindings/nvmxip/nvmxip_qspi.txt F: drivers/mtd/nvmxip/ +F: test/dm/nvmxip.c NVMEM M: Sean Anderson diff --git a/test/dm/Makefile b/test/dm/Makefile index e15bdbf04bc..c8534b5cfa8 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # # Copyright (c) 2013 Google, Inc +# Copyright 2023 Arm Limited and/or its affiliates obj-$(CONFIG_UT_DM) += test-dm.o @@ -17,6 +18,10 @@ obj-$(CONFIG_UT_DM) += test-uclass.o obj-$(CONFIG_UT_DM) += core.o obj-$(CONFIG_UT_DM) += read.o obj-$(CONFIG_UT_DM) += phys2bus.o +ifeq ($(CONFIG_NVMXIP_QSPI)$(CONFIG_SANDBOX64),yy) +obj-y += nvmxip.o +endif + ifneq ($(CONFIG_SANDBOX),) ifeq ($(CONFIG_ACPIGEN),y) obj-y += acpi.o diff --git a/test/dm/nvmxip.c b/test/dm/nvmxip.c new file mode 100644 index 00000000000..e934748eb5d --- /dev/null +++ b/test/dm/nvmxip.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Functional tests for UCLASS_FFA class + * + * Copyright 2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../drivers/mtd/nvmxip/nvmxip.h" + +/* NVMXIP devices described in the device tree */ +#define SANDBOX_NVMXIP_DEVICES 2 + +/* reference device tree data for the probed devices */ +static struct nvmxip_plat nvmqspi_refdata[SANDBOX_NVMXIP_DEVICES] = { + {0x08000000, 9, 4096}, {0x08200000, 9, 2048} +}; + +#define NVMXIP_BLK_START_PATTERN 0x1122334455667788ULL +#define NVMXIP_BLK_END_PATTERN 0xa1a2a3a4a5a6a7a8ULL + +/** + * dm_nvmxip_flash_sanity() - check flash data + * @uts: test state + * @device_idx: the NVMXIP device index + * @buffer: the user buffer where the blocks data is copied to + * + * Mode 1: When buffer is NULL, initialize the flash with pattern data at the start + * and at the end of each block. This pattern data will be used to check data consistency + * when verifying the data read. + * Mode 2: When the user buffer is provided in the argument (not NULL), compare the data + * of the start and the end of each block in the user buffer with the expected pattern data. + * Return an error when the check fails. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int dm_nvmxip_flash_sanity(struct unit_test_state *uts, u8 device_idx, void *buffer) +{ + int i; + u64 *ptr; + u8 *base; + unsigned long blksz; + + blksz = BIT(nvmqspi_refdata[device_idx].lba_shift); + + if (!buffer) { + /* Mode 1: point at the flash start address. Pattern data will be written */ + base = map_sysmem(nvmqspi_refdata[device_idx].phys_base, 0); + } else { + /* Mode 2: point at the user buffer containing the data read and to be verified */ + base = buffer; + } + + for (i = 0; i < nvmqspi_refdata[device_idx].lba ; i++) { + ptr = (u64 *)(base + i * blksz); + + /* write an 8 bytes pattern at the start of the current block */ + if (!buffer) + *ptr = NVMXIP_BLK_START_PATTERN; + else + ut_asserteq_64(NVMXIP_BLK_START_PATTERN, *ptr); + + ptr = (u64 *)((u8 *)ptr + blksz - sizeof(u64)); + + /* write an 8 bytes pattern at the end of the current block */ + if (!buffer) + *ptr = NVMXIP_BLK_END_PATTERN; + else + ut_asserteq_64(NVMXIP_BLK_END_PATTERN, *ptr); + } + + if (!buffer) + unmap_sysmem(base); + + return 0; +} + +/** + * dm_test_nvmxip() - check flash data + * @uts: test state + * Return: + * + * CMD_RET_SUCCESS on success. Otherwise, failure + */ +static int dm_test_nvmxip(struct unit_test_state *uts) +{ + struct nvmxip_plat *plat_data = NULL; + struct udevice *dev = NULL, *bdev = NULL; + u8 device_idx; + void *buffer = NULL; + unsigned long flashsz; + + /* set the flash content first for both devices */ + dm_nvmxip_flash_sanity(uts, 0, NULL); + dm_nvmxip_flash_sanity(uts, 1, NULL); + + /* probing all NVM XIP QSPI devices */ + for (device_idx = 0, uclass_first_device(UCLASS_NVMXIP, &dev); + dev; + uclass_next_device(&dev), device_idx++) { + plat_data = dev_get_plat(dev); + + /* device tree entries checks */ + ut_assertok(nvmqspi_refdata[device_idx].phys_base != plat_data->phys_base); + ut_assertok(nvmqspi_refdata[device_idx].lba_shift != plat_data->lba_shift); + ut_assertok(nvmqspi_refdata[device_idx].lba != plat_data->lba); + + /* before reading all the flash blocks, let's calculate the flash size */ + flashsz = plat_data->lba << plat_data->lba_shift; + + /* allocate the user buffer where to copy the blocks data to */ + buffer = calloc(flashsz, 1); + ut_assertok(!buffer); + + /* the block device is the child of the parent device probed with DT */ + ut_assertok(device_find_first_child(dev, &bdev)); + + /* reading all the flash blocks */ + ut_asserteq(plat_data->lba, blk_read(bdev, 0, plat_data->lba, buffer)); + + /* compare the data read from flash with the expected data */ + dm_nvmxip_flash_sanity(uts, device_idx, buffer); + + free(buffer); + } + + ut_assertok(device_idx != SANDBOX_NVMXIP_DEVICES); + + return CMD_RET_SUCCESS; +} + +DM_TEST(dm_test_nvmxip, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);