[U-Boot] [PATCH 4/8] ahci: support LBA48 data reads for 2+TB drives

Andre Przywara osp at andrep.de
Fri Jun 5 01:58:45 CEST 2015


From: Mark Langsdorf <mark.langsdorf at gmail.com>

Enable full 48-bit LBA48 data reads by passing the upper word of the
LBA block pointer in bytes 9 and 10 of the FIS.

This allows uboot to load data from any arbitrary sector on a drive
with 2 or more TB of available data connected to an AHCI controller.

Signed-off-by: Mark Langsdorf <mark.langsdorf at gmail.com>
Signed-off-by: Andre Przywara <osp at andrep.de>
---
 common/cmd_scsi.c    | 44 ++++++++++++++++++++++++++++++++++++++++++--
 drivers/block/ahci.c | 30 +++++++++++++++++++++++-------
 include/scsi.h       |  1 +
 3 files changed, 66 insertions(+), 9 deletions(-)

diff --git a/common/cmd_scsi.c b/common/cmd_scsi.c
index fe705b6..57d6a3f 100644
--- a/common/cmd_scsi.c
+++ b/common/cmd_scsi.c
@@ -56,6 +56,8 @@ static block_dev_desc_t scsi_dev_desc[CONFIG_SYS_SCSI_MAX_DEVICE];
 void scsi_setup_test_unit_ready(ccb * pccb);
 void scsi_setup_read6(ccb * pccb, lbaint_t start, unsigned short blocks);
 void scsi_setup_read_ext(ccb * pccb, lbaint_t start, unsigned short blocks);
+void scsi_setup_read16(ccb * pccb, lbaint_t start, unsigned long blocks);
+
 static void scsi_setup_write_ext(ccb *pccb, lbaint_t start,
 				unsigned short blocks);
 void scsi_setup_inquiry(ccb * pccb);
@@ -357,7 +359,9 @@ int do_scsi (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
  * scsi_read
  */
 
-#define SCSI_MAX_READ_BLK 0xFFFF /* almost the maximum amount of the scsi_ext command.. */
+/* almost the maximum amount of the scsi_ext command.. */
+#define SCSI_MAX_READ_BLK 0xFFFF
+#define SCSI_LBA48_READ	0xFFFFFFF
 
 static ulong scsi_read(int device, lbaint_t blknr, lbaint_t blkcnt,
 		       void *buffer)
@@ -379,7 +383,14 @@ static ulong scsi_read(int device, lbaint_t blknr, lbaint_t blkcnt,
 	      device, start, blks, (unsigned long)buffer);
 	do {
 		pccb->pdata=(unsigned char *)buf_addr;
-		if(blks>SCSI_MAX_READ_BLK) {
+		if (start > SCSI_LBA48_READ) {
+			unsigned long blocks;
+			blocks = min_t(lbaint_t, blks, SCSI_MAX_READ_BLK);
+			pccb->datalen = scsi_dev_desc[device].blksz * blocks;
+			scsi_setup_read16(pccb, start, blocks);
+			start += blocks;
+			blks -= blocks;
+		} else if (blks > SCSI_MAX_READ_BLK) {
 			pccb->datalen=scsi_dev_desc[device].blksz * SCSI_MAX_READ_BLK;
 			smallblks=SCSI_MAX_READ_BLK;
 			scsi_setup_read_ext(pccb,start,smallblks);
@@ -579,6 +590,35 @@ void scsi_setup_test_unit_ready(ccb * pccb)
 	pccb->msgout[0]=SCSI_IDENTIFY; /* NOT USED */
 }
 
+void scsi_setup_read16(ccb * pccb, lbaint_t start, unsigned long blocks)
+{
+	pccb->cmd[0] = SCSI_READ16;
+	pccb->cmd[1] = pccb->lun<<5;
+	pccb->cmd[2] = ((unsigned char) (start >> 56)) & 0xff;
+	pccb->cmd[3] = ((unsigned char) (start >> 48)) & 0xff;
+	pccb->cmd[4] = ((unsigned char) (start >> 40)) & 0xff;
+	pccb->cmd[5] = ((unsigned char) (start >> 32)) & 0xff;
+	pccb->cmd[6] = ((unsigned char) (start >> 24)) & 0xff;
+	pccb->cmd[7] = ((unsigned char) (start >> 16)) & 0xff;
+	pccb->cmd[8] = ((unsigned char) (start >> 8)) & 0xff;
+	pccb->cmd[9] = ((unsigned char) (start)) & 0xff;
+	pccb->cmd[10] = 0;
+	pccb->cmd[11] = ((unsigned char) (blocks >> 24)) & 0xff;
+	pccb->cmd[12] = ((unsigned char) (blocks >> 16)) & 0xff;
+	pccb->cmd[13] = ((unsigned char) (blocks >> 8)) & 0xff;
+	pccb->cmd[14] = (unsigned char) blocks & 0xff;
+	pccb->cmd[15] = 0;
+	pccb->cmdlen = 16;
+	pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */
+	debug ("scsi_setup_read16: cmd: %02X %02X "
+	       "startblk %02X%02X%02X%02X%02X%02X%02X%02X "
+	       "blccnt %02X%02X%02X%02X\n",
+		pccb->cmd[0], pccb->cmd[1],
+		pccb->cmd[2], pccb->cmd[3], pccb->cmd[4], pccb->cmd[5],
+		pccb->cmd[6], pccb->cmd[7], pccb->cmd[8], pccb->cmd[9],
+		pccb->cmd[11], pccb->cmd[12], pccb->cmd[13], pccb->cmd[14]);
+}
+
 void scsi_setup_read_ext(ccb * pccb, lbaint_t start, unsigned short blocks)
 {
 	pccb->cmd[0]=SCSI_READ10;
diff --git a/drivers/block/ahci.c b/drivers/block/ahci.c
index 6508648..28d0b08 100644
--- a/drivers/block/ahci.c
+++ b/drivers/block/ahci.c
@@ -726,18 +726,25 @@ static int ata_scsiop_inquiry(ccb *pccb)
  */
 static int ata_scsiop_read_write(ccb *pccb, u8 is_write)
 {
-	u32 lba = 0;
+	lbaint_t lba = 0;
 	u16 blocks = 0;
 	u8 fis[20];
 	u8 *user_buffer = pccb->pdata;
 	u32 user_buffer_size = pccb->datalen;
 
 	/* Retrieve the base LBA number from the ccb structure. */
-	memcpy(&lba, pccb->cmd + 2, sizeof(lba));
-	lba = be32_to_cpu(lba);
+	if (pccb->cmd[0] == SCSI_READ16) {
+		memcpy(&lba, pccb->cmd + 2, 8);
+		lba = be64_to_cpu(lba);
+	} else {
+		u32 temp;
+		memcpy(&temp, pccb->cmd + 2, 4);
+		lba = be32_to_cpu(temp);
+	}
 
 	/*
-	 * And the number of blocks.
+	 * Retrieve the base LBA number and the block count from
+	 * the ccb structure.
 	 *
 	 * For 10-byte and 16-byte SCSI R/W commands, transfer
 	 * length 0 means transfer 0 block of data.
@@ -746,10 +753,13 @@ static int ata_scsiop_read_write(ccb *pccb, u8 is_write)
 	 *
 	 * WARNING: one or two older ATA drives treat 0 as 0...
 	 */
-	blocks = (((u16)pccb->cmd[7]) << 8) | ((u16) pccb->cmd[8]);
+	if (pccb->cmd[0] == SCSI_READ16)
+		blocks = (((u16)pccb->cmd[13]) << 8) | ((u16) pccb->cmd[14]);
+	else
+		blocks = (((u16)pccb->cmd[7]) << 8) | ((u16) pccb->cmd[8]);
 
-	debug("scsi_ahci: %s %d blocks starting from lba 0x%x\n",
-	      is_write ?  "write" : "read", (unsigned)lba, blocks);
+	debug("scsi_ahci: %s %u blocks starting from lba 0x" LBAFU "\n",
+	      is_write ?  "write" : "read", blocks, lba);
 
 	/* Preset the FIS */
 	memset(fis, 0, sizeof(fis));
@@ -778,6 +788,11 @@ static int ata_scsiop_read_write(ccb *pccb, u8 is_write)
 		fis[6] = (lba >> 16) & 0xff;
 		fis[7] = 1 << 6; /* device reg: set LBA mode */
 		fis[8] = ((lba >> 24) & 0xff);
+		if (pccb->cmd[0] == SCSI_READ16) {
+			fis[9] = ((lba >> 32) & 0xff);
+			fis[10] = ((lba >> 40) & 0xff);
+		}
+
 		fis[3] = 0xe0; /* features */
 
 		/* Block (sector) count */
@@ -883,6 +898,7 @@ int scsi_exec(ccb *pccb)
 	int ret;
 
 	switch (pccb->cmd[0]) {
+	case SCSI_READ16:
 	case SCSI_READ10:
 		ret = ata_scsiop_read_write(pccb, 0);
 		break;
diff --git a/include/scsi.h b/include/scsi.h
index 73de7b7..7e37591 100644
--- a/include/scsi.h
+++ b/include/scsi.h
@@ -132,6 +132,7 @@ typedef struct SCSI_cmd_block{
 #define SCSI_MED_REMOVL	0x1E		/* Prevent/Allow medium Removal (O) */
 #define SCSI_READ6		0x08		/* Read 6-byte (MANDATORY) */
 #define SCSI_READ10		0x28		/* Read 10-byte (MANDATORY) */
+#define SCSI_READ16	0x48
 #define SCSI_RD_CAPAC	0x25		/* Read Capacity (MANDATORY) */
 #define SCSI_RD_CAPAC10	SCSI_RD_CAPAC	/* Read Capacity (10) */
 #define SCSI_RD_CAPAC16	0x9e		/* Read Capacity (16) */
-- 
1.8.4



More information about the U-Boot mailing list