+// 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 <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)
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
}
-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]) {
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;
}
-#if defined(CONFIG_DM_SCSI)
-void scsi_low_level_init(int busdevfunc, struct udevice *dev)
-#else
-void scsi_low_level_init(int busdevfunc)
-#endif
+static int ahci_start_ports(struct ahci_uc_priv *uc_priv)
{
- struct ahci_uc_priv *uc_priv;
- int i;
u32 linkmap;
-
-#ifndef CONFIG_SCSI_AHCI_PLAT
-# if defined(CONFIG_DM_PCI)
- struct udevice *dev;
- int ret;
-
- ret = dm_pci_bus_find_bdf(busdevfunc, &dev);
- if (ret)
- return;
- ahci_init_one(dev);
-# elif defined(CONFIG_DM_SCSI)
- ahci_init_one(dev);
-# else
- ahci_init_one(busdevfunc);
-# endif
-#endif
- uc_priv = probe_ent;
+ int i;
linkmap = uc_priv->link_port_map;
}
}
}
+
+ return 0;
}
-#ifdef CONFIG_SCSI_AHCI_PLAT
-int ahci_init(void __iomem *base)
+#ifndef CONFIG_DM_SCSI
+void scsi_low_level_init(int busdevfunc)
{
struct ahci_uc_priv *uc_priv;
- int i, rc = 0;
- u32 linkmap;
- probe_ent = malloc(sizeof(struct ahci_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 -ENOMEM;
+ return;
}
+ uc_priv = probe_ent;
+# if defined(CONFIG_DM_PCI)
+ struct udevice *dev;
+ int ret;
+ ret = dm_pci_bus_find_bdf(busdevfunc, &dev);
+ if (ret)
+ return;
+ ahci_init_one(uc_priv, dev);
+# else
+ ahci_init_one(uc_priv, busdevfunc);
+# endif
+#else
uc_priv = probe_ent;
- memset(uc_priv, 0, sizeof(struct ahci_uc_priv));
+#endif
+
+ ahci_start_ports(uc_priv);
+}
+#endif
+
+#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 = dev_get_uclass_priv(dev);
+
+ return ahci_init_one(uc_priv, dev);
+}
+#endif
+#endif
+
+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
ahci_print_info(uc_priv);
- linkmap = uc_priv->link_port_map;
+ rc = ahci_start_ports(uc_priv);
- for (i = 0; i < CONFIG_SYS_SCSI_MAX_SCSI_ID; i++) {
- if (((linkmap >> i) & 0x01)) {
- if (ahci_port_start(uc_priv, (u8) i)) {
- printf("Can not start port %d\n", i);
- continue;
- }
- }
- }
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
return 0;
}
+static int ahci_scsi_bus_reset(struct udevice *dev)
+{
+ /* Not implemented */
+
+ return 0;
+}
+
+#ifdef CONFIG_DM_SCSI
+int ahci_bind_scsi(struct udevice *ahci_dev, struct udevice **devp)
+{
+ 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;
-__weak void scsi_bus_reset(void)
+ return 0;
+}
+
+#ifdef CONFIG_DM_PCI
+int ahci_probe_scsi_pci(struct udevice *ahci_dev)
{
- /*Not implement*/
+ 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