]> git.kernelconcepts.de Git - karo-tx-uboot.git/commitdiff
ahci: Make the AHCI code find the capacity of disks > 128 GB properly
authorGabe Black <gabeblack@chromium.org>
Mon, 29 Oct 2012 05:23:54 +0000 (05:23 +0000)
committerTom Rini <trini@ti.com>
Fri, 2 Nov 2012 22:20:41 +0000 (15:20 -0700)
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.

Signed-off-by: Gabe Black <gabeblack@chromium.org>
Signed-off-by: Simon Glass <sjg@chromium.org>
drivers/block/ahci.c
include/scsi.h

index 5092352758b70d3f1dadb6bc58353464aebfef27..c16e8bae6ffcf36f3913c584087cc977e20d120e 100644 (file)
@@ -672,6 +672,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. "
@@ -680,12 +681,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;
 }
@@ -711,9 +753,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;
index 9681d198dff64e7e05bd56e2189eeb0b0384552d..9da764bdcf31f7fd896ef0d1b8ebea59eebb356d 100644 (file)
@@ -150,6 +150,8 @@ typedef struct SCSI_cmd_block{
 #define SCSI_READ6             0x08            /* Read 6-byte (MANDATORY) */
 #define SCSI_READ10            0x28            /* Read 10-byte (MANDATORY) */
 #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) */
 #define SCSI_RD_DEFECT 0x37            /* Read Defect Data (O) */
 #define SCSI_READ_LONG 0x3E            /* Read Long (O) */
 #define SCSI_REASS_BLK 0x07            /* Reassign Blocks (O) */