ddr: fsl: Impl. Erratum A008109
[oweals/u-boot.git] / drivers / ata / ahci.c
index 4830bcdca00665a7d5d376c60cf3588c40f83967..47cdea1f586a438da39d7bf66b5698d43ef7b910 100644 (file)
@@ -1,13 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (C) Freescale Semiconductor, Inc. 2006.
  * Author: Jason Jin<Jason.jin@freescale.com>
  *         Zhang Wei<wei.zhang@freescale.com>
  *
- * SPDX-License-Identifier:    GPL-2.0+
- *
  * with the reference on libata and ahci drvier in kernel
+ *
+ * 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>
 #include <asm/io.h>
 #include <malloc.h>
 #include <memalign.h>
+#include <pci.h>
 #include <scsi.h>
 #include <libata.h>
 #include <linux/ctype.h>
 #include <ahci.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
 
 static int ata_io_flush(struct ahci_uc_priv *uc_priv, u8 port);
 
+#ifndef CONFIG_DM_SCSI
 struct ahci_uc_priv *probe_ent = NULL;
+#endif
 
 #define writel_with_flush(a,b) do { writel(a,b); readl(b); } while (0)
 
@@ -44,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)
@@ -224,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);
@@ -424,27 +427,18 @@ static void ahci_print_info(struct ahci_uc_priv *uc_priv)
               cap2 & (1 << 0) ? "boh " : "");
 }
 
-#ifndef CONFIG_SCSI_AHCI_PLAT
+#if defined(CONFIG_DM_SCSI) || !defined(CONFIG_SCSI_AHCI_PLAT)
 # if defined(CONFIG_DM_PCI) || defined(CONFIG_DM_SCSI)
-static int ahci_init_one(struct udevice *dev)
+static int ahci_init_one(struct ahci_uc_priv *uc_priv, struct udevice *dev)
 # else
-static int ahci_init_one(pci_dev_t dev)
+static int ahci_init_one(struct ahci_uc_priv *uc_priv, pci_dev_t dev)
 # endif
 {
-       struct ahci_uc_priv *uc_priv;
 #if !defined(CONFIG_DM_SCSI)
        u16 vendor;
 #endif
        int rc;
 
-       probe_ent = malloc(sizeof(struct ahci_uc_priv));
-       if (!probe_ent) {
-               printf("%s: No memory for uc_priv\n", __func__);
-               return -ENOMEM;
-       }
-
-       uc_priv = probe_ent;
-       memset(uc_priv, 0, sizeof(struct ahci_uc_priv));
        uc_priv->dev = dev;
 
        uc_priv->host_flags = ATA_FLAG_SATA
@@ -516,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)
@@ -561,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;
 
@@ -572,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);
 
        /*
@@ -609,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);
@@ -933,9 +933,14 @@ static int ata_scsiop_test_unit_ready(struct ahci_uc_priv *uc_priv,
 }
 
 
-int scsi_exec(struct scsi_cmd *pccb)
+static int ahci_scsi_exec(struct udevice *dev, struct scsi_cmd *pccb)
 {
-       struct ahci_uc_priv *uc_priv = probe_ent;
+       struct ahci_uc_priv *uc_priv;
+#ifdef CONFIG_DM_SCSI
+       uc_priv = dev_get_uclass_priv(dev->parent);
+#else
+       uc_priv = probe_ent;
+#endif
        int ret;
 
        switch (pccb->cmd[0]) {
@@ -960,14 +965,14 @@ int scsi_exec(struct scsi_cmd *pccb)
                break;
        default:
                printf("Unsupport SCSI command 0x%02x\n", pccb->cmd[0]);
-               return false;
+               return -ENOTSUPP;
        }
 
        if (ret) {
                debug("SCSI command 0x%02x ret errno %d\n", pccb->cmd[0], ret);
-               return false;
+               return ret;
        }
-       return true;
+       return 0;
 
 }
 
@@ -978,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);
@@ -990,15 +995,18 @@ static int ahci_start_ports(struct ahci_uc_priv *uc_priv)
        return 0;
 }
 
-#if defined(CONFIG_DM_SCSI)
-void scsi_low_level_init(int busdevfunc, struct udevice *dev)
-#else
+#ifndef CONFIG_DM_SCSI
 void scsi_low_level_init(int busdevfunc)
-#endif
 {
        struct ahci_uc_priv *uc_priv;
 
 #ifndef CONFIG_SCSI_AHCI_PLAT
+       probe_ent = calloc(1, sizeof(struct ahci_uc_priv));
+       if (!probe_ent) {
+               printf("%s: No memory for uc_priv\n", __func__);
+               return;
+       }
+       uc_priv = probe_ent;
 # if defined(CONFIG_DM_PCI)
        struct udevice *dev;
        int ret;
@@ -1006,32 +1014,40 @@ void scsi_low_level_init(int busdevfunc)
        ret = dm_pci_bus_find_bdf(busdevfunc, &dev);
        if (ret)
                return;
-       ahci_init_one(dev);
-# elif defined(CONFIG_DM_SCSI)
-       ahci_init_one(dev);
+       ahci_init_one(uc_priv, dev);
 # else
-       ahci_init_one(busdevfunc);
+       ahci_init_one(uc_priv, busdevfunc);
 # endif
-#endif
+#else
        uc_priv = probe_ent;
+#endif
 
        ahci_start_ports(uc_priv);
 }
+#endif
 
-#ifdef CONFIG_SCSI_AHCI_PLAT
-int ahci_init(void __iomem *base)
+#ifndef CONFIG_SCSI_AHCI_PLAT
+# if defined(CONFIG_DM_PCI) || defined(CONFIG_DM_SCSI)
+int ahci_init_one_dm(struct udevice *dev)
 {
-       struct ahci_uc_priv *uc_priv;
-       int rc = 0;
+       struct ahci_uc_priv *uc_priv = dev_get_uclass_priv(dev);
 
-       probe_ent = malloc(sizeof(struct ahci_uc_priv));
-       if (!probe_ent) {
-               printf("%s: No memory for uc_priv\n", __func__);
-               return -ENOMEM;
-       }
+       return ahci_init_one(uc_priv, dev);
+}
+#endif
+#endif
 
-       uc_priv = probe_ent;
-       memset(uc_priv, 0, sizeof(struct ahci_uc_priv));
+int ahci_start_ports_dm(struct udevice *dev)
+{
+       struct ahci_uc_priv *uc_priv = dev_get_uclass_priv(dev);
+
+       return ahci_start_ports(uc_priv);
+}
+
+#ifdef CONFIG_SCSI_AHCI_PLAT
+static int ahci_init_common(struct ahci_uc_priv *uc_priv, void __iomem *base)
+{
+       int rc;
 
        uc_priv->host_flags = ATA_FLAG_SATA
                                | ATA_FLAG_NO_LEGACY
@@ -1056,11 +1072,36 @@ err_out:
        return rc;
 }
 
+#ifndef CONFIG_DM_SCSI
+int ahci_init(void __iomem *base)
+{
+       struct ahci_uc_priv *uc_priv;
+
+       probe_ent = malloc(sizeof(struct ahci_uc_priv));
+       if (!probe_ent) {
+               printf("%s: No memory for uc_priv\n", __func__);
+               return -ENOMEM;
+       }
+
+       uc_priv = probe_ent;
+       memset(uc_priv, 0, sizeof(struct ahci_uc_priv));
+
+       return ahci_init_common(uc_priv, base);
+}
+#endif
+
+int ahci_init_dm(struct udevice *dev, void __iomem *base)
+{
+       struct ahci_uc_priv *uc_priv = dev_get_uclass_priv(dev);
+
+       return ahci_init_common(uc_priv, base);
+}
+
 void __weak scsi_init(void)
 {
 }
 
-#endif
+#endif /* CONFIG_SCSI_AHCI_PLAT */
 
 /*
  * In the general case of generic rotating media it makes sense to have a
@@ -1098,8 +1139,93 @@ static int ata_io_flush(struct ahci_uc_priv *uc_priv, u8 port)
        return 0;
 }
 
+static int ahci_scsi_bus_reset(struct udevice *dev)
+{
+       /* Not implemented */
 
-__weak void scsi_bus_reset(void)
+       return 0;
+}
+
+#ifdef CONFIG_DM_SCSI
+int ahci_bind_scsi(struct udevice *ahci_dev, struct udevice **devp)
 {
-       /*Not implement*/
+       struct udevice *dev;
+       int ret;
+
+       ret = device_bind_driver(ahci_dev, "ahci_scsi", "ahci_scsi", &dev);
+       if (ret)
+               return ret;
+       *devp = dev;
+
+       return 0;
 }
+
+int ahci_probe_scsi(struct udevice *ahci_dev, ulong base)
+{
+       struct ahci_uc_priv *uc_priv;
+       struct scsi_platdata *uc_plat;
+       struct udevice *dev;
+       int ret;
+
+       device_find_first_child(ahci_dev, &dev);
+       if (!dev)
+               return -ENODEV;
+       uc_plat = dev_get_uclass_platdata(dev);
+       uc_plat->base = base;
+       uc_plat->max_lun = 1;
+       uc_plat->max_id = 2;
+
+       uc_priv = dev_get_uclass_priv(ahci_dev);
+       ret = ahci_init_one(uc_priv, dev);
+       if (ret)
+               return ret;
+       ret = ahci_start_ports(uc_priv);
+       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;
+}
+
+#ifdef CONFIG_DM_PCI
+int ahci_probe_scsi_pci(struct udevice *ahci_dev)
+{
+       ulong base;
+
+       base = (ulong)dm_pci_map_bar(ahci_dev, PCI_BASE_ADDRESS_5,
+                                    PCI_REGION_MEM);
+
+       return ahci_probe_scsi(ahci_dev, base);
+}
+#endif
+
+struct scsi_ops scsi_ops = {
+       .exec           = ahci_scsi_exec,
+       .bus_reset      = ahci_scsi_bus_reset,
+};
+
+U_BOOT_DRIVER(ahci_scsi) = {
+       .name           = "ahci_scsi",
+       .id             = UCLASS_SCSI,
+       .ops            = &scsi_ops,
+};
+#else
+int scsi_exec(struct udevice *dev, struct scsi_cmd *pccb)
+{
+       return ahci_scsi_exec(dev, pccb);
+}
+
+__weak int scsi_bus_reset(struct udevice *dev)
+{
+       return ahci_scsi_bus_reset(dev);
+
+       return 0;
+}
+#endif