summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Bendebury <vbendeb@chromium.org>2011-09-02 16:34:04 -0700
committerVadim Bendebury <vbendeb@chromium.org>2011-09-06 10:32:11 -0700
commit51511b60489063b404f6d7809b761d56e1d3d426 (patch)
treeba869309531a2fd07e3c3e927e18cfe5ee4d0a6d
parentf3d075964621ba7a80ae89ce0a15a8e40811f09a (diff)
Fix AHCI driver to operate on Intel controllers.
First - make sure the ahci driver compiles and links properly when building for x86 platforms. Then it turned out that when trying to use AHCI driver on an x86 platform with an Intel AHCI controller, the driver does not operate properly if the requested amount of blocks to read was exceeding 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). BUG=chromium-os:19837 TEST=manual . program updated image (including vbexport SATA support extension) on an Alex. . restart the machine and enter `vboot_twostop' at u-boot prompt. Observe ChromeOS booting all the way to login screen. Change-Id: I7224ca14ae60f414db6dbe9e2f0a649312a9459c Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-on: http://gerrit.chromium.org/gerrit/7232 Reviewed-by: Gabe Black (Do Not Use) <gabeblack@google.com> Reviewed-by: Duncan Laurie <dlaurie@chromium.org>
-rw-r--r--arch/x86/include/asm/bitops.h7
-rw-r--r--arch/x86/lib/board.c1
-rw-r--r--common/cmd_scsi.c5
-rw-r--r--drivers/block/ahci.c103
4 files changed, 83 insertions, 33 deletions
diff --git a/arch/x86/include/asm/bitops.h b/arch/x86/include/asm/bitops.h
index c7a38f237a..5c68c5f30f 100644
--- a/arch/x86/include/asm/bitops.h
+++ b/arch/x86/include/asm/bitops.h
@@ -315,6 +315,13 @@ static __inline__ int find_next_zero_bit (void * addr, int size, int offset)
return (offset + set + res);
}
+extern __inline__ int __ilog2(unsigned int x)
+{
+ int lz;
+ __asm__("bsr %1, %0" : "=r" (lz) : "r" (x) : "cc");
+ return lz;
+}
+
/**
* ffz - find first zero in word.
* @word: The word to search
diff --git a/arch/x86/lib/board.c b/arch/x86/lib/board.c
index 30b5df35e2..922e137713 100644
--- a/arch/x86/lib/board.c
+++ b/arch/x86/lib/board.c
@@ -40,6 +40,7 @@
#include <malloc.h>
#include <net.h>
#include <ide.h>
+#include <scsi.h>
#include <serial.h>
#include <asm/u-boot-x86.h>
#include <spi.h>
diff --git a/common/cmd_scsi.c b/common/cmd_scsi.c
index be4fe741c3..6e705d2486 100644
--- a/common/cmd_scsi.c
+++ b/common/cmd_scsi.c
@@ -34,6 +34,10 @@
#include <image.h>
#include <pci.h>
+#ifdef CONFIG_SCSI_VEND_ID
+#define SCSI_VEND_ID CONFIG_SCSI_VEND_ID
+#define SCSI_DEV_ID CONFIG_SCSI_DEV_ID
+#else
#ifdef CONFIG_SCSI_SYM53C8XX
#define SCSI_VEND_ID 0x1000
#ifndef CONFIG_SCSI_DEV_ID
@@ -49,6 +53,7 @@
#else
#error no scsi device defined
#endif
+#endif
static ccb tempccb; /* temporary scsi command buffer */
diff --git a/drivers/block/ahci.c b/drivers/block/ahci.c
index a3ca2dcaf7..6b70d3b83d 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)
{
@@ -86,6 +94,8 @@ static int ahci_host_init(struct ahci_probe_ent *probe_ent)
volatile u8 *port_mmio;
unsigned short vendor;
+ debug("ahci_host_init: start\n");
+
cap_save = readl(mmio + HOST_CAP);
cap_save &= ((1 << 28) | (1 << 17));
cap_save |= (1 << 27);
@@ -126,6 +136,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;
@@ -268,8 +281,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:
@@ -389,7 +402,7 @@ static int ahci_port_start(u8 port)
* 32 bytes each in size
*/
pp->cmd_slot = (struct ahci_cmd_hdr *)mem;
- debug("cmd_slot = 0x%x\n", pp->cmd_slot);
+ debug("cmd_slot = 0x%x\n", (unsigned)pp->cmd_slot);
mem += (AHCI_CMD_SLOT_SZ + 224);
/*
@@ -468,7 +481,7 @@ static char *ata_id_strcpy(u16 *target, u16 *src, int len)
{
int i;
for (i = 0; i < len / 2; i++)
- target[i] = le16_to_cpu(src[i]);
+ target[i] = be16_to_cpu(src[i]);
return (char *)target;
}
@@ -552,45 +565,69 @@ static int ata_scsiop_inquiry(ccb *pccb)
*/
static int ata_scsiop_read10(ccb * pccb)
{
- u64 lba = 0;
- u32 len = 0;
+ u32 lba = 0;
+ u16 blocks = 0;
u8 fis[20];
+ u8 *user_buffer = pccb->pdata;
+ u32 user_buffer_size = pccb->datalen;
- lba = (((u64) pccb->cmd[2]) << 24) | (((u64) pccb->cmd[3]) << 16)
- | (((u64) pccb->cmd[4]) << 8) | ((u64) pccb->cmd[5]);
- 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;
@@ -611,7 +648,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;