Patchwork [U-Boot,3/3] ahci: support LBA48 data reads for 2+TB drives

login
register
mail settings
Submitter Mark Langsdorf
Date Sept. 10, 2013, 8:19 p.m.
Message ID <1378844357-15389-4-git-send-email-mark.langsdorf@calxeda.com>
Download mbox | patch
Permalink /patch/274042/
State Accepted
Delegated to: Tom Rini
Headers show

Comments

Mark Langsdorf - Sept. 10, 2013, 8:19 p.m.
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@calxeda.com>

---
 common/cmd_scsi.c    | 41 ++++++++++++++++++++++++++++++++++++++++-
 drivers/block/ahci.c | 30 +++++++++++++++++++++++-------
 include/scsi.h       |  1 +
 3 files changed, 64 insertions(+), 8 deletions(-)
Anatolij Gustschin - Nov. 8, 2013, 8:11 p.m.
On Tue, 10 Sep 2013 15:19:17 -0500
Mark Langsdorf <mark.langsdorf@calxeda.com> wrote:
...
> diff --git a/drivers/block/ahci.c b/drivers/block/ahci.c
> index 8cc9379..c5c942f 100644
> --- a/drivers/block/ahci.c
> +++ b/drivers/block/ahci.c

...
> @@ -689,10 +696,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);

LBAFU is defined as "%llu" or "%lu", so " 0x" in the debug string
will suggest that the lba value is in hexadecimal notation, but
the format specifier outputs as decimal. Please remove "0x" when
applying this patch. Thanks!

Anatolij
Tom Rini - Nov. 8, 2013, 8:12 p.m.
On Tue, Sep 10, 2013 at 03:19:17PM -0500, Mark Langsdorf wrote:

> 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@calxeda.com>

OK, so a few things in here:

[snip]
> +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;

This isn't protected with CONFIG_LBA48, so on non-LBA48 enabled boards
(P2020DS_36BIT is the example I found here) we get warnings here and on

[snip]
> @@ -721,6 +731,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);
> +		}
> +

This hunk.

It's easy enough to guard both of these cases with #ifdef CONFIG_LBA48,
and I've done so now.

But, highbank isn't setting CONFIG_LBA48 and probably really wants to,
yes?

Patch

diff --git a/common/cmd_scsi.c b/common/cmd_scsi.c
index f5ab14f..ba3b74e 100644
--- a/common/cmd_scsi.c
+++ b/common/cmd_scsi.c
@@ -55,6 +55,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);
@@ -362,6 +364,7 @@  int do_scsi (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 
 /* 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)
@@ -383,7 +386,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(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;
@@ -583,6 +593,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 8cc9379..c5c942f 100644
--- a/drivers/block/ahci.c
+++ b/drivers/block/ahci.c
@@ -669,18 +669,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.
@@ -689,10 +696,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));
@@ -721,6 +731,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 */
@@ -826,6 +841,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) */