aboutsummaryrefslogtreecommitdiff
path: root/drivers/mtd/nvmxip
diff options
context:
space:
mode:
authorAbdellatif El Khlifi2023-04-17 10:11:52 +0100
committerTom Rini2023-04-27 17:01:14 -0400
commitf37af2760ea92cec3fbdcfceb6df7be57a618165 (patch)
tree07b0dfdb08e51202c3d8b3304c94aeb29afffa75 /drivers/mtd/nvmxip
parentb197f1f05dee730e173a0756cb1a5f2be5d3ba5b (diff)
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 <abdellatif.elkhlifi@arm.com>
Diffstat (limited to 'drivers/mtd/nvmxip')
-rw-r--r--drivers/mtd/nvmxip/Kconfig13
-rw-r--r--drivers/mtd/nvmxip/Makefile7
-rw-r--r--drivers/mtd/nvmxip/nvmxip-uclass.c67
-rw-r--r--drivers/mtd/nvmxip/nvmxip.c119
-rw-r--r--drivers/mtd/nvmxip/nvmxip.h32
5 files changed, 238 insertions, 0 deletions
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 <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.
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 <open-source-office@arm.com>
+# Authors:
+# Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
+
+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 <open-source-office@arm.com>
+ *
+ * Authors:
+ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#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;
+
+ 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__ */