
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(-)
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) */