summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabe Black <gabeblack@chromium.org>2012-03-13 16:08:37 -0700
committerGerrit <chrome-bot@google.com>2012-03-14 16:34:02 -0700
commit79b44667b53f8dd03714c361c347d2355fe5d4f1 (patch)
tree0260c88811cce9bfa685f1dc00fcce6df02164e9
parent1bd4fbf1cd120275c3f05425ac7273e7d0cb4a0a (diff)
gen: Make the AHCI code find the capacity of disks > 128 GB properly
In the structure returned by the ATA identify device command, there are two fields which describe the device capacity. One is a 32 bit data type which reports the number of sectors as a 28 bit LBA, and the other is a 64 bit data type which is for a 48 bit LBA. If the device doesn't support 48 bit LBAs, the small value is the only value with the correct size. If it supports more, if the number of sectors is small enough to fit into 28 bits, both fields reflect the correct value. If it's too large, the smaller field has 28 bits of 1s, 0xfffffff, and the other field has the correct value. The AHCI driver is implemented by attaching to the generic SCSI code and translating on the fly between SCSI binary data structures and AHCI data structures. It responds to requests to execute specific SCSI commands by executing the equivalent AHCI commands and then crafting a response which matches what a SCSI disk would send. The AHCI driver now considers both fields and chooses the correct one when implementing both the SCSI READ CAPACITY (10) and READ CAPACITY (16) commands. BUG=chrome-os-partner:8180 TEST=Built and booted to ChromeOS with this code on a CRB with a 250 GB drive and a Stumpy with a 16 GB drive. Checked the serial output to make sure U-Boot reported the correct size. Forced the READ CAPACITY (10) command to saturate so that the READ CAPACITY (16) command would be used and verified that that also booted correctly on a CRB. Change-Id: I31b662498f4c9657d70bb90400032c83e9d9c8da Signed-off-by: Gabe Black <gabeblack@google.com> Reviewed-on: https://gerrit.chromium.org/gerrit/18061 Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--drivers/block/ahci.c55
1 files changed, 50 insertions, 5 deletions
diff --git a/drivers/block/ahci.c b/drivers/block/ahci.c
index 1dce6dc493..dc634d5761 100644
--- a/drivers/block/ahci.c
+++ b/drivers/block/ahci.c
@@ -663,6 +663,7 @@ static int ata_scsiop_read_write(ccb *pccb, u8 is_write)
static int ata_scsiop_read_capacity10(ccb *pccb)
{
u32 cap;
+ u32 block_size;
if (!ataid[pccb->target]) {
printf("scsi_ahci: SCSI READ CAPACITY10 command failure. "
@@ -671,12 +672,53 @@ static int ata_scsiop_read_capacity10(ccb *pccb)
return -EPERM;
}
- cap = be32_to_cpu(ataid[pccb->target]->lba_capacity);
+ cap = le32_to_cpu(ataid[pccb->target]->lba_capacity);
+ if (cap == 0xfffffff) {
+ unsigned short *cap48 = ataid[pccb->target]->lba48_capacity;
+ if (cap48[2] || cap48[3]) {
+ cap = 0xffffffff;
+ } else {
+ cap = (le16_to_cpu(cap48[1]) << 16) |
+ (le16_to_cpu(cap48[0]));
+ }
+ }
+
+ cap = cpu_to_be32(cap);
memcpy(pccb->pdata, &cap, sizeof(cap));
- pccb->pdata[4] = pccb->pdata[5] = 0;
- pccb->pdata[6] = 512 >> 8;
- pccb->pdata[7] = 512 & 0xff;
+ block_size = cpu_to_be32((u32)512);
+ memcpy(&pccb->pdata[4], &block_size, 4);
+
+ return 0;
+}
+
+
+/*
+ * SCSI READ CAPACITY16 command operation.
+ */
+static int ata_scsiop_read_capacity16(ccb *pccb)
+{
+ u64 cap;
+ u64 block_size;
+
+ if (!ataid[pccb->target]) {
+ printf("scsi_ahci: SCSI READ CAPACITY16 command failure. "
+ "\tNo ATA info!\n"
+ "\tPlease run SCSI commmand INQUIRY firstly!\n");
+ return -EPERM;
+ }
+
+ cap = le32_to_cpu(ataid[pccb->target]->lba_capacity);
+ if (cap == 0xfffffff) {
+ memcpy(&cap, ataid[pccb->target]->lba48_capacity, sizeof(cap));
+ cap = le64_to_cpu(cap);
+ }
+
+ cap = cpu_to_be64(cap);
+ memcpy(pccb->pdata, &cap, sizeof(cap));
+
+ block_size = cpu_to_be64((u64)512);
+ memcpy(&pccb->pdata[8], &block_size, 8);
return 0;
}
@@ -702,9 +744,12 @@ int scsi_exec(ccb *pccb)
case SCSI_WRITE10:
ret = ata_scsiop_read_write(pccb, 1);
break;
- case SCSI_RD_CAPAC:
+ case SCSI_RD_CAPAC10:
ret = ata_scsiop_read_capacity10(pccb);
break;
+ case SCSI_RD_CAPAC16:
+ ret = ata_scsiop_read_capacity16(pccb);
+ break;
case SCSI_TST_U_RDY:
ret = ata_scsiop_test_unit_ready(pccb);
break;