summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGUI <[email protected]>2024-11-26 11:22:09 +0800
committerGitHub <[email protected]>2024-11-26 11:22:09 +0800
commitd025072837eb974171831df0ed722166a5ca59df (patch)
tree546133af984456e8745bd4da7aba90d5e01d29b0
parent71520fa9f1b769fa838e39ea5f00175424154376 (diff)
[DM/FEATURE] Support ATA AHCI (#9683)
Add ACHI drivers to support some old platform driver such as SATA. Signed-off-by: GuEe-GUI <[email protected]>
-rwxr-xr-xcomponents/drivers/Kconfig1
-rw-r--r--components/drivers/ata/Kconfig22
-rw-r--r--components/drivers/ata/SConscript21
-rw-r--r--components/drivers/ata/ahci-pci.c206
-rw-r--r--components/drivers/ata/ahci.c896
-rw-r--r--components/drivers/include/drivers/ahci.h397
-rw-r--r--components/drivers/include/rtdevice.h6
7 files changed, 1549 insertions, 0 deletions
diff --git a/components/drivers/Kconfig b/components/drivers/Kconfig
index 1187175840..5248fd0ff7 100755
--- a/components/drivers/Kconfig
+++ b/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"
diff --git a/components/drivers/ata/Kconfig b/components/drivers/ata/Kconfig
new file mode 100644
index 0000000000..4b5cee5627
--- /dev/null
+++ b/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
diff --git a/components/drivers/ata/SConscript b/components/drivers/ata/SConscript
new file mode 100644
index 0000000000..f4a0922a84
--- /dev/null
+++ b/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')
diff --git a/components/drivers/ata/ahci-pci.c b/components/drivers/ata/ahci-pci.c
new file mode 100644
index 0000000000..0bbec3ebd6
--- /dev/null
+++ b/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);
diff --git a/components/drivers/ata/ahci.c b/components/drivers/ata/ahci.c
new file mode 100644
index 0000000000..1239a45f33
--- /dev/null
+++ b/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;
+}
diff --git a/components/drivers/include/drivers/ahci.h b/components/drivers/include/drivers/ahci.h
new file mode 100644
index 0000000000..45e47a83f8
--- /dev/null
+++ b/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__ */
diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h
index 34152cefb6..ef395d3192 100644
--- a/components/drivers/include/rtdevice.h
+++ b/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 */