In driver/ata. If the sata driver support AHCI mode, there will provides
a complete set of SCSI interface. If the sata is not support AHCI
mode(NONE_AHCI) there will not provides the SCSI interface.

This patch is to support SCSI interface for None AHCI sata such as fsl_sata.c
sil_sata.c etc.

Signed-off-by: Peng Ma <peng...@nxp.com>
---
 drivers/ata/Kconfig      |  17 +++-
 drivers/ata/Makefile     |   2 +
 drivers/ata/ata-uclass.c |  16 ++++
 drivers/ata/ata.c        | 244 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/ata/sata.c       |   2 +-
 drivers/ata/scsi_ata.h   |  43 +++++++++
 include/dm/uclass-id.h   |   1 +
 7 files changed, 323 insertions(+), 2 deletions(-)
 create mode 100644 drivers/ata/ata-uclass.c
 create mode 100644 drivers/ata/ata.c
 create mode 100644 drivers/ata/scsi_ata.h

diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 87636ae..3d6db2e 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -7,6 +7,13 @@ config AHCI
          operations at present. The block device interface has not been 
converted
          to driver model.
 
+config NONE_AHCI
+       bool "Support None AHCI SATA controllers with driver model"
+       depends on DM
+       help
+         This enables a uclass for disk controllers in U-Boot. such as AHCI. It
+         support None AHCI sata with DM mode
+
 config SATA
        bool "Support SATA controllers"
        select HAVE_BLOCK_DEVICE
@@ -32,6 +39,15 @@ config SCSI_AHCI
        help
          Enable this to allow interfacing SATA devices via the SCSI layer.
 
+config SCSI_NONE_AHCI
+       bool "Enable SCSI interface to None AHCI SATA devices"
+       select LIBATA
+       select SCSI
+       select DM_SCSI
+       help
+         Enable this to allow interfacing None AHCI SATA devices via the DM
+         SCSI layer.
+
 menu "SATA/SCSI device support"
 
 config AHCI_PCI
@@ -49,7 +65,6 @@ config SATA_CEVA
          ZynqMP. Support up to 2 external devices. Complient with SATA 3.1 and
          AHCI 1.3 specifications with hot-plug detect feature.
 
-
 config DWC_AHCI
        bool "Enable Synopsys DWC AHCI driver support"
        select SCSI_AHCI
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 6e03384..cddbdc8 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -6,8 +6,10 @@
 obj-$(CONFIG_DWC_AHCI) += dwc_ahci.o
 obj-$(CONFIG_FSL_AHCI) += fsl_ahci.o
 obj-$(CONFIG_AHCI) += ahci-uclass.o
+obj-$(CONFIG_NONE_AHCI) += ata-uclass.o
 obj-$(CONFIG_AHCI_PCI) += ahci-pci.o
 obj-$(CONFIG_SCSI_AHCI) += ahci.o
+obj-$(CONFIG_SCSI_NONE_AHCI) += ata.o
 obj-$(CONFIG_DWC_AHSATA) += dwc_ahsata.o
 obj-$(CONFIG_FSL_SATA) += fsl_sata.o
 obj-$(CONFIG_LIBATA) += libata.o
diff --git a/drivers/ata/ata-uclass.c b/drivers/ata/ata-uclass.c
new file mode 100644
index 0000000..e2cc499
--- /dev/null
+++ b/drivers/ata/ata-uclass.c
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2019 NXP, Inc
+ * Written by Peng Ma<peng...@nxp.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <scsi.h>
+#include "scsi_ata.h"
+
+UCLASS_DRIVER(ata) = {
+       .id             = UCLASS_NONE_AHCI,
+       .name           = "ata",
+       .per_device_auto_alloc_size = sizeof(struct ata_uc_priv),
+};
diff --git a/drivers/ata/ata.c b/drivers/ata/ata.c
new file mode 100644
index 0000000..bdb7403
--- /dev/null
+++ b/drivers/ata/ata.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) NXP, Inc. 2019.
+ * Author: Peng Ma<peng...@nxp.com>
+ *
+ * with the reference on libata and none ahci drvier in kernel
+ *
+ * This driver provides a DM SCSI interface to None AHCI SATA.
+ */
+
+#include <common.h>
+#include <dm/lists.h>
+#include <dm.h>
+#include <scsi.h>
+#include <libata.h>
+#include <sata.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <fis.h>
+
+#include "scsi_ata.h"
+
+int ata_bind_scsi(struct udevice *ata_dev, struct udevice **devp)
+{
+       int ret;
+       struct udevice *dev;
+
+       ret =  device_bind_driver(ata_dev, "ata_scsi", "ata_scsi", &dev);
+       if (ret)
+               return ret;
+       *devp = dev;
+
+       return 0;
+}
+
+static int scsi_exec_internal(struct udevice *dev, struct scsi_cmd *pccb,
+                             bool is_write)
+{
+       u32 temp;
+       u16 blocks = 0;
+       int ret = -ENODEV;
+       lbaint_t start = 0;
+       u8 port = pccb->target;
+       u8 *buffer = pccb->pdata;
+       struct ata_uc_priv *uc_priv = dev_get_uclass_priv(dev);
+
+       /* Retrieve the base LBA number from the ccb structure. */
+       if (pccb->cmd[0] == SCSI_READ16) {
+               memcpy(&start, pccb->cmd + 2, 8);
+               start = be64_to_cpu(start);
+       } else {
+               memcpy(&temp, pccb->cmd + 2, 4);
+               start = be32_to_cpu(temp);
+       }
+
+       if (pccb->cmd[0] == SCSI_READ16)
+               blocks = (((u16)pccb->cmd[13]) << 8) | ((u16)pccb->cmd[14]);
+       else
+               blocks = (((u16)pccb->cmd[7]) << 8) | ((u16)pccb->cmd[8]);
+
+       debug("scsi_ata: %s %u blocks starting from lba 0x" LBAFU "\n",
+             is_write ?  "write" : "read", blocks, start);
+
+       if (is_write)
+               if (uc_priv->write)
+                       ret = uc_priv->write(dev, start, blocks, buffer, port);
+               else
+                       pr_err("scsi_ata: write ptr is NULL\n");
+       else
+               if (uc_priv->read)
+                       ret = uc_priv->read(dev, start, blocks, buffer, port);
+               else
+                       pr_err("scsi_ata: read ptr is NULL\n");
+       return ret;
+}
+
+static char *fsl_ata_id_strcpy(u16 *target, u16 *src, int len)
+{
+       int i;
+
+       for (i = 0; i < len / 2; i++)
+               target[i] = src[i];
+
+       return (char *)target;
+}
+
+static int ata_scsiop_inquiry(struct udevice *dev, struct scsi_cmd *pccb)
+{
+       u8 port;
+       u16 *idbuf;
+       struct ata_uc_priv *uc_priv = dev_get_uclass_priv(dev);
+
+       ALLOC_CACHE_ALIGN_BUFFER(u16, tmpid, ATA_ID_WORDS);
+
+       /* Clean ccb data buffer */
+       memset(pccb->pdata, 0, pccb->datalen);
+
+       if (pccb->datalen <= 35)
+               return 0;
+
+       /* Read id from sata */
+       port = pccb->target;
+
+       if (uc_priv->ata_inquiry) {
+               uc_priv->ata_inquiry(dev, (u16 *)tmpid, port);
+       } else {
+               pr_err("scsi_ata: ata_inquiry ptr is NULL\n");
+               return -ENODEV;
+       }
+
+       if (!uc_priv->ataid[port]) {
+               uc_priv->ataid[port] = malloc(ATA_ID_WORDS * 2);
+               if (!uc_priv->ataid[port]) {
+                       pr_err("%s: No memory for ataid[port]\n", __func__);
+                       return -ENOMEM;
+               }
+       }
+
+       idbuf = uc_priv->ataid[port];
+
+       memcpy(idbuf, tmpid, ATA_ID_WORDS * 2);
+
+       memcpy(&pccb->pdata[8], "ATA     ", 8);
+       fsl_ata_id_strcpy((u16 *)&pccb->pdata[16], &idbuf[ATA_ID_PROD], 16);
+       fsl_ata_id_strcpy((u16 *)&pccb->pdata[32], &idbuf[ATA_ID_FW_REV], 4);
+
+#ifdef DEBUG
+       ata_dump_id(idbuf);
+#endif
+       return 0;
+}
+
+/*
+ * SCSI READ CAPACITY10 command operation.
+ */
+static int ata_scsiop_read_capacity10(struct ata_uc_priv *uc_priv,
+                                     struct scsi_cmd *pccb)
+{
+       u32 cap;
+       u64 cap64;
+       u32 block_size;
+
+       if (!uc_priv->ataid[pccb->target]) {
+               pr_err("scsi_ata: SCSI READ CAPACITY10 command failure.");
+               pr_err("\tNo ATA info!\n");
+               pr_err("\tPlease run SCSI command INQUIRY first!\n");
+               return -EPERM;
+       }
+
+       cap64 = ata_id_n_sectors(uc_priv->ataid[pccb->target]);
+       if (cap64 > 0x100000000ULL)
+               cap64 = 0xffffffff;
+
+       cap = cpu_to_be32(cap64);
+       memcpy(pccb->pdata, &cap, sizeof(cap));
+
+       block_size = cpu_to_be32((u32)512);
+       memcpy(&pccb->pdata[4], &block_size, 4);
+
+       return 0;
+}
+
+/*
+ * SCSI READ CAPACITY16 command operation.
+ */
+static int ata_scsiop_read_capacity16(struct ata_uc_priv *uc_priv,
+                                     struct scsi_cmd *pccb)
+{
+       u64 cap;
+       u64 block_size;
+
+       if (!uc_priv->ataid[pccb->target]) {
+               pr_err("scsi_ata: SCSI READ CAPACITY16 command failure.");
+               pr_err("\tNo ATA info!\n");
+               pr_err("\tPlease run SCSI command INQUIRY first!\n");
+               return -EPERM;
+       }
+
+       cap = ata_id_n_sectors(uc_priv->ataid[pccb->target]);
+       cap = cpu_to_be64(cap);
+       memcpy(pccb->pdata, &cap, sizeof(cap));
+
+       block_size = cpu_to_be64((u64)512);
+       memcpy(&pccb->pdata[8], &block_size, 8);
+
+       return 0;
+}
+
+/*
+ * SCSI TEST UNIT READY command operation.
+ */
+static int ata_scsiop_test_unit_ready(struct ata_uc_priv *uc_priv,
+                                     struct scsi_cmd *pccb)
+{
+       return (uc_priv->ataid[pccb->target]) ? 0 : -EPERM;
+}
+
+static int ata_scsi_exec(struct udevice *dev, struct scsi_cmd *pccb)
+{
+       struct ata_uc_priv *uc_priv = dev_get_uclass_priv(dev->parent);
+       int ret;
+
+       switch (pccb->cmd[0]) {
+       case SCSI_READ16:
+       case SCSI_READ10:
+               ret = scsi_exec_internal(dev->parent, pccb, 0);
+               break;
+       case SCSI_WRITE10:
+               ret = scsi_exec_internal(dev->parent, pccb, 1);
+               break;
+       case SCSI_RD_CAPAC10:
+               ret = ata_scsiop_read_capacity10(uc_priv, pccb);
+               break;
+       case SCSI_RD_CAPAC16:
+               ret = ata_scsiop_read_capacity16(uc_priv, pccb);
+               break;
+       case SCSI_TST_U_RDY:
+               ret = ata_scsiop_test_unit_ready(uc_priv, pccb);
+               break;
+       case SCSI_INQUIRY:
+               ret = ata_scsiop_inquiry(dev->parent, pccb);
+               break;
+       default:
+               pr_err("Unsupport SCSI command 0x%02x\n", pccb->cmd[0]);
+               return -ENOTSUPP;
+       }
+
+       if (ret) {
+               debug("SCSI command 0x%02x ret errno %d\n", pccb->cmd[0], ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+struct scsi_ops ata_scsi_ops = {
+       .exec           = ata_scsi_exec,
+};
+
+U_BOOT_DRIVER(ata_scsi) = {
+       .name           = "ata_scsi",
+       .id             = UCLASS_SCSI,
+       .ops            = &ata_scsi_ops,
+};
diff --git a/drivers/ata/sata.c b/drivers/ata/sata.c
index e384b80..1fee375 100644
--- a/drivers/ata/sata.c
+++ b/drivers/ata/sata.c
@@ -83,7 +83,7 @@ static unsigned long sata_bwrite(struct blk_desc *block_dev, 
lbaint_t start,
 }
 #endif
 
-#ifndef CONFIG_AHCI
+#ifndef CONFIG_SCSI
 int __sata_initialize(void)
 {
        int rc, ret = -1;
diff --git a/drivers/ata/scsi_ata.h b/drivers/ata/scsi_ata.h
new file mode 100644
index 0000000..472a562
--- /dev/null
+++ b/drivers/ata/scsi_ata.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) NXP Inc. 2019.
+ * Author: Peng Ma<peng...@nxp.com>
+ */
+#ifndef _AHCI_H_
+#define _AHCI_H_
+
+#include <pci.h>
+
+#define ATA_MAX_PORTS          32
+/**
+ * struct ata_uc_priv - information about an ATA controller
+ *
+ * When driver model is used, this is accessible using dev_get_uclass_priv(dev)
+ * where dev is the controller (although at present it sometimes stands alone).
+ */
+struct ata_uc_priv {
+#if defined(CONFIG_DM_PCI) || defined(CONFIG_DM_SCSI)
+       struct udevice *dev;
+#else
+       pci_dev_t       dev;
+#endif
+       u16     *ataid[ATA_MAX_PORTS];
+
+       ulong (*read)(struct udevice *dev, lbaint_t start, u16 blkcnt,
+                     void *buffer, u8 port);
+       ulong (*write)(struct udevice *dev, lbaint_t start, u16 blkcnt,
+                      const void *buffer, u8 port);
+       int (*ata_inquiry)(struct udevice *dev, u16 *buffer, u8 port);
+};
+
+/**
+ * ata_bind_scsi() - bind a new SCSI bus as a child
+ *
+ * Note that the SCSI bus device will itself bind block devices
+ *
+ * @ahci_dev: AHCI parent device
+ * @devp: Returns new SCSI bus device
+ * @return 0 if OK, -ve on error
+ */
+int ata_bind_scsi(struct udevice *ahci_dev, struct udevice **devp);
+#endif
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index d4d9610..087cd2d 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -29,6 +29,7 @@ enum uclass_id {
        /* U-Boot uclasses start here - in alphabetical order */
        UCLASS_ADC,             /* Analog-to-digital converter */
        UCLASS_AHCI,            /* SATA disk controller */
+       UCLASS_NONE_AHCI,       /* SATA disk controller of None AHCI */
        UCLASS_AUDIO_CODEC,     /* Audio codec with control and data path */
        UCLASS_AXI,             /* AXI bus */
        UCLASS_BLK,             /* Block device */
-- 
2.9.5

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot

Reply via email to