summaryrefslogtreecommitdiff
path: root/drivers/block/ahci.c
diff options
context:
space:
mode:
authorVadim Bendebury <vbendeb@chromium.org>2012-10-29 05:23:44 +0000
committerTom Rini <trini@ti.com>2012-11-02 15:20:40 -0700
commit284231e49a2b4ee00647db592c54c3efd1ac7d35 (patch)
treecc7c0ba3f7f7da2d782adbd488342f108a5f23fe /drivers/block/ahci.c
parente08ebf468784668155fd889b9eaf7868f283fe87 (diff)
downloadu-boot-imx-284231e49a2b4ee00647db592c54c3efd1ac7d35.zip
u-boot-imx-284231e49a2b4ee00647db592c54c3efd1ac7d35.tar.gz
u-boot-imx-284231e49a2b4ee00647db592c54c3efd1ac7d35.tar.bz2
ahci: Support splitting of read transactions into multiple chunks
With an Intel AHCI controller, the driver does not operate properly if the requested amount of blocks to read exceeds 255. It is probably possible to specify 0 as the block count and the driver will read 256 blocks, but it was decided to limit the number of blocks read at once to 128 (it should be a power of 2 for the optimal performance of solid state drives). Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Signed-off-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'drivers/block/ahci.c')
-rw-r--r--drivers/block/ahci.c98
1 files changed, 69 insertions, 29 deletions
diff --git a/drivers/block/ahci.c b/drivers/block/ahci.c
index 7b2ec50..d94da1f 100644
--- a/drivers/block/ahci.c
+++ b/drivers/block/ahci.c
@@ -42,6 +42,14 @@ hd_driveid_t *ataid[AHCI_MAX_PORTS];
#define writel_with_flush(a,b) do { writel(a,b); readl(b); } while (0)
+/*
+ * Some controllers limit number of blocks they can read at once. Contemporary
+ * SSD devices work much faster if the read size is aligned to a power of 2.
+ * Let's set default to 128 and allowing to be overwritten if needed.
+ */
+#ifndef MAX_SATA_BLOCKS_READ
+#define MAX_SATA_BLOCKS_READ 0x80
+#endif
static inline u32 ahci_port_base(u32 base, u32 port)
{
@@ -88,6 +96,8 @@ static int ahci_host_init(struct ahci_probe_ent *probe_ent)
int i, j;
volatile u8 *port_mmio;
+ debug("ahci_host_init: start\n");
+
cap_save = readl(mmio + HOST_CAP);
cap_save &= ((1 << 28) | (1 << 17));
cap_save |= (1 << 27);
@@ -129,6 +139,9 @@ static int ahci_host_init(struct ahci_probe_ent *probe_ent)
debug("cap 0x%x port_map 0x%x n_ports %d\n",
probe_ent->cap, probe_ent->port_map, probe_ent->n_ports);
+ if (probe_ent->n_ports > CONFIG_SYS_SCSI_MAX_SCSI_ID)
+ probe_ent->n_ports = CONFIG_SYS_SCSI_MAX_SCSI_ID;
+
for (i = 0; i < probe_ent->n_ports; i++) {
probe_ent->port[i].port_mmio = ahci_port_base((u32) mmio, i);
port_mmio = (u8 *) probe_ent->port[i].port_mmio;
@@ -277,8 +290,8 @@ static int ahci_init_one(pci_dev_t pdev)
probe_ent->pio_mask = 0x1f;
probe_ent->udma_mask = 0x7f; /*Fixme,assume to support UDMA6 */
- probe_ent->mmio_base = (u32)pci_map_bar(pdev, AHCI_PCI_BAR,
- PCI_REGION_MEM);
+ pci_read_config_dword(pdev, PCI_BASE_ADDRESS_5, &probe_ent->mmio_base);
+ debug("ahci mmio_base=0x%08x\n", probe_ent->mmio_base);
/* Take from kernel:
* JMicron-specific fixup:
@@ -398,7 +411,7 @@ static int ahci_port_start(u8 port)
* 32 bytes each in size
*/
pp->cmd_slot = (struct ahci_cmd_hdr *)mem;
- debug("cmd_slot = %p\n", pp->cmd_slot);
+ debug("cmd_slot = 0x%x\n", (unsigned)pp->cmd_slot);
mem += (AHCI_CMD_SLOT_SZ + 224);
/*
@@ -561,42 +574,69 @@ static int ata_scsiop_inquiry(ccb *pccb)
*/
static int ata_scsiop_read10(ccb * pccb)
{
- u32 len = 0;
+ u32 lba = 0;
+ u16 blocks = 0;
u8 fis[20];
+ u8 *user_buffer = pccb->pdata;
+ u32 user_buffer_size = pccb->datalen;
- len = (((u32) pccb->cmd[7]) << 8) | ((u32) pccb->cmd[8]);
+ /* Retrieve the base LBA number from the ccb structure. */
+ memcpy(&lba, pccb->cmd + 2, sizeof(lba));
+ lba = be32_to_cpu(lba);
- /* For 10-byte and 16-byte SCSI R/W commands, transfer
+ /*
+ * And the number of blocks.
+ *
+ * For 10-byte and 16-byte SCSI R/W commands, transfer
* length 0 means transfer 0 block of data.
* However, for ATA R/W commands, sector count 0 means
* 256 or 65536 sectors, not 0 sectors as in SCSI.
*
* WARNING: one or two older ATA drives treat 0 as 0...
*/
- if (!len)
- return 0;
+ blocks = (((u16)pccb->cmd[7]) << 8) | ((u16) pccb->cmd[8]);
+
+ debug("scsi_ahci: read %d blocks starting from lba 0x%x\n",
+ (unsigned)lba, blocks);
+
+ /* Preset the FIS */
memset(fis, 0, 20);
+ fis[0] = 0x27; /* Host to device FIS. */
+ fis[1] = 1 << 7; /* Command FIS. */
+ fis[2] = ATA_CMD_RD_DMA; /* Command byte. */
- /* Construct the FIS */
- fis[0] = 0x27; /* Host to device FIS. */
- fis[1] = 1 << 7; /* Command FIS. */
- fis[2] = ATA_CMD_RD_DMA; /* Command byte. */
-
- /* LBA address, only support LBA28 in this driver */
- fis[4] = pccb->cmd[5];
- fis[5] = pccb->cmd[4];
- fis[6] = pccb->cmd[3];
- fis[7] = (pccb->cmd[2] & 0x0f) | 0xe0;
-
- /* Sector Count */
- fis[12] = pccb->cmd[8];
- fis[13] = pccb->cmd[7];
-
- /* Read from ahci */
- if (get_ahci_device_data(pccb->target, (u8 *) & fis, 20,
- pccb->pdata, pccb->datalen)) {
- debug("scsi_ahci: SCSI READ10 command failure.\n");
- return -EIO;
+ while (blocks) {
+ u16 now_blocks; /* number of blocks per iteration */
+ u32 transfer_size; /* number of bytes per iteration */
+
+ now_blocks = min(MAX_SATA_BLOCKS_READ, blocks);
+
+ transfer_size = ATA_BLOCKSIZE * now_blocks;
+ if (transfer_size > user_buffer_size) {
+ printf("scsi_ahci: Error: buffer too small.\n");
+ return -EIO;
+ }
+
+ /* LBA address, only support LBA28 in this driver */
+ fis[4] = (lba >> 0) & 0xff;
+ fis[5] = (lba >> 8) & 0xff;
+ fis[6] = (lba >> 16) & 0xff;
+ fis[7] = ((lba >> 24) & 0xf) | 0xe0;
+
+ /* Block (sector) count */
+ fis[12] = (now_blocks >> 0) & 0xff;
+ fis[13] = (now_blocks >> 8) & 0xff;
+
+ /* Read from ahci */
+ if (get_ahci_device_data(pccb->target, (u8 *) &fis, sizeof(fis),
+ user_buffer, user_buffer_size)) {
+ debug("scsi_ahci: SCSI READ10 command failure.\n");
+ return -EIO;
+ }
+ user_buffer += transfer_size;
+ user_buffer_size -= transfer_size;
+ blocks -= now_blocks;
+ lba += now_blocks;
}
return 0;
@@ -617,7 +657,7 @@ static int ata_scsiop_read_capacity10(ccb *pccb)
return -EPERM;
}
- cap = le32_to_cpu(ataid[pccb->target]->lba_capacity);
+ cap = be32_to_cpu(ataid[pccb->target]->lba_capacity);
memcpy(pccb->pdata, &cap, sizeof(cap));
pccb->pdata[4] = pccb->pdata[5] = 0;