Merge branch '2023-04-27-introduce-nvm-xip-block-storage-emulation'

To quote the author:
Adding 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 NVM XIP block storage emulation provides the following features:

- Emulate NVM XIP raw flash as a block storage device with read only capability
- Being generic by design and can be used by any platform
- Device tree node
- Platforms can use multiple NVM XIP devices at the same time by defining a
  DT node for each one of them
- A generic NVMXIP block driver allowing to read from the XIP flash
- A generic NVMXIP Uclass driver for binding the block device
- A generic NVMXIP QSPI driver
- Implemented on top of memory-mapped IO (using readq macro)
- Enabling NVMXIP in sandbox64
- A sandbox test case
- Enabling NVMXIP in Corstone1000 platform as a use case

For more details please refer to doc/develop/driver-model/nvmxip.rst
This commit is contained in:
Tom Rini 2023-04-27 19:22:38 -04:00
commit c9c2c95d4c
23 changed files with 672 additions and 3 deletions

View File

@ -1204,6 +1204,14 @@ F: cmd/nvme.c
F: include/nvme.h F: include/nvme.h
F: doc/develop/driver-model/nvme.rst 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 NVMEM
M: Sean Anderson <seanga2@gmail.com> M: Sean Anderson <seanga2@gmail.com>
S: Maintained S: Maintained

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 or MIT // 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. * Copyright (c) 2022, Linaro Limited. All rights reserved.
* *
*/ */
@ -38,6 +38,13 @@
reg = <0x88200000 0x77e00000>; reg = <0x88200000 0x77e00000>;
}; };
nvmxip-qspi@08000000 {
compatible = "nvmxip,qspi";
reg = <0x08000000 0x2000000>;
lba_shift = <9>;
lba = <65536>;
};
gic: interrupt-controller@1c000000 { gic: interrupt-controller@1c000000 {
compatible = "arm,gic-400"; compatible = "arm,gic-400";
#interrupt-cells = <3>; #interrupt-cells = <3>;

View File

@ -230,7 +230,7 @@ phys_addr_t map_to_sysmem(const void *ptr)
return mentry->tag; 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(); struct sandbox_state *state = state_get_current();

View File

@ -89,6 +89,19 @@
cs-gpios = <0>, <&gpio_a 0>; 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" #include "sandbox.dtsi"

View File

@ -1802,6 +1802,20 @@
compatible = "u-boot,fwu-mdata-gpt"; compatible = "u-boot,fwu-mdata-gpt";
fwu-mdata-store = <&mmc0>; 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" #include "sandbox_pmic.dtsi"

View File

@ -45,7 +45,7 @@ static inline void unmap_sysmem(const void *vaddr)
/* Map from a pointer to our RAM buffer */ /* Map from a pointer to our RAM buffer */
phys_addr_t map_to_sysmem(const void *ptr); 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); void sandbox_write(void *addr, unsigned int val, enum sandboxio_size_t size);
#define readb(addr) sandbox_read((const void *)addr, SB_SIZE_8) #define readb(addr) sandbox_read((const void *)addr, SB_SIZE_8)

View File

@ -52,3 +52,4 @@ CONFIG_DM_SERIAL=y
CONFIG_USB=y CONFIG_USB=y
CONFIG_USB_ISP1760=y CONFIG_USB_ISP1760=y
CONFIG_ERRNO_STR=y CONFIG_ERRNO_STR=y
CONFIG_NVMXIP_QSPI=y

View File

@ -260,3 +260,4 @@ CONFIG_FWU_MULTI_BANK_UPDATE=y
CONFIG_UNIT_TEST=y CONFIG_UNIT_TEST=y
CONFIG_UT_TIME=y CONFIG_UT_TIME=y
CONFIG_UT_DM=y CONFIG_UT_DM=y
CONFIG_NVMXIP_QSPI=y

View File

@ -20,6 +20,7 @@ subsystems
livetree livetree
migration migration
nvme nvme
nvmxip
of-plat of-plat
pci-info pci-info
pmic-framework pmic-framework

View File

@ -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>

View File

@ -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>;
};

View File

@ -28,6 +28,7 @@ static struct {
{ UCLASS_AHCI, "sata" }, { UCLASS_AHCI, "sata" },
{ UCLASS_HOST, "host" }, { UCLASS_HOST, "host" },
{ UCLASS_NVME, "nvme" }, { UCLASS_NVME, "nvme" },
{ UCLASS_NVMXIP, "nvmxip" },
{ UCLASS_EFI_MEDIA, "efi" }, { UCLASS_EFI_MEDIA, "efi" },
{ UCLASS_EFI_LOADER, "efiloader" }, { UCLASS_EFI_LOADER, "efiloader" },
{ UCLASS_VIRTIO, "virtio" }, { UCLASS_VIRTIO, "virtio" },

View File

@ -270,4 +270,6 @@ source "drivers/mtd/spi/Kconfig"
source "drivers/mtd/ubi/Kconfig" source "drivers/mtd/ubi/Kconfig"
source "drivers/mtd/nvmxip/Kconfig"
endmenu endmenu

View File

@ -25,6 +25,7 @@ obj-y += nand/
obj-y += onenand/ obj-y += onenand/
obj-y += spi/ obj-y += spi/
obj-$(CONFIG_MTD_UBI) += ubi/ obj-$(CONFIG_MTD_UBI) += ubi/
obj-$(CONFIG_NVMXIP) += nvmxip/
#SPL/TPL build #SPL/TPL build
else else

View File

@ -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

View File

@ -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

View File

@ -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,
};

119
drivers/mtd/nvmxip/nvmxip.c Normal file
View File

@ -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,
};

View File

@ -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__ */

View File

@ -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),
};

View File

@ -89,6 +89,7 @@ enum uclass_id {
UCLASS_NOP, /* No-op devices */ UCLASS_NOP, /* No-op devices */
UCLASS_NORTHBRIDGE, /* Intel Northbridge / SDRAM controller */ UCLASS_NORTHBRIDGE, /* Intel Northbridge / SDRAM controller */
UCLASS_NVME, /* NVM Express device */ UCLASS_NVME, /* NVM Express device */
UCLASS_NVMXIP, /* NVM XIP devices */
UCLASS_P2SB, /* (x86) Primary-to-Sideband Bus */ UCLASS_P2SB, /* (x86) Primary-to-Sideband Bus */
UCLASS_PANEL, /* Display panel, such as an LCD */ UCLASS_PANEL, /* Display panel, such as an LCD */
UCLASS_PANEL_BACKLIGHT, /* Backlight controller for panel */ UCLASS_PANEL_BACKLIGHT, /* Backlight controller for panel */

View File

@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0+ # SPDX-License-Identifier: GPL-2.0+
# #
# Copyright (c) 2013 Google, Inc # 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 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) += core.o
obj-$(CONFIG_UT_DM) += read.o obj-$(CONFIG_UT_DM) += read.o
obj-$(CONFIG_UT_DM) += phys2bus.o obj-$(CONFIG_UT_DM) += phys2bus.o
ifeq ($(CONFIG_NVMXIP_QSPI)$(CONFIG_SANDBOX64),yy)
obj-y += nvmxip.o
endif
ifneq ($(CONFIG_SANDBOX),) ifneq ($(CONFIG_SANDBOX),)
ifeq ($(CONFIG_ACPIGEN),y) ifeq ($(CONFIG_ACPIGEN),y)
obj-y += acpi.o obj-y += acpi.o

145
test/dm/nvmxip.c Normal file
View File

@ -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);