From: Denis Mukhin <[email protected]> Add flush command implementation along with a high-level disk_blk_flush() API to be called at certain checkpoints during the boot (e.g. updating custom bootflow flags stored on NVMe).
Signed-off-by: Denis Mukhin <[email protected]> --- disk/disk-uclass.c | 6 ++++++ drivers/block/blk-uclass.c | 18 ++++++++++++++++++ drivers/nvme/nvme.c | 20 ++++++++++++++++++++ include/blk.h | 28 ++++++++++++++++++++++++++++ include/part.h | 8 ++++++++ 5 files changed, 80 insertions(+) diff --git a/disk/disk-uclass.c b/disk/disk-uclass.c index ee3cc4407d76..b04b6306e5f2 100644 --- a/disk/disk-uclass.c +++ b/disk/disk-uclass.c @@ -122,6 +122,11 @@ unsigned long disk_blk_erase(struct udevice *dev, lbaint_t start, blkcnt); } +unsigned long disk_blk_flush(struct udevice *dev) +{ + return blk_flush(dev_get_parent(dev)); +} + UCLASS_DRIVER(partition) = { .id = UCLASS_PARTITION, .per_device_plat_auto = sizeof(struct disk_part), @@ -132,6 +137,7 @@ static const struct blk_ops blk_part_ops = { .read = disk_blk_read, .write = disk_blk_write, .erase = disk_blk_erase, + .flush = disk_blk_flush, }; U_BOOT_DRIVER(blk_partition) = { diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c index 73c24fd91763..0be1fdab1ba5 100644 --- a/drivers/block/blk-uclass.c +++ b/drivers/block/blk-uclass.c @@ -514,6 +514,19 @@ long blk_erase(struct udevice *dev, lbaint_t start, lbaint_t blkcnt) return ops->erase(dev, start, blkcnt); } +long blk_flush(struct udevice *dev) +{ + struct blk_desc *desc = dev_get_uclass_plat(dev); + const struct blk_ops *ops = blk_get_ops(dev); + + if (!ops->flush) + return -ENOSYS; + + blkcache_invalidate(desc->uclass_id, desc->devnum); + + return ops->flush(dev); +} + ulong blk_dread(struct blk_desc *desc, lbaint_t start, lbaint_t blkcnt, void *buffer) { @@ -531,6 +544,11 @@ ulong blk_derase(struct blk_desc *desc, lbaint_t start, lbaint_t blkcnt) return blk_erase(desc->bdev, start, blkcnt); } +ulong blk_dflush(struct blk_desc *desc) +{ + return blk_flush(desc->bdev); +} + int blk_find_from_parent(struct udevice *parent, struct udevice **devp) { struct udevice *dev; diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c index c3c44e50f19a..d9f099a11593 100644 --- a/drivers/nvme/nvme.c +++ b/drivers/nvme/nvme.c @@ -819,9 +819,29 @@ static ulong nvme_blk_write(struct udevice *udev, lbaint_t blknr, return nvme_blk_rw(udev, blknr, blkcnt, (void *)buffer, false); } +/* + * NVM Flush command (opcode 0x00). + * + * Applies to a single namespace; the controller must commit all dirty + * data for that namespace to storage before completing the command. + */ +static ulong nvme_blk_flush(struct udevice *udev) +{ + struct nvme_ns *ns = dev_get_priv(udev); + struct nvme_dev *dev = ns->dev; + struct nvme_command c; + + memset(&c, 0, sizeof(c)); + c.common.opcode = nvme_cmd_flush; + c.common.nsid = cpu_to_le32(ns->ns_id); + + return nvme_submit_sync_cmd(dev->queues[NVME_IO_Q], &c, NULL, IO_TIMEOUT); +} + static const struct blk_ops nvme_blk_ops = { .read = nvme_blk_read, .write = nvme_blk_write, + .flush = nvme_blk_flush, }; U_BOOT_DRIVER(nvme_blk) = { diff --git a/include/blk.h b/include/blk.h index 8d1b70cabd31..3e2160a1ae4d 100644 --- a/include/blk.h +++ b/include/blk.h @@ -99,6 +99,7 @@ struct blk_desc { unsigned long (*block_erase)(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt); + unsigned long (*block_flush)(struct blk_desc *block_dev); void *priv; /* driver private struct pointer */ #endif }; @@ -275,6 +276,14 @@ struct blk_ops { */ int (*buffer_aligned)(struct udevice *dev, struct bounce_buffer *state); #endif /* CONFIG_BOUNCE_BUFFER */ + + /** + * flush() - commit all dirty data to storage + * + * @dev: Device to flush + * @return 0 if OK, -ve on error + */ + unsigned long (*flush)(struct udevice *dev); }; #if CONFIG_IS_ENABLED(BLK) @@ -291,6 +300,7 @@ unsigned long blk_dwrite(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt, const void *buffer); unsigned long blk_derase(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt); +unsigned long blk_dflush(struct blk_desc *block_dev); #endif /* BLK */ @@ -331,6 +341,14 @@ long blk_write(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, */ long blk_erase(struct udevice *dev, lbaint_t start, lbaint_t blkcnt); +/** + * blk_flush() - Commit data to a block device + * + * @dev: Device to flush + * @return 0 if operation succeeded, or -ve on error. + */ +long blk_flush(struct udevice *dev); + /** * blk_find_device() - Find a block device * @@ -559,6 +577,16 @@ static inline ulong blk_derase(struct blk_desc *block_dev, lbaint_t start, return block_dev->block_erase(block_dev, start, blkcnt); } +static inline ulong blk_dflush(struct blk_desc *block_dev) +{ + blkcache_invalidate(block_dev->uclass_id, block_dev->devnum); + + if (block_dev->block_flush) + return block_dev->block_flush(block_dev); + + return 0; +} + /** * struct blk_driver - Driver for block interface types * diff --git a/include/part.h b/include/part.h index 15daacd7faaa..63982d7b9370 100644 --- a/include/part.h +++ b/include/part.h @@ -454,6 +454,14 @@ ulong disk_blk_write(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, */ ulong disk_blk_erase(struct udevice *dev, lbaint_t start, lbaint_t blkcnt); +/** + * disk_blk_flush() - commit data to a disk + * + * @dev: Device to flush + * Return: 0 success, or -ve error number (see the IS_ERR_VALUE() macro + */ +ulong disk_blk_flush(struct udevice *dev); + /* * We don't support printing partition information in SPL and only support * getting partition information in a few cases. -- 2.54.0

