[U-Boot] ahci: add support for LBA48 data reads

The ext4 fs currently supports drive partitions greater that 2TB in size, but the ahci driver in u-boot can't read data past the first 2TB. Add support for lba48 reads by modifying the calls to use the lbaint_t type, so that lba48 block addresses can be sent to the functions, and then add support for reading those offsets.
--Mark Langsdorf Calxeda, Inc.

Signed-off-by: Mark Langsdorf mark.langsdorf@calxeda.com --- common/cmd_scsi.c | 498 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 255 insertions(+), 243 deletions(-)
diff --git a/common/cmd_scsi.c b/common/cmd_scsi.c index 7b97dc9..bbaed41 100644 --- a/common/cmd_scsi.c +++ b/common/cmd_scsi.c @@ -78,67 +78,72 @@ void scsi_scan(int mode) unsigned char i,perq,modi,lun; lbaint_t capacity; unsigned long blksz; - ccb* pccb=(ccb *)&tempccb; + ccb* pccb = (ccb *) &tempccb;
- if(mode==1) { + if (mode == 1) { printf("scanning bus for devices...\n"); } - for(i=0;i<CONFIG_SYS_SCSI_MAX_DEVICE;i++) { - scsi_dev_desc[i].target=0xff; - scsi_dev_desc[i].lun=0xff; - scsi_dev_desc[i].lba=0; - scsi_dev_desc[i].blksz=0; + for(i = 0; i < CONFIG_SYS_SCSI_MAX_DEVICE ; i++) { + scsi_dev_desc[i].target = 0xff; + scsi_dev_desc[i].lun = 0xff; + scsi_dev_desc[i].lba = 0; + scsi_dev_desc[i].blksz = 0; scsi_dev_desc[i].log2blksz = LOG2_INVALID(typeof(scsi_dev_desc[i].log2blksz)); - scsi_dev_desc[i].type=DEV_TYPE_UNKNOWN; - scsi_dev_desc[i].vendor[0]=0; - scsi_dev_desc[i].product[0]=0; - scsi_dev_desc[i].revision[0]=0; + scsi_dev_desc[i].type = DEV_TYPE_UNKNOWN; + scsi_dev_desc[i].vendor[0] = 0; + scsi_dev_desc[i].product[0] = 0; + scsi_dev_desc[i].revision[0] = 0; scsi_dev_desc[i].removable = false; - scsi_dev_desc[i].if_type=IF_TYPE_SCSI; - scsi_dev_desc[i].dev=i; - scsi_dev_desc[i].part_type=PART_TYPE_UNKNOWN; - scsi_dev_desc[i].block_read=scsi_read; + scsi_dev_desc[i].if_type = IF_TYPE_SCSI; + scsi_dev_desc[i].dev = i; + scsi_dev_desc[i].part_type = PART_TYPE_UNKNOWN; + scsi_dev_desc[i].block_read = scsi_read; scsi_dev_desc[i].block_write = scsi_write; } - scsi_max_devs=0; - for(i=0;i<CONFIG_SYS_SCSI_MAX_SCSI_ID;i++) { - pccb->target=i; - for(lun=0;lun<CONFIG_SYS_SCSI_MAX_LUN;lun++) { - pccb->lun=lun; - pccb->pdata=(unsigned char *)&tempbuff; - pccb->datalen=512; + scsi_max_devs = 0; + for(i = 0; i < CONFIG_SYS_SCSI_MAX_SCSI_ID; i++) { + pccb->target = i; + for(lun = 0; lun < CONFIG_SYS_SCSI_MAX_LUN; lun++) { + pccb->lun = lun; + pccb->pdata = (unsigned char *) &tempbuff; + pccb->datalen = 512; scsi_setup_inquiry(pccb); if (scsi_exec(pccb) != true) { - if(pccb->contr_stat==SCSI_SEL_TIME_OUT) { - debug ("Selection timeout ID %d\n",pccb->target); - continue; /* selection timeout => assuming no device present */ + /* selection timeout => no device present */ + if (pccb->contr_stat == SCSI_SEL_TIME_OUT) { + debug ("Selection timeout ID %d\n", + pccb->target); + continue; } scsi_print_error(pccb); continue; } - perq=tempbuff[0]; - modi=tempbuff[1]; - if((perq & 0x1f)==0x1f) { + perq = tempbuff[0]; + modi = tempbuff[1]; + if ((perq & 0x1f) == 0x1f) { continue; /* skip unknown devices */ } - if((modi&0x80)==0x80) /* drive is removable */ - scsi_dev_desc[scsi_max_devs].removable=true; + if((modi & 0x80) == 0x80) /* drive is removable */ + scsi_dev_desc[scsi_max_devs].removable = true; /* get info for this device */ - scsi_ident_cpy((unsigned char *)&scsi_dev_desc[scsi_max_devs].vendor[0], + scsi_ident_cpy((unsigned char *) + &scsi_dev_desc[scsi_max_devs].vendor[0], &tempbuff[8], 8); - scsi_ident_cpy((unsigned char *)&scsi_dev_desc[scsi_max_devs].product[0], + scsi_ident_cpy((unsigned char *) + &scsi_dev_desc[scsi_max_devs].product[0], &tempbuff[16], 16); - scsi_ident_cpy((unsigned char *)&scsi_dev_desc[scsi_max_devs].revision[0], + scsi_ident_cpy((unsigned char *) + &scsi_dev_desc[scsi_max_devs].revision[0], &tempbuff[32], 4); - scsi_dev_desc[scsi_max_devs].target=pccb->target; - scsi_dev_desc[scsi_max_devs].lun=pccb->lun; + scsi_dev_desc[scsi_max_devs].target = pccb->target; + scsi_dev_desc[scsi_max_devs].lun = pccb->lun;
- pccb->datalen=0; + pccb->datalen = 0; scsi_setup_test_unit_ready(pccb); if (scsi_exec(pccb) != true) { if (scsi_dev_desc[scsi_max_devs].removable == true) { - scsi_dev_desc[scsi_max_devs].type=perq; + scsi_dev_desc[scsi_max_devs].type = perq; goto removable; } scsi_print_error(pccb); @@ -148,22 +153,22 @@ void scsi_scan(int mode) scsi_print_error(pccb); continue; } - scsi_dev_desc[scsi_max_devs].lba=capacity; - scsi_dev_desc[scsi_max_devs].blksz=blksz; + scsi_dev_desc[scsi_max_devs].lba = capacity; + scsi_dev_desc[scsi_max_devs].blksz = blksz; scsi_dev_desc[scsi_max_devs].log2blksz = LOG2(scsi_dev_desc[scsi_max_devs].blksz); scsi_dev_desc[scsi_max_devs].type=perq; init_part(&scsi_dev_desc[scsi_max_devs]); removable: - if(mode==1) { - printf (" Device %d: ", scsi_max_devs); + if (mode == 1) { + printf(" Device %d: ", scsi_max_devs); dev_print(&scsi_dev_desc[scsi_max_devs]); } /* if mode */ scsi_max_devs++; } /* next LUN */ } - if(scsi_max_devs>0) - scsi_curr_dev=0; + if (scsi_max_devs > 0) + scsi_curr_dev = 0; else scsi_curr_dev = -1;
@@ -245,114 +250,118 @@ int do_scsi (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return CMD_RET_USAGE;
case 2: - if (strncmp(argv[1],"res",3) == 0) { - printf("\nReset SCSI\n"); - scsi_bus_reset(); - scsi_scan(1); - return 0; + if (strncmp(argv[1], "res", 3) == 0) { + printf("\nReset SCSI\n"); + scsi_bus_reset(); + scsi_scan(1); + return 0; + } + if (strncmp(argv[1], "inf", 3) == 0) { + int i; + for (i=0; i < CONFIG_SYS_SCSI_MAX_DEVICE; ++i) { + if (scsi_dev_desc[i].type == DEV_TYPE_UNKNOWN) + continue; /* list only known devices */ + printf("SCSI dev. %d: ", i); + dev_print(&scsi_dev_desc[i]); } - if (strncmp(argv[1],"inf",3) == 0) { - int i; - for (i=0; i<CONFIG_SYS_SCSI_MAX_DEVICE; ++i) { - if(scsi_dev_desc[i].type==DEV_TYPE_UNKNOWN) - continue; /* list only known devices */ - printf ("SCSI dev. %d: ", i); - dev_print(&scsi_dev_desc[i]); - } - return 0; + return 0; + } + if (strncmp(argv[1], "dev", 3) == 0) { + if ((scsi_curr_dev < 0) || + (scsi_curr_dev >= CONFIG_SYS_SCSI_MAX_DEVICE)) { + printf("\nno SCSI devices available\n"); + return 1; } - if (strncmp(argv[1],"dev",3) == 0) { - if ((scsi_curr_dev < 0) || (scsi_curr_dev >= CONFIG_SYS_SCSI_MAX_DEVICE)) { - printf("\nno SCSI devices available\n"); - return 1; + printf("\n Device %d: ", scsi_curr_dev); + dev_print(&scsi_dev_desc[scsi_curr_dev]); + return 0; + } + if (strncmp(argv[1], "scan", 4) == 0) { + scsi_scan(1); + return 0; + } + if (strncmp(argv[1], "part", 4) == 0) { + int dev, ok; + for (ok = 0, dev = 0; + dev< CONFIG_SYS_SCSI_MAX_DEVICE; ++dev) { + if (scsi_dev_desc[dev].type != + DEV_TYPE_UNKNOWN) { + ok++; + if (dev) + printf("\n"); + debug("print_part of %x\n",dev); + print_part(&scsi_dev_desc[dev]); } - printf ("\n Device %d: ", scsi_curr_dev); - dev_print(&scsi_dev_desc[scsi_curr_dev]); - return 0; - } - if (strncmp(argv[1],"scan",4) == 0) { - scsi_scan(1); - return 0; } - if (strncmp(argv[1],"part",4) == 0) { - int dev, ok; - for (ok=0, dev=0; dev<CONFIG_SYS_SCSI_MAX_DEVICE; ++dev) { - if (scsi_dev_desc[dev].type!=DEV_TYPE_UNKNOWN) { - ok++; - if (dev) - printf("\n"); - debug ("print_part of %x\n",dev); - print_part(&scsi_dev_desc[dev]); - } - } - if (!ok) - printf("\nno SCSI devices available\n"); + if (!ok) + printf("\nno SCSI devices available\n"); return 1; } return CMD_RET_USAGE; case 3: - if (strncmp(argv[1],"dev",3) == 0) { - int dev = (int)simple_strtoul(argv[2], NULL, 10); - printf ("\nSCSI device %d: ", dev); - if (dev >= CONFIG_SYS_SCSI_MAX_DEVICE) { - printf("unknown device\n"); - return 1; - } - printf ("\n Device %d: ", dev); - dev_print(&scsi_dev_desc[dev]); - if(scsi_dev_desc[dev].type == DEV_TYPE_UNKNOWN) { - return 1; - } - scsi_curr_dev = dev; - printf("... is now current device\n"); - return 0; + if (strncmp(argv[1], "dev", 3) == 0) { + int dev = (int) simple_strtoul(argv[2], NULL, 10); + printf ("\nSCSI device %d: ", dev); + if (dev >= CONFIG_SYS_SCSI_MAX_DEVICE) { + printf("unknown device\n"); + return 1; } - if (strncmp(argv[1],"part",4) == 0) { - int dev = (int)simple_strtoul(argv[2], NULL, 10); - if(scsi_dev_desc[dev].type != DEV_TYPE_UNKNOWN) { - print_part(&scsi_dev_desc[dev]); - } - else { - printf ("\nSCSI device %d not available\n", dev); - } + printf("\n Device %d: ", dev); + dev_print(&scsi_dev_desc[dev]); + if (scsi_dev_desc[dev].type == DEV_TYPE_UNKNOWN) { return 1; } - return CMD_RET_USAGE; - default: - /* at least 4 args */ - if (strcmp(argv[1],"read") == 0) { - ulong addr = simple_strtoul(argv[2], NULL, 16); - ulong blk = simple_strtoul(argv[3], NULL, 16); - ulong cnt = simple_strtoul(argv[4], NULL, 16); - ulong n; - printf ("\nSCSI read: device %d block # %ld, count %ld ... ", - scsi_curr_dev, blk, cnt); - n = scsi_read(scsi_curr_dev, blk, cnt, (ulong *)addr); - printf ("%ld blocks read: %s\n",n,(n==cnt) ? "OK" : "ERROR"); - return 0; - } else if (strcmp(argv[1], "write") == 0) { - ulong addr = simple_strtoul(argv[2], NULL, 16); - ulong blk = simple_strtoul(argv[3], NULL, 16); - ulong cnt = simple_strtoul(argv[4], NULL, 16); - ulong n; - printf("\nSCSI write: device %d block # %ld, " - "count %ld ... ", - scsi_curr_dev, blk, cnt); - n = scsi_write(scsi_curr_dev, blk, cnt, - (ulong *)addr); - printf("%ld blocks written: %s\n", n, - (n == cnt) ? "OK" : "ERROR"); - return 0; + scsi_curr_dev = dev; + printf("... is now current device\n"); + return 0; + } + if (strncmp(argv[1], "part", 4) == 0) { + int dev = (int) simple_strtoul(argv[2], NULL, 10); + if(scsi_dev_desc[dev].type != DEV_TYPE_UNKNOWN) { + print_part(&scsi_dev_desc[dev]); + } else { + printf("\nSCSI device %d not available\n", dev); } + return 1; + } + return CMD_RET_USAGE; + default: + /* at least 4 args */ + if (strcmp(argv[1],"read") == 0) { + ulong addr = simple_strtoul(argv[2], NULL, 16); + ulong blk = simple_strtoul(argv[3], NULL, 16); + ulong cnt = simple_strtoul(argv[4], NULL, 16); + ulong n; + printf ("\nSCSI read: device %d block # %ld, count %ld ... ", + scsi_curr_dev, blk, cnt); + n = scsi_read(scsi_curr_dev, blk, cnt, (ulong *)addr); + printf ("%ld blocks read: %s\n", n, + (n == cnt) ? "OK" : "ERROR"); + return 0; + } else if (strcmp(argv[1], "write") == 0) { + ulong addr = simple_strtoul(argv[2], NULL, 16); + ulong blk = simple_strtoul(argv[3], NULL, 16); + ulong cnt = simple_strtoul(argv[4], NULL, 16); + ulong n; + printf("\nSCSI write: device %d block # %ld, " + "count %ld ... ", + scsi_curr_dev, blk, cnt); + n = scsi_write(scsi_curr_dev, blk, cnt, + (ulong *)addr); + printf("%ld blocks written: %s\n", n, + (n == cnt) ? "OK" : "ERROR"); + return 0; + } } /* switch */ return CMD_RET_USAGE; }
-/**************************************************************************************** +/***************************************************************************** * 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
static ulong scsi_read(int device, lbaint_t blknr, lbaint_t blkcnt, void *buffer) @@ -360,47 +369,47 @@ static ulong scsi_read(int device, lbaint_t blknr, lbaint_t blkcnt, lbaint_t start, blks; uintptr_t buf_addr; unsigned short smallblks; - ccb* pccb=(ccb *)&tempccb; - device&=0xff; + ccb* pccb = (ccb *) &tempccb; + device &= 0xff; /* Setup device */ - pccb->target=scsi_dev_desc[device].target; - pccb->lun=scsi_dev_desc[device].lun; - buf_addr=(unsigned long)buffer; - start=blknr; - blks=blkcnt; + pccb->target = scsi_dev_desc[device].target; + pccb->lun = scsi_dev_desc[device].lun; + buf_addr = (unsigned long) buffer; + start = blknr; + blks = blkcnt; debug("\nscsi_read: dev %d startblk " LBAF ", blccnt " LBAF " buffer %lx\n", - device, start, blks, (unsigned long)buffer); + device, start, blks, (unsigned long) buffer); do { - pccb->pdata=(unsigned char *)buf_addr; - 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); - start+=SCSI_MAX_READ_BLK; - blks-=SCSI_MAX_READ_BLK; - } - else { - pccb->datalen=scsi_dev_desc[device].blksz * blks; - smallblks=(unsigned short) blks; - scsi_setup_read_ext(pccb,start,smallblks); - start+=blks; - blks=0; + pccb->pdata = (unsigned char *) buf_addr; + 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); + start += SCSI_MAX_READ_BLK; + blks -= SCSI_MAX_READ_BLK; + } else { + pccb->datalen = scsi_dev_desc[device].blksz * blks; + smallblks = (unsigned short) blks; + scsi_setup_read_ext(pccb, start, smallblks); + start += blks; + blks = 0; } debug("scsi_read_ext: startblk " LBAF ", blccnt %x buffer %lx\n", start, smallblks, buf_addr); if (scsi_exec(pccb) != true) { scsi_print_error(pccb); - blkcnt-=blks; + blkcnt -= blks; break; } - buf_addr+=pccb->datalen; - } while(blks!=0); + buf_addr += pccb->datalen; + } while (blks != 0); debug("scsi_read_ext: end startblk " LBAF ", blccnt %x buffer %lx\n", start, smallblks, buf_addr); - return(blkcnt); + return (blkcnt); }
/******************************************************************************* @@ -464,22 +473,22 @@ void scsi_ident_cpy (unsigned char *dest, unsigned char *src, unsigned int len) { int start,end;
- start=0; - while(start<len) { - if(src[start]!=' ') + start = 0; + while (start < len) { + if (src[start] != ' ') break; start++; } - end=len-1; - while(end>start) { - if(src[end]!=' ') + end = len - 1; + while (end > start) { + if (src[end] != ' ') break; end--; } - for( ; start<=end; start++) { - *dest++=src[start]; + for( ; start <= end; start++) { + *dest++ = src[start]; } - *dest='\0'; + *dest = '\0'; }
@@ -511,17 +520,17 @@ int scsi_read_capacity(ccb *pccb, lbaint_t *capacity, unsigned long *blksz) if (scsi_exec(pccb) != true) return 1;
- *capacity = ((lbaint_t)pccb->pdata[0] << 24) | - ((lbaint_t)pccb->pdata[1] << 16) | - ((lbaint_t)pccb->pdata[2] << 8) | - ((lbaint_t)pccb->pdata[3]); + *capacity = ((lbaint_t) pccb->pdata[0] << 24) | + ((lbaint_t) pccb->pdata[1] << 16) | + ((lbaint_t) pccb->pdata[2] << 8) | + ((lbaint_t) pccb->pdata[3]);
if (*capacity != 0xffffffff) { /* Read capacity (10) was sufficient for this drive. */ - *blksz = ((unsigned long)pccb->pdata[4] << 24) | - ((unsigned long)pccb->pdata[5] << 16) | - ((unsigned long)pccb->pdata[6] << 8) | - ((unsigned long)pccb->pdata[7]); + *blksz = ((unsigned long) pccb->pdata[4] << 24) | + ((unsigned long) pccb->pdata[5] << 16) | + ((unsigned long) pccb->pdata[6] << 8) | + ((unsigned long) pccb->pdata[7]); return 0; }
@@ -537,23 +546,23 @@ int scsi_read_capacity(ccb *pccb, lbaint_t *capacity, unsigned long *blksz) if (scsi_exec(pccb) != true) return 1;
- *capacity = ((uint64_t)pccb->pdata[0] << 56) | - ((uint64_t)pccb->pdata[1] << 48) | - ((uint64_t)pccb->pdata[2] << 40) | - ((uint64_t)pccb->pdata[3] << 32) | - ((uint64_t)pccb->pdata[4] << 24) | - ((uint64_t)pccb->pdata[5] << 16) | - ((uint64_t)pccb->pdata[6] << 8) | - ((uint64_t)pccb->pdata[7]); - - *blksz = ((uint64_t)pccb->pdata[8] << 56) | - ((uint64_t)pccb->pdata[9] << 48) | - ((uint64_t)pccb->pdata[10] << 40) | - ((uint64_t)pccb->pdata[11] << 32) | - ((uint64_t)pccb->pdata[12] << 24) | - ((uint64_t)pccb->pdata[13] << 16) | - ((uint64_t)pccb->pdata[14] << 8) | - ((uint64_t)pccb->pdata[15]); + *capacity = ((uint64_t) pccb->pdata[0] << 56) | + ((uint64_t) pccb->pdata[1] << 48) | + ((uint64_t) pccb->pdata[2] << 40) | + ((uint64_t) pccb->pdata[3] << 32) | + ((uint64_t) pccb->pdata[4] << 24) | + ((uint64_t) pccb->pdata[5] << 16) | + ((uint64_t) pccb->pdata[6] << 8) | + ((uint64_t) pccb->pdata[7]); + + *blksz = ((uint64_t) pccb->pdata[8] << 56) | + ((uint64_t) pccb->pdata[9] << 48) | + ((uint64_t) pccb->pdata[10] << 40) | + ((uint64_t) pccb->pdata[11] << 32) | + ((uint64_t) pccb->pdata[12] << 24) | + ((uint64_t) pccb->pdata[13] << 16) | + ((uint64_t) pccb->pdata[14] << 8) | + ((uint64_t) pccb->pdata[15]);
return 0; } @@ -564,51 +573,53 @@ int scsi_read_capacity(ccb *pccb, lbaint_t *capacity, unsigned long *blksz) */ void scsi_setup_test_unit_ready(ccb * pccb) { - pccb->cmd[0]=SCSI_TST_U_RDY; - pccb->cmd[1]=pccb->lun<<5; - pccb->cmd[2]=0; - pccb->cmd[3]=0; - pccb->cmd[4]=0; - pccb->cmd[5]=0; - pccb->cmdlen=6; - pccb->msgout[0]=SCSI_IDENTIFY; /* NOT USED */ + pccb->cmd[0] = SCSI_TST_U_RDY; + pccb->cmd[1] = pccb->lun << 5; + pccb->cmd[2] = 0; + pccb->cmd[3] = 0; + pccb->cmd[4] = 0; + pccb->cmd[5] = 0; + pccb->cmdlen = 6; + pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ }
void scsi_setup_read_ext(ccb * pccb, unsigned long start, unsigned short blocks) { - pccb->cmd[0]=SCSI_READ10; - pccb->cmd[1]=pccb->lun<<5; - pccb->cmd[2]=((unsigned char) (start>>24))&0xff; - pccb->cmd[3]=((unsigned char) (start>>16))&0xff; - pccb->cmd[4]=((unsigned char) (start>>8))&0xff; - pccb->cmd[5]=((unsigned char) (start))&0xff; - pccb->cmd[6]=0; - pccb->cmd[7]=((unsigned char) (blocks>>8))&0xff; - pccb->cmd[8]=(unsigned char) blocks & 0xff; - pccb->cmd[6]=0; - pccb->cmdlen=10; - pccb->msgout[0]=SCSI_IDENTIFY; /* NOT USED */ - debug ("scsi_setup_read_ext: cmd: %02X %02X startblk %02X%02X%02X%02X blccnt %02X%02X\n", - pccb->cmd[0],pccb->cmd[1], - pccb->cmd[2],pccb->cmd[3],pccb->cmd[4],pccb->cmd[5], - pccb->cmd[7],pccb->cmd[8]); + pccb->cmd[0] = SCSI_READ10; + pccb->cmd[1] = pccb->lun << 5; + pccb->cmd[2] = ((unsigned char) (start >> 24)) & 0xff; + pccb->cmd[3] = ((unsigned char) (start >> 16)) & 0xff; + pccb->cmd[4] = ((unsigned char) (start >> 8)) & 0xff; + pccb->cmd[5] = ((unsigned char) (start)) & 0xff; + pccb->cmd[6] = 0; + pccb->cmd[7] = ((unsigned char) (blocks >> 8)) & 0xff; + pccb->cmd[8] = (unsigned char) blocks & 0xff; + pccb->cmd[6] = 0; + pccb->cmdlen = 10; + pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ + debug ("scsi_setup_read_ext: cmd: %02X %02X " + "startblk %02X%02X%02X%02X blccnt %02X%02X\n", + pccb->cmd[0], pccb->cmd[1], + pccb->cmd[2], pccb->cmd[3], pccb->cmd[4], pccb->cmd[5], + pccb->cmd[7], pccb->cmd[8]); }
void scsi_setup_write_ext(ccb *pccb, unsigned long start, unsigned short blocks) { pccb->cmd[0] = SCSI_WRITE10; pccb->cmd[1] = pccb->lun << 5; - pccb->cmd[2] = ((unsigned char) (start>>24)) & 0xff; - pccb->cmd[3] = ((unsigned char) (start>>16)) & 0xff; - pccb->cmd[4] = ((unsigned char) (start>>8)) & 0xff; + pccb->cmd[2] = ((unsigned char) (start >> 24)) & 0xff; + pccb->cmd[3] = ((unsigned char) (start >> 16)) & 0xff; + pccb->cmd[4] = ((unsigned char) (start >> 8)) & 0xff; pccb->cmd[5] = ((unsigned char) (start)) & 0xff; pccb->cmd[6] = 0; - pccb->cmd[7] = ((unsigned char) (blocks>>8)) & 0xff; - pccb->cmd[8] = (unsigned char)blocks & 0xff; + pccb->cmd[7] = ((unsigned char) (blocks >> 8)) & 0xff; + pccb->cmd[8] = (unsigned char) blocks & 0xff; pccb->cmd[9] = 0; pccb->cmdlen = 10; pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ - debug("%s: cmd: %02X %02X startblk %02X%02X%02X%02X blccnt %02X%02X\n", + debug("%s: cmd: %02X %02X startblk %02X%02X%02X%02X i" + " blccnt %02X%02X\n", __func__, pccb->cmd[0], pccb->cmd[1], pccb->cmd[2], pccb->cmd[3], pccb->cmd[4], pccb->cmd[5], @@ -617,33 +628,34 @@ void scsi_setup_write_ext(ccb *pccb, unsigned long start, unsigned short blocks)
void scsi_setup_read6(ccb * pccb, unsigned long start, unsigned short blocks) { - pccb->cmd[0]=SCSI_READ6; - pccb->cmd[1]=pccb->lun<<5 | (((unsigned char)(start>>16))&0x1f); - pccb->cmd[2]=((unsigned char) (start>>8))&0xff; - pccb->cmd[3]=((unsigned char) (start))&0xff; - pccb->cmd[4]=(unsigned char) blocks & 0xff; - pccb->cmd[5]=0; - pccb->cmdlen=6; - pccb->msgout[0]=SCSI_IDENTIFY; /* NOT USED */ - debug ("scsi_setup_read6: cmd: %02X %02X startblk %02X%02X blccnt %02X\n", - pccb->cmd[0],pccb->cmd[1], - pccb->cmd[2],pccb->cmd[3],pccb->cmd[4]); + pccb->cmd[0] = SCSI_READ6; + pccb->cmd[1] = pccb->lun << 5 | (((unsigned char)(start >> 16)) & 0x1f); + pccb->cmd[2] = ((unsigned char) (start >> 8)) & 0xff; + pccb->cmd[3] = ((unsigned char) (start)) & 0xff; + pccb->cmd[4] = (unsigned char) blocks & 0xff; + pccb->cmd[5] = 0; + pccb->cmdlen = 6; + pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ + debug ("scsi_setup_read6: cmd: %02X %02X " + "startblk %02X%02X blccnt %02X\n", + pccb->cmd[0], pccb->cmd[1], + pccb->cmd[2], pccb->cmd[3], pccb->cmd[4]); }
void scsi_setup_inquiry(ccb * pccb) { - pccb->cmd[0]=SCSI_INQUIRY; - pccb->cmd[1]=pccb->lun<<5; - pccb->cmd[2]=0; - pccb->cmd[3]=0; - if(pccb->datalen>255) - pccb->cmd[4]=255; + pccb->cmd[0] = SCSI_INQUIRY; + pccb->cmd[1] = pccb->lun << 5; + pccb->cmd[2] = 0; + pccb->cmd[3] = 0; + if(pccb->datalen > 255) + pccb->cmd[4] = 255; else - pccb->cmd[4]=(unsigned char)pccb->datalen; - pccb->cmd[5]=0; - pccb->cmdlen=6; - pccb->msgout[0]=SCSI_IDENTIFY; /* NOT USED */ + pccb->cmd[4] = (unsigned char) pccb->datalen; + pccb->cmd[5] = 0; + pccb->cmdlen = 6; + pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ }

The lbaint_t type exists, and should be used in preference to unsigned long, which may not be correct when support lba48 compatible drives.
Signed-off-by: Mark Langsdorf mark.langsdorf@calxeda.com --- common/cmd_scsi.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/common/cmd_scsi.c b/common/cmd_scsi.c index bbaed41..f5ab14f 100644 --- a/common/cmd_scsi.c +++ b/common/cmd_scsi.c @@ -53,10 +53,10 @@ static block_dev_desc_t scsi_dev_desc[CONFIG_SYS_SCSI_MAX_DEVICE]; * forward declerations of some Setup Routines */ void scsi_setup_test_unit_ready(ccb * pccb); -void scsi_setup_read6(ccb * pccb, unsigned long start, unsigned short blocks); -void scsi_setup_read_ext(ccb * pccb, unsigned long start, unsigned short blocks); -static void scsi_setup_write_ext(ccb *pccb, unsigned long 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); +static void scsi_setup_write_ext(ccb *pccb, lbaint_t start, + unsigned short blocks); void scsi_setup_inquiry(ccb * pccb); void scsi_ident_cpy (unsigned char *dest, unsigned char *src, unsigned int len);
@@ -583,7 +583,7 @@ void scsi_setup_test_unit_ready(ccb * pccb) pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ }
-void scsi_setup_read_ext(ccb * pccb, unsigned long start, unsigned short blocks) +void scsi_setup_read_ext(ccb * pccb, lbaint_t start, unsigned short blocks) { pccb->cmd[0] = SCSI_READ10; pccb->cmd[1] = pccb->lun << 5; @@ -604,7 +604,7 @@ void scsi_setup_read_ext(ccb * pccb, unsigned long start, unsigned short blocks) pccb->cmd[7], pccb->cmd[8]); }
-void scsi_setup_write_ext(ccb *pccb, unsigned long start, unsigned short blocks) +void scsi_setup_write_ext(ccb *pccb, lbaint_t start, unsigned short blocks) { pccb->cmd[0] = SCSI_WRITE10; pccb->cmd[1] = pccb->lun << 5; @@ -626,7 +626,7 @@ void scsi_setup_write_ext(ccb *pccb, unsigned long start, unsigned short blocks) pccb->cmd[7], pccb->cmd[8]); }
-void scsi_setup_read6(ccb * pccb, unsigned long start, unsigned short blocks) +void scsi_setup_read6(ccb * pccb, lbaint_t start, unsigned short blocks) { pccb->cmd[0] = SCSI_READ6; pccb->cmd[1] = pccb->lun << 5 | (((unsigned char)(start >> 16)) & 0x1f);

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

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

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?
participants (3)
-
Anatolij Gustschin
-
Mark Langsdorf
-
Tom Rini