[PATCH v1 2/2] drivers: scsi: Add 'erase' support

Varadarajan Narayanan quic_varada at quicinc.com
Mon Mar 24 11:01:21 CET 2025


UFS devices uses the block and scsi frameworks. Enable UFS erase
support by adding erase support to SCSI.

Signed-off-by: Varadarajan Narayanan <quic_varada at quicinc.com>
---
 cmd/scsi.c          |  3 +-
 drivers/scsi/scsi.c | 92 +++++++++++++++++++++++++++++++++++++++++++++
 include/scsi.h      | 13 +++++++
 3 files changed, 107 insertions(+), 1 deletion(-)

diff --git a/cmd/scsi.c b/cmd/scsi.c
index c286bdc0726..9f7613424e5 100644
--- a/cmd/scsi.c
+++ b/cmd/scsi.c
@@ -60,7 +60,8 @@ U_BOOT_CMD(
 	"scsi read addr blk# cnt - read `cnt' blocks starting at block `blk#'\n"
 	"     to memory address `addr'\n"
 	"scsi write addr blk# cnt - write `cnt' blocks starting at block\n"
-	"     `blk#' from memory address `addr'"
+	"     `blk#' from memory address `addr'\n"
+	"scsi erase blk# cnt - erase `cnt' blocks starting at block `blk#'"
 );
 
 U_BOOT_CMD(
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index cd0b84c0622..dc93392642d 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -28,6 +28,10 @@ DEFINE_CACHE_ALIGN_BUFFER(u8, tempbuff, 512);	/* temporary data buffer */
 #define SCSI_MAX_BLK 0xFFFF
 #define SCSI_LBA48_READ	0xFFFFFFF
 
+#define SCSI_UNMAP_PARAM_RESERVED 0
+#define SCSI_UNMAP_PARAM_LEN 22
+#define SCSI_UNMAP_PARAM_DATA_LEN 16
+
 static void scsi_print_error(struct scsi_cmd *pccb)
 {
 	/* Dummy function that could print an error for debugging */
@@ -121,6 +125,51 @@ static void scsi_setup_write_ext(struct scsi_cmd *pccb, lbaint_t start,
 	      pccb->cmd[7], pccb->cmd[8]);
 }
 
+static void scsi_setup_erase_ext(struct scsi_cmd *pccb, lbaint_t start,
+				 unsigned short blocks)
+{
+	u8 *param = tempbuff;
+	const u8 param_size = 24;
+
+	memset(param, 0, param_size);
+	param[0] = SCSI_UNMAP_PARAM_RESERVED;
+	param[1] = SCSI_UNMAP_PARAM_LEN;
+	param[2] = SCSI_UNMAP_PARAM_RESERVED;
+	param[3] = SCSI_UNMAP_PARAM_DATA_LEN;
+
+	param[8]  = 0x0;
+	param[9]  = 0x0;
+	param[10] = 0x0;
+	param[11] = 0x0;
+	param[12] = (start >> 24) & 0xff;
+	param[13] = (start >> 16) & 0xff;
+	param[14] = (start >> 8) & 0xff;
+	param[15] = (start) & 0xff;
+	param[16] = (blocks >> 24) & 0xff;
+	param[17] = (blocks >> 16) & 0xff;
+	param[18] = (blocks >> 8) & 0xff;
+	param[19] = (blocks) & 0xff;
+
+	memset(pccb->cmd, 0, sizeof(pccb->cmd));
+	pccb->cmd[0] = SCSI_UNMAP;
+	pccb->cmd[1] = 0;
+	pccb->cmd[6] = 0;
+	pccb->cmd[7] = 0;
+	pccb->cmd[8] = param_size;
+	pccb->cmd[9] = 0;
+	pccb->cmdlen = 10;
+
+	pccb->pdata = param;
+	pccb->datalen = param_size;
+	pccb->dma_dir = DMA_TO_DEVICE;
+
+	debug("%s: cmd: %02X %02X startblk %02X%02X%02X%02X blccnt %02X%02X\n",
+	      __func__,
+	      pccb->cmd[0], pccb->cmd[1],
+	      pccb->cmd[2], pccb->cmd[3], pccb->cmd[4], pccb->cmd[5],
+	      pccb->cmd[7], pccb->cmd[8]);
+}
+
 static ulong scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
 		       void *buffer)
 {
@@ -245,6 +294,48 @@ static ulong scsi_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
 	return blkcnt;
 }
 
+/*******************************************************************************
+ * scsi_erase
+ */
+static ulong scsi_erase(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt)
+{
+	struct blk_desc *block_dev = dev_get_uclass_plat(dev);
+	struct udevice *bdev = dev->parent;
+	struct scsi_plat *uc_plat = dev_get_uclass_plat(bdev);
+	lbaint_t start, blks, max_blks;
+	struct scsi_cmd *pccb = (struct scsi_cmd *)&tempccb;
+
+	/* Setup device */
+	pccb->target = block_dev->target;
+	pccb->lun = block_dev->lun;
+	start = blknr;
+	blks = blkcnt;
+	if (uc_plat->max_bytes_per_req)
+		max_blks = uc_plat->max_bytes_per_req / block_dev->blksz;
+	else
+		max_blks = SCSI_MAX_BLK;
+
+	debug("\n%s: dev %d startblk " LBAF ", blccnt " LBAF "\n",
+	      __func__, block_dev->devnum, start, blks);
+	do {
+		if (blks > max_blks) {
+			scsi_setup_erase_ext(pccb, start, max_blks);
+			start += max_blks;
+			blks -= max_blks;
+		} else {
+			scsi_setup_erase_ext(pccb, start, blks);
+			start += blks;
+			blks = 0;
+		}
+		if (scsi_exec(bdev, pccb)) {
+			scsi_print_error(pccb);
+			blkcnt -= blks;
+			break;
+		}
+	} while (blks != 0);
+	return blkcnt;
+}
+
 #if IS_ENABLED(CONFIG_BOUNCE_BUFFER)
 static int scsi_buffer_aligned(struct udevice *dev, struct bounce_buffer *state)
 {
@@ -592,6 +683,7 @@ int scsi_scan(bool verbose)
 static const struct blk_ops scsi_blk_ops = {
 	.read	= scsi_read,
 	.write	= scsi_write,
+	.erase  = scsi_erase,
 #if IS_ENABLED(CONFIG_BOUNCE_BUFFER)
 	.buffer_aligned	= scsi_buffer_aligned,
 #endif	/* CONFIG_BOUNCE_BUFFER */
diff --git a/include/scsi.h b/include/scsi.h
index b18ae37b861..d6b6e6b665f 100644
--- a/include/scsi.h
+++ b/include/scsi.h
@@ -9,6 +9,7 @@
 #include <asm/cache.h>
 #include <bouncebuf.h>
 #include <linux/dma-direction.h>
+#include <part.h>
 
 struct udevice;
 
@@ -181,6 +182,7 @@ struct scsi_cmd {
 #define SCSI_WRT_VERIFY	0x2E		/* Write and Verify (O) */
 #define SCSI_WRITE_LONG	0x3F		/* Write Long (O) */
 #define SCSI_WRITE_SAME	0x41		/* Write Same (O) */
+#define SCSI_UNMAP	0x42		/* Write 10-Byte (MANDATORY) */
 
 /**
  * enum scsi_cmd_phase - current phase of the SCSI protocol
@@ -349,6 +351,17 @@ int scsi_scan(bool verbose);
  */
 int scsi_scan_dev(struct udevice *dev, bool verbose);
 
+/**
+ * scsi_get_blk() - Provides SCSI partition information.
+ *
+ * @partition_name: Partition name for fetching its info
+ * @blk_desc_ptr:   Provides the blk descriptor
+ * @part_info_ptr:  Provides partition info
+ */
+int scsi_get_blk(const char *partition_name,
+		 struct blk_desc **blk_desc_ptr,
+		 struct disk_partition *part_info_ptr);
+
 #define SCSI_IDENTIFY					0xC0  /* not used */
 
 /* Hardware errors  */
-- 
2.34.1



More information about the U-Boot mailing list