ahci: support LBA48 data reads for 2+TB drives
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@gmail.com> Signed-off-by: Andre Przywara <osp@andrep.de> [trini: Make use of CONFIG_SYS_64BIT_LBA in a few places to drop warnings on platforms that don't enable that feature ] Signed-off-by: Tom Rini <trini@konsulko.com>
This commit is contained in:
parent
35df893199
commit
2b42c9317d
|
@ -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_test_unit_ready(ccb * pccb);
|
||||||
void scsi_setup_read6(ccb * pccb, lbaint_t start, unsigned short blocks);
|
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_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,
|
static void scsi_setup_write_ext(ccb *pccb, lbaint_t start,
|
||||||
unsigned short blocks);
|
unsigned short blocks);
|
||||||
void scsi_setup_inquiry(ccb * pccb);
|
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
|
* 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,
|
static ulong scsi_read(int device, lbaint_t blknr, lbaint_t blkcnt,
|
||||||
void *buffer)
|
void *buffer)
|
||||||
|
@ -379,6 +383,16 @@ static ulong scsi_read(int device, lbaint_t blknr, lbaint_t blkcnt,
|
||||||
device, start, blks, (unsigned long)buffer);
|
device, start, blks, (unsigned long)buffer);
|
||||||
do {
|
do {
|
||||||
pccb->pdata=(unsigned char *)buf_addr;
|
pccb->pdata=(unsigned char *)buf_addr;
|
||||||
|
#ifdef CONFIG_SYS_64BIT_LBA
|
||||||
|
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
|
||||||
|
#endif
|
||||||
if (blks > SCSI_MAX_READ_BLK) {
|
if (blks > SCSI_MAX_READ_BLK) {
|
||||||
pccb->datalen=scsi_dev_desc[device].blksz * SCSI_MAX_READ_BLK;
|
pccb->datalen=scsi_dev_desc[device].blksz * SCSI_MAX_READ_BLK;
|
||||||
smallblks=SCSI_MAX_READ_BLK;
|
smallblks=SCSI_MAX_READ_BLK;
|
||||||
|
@ -579,6 +593,37 @@ void scsi_setup_test_unit_ready(ccb * pccb)
|
||||||
pccb->msgout[0]=SCSI_IDENTIFY; /* NOT USED */
|
pccb->msgout[0]=SCSI_IDENTIFY; /* NOT USED */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_SYS_64BIT_LBA
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void scsi_setup_read_ext(ccb * pccb, lbaint_t start, unsigned short blocks)
|
void scsi_setup_read_ext(ccb * pccb, lbaint_t start, unsigned short blocks)
|
||||||
{
|
{
|
||||||
pccb->cmd[0]=SCSI_READ10;
|
pccb->cmd[0]=SCSI_READ10;
|
||||||
|
|
|
@ -726,18 +726,25 @@ static int ata_scsiop_inquiry(ccb *pccb)
|
||||||
*/
|
*/
|
||||||
static int ata_scsiop_read_write(ccb *pccb, u8 is_write)
|
static int ata_scsiop_read_write(ccb *pccb, u8 is_write)
|
||||||
{
|
{
|
||||||
u32 lba = 0;
|
lbaint_t lba = 0;
|
||||||
u16 blocks = 0;
|
u16 blocks = 0;
|
||||||
u8 fis[20];
|
u8 fis[20];
|
||||||
u8 *user_buffer = pccb->pdata;
|
u8 *user_buffer = pccb->pdata;
|
||||||
u32 user_buffer_size = pccb->datalen;
|
u32 user_buffer_size = pccb->datalen;
|
||||||
|
|
||||||
/* Retrieve the base LBA number from the ccb structure. */
|
/* Retrieve the base LBA number from the ccb structure. */
|
||||||
memcpy(&lba, pccb->cmd + 2, sizeof(lba));
|
if (pccb->cmd[0] == SCSI_READ16) {
|
||||||
lba = be32_to_cpu(lba);
|
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
|
* For 10-byte and 16-byte SCSI R/W commands, transfer
|
||||||
* length 0 means transfer 0 block of data.
|
* 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...
|
* WARNING: one or two older ATA drives treat 0 as 0...
|
||||||
*/
|
*/
|
||||||
|
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]);
|
blocks = (((u16)pccb->cmd[7]) << 8) | ((u16) pccb->cmd[8]);
|
||||||
|
|
||||||
debug("scsi_ahci: %s %d blocks starting from lba 0x%x\n",
|
debug("scsi_ahci: %s %u blocks starting from lba 0x" LBAFU "\n",
|
||||||
is_write ? "write" : "read", (unsigned)lba, blocks);
|
is_write ? "write" : "read", blocks, lba);
|
||||||
|
|
||||||
/* Preset the FIS */
|
/* Preset the FIS */
|
||||||
memset(fis, 0, sizeof(fis));
|
memset(fis, 0, sizeof(fis));
|
||||||
|
@ -770,14 +780,23 @@ static int ata_scsiop_read_write(ccb *pccb, u8 is_write)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* LBA48 SATA command but only use 32bit address range within
|
/*
|
||||||
* that. The next smaller command range (28bit) is too small.
|
* LBA48 SATA command but only use 32bit address range within
|
||||||
|
* that (unless we've enabled 64bit LBA support). The next
|
||||||
|
* smaller command range (28bit) is too small.
|
||||||
*/
|
*/
|
||||||
fis[4] = (lba >> 0) & 0xff;
|
fis[4] = (lba >> 0) & 0xff;
|
||||||
fis[5] = (lba >> 8) & 0xff;
|
fis[5] = (lba >> 8) & 0xff;
|
||||||
fis[6] = (lba >> 16) & 0xff;
|
fis[6] = (lba >> 16) & 0xff;
|
||||||
fis[7] = 1 << 6; /* device reg: set LBA mode */
|
fis[7] = 1 << 6; /* device reg: set LBA mode */
|
||||||
fis[8] = ((lba >> 24) & 0xff);
|
fis[8] = ((lba >> 24) & 0xff);
|
||||||
|
#ifdef CONFIG_SYS_64BIT_LBA
|
||||||
|
if (pccb->cmd[0] == SCSI_READ16) {
|
||||||
|
fis[9] = ((lba >> 32) & 0xff);
|
||||||
|
fis[10] = ((lba >> 40) & 0xff);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
fis[3] = 0xe0; /* features */
|
fis[3] = 0xe0; /* features */
|
||||||
|
|
||||||
/* Block (sector) count */
|
/* Block (sector) count */
|
||||||
|
@ -883,6 +902,7 @@ int scsi_exec(ccb *pccb)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
switch (pccb->cmd[0]) {
|
switch (pccb->cmd[0]) {
|
||||||
|
case SCSI_READ16:
|
||||||
case SCSI_READ10:
|
case SCSI_READ10:
|
||||||
ret = ata_scsiop_read_write(pccb, 0);
|
ret = ata_scsiop_read_write(pccb, 0);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -132,6 +132,7 @@ typedef struct SCSI_cmd_block{
|
||||||
#define SCSI_MED_REMOVL 0x1E /* Prevent/Allow medium Removal (O) */
|
#define SCSI_MED_REMOVL 0x1E /* Prevent/Allow medium Removal (O) */
|
||||||
#define SCSI_READ6 0x08 /* Read 6-byte (MANDATORY) */
|
#define SCSI_READ6 0x08 /* Read 6-byte (MANDATORY) */
|
||||||
#define SCSI_READ10 0x28 /* Read 10-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_CAPAC 0x25 /* Read Capacity (MANDATORY) */
|
||||||
#define SCSI_RD_CAPAC10 SCSI_RD_CAPAC /* Read Capacity (10) */
|
#define SCSI_RD_CAPAC10 SCSI_RD_CAPAC /* Read Capacity (10) */
|
||||||
#define SCSI_RD_CAPAC16 0x9e /* Read Capacity (16) */
|
#define SCSI_RD_CAPAC16 0x9e /* Read Capacity (16) */
|
||||||
|
|
Loading…
Reference in New Issue