summaryrefslogtreecommitdiff
path: root/drivers/ata/ahci.c
diff options
context:
space:
mode:
authorSimon Glass <sjg@chromium.org>2017-06-14 21:28:46 -0600
committerSimon Glass <sjg@chromium.org>2017-07-11 10:08:19 -0600
commit681357ffd9fe4528d020a878ef6ee61f519d8d85 (patch)
tree271008f521aad643de4c1fa9909920698ba13a81 /drivers/ata/ahci.c
parent5c56176318c8a602fa78813ac273f16a10278a2d (diff)
dm: ahci: Add a driver for SCSI on AHCI
Some AHCI drivers use SCSI under the hood. Rather than making the AHCI driver be in the SCSI uclass it makes sense to have the AHCI device create a SCSI device as a child. That way we can handle any AHCI-specific operations rather than trying to pretend tha the device is just SCSI. To handle this we need to provide a way for AHCI drivers to bind a SCSI device as its child, and probe it. Add functions for this. Signed-off-by: Simon Glass <sjg@chromium.org> Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Diffstat (limited to 'drivers/ata/ahci.c')
-rw-r--r--drivers/ata/ahci.c57
1 files changed, 57 insertions, 0 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 3528a1f3da..6da412d178 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -19,10 +19,13 @@
#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);
@@ -1142,10 +1145,64 @@ static int ahci_scsi_bus_reset(struct udevice *dev)
}
#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)
+{
+#ifdef CONFIG_SCSI_AHCI_PLAT
+ return -ENOSYS; /* TODO(sjg@chromium.org): Support non-PCI AHCI */
+#else
+ 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 = (ulong)dm_pci_map_bar(ahci_dev, PCI_BASE_ADDRESS_5,
+ PCI_REGION_MEM);
+ uc_plat->max_lun = 1;
+ uc_plat->max_id = 2;
+ uc_priv = dev_get_uclass_priv(dev);
+ ret = ahci_init_one(uc_priv, dev);
+ if (ret)
+ return ret;
+ ret = ahci_start_ports(uc_priv);
+ if (ret)
+ return ret;
+
+ debug("Scanning %s\n", dev->name);
+ ret = scsi_scan_dev(dev, true);
+ if (ret)
+ return ret;
+#endif
+
+ return 0;
+}
+
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)
{