ddr: fsl: Impl. Erratum A008109
[oweals/u-boot.git] / drivers / ata / ahci.c
index c35912bd3341f97423d6bde0ab7ed71b95ed592b..47cdea1f586a438da39d7bf66b5698d43ef7b910 100644 (file)
@@ -9,6 +9,11 @@
  * This driver provides a SCSI interface to SATA.
  */
 #include <common.h>
+#include <blk.h>
+#include <cpu_func.h>
+#include <log.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
 
 #include <command.h>
 #include <dm.h>
@@ -50,22 +55,13 @@ struct ahci_uc_priv *probe_ent = NULL;
 #define WAIT_MS_FLUSH  5000
 #define WAIT_MS_LINKUP 200
 
+#define AHCI_CAP_S64A BIT(31)
+
 __weak void __iomem *ahci_port_base(void __iomem *base, u32 port)
 {
        return base + 0x100 + (port * 0x80);
 }
 
-
-static void ahci_setup_port(struct ahci_ioports *port, void __iomem *base,
-                           unsigned int port_idx)
-{
-       base = ahci_port_base(base, port_idx);
-
-       port->cmd_addr = base;
-       port->scr_addr = base + PORT_SCR;
-}
-
-
 #define msleep(a) udelay(a * 1000)
 
 static void ahci_dcache_flush_range(unsigned long begin, unsigned long len)
@@ -230,15 +226,16 @@ static int ahci_host_init(struct ahci_uc_priv *uc_priv)
        debug("cap 0x%x  port_map 0x%x  n_ports %d\n",
              uc_priv->cap, uc_priv->port_map, uc_priv->n_ports);
 
+#if !defined(CONFIG_DM_SCSI)
        if (uc_priv->n_ports > CONFIG_SYS_SCSI_MAX_SCSI_ID)
                uc_priv->n_ports = CONFIG_SYS_SCSI_MAX_SCSI_ID;
+#endif
 
        for (i = 0; i < uc_priv->n_ports; i++) {
                if (!(port_map & (1 << i)))
                        continue;
                uc_priv->port[i].port_mmio = ahci_port_base(mmio, i);
                port_mmio = (u8 *)uc_priv->port[i].port_mmio;
-               ahci_setup_port(&uc_priv->port[i], mmio, i);
 
                /* make sure port is not active */
                tmp = readl(port_mmio + PORT_CMD);
@@ -513,9 +510,15 @@ static int ahci_fill_sg(struct ahci_uc_priv *uc_priv, u8 port,
        }
 
        for (i = 0; i < sg_count; i++) {
-               ahci_sg->addr =
-                   cpu_to_le32((unsigned long) buf + i * MAX_DATA_BYTE_COUNT);
-               ahci_sg->addr_hi = 0;
+               /* We assume virt=phys */
+               phys_addr_t pa = (unsigned long)buf + i * MAX_DATA_BYTE_COUNT;
+
+               ahci_sg->addr = cpu_to_le32(lower_32_bits(pa));
+               ahci_sg->addr_hi = cpu_to_le32(upper_32_bits(pa));
+               if (ahci_sg->addr_hi && !(uc_priv->cap & AHCI_CAP_S64A)) {
+                       printf("Error: DMA address too high\n");
+                       return -1;
+               }
                ahci_sg->flags_size = cpu_to_le32(0x3fffff &
                                          (buf_len < MAX_DATA_BYTE_COUNT
                                           ? (buf_len - 1)
@@ -558,6 +561,7 @@ static int ahci_port_start(struct ahci_uc_priv *uc_priv, u8 port)
 {
        struct ahci_ioports *pp = &(uc_priv->port[port]);
        void __iomem *port_mmio = pp->port_mmio;
+       u64 dma_addr;
        u32 port_status;
        void __iomem *mem;
 
@@ -569,15 +573,12 @@ static int ahci_port_start(struct ahci_uc_priv *uc_priv, u8 port)
                return -1;
        }
 
-       mem = malloc(AHCI_PORT_PRIV_DMA_SZ + 2048);
+       mem = memalign(2048, AHCI_PORT_PRIV_DMA_SZ);
        if (!mem) {
                free(pp);
                printf("%s: No mem for table!\n", __func__);
                return -ENOMEM;
        }
-
-       /* Aligned to 2048-bytes */
-       mem = memalign(2048, AHCI_PORT_PRIV_DMA_SZ);
        memset(mem, 0, AHCI_PORT_PRIV_DMA_SZ);
 
        /*
@@ -606,10 +607,12 @@ static int ahci_port_start(struct ahci_uc_priv *uc_priv, u8 port)
        pp->cmd_tbl_sg =
                        (struct ahci_sg *)(uintptr_t)virt_to_phys((void *)mem);
 
-       writel_with_flush((unsigned long)pp->cmd_slot,
-                         port_mmio + PORT_LST_ADDR);
-
-       writel_with_flush(pp->rx_fis, port_mmio + PORT_FIS_ADDR);
+       dma_addr = (ulong)pp->cmd_slot;
+       writel_with_flush(dma_addr, port_mmio + PORT_LST_ADDR);
+       writel_with_flush(dma_addr >> 32, port_mmio + PORT_LST_ADDR_HI);
+       dma_addr = (ulong)pp->rx_fis;
+       writel_with_flush(dma_addr, port_mmio + PORT_FIS_ADDR);
+       writel_with_flush(dma_addr >> 32, port_mmio + PORT_FIS_ADDR_HI);
 
 #ifdef CONFIG_SUNXI_AHCI
        sunxi_dma_init(port_mmio);
@@ -980,7 +983,7 @@ static int ahci_start_ports(struct ahci_uc_priv *uc_priv)
 
        linkmap = uc_priv->link_port_map;
 
-       for (i = 0; i < CONFIG_SYS_SCSI_MAX_SCSI_ID; i++) {
+       for (i = 0; i < uc_priv->n_ports; i++) {
                if (((linkmap >> i) & 0x01)) {
                        if (ahci_port_start(uc_priv, (u8) i)) {
                                printf("Can not start port %d\n", i);
@@ -1180,6 +1183,14 @@ int ahci_probe_scsi(struct udevice *ahci_dev, ulong base)
        if (ret)
                return ret;
 
+       /*
+        * scsi_scan_dev() scans devices up-to the number of max_id.
+        * Update max_id if the number of detected ports exceeds max_id.
+        * This allows SCSI to scan all detected ports.
+        */
+       uc_plat->max_id = max_t(unsigned long, uc_priv->n_ports,
+                               uc_plat->max_id);
+
        return 0;
 }