diff options
-rw-r--r-- | MAINTAINERS | 8 | ||||
-rw-r--r-- | arch/arm/dts/corstone1000.dtsi | 9 | ||||
-rw-r--r-- | arch/sandbox/cpu/cpu.c | 2 | ||||
-rw-r--r-- | arch/sandbox/dts/sandbox64.dts | 13 | ||||
-rw-r--r-- | arch/sandbox/dts/test.dts | 14 | ||||
-rw-r--r-- | arch/sandbox/include/asm/io.h | 2 | ||||
-rw-r--r-- | configs/corstone1000_defconfig | 1 | ||||
-rw-r--r-- | configs/sandbox64_defconfig | 1 | ||||
-rw-r--r-- | doc/develop/driver-model/index.rst | 1 | ||||
-rw-r--r-- | doc/develop/driver-model/nvmxip.rst | 91 | ||||
-rw-r--r-- | doc/device-tree-bindings/nvmxip/nvmxip_qspi.txt | 56 | ||||
-rw-r--r-- | drivers/block/blk-uclass.c | 1 | ||||
-rw-r--r-- | drivers/mtd/Kconfig | 2 | ||||
-rw-r--r-- | drivers/mtd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/nvmxip/Kconfig | 19 | ||||
-rw-r--r-- | drivers/mtd/nvmxip/Makefile | 8 | ||||
-rw-r--r-- | drivers/mtd/nvmxip/nvmxip-uclass.c | 74 | ||||
-rw-r--r-- | drivers/mtd/nvmxip/nvmxip.c | 119 | ||||
-rw-r--r-- | drivers/mtd/nvmxip/nvmxip.h | 32 | ||||
-rw-r--r-- | drivers/mtd/nvmxip/nvmxip_qspi.c | 70 | ||||
-rw-r--r-- | include/dm/uclass-id.h | 1 | ||||
-rw-r--r-- | test/dm/Makefile | 5 | ||||
-rw-r--r-- | test/dm/nvmxip.c | 145 |
23 files changed, 672 insertions, 3 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 02a5a8682f8..0257526bc82 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1204,6 +1204,14 @@ F: cmd/nvme.c F: include/nvme.h F: doc/develop/driver-model/nvme.rst +NVMXIP +M: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> +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 <seanga2@gmail.com> S: Maintained 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 <open-source-office@arm.com> * 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>; 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/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/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) 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 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/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..e85dc220b9c --- /dev/null +++ b/doc/develop/driver-model/nvmxip.rst @@ -0,0 +1,91 @@ +.. 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 + + nvmxip_qspi driver : + + The driver probed with the DT and is the parent of the blk#<id> 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 +-------------------------------- + +Any plaform supporting readq(). + +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. + +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 <abdellatif.elkhlifi@arm.com> 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..882728d5413 --- /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 = /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). + +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 = /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>; + }; 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..3ef71050264 --- /dev/null +++ b/drivers/mtd/nvmxip/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com> +# Authors: +# Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> + +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. + +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 new file mode 100644 index 00000000000..54eacc102e6 --- /dev/null +++ b/drivers/mtd/nvmxip/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com> +# Authors: +# Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> + +obj-y += nvmxip-uclass.o nvmxip.o +obj-$(CONFIG_NVMXIP_QSPI) += nvmxip_qspi.o diff --git a/drivers/mtd/nvmxip/nvmxip-uclass.c b/drivers/mtd/nvmxip/nvmxip-uclass.c new file mode 100644 index 00000000000..6d8eb177b50 --- /dev/null +++ b/drivers/mtd/nvmxip/nvmxip-uclass.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com> + * + * Authors: + * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#if CONFIG_IS_ENABLED(SANDBOX64) +#include <asm/test.h> +#endif +#include <linux/bitops.h> +#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; + +#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); + + 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 <open-source-office@arm.com> + * + * Authors: + * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <mapmem.h> +#include <asm/io.h> +#include <linux/bitops.h> +#include <linux/errno.h> +#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 <open-source-office@arm.com> + * + * Authors: + * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> + */ + +#ifndef __DRIVER_NVMXIP_H__ +#define __DRIVER_NVMXIP_H__ + +#include <blk.h> + +#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/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 <open-source-office@arm.com> + * + * Authors: + * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> + */ + +#include <common.h> +#include <dm.h> +#include <fdt_support.h> +#include <linux/errno.h> +#include "nvmxip.h" + +#include <asm/global_data.h> +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), +}; 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 */ 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 <open-source-office@arm.com> 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 <open-source-office@arm.com> + * + * Authors: + * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> + */ + +#include <common.h> +#include <blk.h> +#include <console.h> +#include <dm.h> +#include <mapmem.h> +#include <dm/test.h> +#include <linux/bitops.h> +#include <test/test.h> +#include <test/ut.h> +#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); |