From d025072837eb974171831df0ed722166a5ca59df Mon Sep 17 00:00:00 2001 From: GUI <39894654+GuEe-GUI@users.noreply.github.com> Date: Tue, 26 Nov 2024 11:22:09 +0800 Subject: [PATCH] [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> --- components/drivers/Kconfig | 1 + components/drivers/ata/Kconfig | 22 + components/drivers/ata/SConscript | 21 + components/drivers/ata/ahci-pci.c | 206 +++++ components/drivers/ata/ahci.c | 896 ++++++++++++++++++++++ components/drivers/include/drivers/ahci.h | 397 ++++++++++ components/drivers/include/rtdevice.h | 6 + 7 files changed, 1549 insertions(+) create mode 100644 components/drivers/ata/Kconfig create mode 100644 components/drivers/ata/SConscript create mode 100644 components/drivers/ata/ahci-pci.c create mode 100644 components/drivers/ata/ahci.c create mode 100644 components/drivers/include/drivers/ahci.h 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 +#include + +#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 +#include +#include + +#define DBG_TAG "rtdm.ahci" +#define DBG_LVL DBG_INFO +#include + +#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 +#include +#include +#include + +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 */