Browse Source

[DM/FEATURE] Support ATA AHCI (#9683)

Add ACHI drivers to support some old platform
driver such as SATA.

Signed-off-by: GuEe-GUI <2991707448@qq.com>
GUI 7 months ago
parent
commit
d025072837

+ 1 - 0
components/drivers/Kconfig

@@ -23,6 +23,7 @@ rsource "hwcrypto/Kconfig"
 rsource "wlan/Kconfig"
 rsource "mailbox/Kconfig"
 rsource "phye/Kconfig"
+rsource "ata/Kconfig"
 rsource "block/Kconfig"
 rsource "nvme/Kconfig"
 rsource "scsi/Kconfig"

+ 22 - 0
components/drivers/ata/Kconfig

@@ -0,0 +1,22 @@
+menuconfig RT_USING_ATA
+    bool "Using Advanced Technology Attachment (ATA) device drivers"
+    depends on RT_USING_DM
+    depends on RT_USING_BLK
+    depends on RT_USING_DMA
+    default n
+
+config RT_ATA_AHCI
+    bool "Advanced Host Controller Interface (AHCI)"
+    depends on RT_USING_ATA
+    depends on RT_USING_SCSI
+    default y
+
+config RT_ATA_AHCI_PCI
+    bool "AHCI support on PCI bus"
+    depends on RT_ATA_AHCI
+    depends on RT_USING_PCI
+    default n
+
+if RT_USING_ATA
+    osource "$(SOC_DM_ATA_DIR)/Kconfig"
+endif

+ 21 - 0
components/drivers/ata/SConscript

@@ -0,0 +1,21 @@
+from building import *
+
+group = []
+
+if not GetDepend(['RT_USING_ATA']):
+    Return('group')
+
+cwd = GetCurrentDir()
+CPPPATH = [cwd + '/../include']
+
+src = []
+
+if GetDepend(['RT_ATA_AHCI']):
+    src += ['ahci.c']
+
+    if GetDepend(['RT_ATA_AHCI_PCI']):
+        src += ['ahci-pci.c']
+
+group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
+
+Return('group')

+ 206 - 0
components/drivers/ata/ahci-pci.c

@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-02-25     GuEe-GUI     the first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define AHCI_REG_BAR 5
+
+struct pci_ahci_quirk
+{
+    int bar_idx;
+    rt_bool_t bar_offset;
+
+    const struct rt_ahci_ops *ops;
+};
+
+struct pci_ahci_host
+{
+    struct rt_ahci_host parent;
+    const struct pci_ahci_quirk *quirk;
+
+    rt_bool_t is_msi;
+};
+
+#define raw_to_pci_ahci_host(raw) rt_container_of(raw, struct pci_ahci_host, parent)
+
+static rt_err_t pci_ahci_init(struct rt_ahci_host *host)
+{
+    struct rt_pci_device *pdev;
+
+    pdev = rt_container_of(host->parent.dev, struct rt_pci_device, parent);
+
+    if (pdev->vendor == PCI_VENDOR_ID_JMICRON)
+    {
+        rt_pci_write_config_u8(pdev, 0x41, 0xa1);
+    }
+
+    return RT_EOK;
+}
+
+static const struct rt_ahci_ops pci_ahci_ops =
+{
+    .host_init = pci_ahci_init,
+};
+
+static rt_err_t pci_ahci_intel_init(struct rt_ahci_host *host)
+{
+    rt_uint16_t val;
+    struct rt_pci_device *pdev;
+
+    pdev = rt_container_of(host->parent.dev, struct rt_pci_device, parent);
+
+    rt_pci_read_config_u16(pdev, 0x92, &val);
+    rt_pci_write_config_u16(pdev, 0x92, val & ~0xf);
+
+    rt_thread_mdelay(10);
+    rt_pci_write_config_u16(pdev, 0x92, val | 0xf);
+
+    return RT_EOK;
+}
+
+static const struct rt_ahci_ops pci_ahci_intel_ops =
+{
+    .host_init = pci_ahci_intel_init,
+};
+
+static rt_err_t pci_ahci_probe(struct rt_pci_device *pdev)
+{
+    rt_err_t err;
+    int bar_idx;
+    struct rt_ahci_host *ahci;
+    struct pci_ahci_host *pci_ahci = rt_calloc(1, sizeof(*pci_ahci));
+    const struct pci_ahci_quirk *quirk = pdev->id->data;
+
+    if (!pci_ahci)
+    {
+        return -RT_ENOMEM;
+    }
+
+    pci_ahci->quirk = quirk;
+    ahci = &pci_ahci->parent;
+    ahci->parent.dev = &pdev->parent;
+
+    bar_idx = quirk && quirk->bar_offset ? quirk->bar_idx : AHCI_REG_BAR;
+
+    ahci->regs = rt_pci_iomap(pdev, bar_idx);
+
+    if (!ahci->regs)
+    {
+        err = -RT_EIO;
+        goto _fail;
+    }
+
+    ahci->ops = quirk && quirk->ops ? quirk->ops : &pci_ahci_ops;
+
+    if (rt_pci_msi_enable(pdev) > 0)
+    {
+        pci_ahci->is_msi = RT_TRUE;
+    }
+    else
+    {
+        rt_pci_irq_unmask(pdev);
+    }
+    ahci->irq = pdev->irq;
+
+    rt_pci_set_master(pdev);
+
+    if ((err = rt_ahci_host_register(ahci)))
+    {
+        goto _disable;
+    }
+
+    pdev->parent.user_data = pci_ahci;
+
+    return RT_EOK;
+
+_disable:
+    if (pci_ahci->is_msi)
+    {
+        rt_pci_msix_disable(pdev);
+    }
+    else
+    {
+        rt_pci_irq_mask(pdev);
+    }
+    rt_pci_clear_master(pdev);
+    rt_iounmap(ahci->regs);
+
+_fail:
+    rt_free(pci_ahci);
+
+    return err;
+}
+
+static rt_err_t pci_ahci_remove(struct rt_pci_device *pdev)
+{
+    struct rt_ahci_host *ahci;
+    struct pci_ahci_host *pci_ahci = pdev->parent.user_data;
+
+    ahci = &pci_ahci->parent;
+
+    rt_ahci_host_unregister(ahci);
+
+    if (pci_ahci->is_msi)
+    {
+        rt_pci_msi_disable(pdev);
+    }
+    else
+    {
+        /* INTx is shared, don't mask all */
+        rt_hw_interrupt_umask(pdev->irq);
+        rt_pci_irq_mask(pdev);
+    }
+
+    rt_pci_clear_master(pdev);
+
+    rt_iounmap(ahci->regs);
+    rt_free(pci_ahci);
+
+    return RT_EOK;
+}
+
+static rt_err_t pci_ahci_shutdown(struct rt_pci_device *pdev)
+{
+    return pci_ahci_remove(pdev);
+}
+
+static struct pci_ahci_quirk intel_quirk =
+{
+    .ops = &pci_ahci_intel_ops,
+};
+
+static struct pci_ahci_quirk cavium_sata_quirk =
+{
+    .bar_idx = 0,
+    .bar_offset = RT_TRUE,
+};
+
+static const struct rt_pci_device_id pci_ahci_ids[] =
+{
+    { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_INTEL, 0x2922), .data = &intel_quirk },
+    { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_ASMEDIA, 0x0611) },
+    { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_MARVELL, 0x6121) },
+    { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_MARVELL, 0x6145) },
+    { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_CAVIUM, 0xa01c), .data = &cavium_sata_quirk },
+    { RT_PCI_DEVICE_CLASS(PCIS_STORAGE_SATA_AHCI, ~0) },
+    { /* sentinel */ }
+};
+
+static struct rt_pci_driver pci_ahci_driver =
+{
+    .name = "ahci-pci",
+
+    .ids = pci_ahci_ids,
+    .probe = pci_ahci_probe,
+    .remove = pci_ahci_remove,
+    .shutdown = pci_ahci_shutdown,
+};
+RT_PCI_DRIVER_EXPORT(pci_ahci_driver);

+ 896 - 0
components/drivers/ata/ahci.c

@@ -0,0 +1,896 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-02-25     GuEe-GUI     the first version
+ */
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "rtdm.ahci"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#define HWREG32_FLUSH(base, value)  \
+do {                                \
+    rt_uint32_t __value = value;    \
+    HWREG32(base) = __value;        \
+    __value = HWREG32(base);        \
+} while (0)
+
+static void ahci_fill_cmd_slot(struct rt_ahci_port *port, rt_uint32_t opts)
+{
+    rt_ubase_t dma_addr = port->cmd_tbl_dma;
+    struct rt_ahci_cmd_hdr *cmd_slot = port->cmd_slot;
+
+    cmd_slot->opts = rt_cpu_to_le32(opts);
+    cmd_slot->status = 0;
+    cmd_slot->tbl_addr_lo = rt_cpu_to_le32(rt_lower_32_bits(dma_addr));
+    cmd_slot->tbl_addr_hi = rt_cpu_to_le32(rt_upper_32_bits(dma_addr));
+}
+
+static int ahci_fill_sg(struct rt_ahci_host *host, int id,
+        void *buffer, rt_size_t buffer_size)
+{
+    int sg_count;
+    rt_ubase_t dma_addr;
+    struct rt_ahci_port *port = &host->ports[id];
+    struct rt_ahci_sg *ahci_sg = port->cmd_tbl_sg;
+
+    sg_count = ((buffer_size - 1) / RT_ACHI_PRDT_BYTES_MAX) + 1;
+
+    if (sg_count > RT_AHCI_MAX_SG)
+    {
+        return -1;
+    }
+
+    dma_addr = (rt_ubase_t)rt_kmem_v2p(buffer);
+
+    for (int i = 0; i < sg_count; ++i, ++ahci_sg)
+    {
+        ahci_sg->addr_lo = rt_cpu_to_le32(rt_lower_32_bits(dma_addr));
+        ahci_sg->addr_hi = rt_cpu_to_le32(rt_upper_32_bits(dma_addr));
+
+        if (ahci_sg->addr_hi && !(host->cap & RT_AHCI_CAP_64))
+        {
+            return -1;
+        }
+
+        ahci_sg->flags_size = rt_cpu_to_le32(0x3fffff &
+                (rt_min_t(rt_uint32_t, buffer_size, RT_ACHI_PRDT_BYTES_MAX) - 1));
+
+        dma_addr += RT_ACHI_PRDT_BYTES_MAX;
+        buffer_size -= RT_ACHI_PRDT_BYTES_MAX;
+    }
+
+    return sg_count;
+}
+
+static rt_err_t ahci_request_io(struct rt_ahci_host *host, int id,
+        void *fis, rt_size_t fis_size,
+        void *buffer, rt_size_t buffer_size, rt_bool_t is_read)
+{
+    int sg_count;
+    rt_err_t err;
+    struct rt_ahci_port *port = &host->ports[id];
+
+    if ((HWREG32(port->regs + RT_AHCI_PORT_SSTS) & 0xf) != RT_AHCI_PORT_SSTS_DET_PHYRDY)
+    {
+        return -RT_EIO;
+    }
+
+    if ((sg_count = ahci_fill_sg(host, id, buffer, buffer_size)) <= 0)
+    {
+        return -RT_EINVAL;
+    }
+
+    rt_memcpy(port->cmd_tbl, fis, fis_size);
+    ahci_fill_cmd_slot(port, (fis_size >> 2) | (sg_count << 16) | (!is_read << 6));
+
+    if (!is_read)
+    {
+        rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, buffer, buffer_size);
+    }
+
+    HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CI, 1);
+
+    err = rt_completion_wait(&port->done, rt_tick_from_millisecond(10000));
+
+    if (!err && is_read)
+    {
+        rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, buffer, buffer_size);
+    }
+
+    return err;
+}
+
+static rt_err_t ahci_scsi_cmd_rw(struct rt_ahci_host *host, int id,
+        rt_off_t lba, void *buffer, rt_ssize_t size, rt_bool_t is_read)
+{
+    rt_err_t err;
+    rt_uint8_t fis[20];
+    struct rt_ahci_port *port = &host->ports[id];
+
+    rt_memset(fis, 0, sizeof(fis));
+    fis[0] = RT_AHCI_FIS_TYPE_REG_H2D;
+    fis[1] = 1 << 7; /* Command */
+    fis[2] = is_read ? RT_AHCI_ATA_CMD_READ_EXT : RT_AHCI_ATA_CMD_WRITE_EXT;
+
+    while (size > 0)
+    {
+        rt_size_t t_size, t_lba;
+
+        t_lba = rt_min_t(rt_size_t, host->max_blocks, size);
+        t_size = port->block_size * t_lba;
+
+        fis[3] = 0xe0;                  /* Features */
+        fis[4] = (lba >> 0) & 0xff;     /* LBA low register */
+        fis[5] = (lba >> 8) & 0xff;     /* LBA mid register */
+        fis[6] = (lba >> 16) & 0xff;    /* LBA high register */
+        fis[7] = 1 << 6;                /* Device */
+        fis[8] = ((lba >> 24) & 0xff);  /* LBA register, 31:24 */
+        fis[9] = ((lba >> 32) & 0xff);  /* LBA register, 39:32 */
+        fis[10] = ((lba >> 40) & 0xff); /* LBA register, 47:40 */
+        fis[12] = (t_lba >> 0) & 0xff;  /* Count register, 7:0 */
+        fis[13] = (t_lba >> 8) & 0xff;  /* Count register, 15:8 */
+
+        if ((err = ahci_request_io(host, id, fis, sizeof(fis), buffer, t_size, is_read)))
+        {
+            return err;
+        }
+
+        size -= t_lba;
+        lba += t_lba;
+        buffer += t_size;
+    }
+
+    return RT_EOK;
+}
+
+static rt_err_t ahci_scsi_synchronize_cache(struct rt_ahci_host *host, int id,
+        rt_off_t lba, rt_size_t size)
+{
+    rt_uint8_t fis[20];
+    rt_uint16_t *ataid;
+    struct rt_ahci_port *port = &host->ports[id];
+
+    ataid = port->ataid;
+
+    if (!rt_ahci_ata_id_wcache_enabled(ataid) &&
+        !rt_ahci_ata_id_has_flush(ataid) &&
+        !rt_ahci_ata_id_has_flush_ext(ataid))
+    {
+        return -RT_ENOSYS;
+    }
+
+    rt_memset(fis, 0, sizeof(fis));
+    fis[0] = RT_AHCI_FIS_TYPE_REG_H2D;
+    fis[1] = 1 << 7; /* Command */
+
+    if (rt_ahci_ata_id_has_flush_ext(ataid))
+    {
+        fis[2] = RT_AHCI_ATA_CMD_FLUSH_EXT;
+    }
+    else
+    {
+        fis[2] = RT_AHCI_ATA_CMD_FLUSH;
+    }
+
+    rt_memcpy(port->cmd_tbl, fis, 20);
+    ahci_fill_cmd_slot(port, 5);
+
+    HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CI, 1);
+
+    return rt_completion_wait(&port->done, rt_tick_from_millisecond(5000));
+}
+
+static rt_err_t ahci_scsi_cmd_write_same(struct rt_ahci_host *host, int id,
+        rt_off_t lba, rt_size_t size)
+{
+    rt_uint8_t fis[20];
+    struct rt_ahci_port *port = &host->ports[id];
+
+    rt_memset(fis, 0, sizeof(fis));
+    fis[0] = RT_AHCI_FIS_TYPE_REG_H2D;
+    fis[1] = 1 << 7;                /* Command */
+    fis[2] = RT_AHCI_ATA_CMD_DSM;
+    fis[3] = RT_AHCI_ATA_DSM_TRIM;  /* Features */
+    fis[4] = (lba >> 0) & 0xff;     /* LBA low register */
+    fis[5] = (lba >> 8) & 0xff;     /* LBA mid register */
+    fis[6] = (lba >> 16) & 0xff;    /* LBA high register */
+    fis[7] = 1 << 6;                /* Device */
+    fis[8] = ((lba >> 24) & 0xff);  /* LBA register, 31:24 */
+    fis[9] = ((lba >> 32) & 0xff);  /* LBA register, 39:32 */
+    fis[10] = ((lba >> 40) & 0xff); /* LBA register, 47:40 */
+    fis[12] = (size >> 0) & 0xff;   /* Count register, 7:0 */
+    fis[13] = (size >> 8) & 0xff;   /* Count register, 15:8 */
+
+    HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CI, 1);
+
+    return rt_completion_wait(&port->done, rt_tick_from_millisecond(5000));
+}
+
+static rt_err_t ahci_scsi_cmd_read_capacity(struct rt_ahci_host *host, int id,
+        rt_size_t *out_last_block, rt_size_t *out_block_size)
+{
+    struct rt_ahci_port *port = &host->ports[id];
+
+    if (!port->ataid)
+    {
+        return -RT_EIO;
+    }
+
+    *out_last_block = rt_ahci_ata_id_n_sectors(port->ataid) - 1;
+    *out_block_size = port->block_size;
+
+    return RT_EOK;
+}
+
+static rt_err_t ahci_scsi_cmd_test_unit_ready(struct rt_ahci_host *host, int id)
+{
+    struct rt_ahci_port *port = &host->ports[id];
+
+    return port->ataid ? RT_EOK : -RT_EIO;
+}
+
+static rt_err_t ahci_scsi_cmd_inquiry(struct rt_ahci_host *host, int id,
+        char *prodid, rt_size_t prodid_len, char *prodrev, rt_size_t prodrev_len)
+{
+    rt_err_t err;
+    rt_uint8_t fis[20];
+    rt_uint16_t *ataid;
+    struct rt_ahci_port *port = &host->ports[id];
+
+    if (!port->link)
+    {
+        return -RT_EIO;
+    }
+
+    if (!port->ataid && !(port->ataid = rt_malloc(RT_AHCI_ATA_ID_WORDS * 2)))
+    {
+        return -RT_ENOMEM;
+    }
+    ataid = port->ataid;
+
+    rt_memset(fis, 0, sizeof(fis));
+    fis[0] = RT_AHCI_FIS_TYPE_REG_H2D;
+    fis[1] = 1 << 7; /* Command */
+    fis[2] = RT_AHCI_ATA_CMD_ID_ATA;
+
+    if ((err = ahci_request_io(host, id, fis, sizeof(fis),
+            ataid, RT_AHCI_ATA_ID_WORDS * 2, RT_TRUE)))
+    {
+        return err;
+    }
+
+    for (int i = 0; i < RT_AHCI_ATA_ID_WORDS; ++i)
+    {
+        ataid[i] = rt_le16_to_cpu(ataid[i]);
+    }
+
+    for (int i = 0; i < prodid_len / 2; ++i)
+    {
+        rt_uint16_t src = ataid[RT_AHCI_ATA_ID_PROD + i];
+
+        prodid[i] = (src & 0x00ff) << 8 | (src & 0xff00) >> 8;
+    }
+
+    for (int i = 0; i < prodrev_len / 2; ++i)
+    {
+        rt_uint16_t src = ataid[RT_AHCI_ATA_ID_FW_REV + i];
+
+        prodrev[i] = (src & 0x00ff) << 8 | (src & 0xff00) >> 8;
+    }
+
+    return err;
+}
+
+static rt_err_t ahci_scsi_transfer(struct rt_scsi_device *sdev,
+        struct rt_scsi_cmd *cmd)
+{
+    rt_err_t err;
+    struct rt_ahci_host *host;
+
+    host = rt_container_of(sdev->host, struct rt_ahci_host, parent);
+
+    switch (cmd->op.unknow.opcode)
+    {
+    case RT_SCSI_CMD_REQUEST_SENSE:
+    {
+        struct rt_scsi_request_sense_data *request_sense = &cmd->data.request_sense;
+
+        request_sense->error_code = 0x72;
+
+        err = RT_EOK;
+    }
+        break;
+
+    case RT_SCSI_CMD_READ10:
+    {
+        struct rt_scsi_read10 *read10 = &cmd->op.read10;
+
+        err = ahci_scsi_cmd_rw(host, sdev->id,
+                rt_be32_to_cpu(read10->lba),
+                cmd->data.ptr,
+                rt_be16_to_cpu(read10->size),
+                RT_TRUE);
+    }
+        break;
+
+    case RT_SCSI_CMD_READ16:
+    {
+        struct rt_scsi_read16 *read16 = &cmd->op.read16;
+
+        err = ahci_scsi_cmd_rw(host, sdev->id,
+                rt_be64_to_cpu(read16->lba),
+                cmd->data.ptr,
+                rt_be32_to_cpu(read16->size),
+                RT_TRUE);
+    }
+        break;
+
+    case RT_SCSI_CMD_READ12:
+    {
+        struct rt_scsi_read12 *read12 = &cmd->op.read12;
+
+        err = ahci_scsi_cmd_rw(host, sdev->id,
+                rt_be32_to_cpu(read12->lba),
+                cmd->data.ptr,
+                rt_be32_to_cpu(read12->size),
+                RT_TRUE);
+    }
+        break;
+
+    case RT_SCSI_CMD_WRITE10:
+    {
+        struct rt_scsi_write10 *write10 = &cmd->op.write10;
+
+        err = ahci_scsi_cmd_rw(host, sdev->id,
+                rt_be32_to_cpu(write10->lba),
+                cmd->data.ptr,
+                rt_be16_to_cpu(write10->size),
+                RT_FALSE);
+    }
+        break;
+
+    case RT_SCSI_CMD_WRITE16:
+    {
+        struct rt_scsi_write16 *write16 = &cmd->op.write16;
+
+        err = ahci_scsi_cmd_rw(host, sdev->id,
+                rt_be64_to_cpu(write16->lba),
+                cmd->data.ptr,
+                rt_be32_to_cpu(write16->size),
+                RT_FALSE);
+    }
+        break;
+
+    case RT_SCSI_CMD_WRITE12:
+    {
+        struct rt_scsi_write12 *write12 = &cmd->op.write12;
+
+        err = ahci_scsi_cmd_rw(host, sdev->id,
+                rt_be32_to_cpu(write12->lba),
+                cmd->data.ptr,
+                rt_be32_to_cpu(write12->size),
+                RT_FALSE);
+    }
+        break;
+
+    case RT_SCSI_CMD_SYNCHRONIZE_CACHE10:
+    {
+        struct rt_scsi_synchronize_cache10 *synchronize_cache10 = &cmd->op.synchronize_cache10;
+
+        err = ahci_scsi_synchronize_cache(host, sdev->id,
+                rt_be32_to_cpu(synchronize_cache10->lba),
+                rt_be16_to_cpu(synchronize_cache10->size));
+    }
+        break;
+
+    case RT_SCSI_CMD_SYNCHRONIZE_CACHE16:
+    {
+        struct rt_scsi_synchronize_cache16 *synchronize_cache16 = &cmd->op.synchronize_cache16;
+
+        err = ahci_scsi_synchronize_cache(host, sdev->id,
+                rt_be64_to_cpu(synchronize_cache16->lba),
+                rt_be32_to_cpu(synchronize_cache16->size));
+    }
+        break;
+
+    case RT_SCSI_CMD_WRITE_SAME10:
+    {
+        struct rt_scsi_write_same10 *write_same10 = &cmd->op.write_same10;
+
+        err = ahci_scsi_cmd_write_same(host, sdev->id,
+                rt_be32_to_cpu(write_same10->lba), rt_be16_to_cpu(write_same10->size));
+    }
+        break;
+
+    case RT_SCSI_CMD_WRITE_SAME16:
+    {
+        struct rt_scsi_write_same16 *write_same16 = &cmd->op.write_same16;
+
+        err = ahci_scsi_cmd_write_same(host, sdev->id,
+                rt_be64_to_cpu(write_same16->lba), rt_be32_to_cpu(write_same16->size));
+    }
+        break;
+
+    case RT_SCSI_CMD_READ_CAPACITY10:
+    {
+        rt_size_t last_block, block_size;
+        struct rt_scsi_read_capacity10_data *data = &cmd->data.read_capacity10;
+
+        err = ahci_scsi_cmd_read_capacity(host, sdev->id, &last_block, &block_size);
+
+        if (!err)
+        {
+            if (last_block > 0x100000000ULL)
+            {
+                last_block = 0xffffffff;
+            }
+
+            data->last_block = rt_cpu_to_be32(last_block);
+            data->block_size = rt_cpu_to_be32(block_size);
+        }
+    }
+        break;
+
+    case RT_SCSI_CMD_READ_CAPACITY16:
+    {
+        rt_size_t last_block, block_size;
+        struct rt_scsi_read_capacity16_data *data = &cmd->data.read_capacity16;
+
+        err = ahci_scsi_cmd_read_capacity(host, sdev->id, &last_block, &block_size);
+
+        if (!err)
+        {
+            data->last_block = rt_cpu_to_be64(last_block);
+            data->block_size = rt_cpu_to_be32(block_size);
+        }
+    }
+        break;
+
+    case RT_SCSI_CMD_TEST_UNIT_READY:
+        err = ahci_scsi_cmd_test_unit_ready(host, sdev->id);
+        break;
+
+    case RT_SCSI_CMD_INQUIRY:
+    {
+        struct rt_ahci_port *port = &host->ports[sdev->id];
+        struct rt_scsi_inquiry_data *inquiry = &cmd->data.inquiry;
+
+        err = ahci_scsi_cmd_inquiry(host, sdev->id,
+                inquiry->prodid, sizeof(inquiry->prodid),
+                inquiry->prodrev, sizeof(inquiry->prodrev));
+
+        if (!err)
+        {
+            rt_memcpy(inquiry->vendor, "ATA     ", sizeof(inquiry->vendor));
+
+            if (HWREG32(port->regs + RT_AHCI_PORT_SIG) != RT_AHCI_PORT_SIG_SATA_CDROM)
+            {
+                port->block_size = 512;
+                inquiry->devtype = SCSI_DEVICE_TYPE_DIRECT;
+            }
+            else
+            {
+                port->block_size = 2048;
+                inquiry->devtype = SCSI_DEVICE_TYPE_CDROM;
+            }
+            inquiry->rmb = 0;
+            inquiry->length = 95 - 4;
+        }
+    }
+        break;
+
+    case RT_SCSI_CMD_MODE_SENSE:
+    case RT_SCSI_CMD_MODE_SENSE10:
+    case RT_SCSI_CMD_MODE_SELECT:
+    case RT_SCSI_CMD_MODE_SELECT10:
+        return -RT_ENOSYS;
+
+    default:
+        return -RT_EINVAL;
+    }
+
+    return err;
+}
+
+static struct rt_scsi_ops ahci_scsi_ops =
+{
+    .transfer = ahci_scsi_transfer,
+};
+
+static void ahci_isr(int irqno, void *param)
+{
+    int id;
+    rt_uint32_t isr;
+    bitmap_t int_map;
+    struct rt_ahci_port *port;
+    struct rt_ahci_host *host = param;
+
+    int_map = HWREG32(host->regs + RT_AHCI_HBA_INTS);
+
+    bitmap_for_each_set_bit(&int_map, id, host->ports_nr)
+    {
+        port = &host->ports[id];
+
+        isr = HWREG32(port->regs + RT_AHCI_PORT_INTS);
+
+        if (port->link)
+        {
+            if (host->ops->port_isr)
+            {
+                host->ops->port_isr(host, port, isr);
+            }
+
+            rt_completion_done(&port->done);
+        }
+
+        HWREG32(port->regs + RT_AHCI_PORT_INTS) = isr;
+    }
+
+    HWREG32(host->regs + RT_AHCI_HBA_INTS) = isr;
+}
+
+rt_err_t rt_ahci_host_register(struct rt_ahci_host *host)
+{
+    rt_err_t err;
+    rt_uint32_t value;
+    char dev_name[RT_NAME_MAX];
+    struct rt_scsi_host *scsi;
+
+    if (!host || !host->parent.dev || !host->ops)
+    {
+        return -RT_EINVAL;
+    }
+
+    host->max_blocks = host->max_blocks ? : 0x80;
+
+    /*
+     * 1. Reset HBA.
+     */
+    err = -RT_EIO;
+    value = HWREG32(host->regs + RT_AHCI_HBA_GHC);
+
+    if (!(value & RT_AHCI_GHC_RESET))
+    {
+        HWREG32_FLUSH(host->regs + RT_AHCI_HBA_GHC, value | RT_AHCI_GHC_RESET);
+    }
+
+    for (int i = 0; i < 5; ++i)
+    {
+        rt_thread_mdelay(200);
+
+        if (!(HWREG32(host->regs + RT_AHCI_HBA_GHC) & RT_AHCI_GHC_RESET))
+        {
+            err = RT_EOK;
+            break;
+        }
+    }
+
+    if (err)
+    {
+        goto _fail;
+    }
+
+    /*
+     * 2. Enable AHCI and get the ports' information.
+     */
+    HWREG32_FLUSH(host->regs + RT_AHCI_HBA_GHC, RT_AHCI_GHC_AHCI_EN);
+
+    host->cap = HWREG32(host->regs + RT_AHCI_HBA_CAP);
+    host->cap &= RT_AHCI_CAP_SPM | RT_AHCI_CAP_SSS | RT_AHCI_CAP_SIS;
+    HWREG32(host->regs + RT_AHCI_HBA_CAP) = host->cap;
+    host->cap = HWREG32(host->regs + RT_AHCI_HBA_CAP);
+
+    HWREG32_FLUSH(host->regs + RT_AHCI_HBA_PI, 0xf);
+
+    if (host->ops->host_init && (err = host->ops->host_init(host)))
+    {
+        goto _fail;
+    }
+
+    host->ports_nr = (host->cap & RT_AHCI_CAP_NP) + 1;
+    host->ports_map = HWREG32(host->regs + RT_AHCI_HBA_PI);
+
+    /* Check implemented in firmware */
+    rt_dm_dev_prop_read_u32(host->parent.dev, "ports-implemented", &host->ports_map);
+
+    for (int i = 0; i < host->ports_nr; ++i)
+    {
+        struct rt_ahci_port *port;
+
+        if (!(host->ports_map & RT_BIT(i)))
+        {
+            continue;
+        }
+        port = &host->ports[i];
+
+        /*
+         * 3. Alloc port io memory.
+         */
+        port->regs = host->regs + 0x100 + (i * 0x80);
+
+        /*
+         * 4. Make port stop.
+         */
+        value = HWREG32(port->regs + RT_AHCI_PORT_CMD);
+        if (value & (RT_AHCI_PORT_CMD_LIST_ON | RT_AHCI_PORT_CMD_FIS_ON |
+                RT_AHCI_PORT_CMD_FIS_RX | RT_AHCI_PORT_CMD_START))
+        {
+            value &= ~(RT_AHCI_PORT_CMD_LIST_ON | RT_AHCI_PORT_CMD_FIS_ON |
+                    RT_AHCI_PORT_CMD_FIS_RX | RT_AHCI_PORT_CMD_START);
+
+            HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CMD, value);
+
+            rt_thread_mdelay(500);
+        }
+
+        if (host->ops->port_init && (err = host->ops->port_init(host, port)))
+        {
+            LOG_E("Init port[%d] error = %s", rt_strerror(err));
+            continue;
+        }
+
+        value = HWREG32(port->regs + RT_AHCI_PORT_CMD);
+        value |= RT_AHCI_PORT_CMD_SPIN_UP;
+        HWREG32(port->regs + RT_AHCI_PORT_CMD) = value;
+
+        /*
+         * 5. Enable port's SATA link.
+         */
+        if (host->ops->port_link_up)
+        {
+            err = host->ops->port_link_up(host, port);
+        }
+        else
+        {
+            err = -RT_ETIMEOUT;
+
+            for (int retry = 0; retry < 5; ++retry)
+            {
+                value = HWREG32(port->regs + RT_AHCI_PORT_SSTS);
+
+                if ((value & RT_AHCI_PORT_SSTS_DET_MASK) == RT_AHCI_PORT_SSTS_DET_PHYRDY)
+                {
+                    err = RT_EOK;
+                    break;
+                }
+
+                rt_thread_mdelay(2);
+            }
+        }
+
+        if (err)
+        {
+            if (HWREG32(port->regs + RT_AHCI_PORT_SSTS) & RT_AHCI_PORT_SSTS_DET_MASK)
+            {
+                LOG_E("SATA[%d] link error = %s", i, rt_strerror(err));
+            }
+            else
+            {
+                LOG_D("SATA[%d] not device", i);
+            }
+
+            continue;
+        }
+
+        /* Clear error status */
+        if ((value = HWREG32(port->regs + RT_AHCI_PORT_SERR)))
+        {
+            HWREG32(port->regs + RT_AHCI_PORT_SERR) = value;
+        }
+
+        for (int retry = 0; retry < 5; ++retry)
+        {
+            value = HWREG32(port->regs + RT_AHCI_PORT_TFD);
+            if (!(value & (RT_AHCI_PORT_TFDATA_BSY | RT_AHCI_PORT_TFDATA_DRQ)))
+            {
+                break;
+            }
+
+            rt_thread_mdelay(2);
+
+            value = HWREG32(port->regs + RT_AHCI_PORT_SSTS);
+            if ((value & RT_AHCI_PORT_SSTS_DET_MASK) == RT_AHCI_PORT_SSTS_DET_PHYRDY)
+            {
+                break;
+            }
+        }
+
+        value = HWREG32(port->regs + RT_AHCI_PORT_SSTS) & RT_AHCI_PORT_SSTS_DET_MASK;
+        if (value == RT_AHCI_PORT_SSTS_DET_COMINIT)
+        {
+            /* Retry to setup */
+            --i;
+            continue;
+        }
+
+        /* Clear error */
+        value = HWREG32(port->regs + RT_AHCI_PORT_SERR);
+        HWREG32(port->regs + RT_AHCI_PORT_SERR) = value;
+
+        /* Clear pending IRQ */
+        if ((value = HWREG32(port->regs + RT_AHCI_PORT_INTS)))
+        {
+            HWREG32(port->regs + RT_AHCI_PORT_INTS) = value;
+        }
+
+        HWREG32(host->regs + RT_AHCI_HBA_INTS) = RT_BIT(i);
+
+        value = HWREG32(port->regs + RT_AHCI_PORT_SSTS);
+        if ((value & RT_AHCI_PORT_SSTS_DET_MASK) == RT_AHCI_PORT_SSTS_DET_PHYRDY)
+        {
+            port->link = RT_TRUE;
+        }
+    }
+
+    HWREG32(host->regs + RT_AHCI_HBA_GHC) |= RT_AHCI_GHC_IRQ_EN;
+
+    for (int i = 0; i < host->ports_nr; ++i)
+    {
+        void *dma;
+        rt_ubase_t dma_addr;
+        rt_tick_t timeout;
+        struct rt_ahci_port *port = &host->ports[i];
+
+        if (!port->link)
+        {
+            continue;
+        }
+
+        /*
+         * 6. Alloc transport memory, Port x Command List and FIS Base Address.
+         */
+        port->dma = rt_dma_alloc_coherent(host->parent.dev,
+                RT_AHCI_DMA_SIZE, &port->dma_handle);
+
+        if (!port->dma)
+        {
+            LOG_E("No memory to setup port[%d]", i);
+            break;
+        }
+        dma = port->dma;
+
+        rt_memset(dma, 0, RT_AHCI_DMA_SIZE);
+
+        port->cmd_slot = dma;
+        dma += (RT_AHCI_CMD_SLOT_SIZE + 224);
+
+        port->rx_fis = dma;
+        dma += RT_AHCI_RX_FIS_SIZE;
+
+        port->cmd_tbl = dma;
+        port->cmd_tbl_dma = (rt_ubase_t)rt_kmem_v2p(dma);
+        dma += RT_AHCI_CMD_TBL_HDR;
+
+        port->cmd_tbl_sg = dma;
+
+        dma_addr = (rt_ubase_t)rt_kmem_v2p(port->cmd_slot);
+        HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CLB, rt_lower_32_bits(dma_addr));
+        HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CLBU, rt_upper_32_bits(dma_addr));
+
+        dma_addr = (rt_ubase_t)rt_kmem_v2p(port->rx_fis);
+        HWREG32_FLUSH(port->regs + RT_AHCI_PORT_FB, rt_lower_32_bits(dma_addr));
+        HWREG32_FLUSH(port->regs + RT_AHCI_PORT_FBU, rt_upper_32_bits(dma_addr));
+
+        if (host->ops->port_dma_init && (err = host->ops->port_dma_init(host, port)))
+        {
+            LOG_E("Init port[%d] DMA error = %s", rt_strerror(err));
+        }
+
+        HWREG32_FLUSH(port->regs + RT_AHCI_PORT_CMD, RT_AHCI_PORT_CMD_ACTIVE |
+                RT_AHCI_PORT_CMD_FIS_RX | RT_AHCI_PORT_CMD_POWER_ON |
+                RT_AHCI_PORT_CMD_SPIN_UP | RT_AHCI_PORT_CMD_START);
+
+        /* Wait spinup */
+        err = -RT_ETIMEOUT;
+        timeout = rt_tick_from_millisecond(20000);
+        timeout += rt_tick_get();
+        do {
+            if (!(HWREG32(port->regs + RT_AHCI_PORT_TFD) & RT_AHCI_PORT_TFDATA_BSY))
+            {
+                err = RT_EOK;
+                break;
+            }
+
+            rt_hw_cpu_relax();
+        } while (rt_tick_get() < timeout);
+
+        if (err)
+        {
+            rt_dma_free_coherent(host->parent.dev, RT_AHCI_DMA_SIZE, port->dma,
+                    port->dma_handle);
+            port->dma = RT_NULL;
+
+            LOG_E("Start up port[%d] fail", i);
+            continue;
+        }
+
+        port->int_enabled |= RT_AHCI_PORT_INTE_HBUS_ERR | RT_AHCI_PORT_INTE_IF_ERR |
+                RT_AHCI_PORT_INTE_CONNECT | RT_AHCI_PORT_INTE_PHYRDY |
+                RT_AHCI_PORT_INTE_UNK_FIS | RT_AHCI_PORT_INTE_BAD_PMP |
+                RT_AHCI_PORT_INTE_TF_ERR | RT_AHCI_PORT_INTE_HBUS_DATA_ERR |
+                RT_AHCI_PORT_INTE_SG_DONE | RT_AHCI_PORT_INTE_SDB_FIS |
+                RT_AHCI_PORT_INTE_DMAS_FIS | RT_AHCI_PORT_INTE_PIOS_FIS |
+                RT_AHCI_PORT_INTE_D2H_REG_FIS;
+
+        HWREG32(port->regs + RT_AHCI_PORT_INTE) = port->int_enabled;
+
+        rt_completion_init(&port->done);
+    }
+
+    rt_snprintf(dev_name, sizeof(dev_name), "ahci-%s",
+            rt_dm_dev_get_name(host->parent.dev));
+
+    rt_hw_interrupt_install(host->irq, ahci_isr, host, dev_name);
+    rt_hw_interrupt_umask(host->irq);
+
+    scsi = &host->parent;
+    scsi->max_lun = rt_max_t(rt_size_t, scsi->max_lun, 1);
+    scsi->max_id = host->ports_nr;
+    scsi->ops = &ahci_scsi_ops;
+
+    if ((err = rt_scsi_host_register(scsi)))
+    {
+        goto _fail;
+    }
+
+    return RT_EOK;
+
+_fail:
+    rt_hw_interrupt_mask(host->irq);
+    rt_pic_detach_irq(host->irq, host);
+
+    return err;
+}
+
+rt_err_t rt_ahci_host_unregister(struct rt_ahci_host *host)
+{
+    rt_err_t err;
+    struct rt_scsi_host *scsi;
+
+    if (!host)
+    {
+        return -RT_EINVAL;
+    }
+
+    scsi = &host->parent;
+
+    if ((err = rt_scsi_host_unregister(scsi)))
+    {
+        return err;
+    }
+
+    rt_hw_interrupt_mask(host->irq);
+    rt_pic_detach_irq(host->irq, host);
+
+    for (int i = 0; i < host->ports_nr; ++i)
+    {
+        struct rt_ahci_port *port = &host->ports[i];
+
+        if (port->ataid)
+        {
+            rt_free(port->ataid);
+        }
+
+        HWREG32(port->regs) &= ~(RT_AHCI_PORT_CMD_ACTIVE | RT_AHCI_PORT_CMD_POWER_ON |
+                RT_AHCI_PORT_CMD_SPIN_UP | RT_AHCI_PORT_CMD_START);
+
+        if (port->dma)
+        {
+            rt_dma_free_coherent(host->parent.dev, RT_AHCI_DMA_SIZE, port->dma,
+                    port->dma_handle);
+        }
+    }
+
+    HWREG32(host->regs + RT_AHCI_HBA_GHC) &= ~(RT_AHCI_GHC_AHCI_EN | RT_AHCI_GHC_IRQ_EN);
+
+    return RT_EOK;
+}

+ 397 - 0
components/drivers/include/drivers/ahci.h

@@ -0,0 +1,397 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-02-25     GuEe-GUI     the first version
+ */
+
+#ifndef __AHCI_H__
+#define __AHCI_H__
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <drivers/scsi.h>
+#include <drivers/misc.h>
+
+struct rt_ahci_ops;
+
+/* Generic Host Control */
+#define RT_AHCI_HBA_CAP                     0x00    /* Host capability*/
+#define   RT_AHCI_CAP_NP                    RT_GENMASK(4, 0)    /* Number of Ports */
+#define   RT_AHCI_CAP_NCS                   RT_GENMASK(8, 12)   /* Number of Command Slots */
+#define   RT_AHCI_CAP_PSC                   RT_BIT(13)          /* Partial State Capable */
+#define   RT_AHCI_CAP_SSC                   RT_BIT(14)          /* Slumber capable */
+#define   RT_AHCI_CAP_PMD                   RT_BIT(15)          /* PIO Multiple DRQ Block */
+#define   RT_AHCI_CAP_SPM                   RT_BIT(17)          /* Port Multiplier */
+#define   RT_AHCI_CAP_AHCI                  RT_BIT(18)          /* AHCI only */
+#define   RT_AHCI_CAP_SNZO                  RT_BIT(19)          /* Non-Zero DMA Offsets */
+#define   RT_AHCI_CAP_ISS                   RT_GENMASK(23, 20)  /* Interface Speed Support */
+#define   RT_AHCI_CAP_CLO                   RT_BIT(24)          /* Command List Override support */
+#define   RT_AHCI_CAP_SAL                   RT_BIT(25)          /* Activity LED */
+#define   RT_AHCI_CAP_SALP                  RT_BIT(26)          /* Aggressive Link Power Management */
+#define   RT_AHCI_CAP_SSS                   RT_BIT(27)          /* Staggered Spin-up */
+#define   RT_AHCI_CAP_SIS                   RT_BIT(28)          /* Interlock Switch */
+#define   RT_AHCI_CAP_NCQ                   RT_BIT(30)          /* Native Command Queueing */
+#define   RT_AHCI_CAP_64                    RT_BIT(31)          /* PCI DAC (64-bit DMA) support */
+#define RT_AHCI_HBA_GHC                     0x04    /* Global host control */
+#define   RT_AHCI_GHC_RESET                 RT_BIT(0)           /* Reset controller; self-clear */
+#define   RT_AHCI_GHC_IRQ_EN                RT_BIT(1)           /* Global IRQ enable */
+#define   RT_AHCI_GHC_AHCI_EN               RT_BIT(31)          /* AHCI enabled */
+#define RT_AHCI_HBA_INTS                    0x08    /* Interrupt status */
+#define RT_AHCI_HBA_PI                      0x0c    /* Port implemented */
+#define RT_AHCI_HBA_VS                      0x10    /* Version */
+#define RT_AHCI_HBA_CCC_CTL                 0x14    /* Command completion coalescing control */
+#define RT_AHCI_HBA_CCC_PTS                 0x18    /* Command completion coalescing ports */
+#define RT_AHCI_HBA_EM_LOC                  0x1c    /* Enclosure management location */
+#define RT_AHCI_HBA_EM_CTL                  0x20    /* Enclosure management control */
+#define RT_AHCI_HBA_CAP2                    0x24    /* Host capabilities extended */
+#define RT_AHCI_HBA_BOHC                    0x28    /* BIOS/OS handoff control and status */
+#define RT_AHCI_HBA_VENDOR                  0xa0    /* Vendor specific registers (0xa0 - 0xff) */
+
+#define RT_AHCI_PORT_CLB                    0x00    /* Command list base address, 1K-byte aligned */
+#define RT_AHCI_PORT_CLBU                   0x04    /* Command list base address upper 32 bits */
+#define RT_AHCI_PORT_FB                     0x08    /* FIS base address, 256-byte aligned */
+#define RT_AHCI_PORT_FBU                    0x0C    /* FIS base address upper 32 bits */
+#define RT_AHCI_PORT_INTS                   0x10    /* Interrupt status */
+#define RT_AHCI_PORT_INTE                   0x14    /* Interrupt enable */
+#define   RT_AHCI_PORT_INTE_D2H_REG_FIS     RT_BIT(0)           /* D2H Register FIS rx'd */
+#define   RT_AHCI_PORT_INTE_PIOS_FIS        RT_BIT(1)           /* PIO Setup FIS rx'd */
+#define   RT_AHCI_PORT_INTE_DMAS_FIS        RT_BIT(2)           /* DMA Setup FIS rx'd */
+#define   RT_AHCI_PORT_INTE_SDB_FIS         RT_BIT(3)           /* Set Device Bits FIS rx'd */
+#define   RT_AHCI_PORT_INTE_UNK_FIS         RT_BIT(4)           /* Unknown FIS rx'd */
+#define   RT_AHCI_PORT_INTE_SG_DONE         RT_BIT(5)           /* Descriptor processed */
+#define   RT_AHCI_PORT_INTE_CONNECT         RT_BIT(6)           /* Port connect change status */
+#define   RT_AHCI_PORT_INTE_DMPS            RT_BIT(7)           /* Mechanical presence status */
+#define   RT_AHCI_PORT_INTE_PHYRDY          RT_BIT(22)          /* PhyRdy changed */
+#define   RT_AHCI_PORT_INTE_BAD_PMP         RT_BIT(23)          /* Incorrect port multiplier */
+#define   RT_AHCI_PORT_INTE_OVERFLOW        RT_BIT(24)          /* Xfer exhausted available S/G */
+#define   RT_AHCI_PORT_INTE_IF_NONFATAL     RT_BIT(26)          /* Interface non-fatal error */
+#define   RT_AHCI_PORT_INTE_IF_ERR          RT_BIT(27)          /* Interface fatal error */
+#define   RT_AHCI_PORT_INTE_HBUS_DATA_ERR   RT_BIT(28)          /* Host bus data error */
+#define   RT_AHCI_PORT_INTE_HBUS_ERR        RT_BIT(29)          /* Host bus fatal error */
+#define   RT_AHCI_PORT_INTE_TF_ERR          RT_BIT(30)          /* Task file error */
+#define   RT_AHCI_PORT_INTE_COLD_PRES       RT_BIT(31)          /* Cold presence detect */
+#define RT_AHCI_PORT_CMD                    0x18    /* Command and status */
+#define   RT_AHCI_PORT_CMD_START            RT_BIT(0)           /* Enable port DMA engine */
+#define   RT_AHCI_PORT_CMD_SPIN_UP          RT_BIT(1)           /* Spin up device */
+#define   RT_AHCI_PORT_CMD_POWER_ON         RT_BIT(2)           /* Power up device */
+#define   RT_AHCI_PORT_CMD_CLO              RT_BIT(3)           /* Command list override */
+#define   RT_AHCI_PORT_CMD_FIS_RX           RT_BIT(4)           /* Enable FIS receive DMA engine */
+#define   RT_AHCI_PORT_CMD_FIS_ON           RT_BIT(14)          /* FIS DMA engine running */
+#define   RT_AHCI_PORT_CMD_LIST_ON          RT_BIT(15)          /* cmd list DMA engine running */
+#define   RT_AHCI_PORT_CMD_ATAPI            RT_BIT(24)          /* Device is ATAPI */
+#define   RT_AHCI_PORT_CMD_ACTIVE           RT_BIT(28)          /* Active state */
+#define RT_AHCI_PORT_TFD                    0x20    /* Task file data */
+#define   RT_AHCI_PORT_TFDATA_ERR           RT_BIT(0)           /* Indicates an error during the transfer */
+#define   RT_AHCI_PORT_TFDATA_DRQ           RT_BIT(3)           /* Indicates a data transfer is requested */
+#define   RT_AHCI_PORT_TFDATA_BSY           RT_BIT(7)           /* Indicates the interface is busy */
+#define RT_AHCI_PORT_SIG                    0x24    /* Signature */
+#define   RT_AHCI_PORT_SIG_REG_MASK         0xff
+#define   RT_AHCI_PORT_SIG_SECTOR_NR_SHIFT  0       /* Sector Count Register */
+#define   RT_AHCI_PORT_SIG_LBA_LOW_SHIFT    8       /* LBA Low Register */
+#define   RT_AHCI_PORT_SIG_LBA_MID_SHIFT    16      /* LBA Mid Register */
+#define   RT_AHCI_PORT_SIG_LBA_HIGH_SHIFT   24      /* LBA High Register */
+#define RT_AHCI_PORT_SIG_SATA_CDROM         0xeb140101
+#define RT_AHCI_PORT_SIG_SATA_DISK          0x00000101
+#define RT_AHCI_PORT_SSTS                   0x28    /* SATA status (SCR0:SStatus) */
+#define   RT_AHCI_PORT_SSTS_DET_MASK        0x3
+#define   RT_AHCI_PORT_SSTS_DET_COMINIT     0x1
+#define   RT_AHCI_PORT_SSTS_DET_PHYRDY      0x3
+#define RT_AHCI_PORT_SCTL                   0x2c    /* SATA control (SCR2:SControl) */
+#define RT_AHCI_PORT_SERR                   0x30    /* SATA error (SCR1:SError) */
+#define   RT_AHCI_PORT_SERR_ERR_I           RT_BIT(0)           /* Recovered Data Integrity Error */
+#define   RT_AHCI_PORT_SERR_ERR_M           RT_BIT(1)           /* Recovered Communications Error */
+#define   RT_AHCI_PORT_SERR_ERR_T           RT_BIT(8)           /* Transient Data Integrity Error */
+#define   RT_AHCI_PORT_SERR_ERR_C           RT_BIT(9)           /* Persistent Communication or Data Integrity Error */
+#define   RT_AHCI_PORT_SERR_ERR_P           RT_BIT(10)          /* Protocol Error */
+#define   RT_AHCI_PORT_SERR_ERR_E           RT_BIT(11)          /* Internal Error */
+#define   RT_AHCI_PORT_SERR_DIAG_N          RT_BIT(16)          /* PhyRdy Change */
+#define   RT_AHCI_PORT_SERR_DIAG_I          RT_BIT(17)          /* Phy Internal Error */
+#define   RT_AHCI_PORT_SERR_DIAG_W          RT_BIT(18)          /* Comm Wake */
+#define   RT_AHCI_PORT_SERR_DIAG_B          RT_BIT(19)          /* 10B to 8B Decode Error */
+#define   RT_AHCI_PORT_SERR_DIAG_D          RT_BIT(20)          /* Disparity Error */
+#define   RT_AHCI_PORT_SERR_DIAG_C          RT_BIT(21)          /* CRC Error */
+#define   RT_AHCI_PORT_SERR_DIAG_H          RT_BIT(22)          /* Handshake Error */
+#define   RT_AHCI_PORT_SERR_DIAG_S          RT_BIT(23)          /* Link Sequence Error */
+#define   RT_AHCI_PORT_SERR_DIAG_T          RT_BIT(24)          /* Transport state transition error */
+#define   RT_AHCI_PORT_SERR_DIAG_F          RT_BIT(25)          /* Unknown FIS Type */
+#define   RT_AHCI_PORT_SERR_DIAG_X          RT_BIT(26)          /* Exchanged */
+#define RT_AHCI_PORT_SACT                   0x34    /* SATA active (SCR3:SActive) */
+#define RT_AHCI_PORT_CI                     0x38    /* Command issue */
+#define RT_AHCI_PORT_SNTF                   0x3c    /* SATA notification (SCR4:SNotification) */
+#define RT_AHCI_PORT_FBS                    0x40    /* FIS-based switch control */
+#define RT_AHCI_PORT_VENDOR                 0x70    /* Vendor specific (0x70 - 0x7f) */
+
+#define RT_AHCI_MAX_SG                      56
+#define RT_AHCI_CMD_SLOT_SIZE               32
+#define RT_AHCI_MAX_CMD_SLOT                32
+#define RT_AHCI_RX_FIS_SIZE                 256
+#define RT_AHCI_CMD_TBL_HDR                 0x80
+#define RT_AHCI_CMD_TBL_CDB                 0x40
+#define RT_AHCI_CMD_TBL_SIZE                RT_AHCI_CMD_TBL_HDR + (RT_AHCI_MAX_SG * 16)
+#define RT_AHCI_DMA_SIZE                    (RT_AHCI_CMD_SLOT_SIZE * RT_AHCI_MAX_CMD_SLOT + RT_AHCI_CMD_TBL_SIZE + RT_AHCI_RX_FIS_SIZE)
+#define RT_ACHI_PRDT_BYTES_MAX              (4 * 1024 * 1024)
+
+#define RT_AHCI_FIS_TYPE_REG_H2D            0x27    /* Register FIS - host to device */
+#define RT_AHCI_FIS_TYPE_REG_D2H            0x34    /* Register FIS - device to host */
+#define RT_AHCI_FIS_TYPE_DMA_ACT            0x39    /* DMA activate FIS - device to host */
+#define RT_AHCI_FIS_TYPE_DMA_SETUP          0x41    /* DMA setup FIS - bidirectional */
+#define RT_AHCI_FIS_TYPE_DATA               0x46    /* Data FIS - bidirectional */
+#define RT_AHCI_FIS_TYPE_BIST               0x58    /* BIST activate FIS - bidirectional */
+#define RT_AHCI_FIS_TYPE_PIO_SETUP          0x5f    /* PIO setup FIS - device to host */
+#define RT_AHCI_FIS_TYPE_DEV_BITS           0xa1    /* Set device bits FIS - device to host */
+
+#define RT_AHCI_ATA_ID_WORDS                256
+#define RT_AHCI_ATA_ID_CONFIG               0
+#define RT_AHCI_ATA_ID_CYLS                 1
+#define RT_AHCI_ATA_ID_HEADS                3
+#define RT_AHCI_ATA_ID_SECTORS              6
+#define RT_AHCI_ATA_ID_SERNO                10
+#define RT_AHCI_ATA_ID_BUF_SIZE             21
+#define RT_AHCI_ATA_ID_FW_REV               23
+#define RT_AHCI_ATA_ID_PROD                 27
+#define RT_AHCI_ATA_ID_MAX_MULTSECT         47
+#define RT_AHCI_ATA_ID_DWORD_IO             48
+#define RT_AHCI_ATA_ID_TRUSTED              48
+#define RT_AHCI_ATA_ID_CAPABILITY           49
+#define RT_AHCI_ATA_ID_OLD_PIO_MODES        51
+#define RT_AHCI_ATA_ID_OLD_DMA_MODES        52
+#define RT_AHCI_ATA_ID_FIELD_VALID          53
+#define RT_AHCI_ATA_ID_CUR_CYLS             54
+#define RT_AHCI_ATA_ID_CUR_HEADS            55
+#define RT_AHCI_ATA_ID_CUR_SECTORS          56
+#define RT_AHCI_ATA_ID_MULTSECT             59
+#define RT_AHCI_ATA_ID_LBA_CAPACITY         60
+#define RT_AHCI_ATA_ID_SWDMA_MODES          62
+#define RT_AHCI_ATA_ID_MWDMA_MODES          63
+#define RT_AHCI_ATA_ID_PIO_MODES            64
+#define RT_AHCI_ATA_ID_EIDE_DMA_MIN         65
+#define RT_AHCI_ATA_ID_EIDE_DMA_TIME        66
+#define RT_AHCI_ATA_ID_EIDE_PIO             67
+#define RT_AHCI_ATA_ID_EIDE_PIO_IORDY       68
+#define RT_AHCI_ATA_ID_ADDITIONAL_SUPP      69
+#define RT_AHCI_ATA_ID_QUEUE_DEPTH          75
+#define RT_AHCI_ATA_ID_SATA_CAPABILITY      76
+#define RT_AHCI_ATA_ID_SATA_CAPABILITY_2    77
+#define RT_AHCI_ATA_ID_FEATURE_SUPP         78
+#define RT_AHCI_ATA_ID_MAJOR_VER            80
+#define RT_AHCI_ATA_ID_COMMAND_SET_1        82
+#define RT_AHCI_ATA_ID_COMMAND_SET_2        83
+#define RT_AHCI_ATA_ID_CFSSE                84
+#define RT_AHCI_ATA_ID_CFS_ENABLE_1         85
+#define RT_AHCI_ATA_ID_CFS_ENABLE_2         86
+#define RT_AHCI_ATA_ID_CSF_DEFAULT          87
+#define RT_AHCI_ATA_ID_UDMA_MODES           88
+#define RT_AHCI_ATA_ID_HW_CONFIG            93
+#define RT_AHCI_ATA_ID_SPG                  98
+#define RT_AHCI_ATA_ID_LBA_CAPACITY_2       100
+#define RT_AHCI_ATA_ID_SECTOR_SIZE          106
+#define RT_AHCI_ATA_ID_WWN                  108
+#define RT_AHCI_ATA_ID_LOGICAL_SECTOR_SIZE  117
+#define RT_AHCI_ATA_ID_COMMAND_SET_3        119
+#define RT_AHCI_ATA_ID_COMMAND_SET_4        120
+#define RT_AHCI_ATA_ID_LAST_LUN             126
+#define RT_AHCI_ATA_ID_DLF                  128
+#define RT_AHCI_ATA_ID_CSFO                 129
+#define RT_AHCI_ATA_ID_CFA_POWER            160
+#define RT_AHCI_ATA_ID_CFA_KEY_MGMT         162
+#define RT_AHCI_ATA_ID_CFA_MODES            163
+#define RT_AHCI_ATA_ID_DATA_SET_MGMT        169
+#define RT_AHCI_ATA_ID_SCT_CMD_XPORT        206
+#define RT_AHCI_ATA_ID_ROT_SPEED            217
+#define RT_AHCI_ATA_ID_PIO4                 (1 << 1)
+#define RT_AHCI_ATA_ID_SERNO_LEN            20
+#define RT_AHCI_ATA_ID_FW_REV_LEN           8
+#define RT_AHCI_ATA_ID_PROD_LEN             40
+#define RT_AHCI_ATA_ID_WWN_LEN              8
+
+#define RT_AHCI_ATA_CMD_DSM                 0x06
+#define RT_AHCI_ATA_CMD_DEV_RESET           0x08    /* ATAPI device reset */
+#define RT_AHCI_ATA_CMD_PIO_READ            0x20    /* Read sectors with retry */
+#define RT_AHCI_ATA_CMD_PIO_READ_EXT        0x24
+#define RT_AHCI_ATA_CMD_READ_EXT            0x25
+#define RT_AHCI_ATA_CMD_READ_NATIVE_MAX_EXT 0x27
+#define RT_AHCI_ATA_CMD_READ_MULTI_EXT      0x29
+#define RT_AHCI_ATA_CMD_READ_LOG_EXT        0x2f
+#define RT_AHCI_ATA_CMD_PIO_WRITE           0x30    /* Write sectors with retry */
+#define RT_AHCI_ATA_CMD_PIO_WRITE_EXT       0x34
+#define RT_AHCI_ATA_CMD_WRITE_EXT           0x35
+#define RT_AHCI_ATA_CMD_SET_MAX_EXT         0x37
+#define RT_AHCI_ATA_CMD_WRITE_MULTI_EXT     0x39
+#define RT_AHCI_ATA_CMD_WRITE_FUA_EXT       0x3d
+#define RT_AHCI_ATA_CMD_VERIFY              0x40    /* Read verify sectors with retry */
+#define RT_AHCI_ATA_CMD_VERIFY_EXT          0x42
+#define RT_AHCI_ATA_CMD_FPDMA_READ          0x60
+#define RT_AHCI_ATA_CMD_FPDMA_WRITE         0x61
+#define RT_AHCI_ATA_CMD_EDD                 0x90    /* Execute device diagnostic */
+#define RT_AHCI_ATA_CMD_INIT_DEV_PARAMS     0x91    /* Initialize device parameters */
+#define RT_AHCI_ATA_CMD_PACKET              0xa0    /* ATAPI packet */
+#define RT_AHCI_ATA_CMD_ID_ATAPI            0xa1    /* ATAPI identify device */
+#define RT_AHCI_ATA_CMD_CONF_OVERLAY        0xb1
+#define RT_AHCI_ATA_CMD_READ_MULTI          0xc4    /* Read multiple */
+#define RT_AHCI_ATA_CMD_WRITE_MULTI         0xc5    /* Write multiple */
+#define RT_AHCI_ATA_CMD_SET_MULTI           0xc6    /* Set multiple mode */
+#define RT_AHCI_ATA_CMD_READ                0xc8    /* Read DMA with retry */
+#define RT_AHCI_ATA_CMD_WRITE               0xca    /* Write DMA with retry */
+#define RT_AHCI_ATA_CMD_WRITE_MULTI_FUA_EXT 0xce
+#define RT_AHCI_ATA_CMD_STANDBYNOW1         0xe0    /* Standby immediate */
+#define RT_AHCI_ATA_CMD_IDLEIMMEDIATE       0xe1    /* Idle immediate */
+#define RT_AHCI_ATA_CMD_STANDBY             0xe2    /* Place in standby power mode */
+#define RT_AHCI_ATA_CMD_IDLE                0xe3    /* Place in idle power mode */
+#define RT_AHCI_ATA_CMD_PMP_READ            0xe4    /* Read buffer */
+#define RT_AHCI_ATA_CMD_CHK_POWER           0xe5    /* Check power mode */
+#define RT_AHCI_ATA_CMD_SLEEP               0xe6    /* Sleep */
+#define RT_AHCI_ATA_CMD_FLUSH               0xe7
+#define RT_AHCI_ATA_CMD_PMP_WRITE           0xe8    /* Write buffer */
+#define RT_AHCI_ATA_CMD_FLUSH_EXT           0xea
+#define RT_AHCI_ATA_CMD_ID_ATA              0xec    /* Identify device */
+#define RT_AHCI_ATA_CMD_SET_FEATURES        0xef    /* Set features */
+#define RT_AHCI_ATA_CMD_SEC_FREEZE_LOCK     0xf5    /* Security freeze */
+#define RT_AHCI_ATA_CMD_READ_NATIVE_MAX     0xf8
+#define RT_AHCI_ATA_CMD_SET_MAX             0xf9
+
+#define RT_AHCI_ATA_DSM_TRIM                0x01
+
+#define RT_AHCI_ATA_PROT_FLAG_PIO           RT_BIT(0)
+#define RT_AHCI_ATA_PROT_FLAG_DMA           RT_BIT(1)
+#define RT_AHCI_ATA_PROT_FLAG_NCQ           RT_BIT(2)
+#define RT_AHCI_ATA_PROT_FLAG_ATAPI         RT_BIT(3)
+
+#define rt_ahci_ata_id_is_ata(id)           (((id)[0] & (1 << 15)) == 0)
+#define rt_ahci_ata_id_has_lba(id)          ((id)[49] & (1 << 9))
+#define rt_ahci_ata_id_has_dma(id)          ((id)[49] & (1 << 8))
+#define rt_ahci_ata_id_has_ncq(id)          ((id)[76] & (1 << 8))
+#define rt_ahci_ata_id_queue_depth(id)      (((id)[75] & 0x1f) + 1)
+#define rt_ahci_ata_id_removeable(id)       ((id)[0] & (1 << 7))
+#define rt_ahci_ata_id_iordy_disable(id)    ((id)[49] & (1 << 10))
+#define rt_ahci_ata_id_has_iordy(id)        ((id)[49] & (1 << 11))
+
+#define rt_ahci_ata_id_u32(id, n)           (((rt_uint32_t)(id)[(n) + 1] << 16) | ((rt_uint32_t) (id)[(n)]))
+#define rt_ahci_ata_id_u64(id, n)           (((rt_uint64_t)(id)[(n) + 3] << 48) | ((rt_uint64_t)(id)[(n) + 2] << 32) | \
+                                            ((rt_uint64_t)(id)[(n) + 1] << 16) | ((rt_uint64_t)(id)[(n) + 0]) )
+
+rt_inline rt_bool_t rt_ahci_ata_id_has_lba48(const rt_uint16_t *id)
+{
+    if ((id[RT_AHCI_ATA_ID_COMMAND_SET_2] & 0xc000) != 0x4000 ||
+        !rt_ahci_ata_id_u64(id, RT_AHCI_ATA_ID_LBA_CAPACITY_2))
+    {
+        return 0;
+    }
+
+    return !!(id[RT_AHCI_ATA_ID_COMMAND_SET_2] & (1 << 10));
+}
+
+rt_inline rt_uint64_t rt_ahci_ata_id_n_sectors(rt_uint16_t *id)
+{
+    if (rt_ahci_ata_id_has_lba(id))
+    {
+        if (rt_ahci_ata_id_has_lba48(id))
+        {
+            return rt_ahci_ata_id_u64(id, RT_AHCI_ATA_ID_LBA_CAPACITY_2);
+        }
+
+        return rt_ahci_ata_id_u32(id, RT_AHCI_ATA_ID_LBA_CAPACITY);
+    }
+
+    return 0;
+}
+
+rt_inline rt_bool_t rt_ahci_ata_id_wcache_enabled(const rt_uint16_t *id)
+{
+    if ((id[RT_AHCI_ATA_ID_CSF_DEFAULT] & 0xc000) != 0x4000)
+    {
+        return RT_FALSE;
+    }
+    return id[RT_AHCI_ATA_ID_CFS_ENABLE_1] & (1 << 5);
+}
+
+rt_inline rt_bool_t rt_ahci_ata_id_has_flush(const rt_uint16_t *id)
+{
+    if ((id[RT_AHCI_ATA_ID_COMMAND_SET_2] & 0xc000) != 0x4000)
+    {
+        return RT_FALSE;
+    }
+    return id[RT_AHCI_ATA_ID_COMMAND_SET_2] & (1 << 12);
+}
+
+rt_inline rt_bool_t rt_ahci_ata_id_has_flush_ext(const rt_uint16_t *id)
+{
+    if ((id[RT_AHCI_ATA_ID_COMMAND_SET_2] & 0xc000) != 0x4000)
+    {
+        return RT_FALSE;
+    }
+    return id[RT_AHCI_ATA_ID_COMMAND_SET_2] & (1 << 13);
+}
+
+struct rt_ahci_cmd_hdr
+{
+    rt_uint32_t opts;
+    rt_uint32_t status;
+    rt_uint32_t tbl_addr_lo;
+    rt_uint32_t tbl_addr_hi;
+    rt_uint32_t reserved[4];
+};
+
+struct rt_ahci_sg
+{
+    rt_uint32_t addr_lo;
+    rt_uint32_t addr_hi;
+    rt_uint32_t reserved;
+    rt_uint32_t flags_size;
+};
+
+struct rt_ahci_port
+{
+    void *regs;
+
+    void *dma;
+    rt_ubase_t dma_handle;
+
+    struct rt_ahci_cmd_hdr *cmd_slot;
+    struct rt_ahci_sg *cmd_tbl_sg;
+    void *cmd_tbl;
+    rt_ubase_t cmd_tbl_dma;
+    void *rx_fis;
+
+    rt_uint32_t int_enabled;
+    rt_size_t block_size;
+
+    rt_uint16_t *ataid;
+
+    rt_bool_t link;
+    struct rt_completion done;
+};
+
+struct rt_ahci_host
+{
+    struct rt_scsi_host parent;
+
+    int irq;
+    void *regs;
+
+    rt_size_t ports_nr;
+    rt_uint32_t ports_map;
+    struct rt_ahci_port ports[32];
+
+    rt_uint32_t cap;
+    rt_uint32_t max_blocks;
+
+    const struct rt_ahci_ops *ops;
+};
+
+struct rt_ahci_ops
+{
+    rt_err_t (*host_init)(struct rt_ahci_host *host);
+    rt_err_t (*port_init)(struct rt_ahci_host *host, struct rt_ahci_port *port);
+    rt_err_t (*port_link_up)(struct rt_ahci_host *host, struct rt_ahci_port *port);
+    rt_err_t (*port_dma_init)(struct rt_ahci_host *host, struct rt_ahci_port *port);
+    rt_err_t (*port_isr)(struct rt_ahci_host *host, struct rt_ahci_port *port, rt_uint32_t isr);
+};
+
+rt_err_t rt_ahci_host_register(struct rt_ahci_host *host);
+rt_err_t rt_ahci_host_unregister(struct rt_ahci_host *host);
+
+#endif /* __AHCI_H__ */

+ 6 - 0
components/drivers/include/rtdevice.h

@@ -45,6 +45,12 @@ extern "C" {
 #include "drivers/core/power_domain.h"
 #include "drivers/platform.h"
 
+#ifdef RT_USING_ATA
+#ifdef RT_ATA_AHCI
+#include "drivers/ahci.h"
+#endif /* RT_ATA_AHCI */
+#endif /* RT_USING_ATA */
+
 #ifdef RT_USING_MBOX
 #include "drivers/mailbox.h"
 #endif /* RT_USING_MBOX */