Patchwork [U-Boot,v3,01/22] ahci: Support splitting of read transactions into multiple chunks

login
register
mail settings
Submitter Simon Glass
Date Oct. 29, 2012, 3:23 p.m.
Message ID <1351524245-19584-2-git-send-email-sjg@chromium.org>
Download mbox | patch
Permalink /patch/195031/
State Accepted, archived
Delegated to: Tom Rini
Headers show

Comments

Simon Glass - Oct. 29, 2012, 3:23 p.m.
From: Vadim Bendebury <vbendeb@chromium.org>

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>
---

 drivers/block/ahci.c |   98 +++++++++++++++++++++++++++++++++++---------------
 1 files changed, 69 insertions(+), 29 deletions(-)

Patch

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;