Преглед изворни кода

[dm2.0] add general sdhci driver it support pio&&sdma

zhujiale пре 9 месеци
родитељ
комит
2d78d8cad1

+ 5 - 2
components/drivers/sdio/Kconfig

@@ -25,5 +25,8 @@ config RT_USING_SDIO
             default 16
         config RT_SDIO_DEBUG
             bool "Enable SDIO debug log output"
-        default n
-    endif
+            default n
+        config RT_USING_SDHCI
+            bool "Using sdhci for sd/mmc drivers"
+            default n
+        endif

+ 6 - 1
components/drivers/sdio/SConscript

@@ -11,7 +11,12 @@ dev_mmc.c
 """)
 
 # The set of source files associated with this SConscript file.
-path = [cwd + '/../include']
+path = [cwd + '/../include' , cwd + '/sdhci/include']
+
+if GetDepend('RT_USING_SDHCI'):
+    src += [os.path.join('sdhci', 'sdhci.c')]
+    src += [os.path.join('sdhci', 'fit-mmc.c')]
+    src += [os.path.join('sdhci', 'sdhci-platform.c')]
 
 group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_SDIO'], CPPPATH = path)
 

+ 320 - 0
components/drivers/sdio/sdhci/fit-mmc.c

@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2006-2024 RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author        Notes
+ * 2024-08-16     zhujiale     first version
+ */
+#include <rtthread.h>
+#include "sdhci.h"
+#include <rtdbg.h>
+#include <mmu.h>
+#include <drivers/core/dm.h>
+
+
+static void rt_plat_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req)
+{
+    struct rt_mmc_host *mmc   = (struct rt_mmc_host *)host;
+    rt_uint32_t      flags = req->cmd->flags;
+
+    switch (flags & RESP_MASK)
+    {
+    case RESP_NONE:
+        flags |= MMC_RSP_NONE;
+        break;
+    case RESP_R1:
+        flags |= MMC_RSP_R1;
+        break;
+    case RESP_R1B:
+        flags |= MMC_RSP_R1B;
+        break;
+    case RESP_R2:
+        flags |= MMC_RSP_R2;
+        break;
+    case RESP_R3:
+        flags |= MMC_RSP_R3;
+        break;
+    case RESP_R4:
+        flags |= MMC_RSP_R4;
+        break;
+    case RESP_R5:
+        flags |= MMC_RSP_R5;
+        break;
+    case RESP_R6:
+        flags |= MMC_RSP_R6;
+        break;
+    case RESP_R7:
+        flags |= MMC_RSP_R7;
+        break;
+    }
+    if (req->data)
+    {
+        if ((rt_uint64_t)rt_kmem_v2p(req->data->buf) > 0xffffffff)
+        {
+            void *dma_buffer = rt_malloc(req->data->blks * req->data->blksize);
+            void *req_buf    = NULL;
+
+            if (req->data->flags & DATA_DIR_WRITE)
+            {
+                rt_memcpy(dma_buffer, req->data->buf, req->data->blks * req->data->blksize);
+                req_buf        = req->data->buf;
+                req->data->buf = dma_buffer;
+            }
+            else if (req->data->flags & DATA_DIR_READ)
+            {
+                req_buf        = req->data->buf;
+                req->data->buf = dma_buffer;
+            }
+            req->cmd->flags |= flags;
+            mmc->ops->request(mmc, req);
+
+            rt_sem_take(&host->sem_ack, RT_WAITING_FOREVER);
+
+            if (req->data->flags & DATA_DIR_READ)
+            {
+                rt_memcpy(req_buf, dma_buffer, req->data->blksize * req->data->blks);
+                req->data->buf = req_buf;
+            }else{
+                req->data->buf = req_buf;
+            }
+
+            rt_free(dma_buffer);
+            rt_sem_release(&host->sem_ack);
+        }
+        else
+        {
+            req->cmd->flags |= flags;
+            mmc->ops->request(mmc, req);
+        }
+    }
+    else
+    {
+        req->cmd->flags |= flags;
+        mmc->ops->request(mmc, req);
+    }
+}
+
+static void rt_plat_set_ioconfig(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *iocfg)
+{
+    struct rt_mmc_host *mmc = (struct rt_mmc_host *)host;
+
+    LOG_D("clock:%d,width:%d,power:%d,vdd:%d,timing:%d\n",
+          iocfg->clock, iocfg->bus_width,
+          iocfg->power_mode, iocfg->vdd, iocfg->timing);
+
+    mmc->ops->set_ios(mmc, iocfg);
+}
+
+static rt_int32_t rt_plat_get_card_status(struct rt_mmcsd_host *host)
+{
+    struct rt_mmc_host *mmc = (struct rt_mmc_host *)host;
+
+    return mmc->ops->get_cd(mmc);
+}
+
+static rt_int32_t rt_plat_execute_tuning(struct rt_mmcsd_host *host, rt_int32_t opcode)
+{
+    struct rt_mmc_host *mmc = (struct rt_mmc_host *)host;
+
+    return mmc->ops->execute_tuning(mmc, opcode);
+}
+
+static void rt_plat_enable_sdio_irq(struct rt_mmcsd_host *host, rt_int32_t en)
+{
+    struct rt_mmc_host *mmc = (struct rt_mmc_host *)host;
+
+    return mmc->ops->enable_sdio_irq(mmc, en);
+}
+
+
+static const struct rt_mmcsd_host_ops rt_mmcsd_ops = {
+    .request         = rt_plat_request,
+    .set_iocfg       = rt_plat_set_ioconfig,
+    .get_card_status = rt_plat_get_card_status,
+    .enable_sdio_irq = rt_plat_enable_sdio_irq,
+    .execute_tuning  = rt_plat_execute_tuning,
+};
+
+
+void rt_mmc_request_done(struct rt_mmc_host *host, struct rt_mmcsd_req *mrq)
+{
+    mmcsd_req_complete(&host->rthost);
+}
+
+/*add host in rtt while sdhci complete*/
+int rt_mmc_add_host(struct rt_mmc_host *mmc)
+{
+    mmc->rthost.ops           = &rt_mmcsd_ops;
+    mmc->rthost.flags         = mmc->caps;
+    mmc->rthost.freq_max      = mmc->f_max;
+    mmc->rthost.freq_min      = 400000;
+    mmc->rthost.max_dma_segs  = mmc->max_segs;
+    mmc->rthost.max_seg_size  = mmc->max_seg_size;
+    mmc->rthost.max_blk_size  = mmc->max_blk_size;
+    mmc->rthost.max_blk_count = mmc->max_blk_count;
+    mmc->rthost.valid_ocr     = VDD_165_195|VDD_20_21|VDD_21_22|VDD_22_23|VDD_24_25|VDD_25_26|VDD_26_27|VDD_27_28|VDD_28_29|VDD_29_30|VDD_30_31|VDD_32_33|VDD_33_34|VDD_34_35|VDD_35_36;
+
+
+    mmcsd_change(&mmc->rthost);
+    return 0;
+}
+
+struct rt_mmc_host *rt_mmc_alloc_host(int extra, struct rt_device *dev)
+{
+    struct rt_mmc_host *mmc;
+
+    mmc = rt_malloc(sizeof(*mmc) + extra);
+    if (mmc)
+    {
+        rt_memset(mmc, 0, sizeof(*mmc) + extra);
+        mmc->parent = dev;
+        mmcsd_host_init(&mmc->rthost);
+    }
+
+    return mmc;
+}
+
+void rt_mmc_remove_host(struct rt_mmc_host *host)
+{
+    rt_free(host);
+}
+
+int rt_mmc_abort_tuning(struct rt_mmc_host *host, rt_uint32_t opcode)
+{
+    return 0;
+}
+
+
+int rt_mmc_gpio_get_cd(struct rt_mmc_host *host)
+{
+    return -ENOSYS;
+}
+
+void rt_mmc_detect_change(struct rt_mmc_host *host, unsigned long delay)
+{
+}
+
+
+int rt_mmc_regulator_set_vqmmc(struct rt_mmc_host *mmc, struct rt_mmcsd_io_cfg *ios)
+{
+    return 0;
+}
+
+rt_bool_t rt_mmc_can_gpio_ro(struct rt_mmc_host *host)
+{
+    return RT_FALSE;
+}
+
+int rt_mmc_gpio_get_ro(struct rt_mmc_host *host)
+{
+    return 0;
+}
+
+int rt_mmc_send_abort_tuning(struct rt_mmc_host *host, rt_uint32_t opcode)
+{
+    return 0;
+}
+int rt_mmc_of_parse(struct rt_mmc_host *host)
+{
+    struct rt_device *dev = host->parent;
+    rt_uint32_t       bus_width;
+
+    if (!dev || !dev->ofw_node)
+        return 0;
+
+    /* "bus-width" is translated to MMC_CAP_*_BIT_DATA flags */
+    if (rt_dm_dev_prop_read_u32(dev, "bus-width", &bus_width) < 0)
+    {
+        bus_width = 1;
+    }
+
+    switch (bus_width)
+    {
+    case 8:
+        host->caps |= MMC_CAP_8_BIT_DATA;
+        break; /* Hosts capable of 8-bit can also do 4 bits */
+    case 4:
+        host->caps |= MMC_CAP_4_BIT_DATA;
+        break;
+    case 1:
+        break;
+    default:
+        return -EINVAL;
+    }
+
+    /* f_max is obtained from the optional "max-frequency" property */
+    rt_dm_dev_prop_read_u32(dev, "max-frequency", &host->f_max);
+
+    if (rt_dm_dev_prop_read_bool(dev, "cap-mmc-highspeed"))
+    {
+        host->caps |= MMC_CAP_MMC_HIGHSPEED;
+    }
+
+    if (rt_dm_dev_prop_read_bool(dev, "mmc-hs200-1_8v"))
+    {
+        host->caps |= MMC_CAP2_HS200_1_8V_SDR;
+    }
+
+    if (rt_dm_dev_prop_read_bool(dev, "non-removable"))
+    {
+        host->caps |= MMC_CAP_NONREMOVABLE;
+    }
+
+    if (rt_dm_dev_prop_read_bool(dev, "no-sdio"))
+    {
+        host->caps2 |= MMC_CAP2_NO_SDIO;
+    }
+
+    if (rt_dm_dev_prop_read_bool(dev, "no-sd"))
+    {
+        host->caps2 |= MMC_CAP2_NO_SD;
+    }
+
+    if (rt_dm_dev_prop_read_bool(dev, "mmc-ddr-3_3v"))
+    {
+        host->caps |= MMC_CAP_3_3V_DDR;
+    }
+
+    if (rt_dm_dev_prop_read_bool(dev, "mmc-ddr-1_8v"))
+    {
+        host->caps |= MMC_CAP_1_8V_DDR;
+    }
+
+    if (rt_dm_dev_prop_read_bool(dev, "mmc-ddr-1_2v"))
+    {
+        host->caps |= MMC_CAP_1_2V_DDR;
+    }
+
+    return 0;
+}
+
+
+void rt_mmc_free_host(struct rt_mmc_host *host)
+{
+}
+
+rt_bool_t rt_mmc_can_gpio_cd(struct rt_mmc_host *host)
+{
+    return RT_FALSE;
+}
+
+int mmc_regulator_get_supply(struct rt_mmc_host *mmc)
+{
+    mmc->supply.vmmc  = -RT_NULL;
+    mmc->supply.vqmmc = -RT_NULL;
+
+    return 0;
+}
+int regulator_get_current_limit(struct regulator *regulator)
+{
+    return 0;
+}
+
+int regulator_is_supported_voltage(struct regulator *regulator,
+
+                                   int min_uV, int max_uV)
+{
+    return 0;
+}

+ 66 - 0
components/drivers/sdio/sdhci/include/sdhci-platform.h

@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2006-2024 RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author        Notes
+ * 2024-08-16     zhujiale     first version
+ */
+
+#ifndef _DRIVERS_MMC_RT_SDHCI_PLTFM_H
+#define _DRIVERS_MMC_RT_SDHCI_PLTFM_H
+#include <rtthread.h>
+#include <drivers/core/dm.h>
+#include <drivers/ofw.h>
+#include <drivers/platform.h>
+#include <drivers/clk.h>
+#include "sdhci.h"
+
+struct rt_sdhci_pltfm_data
+{
+    const struct rt_sdhci_ops *ops;
+    unsigned int            quirks;
+    unsigned int            quirks2;
+};
+
+struct rt_sdhci_pltfm_host
+{
+    struct rt_clk *clk;
+    unsigned int clock;
+    rt_uint64_t  xfer_mode_shadow;
+
+    unsigned long private[];
+};
+void            rt_sdhci_get_property(struct rt_platform_device *pdev);
+
+static inline void sdhci_get_of_property(struct rt_platform_device *pdev)
+{
+    return rt_sdhci_get_property(pdev);
+}
+extern struct   rt_sdhci_host *rt_sdhci_pltfm_init(struct rt_platform_device     *pdev,
+                                           const struct rt_sdhci_pltfm_data *pdata,
+                                           size_t                         priv_size);
+extern void               rt_sdhci_pltfm_free(struct rt_platform_device *pdev);
+
+extern int      rt_sdhci_pltfm_init_and_add_host(struct rt_platform_device     *pdev,
+                                          const struct rt_sdhci_pltfm_data *pdata,
+                                          size_t                         priv_size);
+extern void     rt_sdhci_pltfm_remove(struct rt_platform_device *pdev);
+
+extern unsigned int rt_sdhci_pltfm_clk_get_max_clock(struct rt_sdhci_host *host);
+
+static inline void *sdhci_pltfm_priv(struct rt_sdhci_pltfm_host *host)
+{
+    return host->private;
+}
+
+static inline int sdhci_pltfm_suspend(struct rt_device *dev)
+{
+    return 0;
+}
+static inline int sdhci_pltfm_resume(struct rt_device *dev)
+{
+    return 0;
+}
+#endif

+ 677 - 0
components/drivers/sdio/sdhci/include/sdhci.h

@@ -0,0 +1,677 @@
+/*
+ * Copyright (c) 2006-2024 RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author        Notes
+ * 2024-08-16     zhujiale     first version
+ */
+#ifndef __RT_SDHCI_HW_H
+#define __RT_SDHCI_HW_H
+
+#include "sdhci_host.h"
+#include "sdhci_misc.h"
+#include "sdhci-platform.h"
+#include <drivers/mmcsd_cmd.h>
+#include <drivers/dev_mmcsd_core.h>
+#include <drivers/mmcsd_host.h>
+#include <rtdevice.h>
+
+#define lower_32_bits(n) ((rt_uint32_t)((n) & 0xffffffff))
+#define upper_32_bits(n) ((rt_uint32_t)(((n) >> 16) >> 16))
+
+#define MAX_TUNING_LOOP 40
+/*
+ * Controller registers
+ */
+#define RT_SDHCI_DMA_ADDRESS   0x00
+#define RT_SDHCI_ARGUMENT2     RT_SDHCI_DMA_ADDRESS
+#define RT_SDHCI_32BIT_BLK_CNT RT_SDHCI_DMA_ADDRESS
+
+#define RT_SDHCI_BLOCK_SIZE             0x04
+#define RT_SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
+
+#define RT_SDHCI_BLOCK_COUNT 0x06
+
+#define RT_SDHCI_ARGUMENT 0x08
+
+#define RT_SDHCI_TRANSFER_MODE   0x0C
+#define RT_SDHCI_TRNS_DMA        0x01
+#define RT_SDHCI_TRNS_BLK_CNT_EN 0x02
+#define RT_SDHCI_TRNS_AUTO_CMD12 0x04
+#define RT_SDHCI_TRNS_AUTO_CMD23 0x08
+#define RT_SDHCI_TRNS_AUTO_SEL   0x0C
+#define RT_SDHCI_TRNS_READ       0x10
+#define RT_SDHCI_TRNS_MULTI      0x20
+
+#define RT_SDHCI_COMMAND       0x0E
+#define RT_SDHCI_CMD_RESP_MASK 0x03
+#define RT_SDHCI_CMD_CRC       0x08
+#define RT_SDHCI_CMD_INDEX     0x10
+#define RT_SDHCI_CMD_DATA      0x20
+#define RT_SDHCI_CMD_ABORTCMD  0xC0
+
+#define RT_SDHCI_CMD_RESP_NONE       0x00
+#define RT_SDHCI_CMD_RESP_LONG       0x01
+#define RT_SDHCI_CMD_RESP_SHORT      0x02
+#define RT_SDHCI_CMD_RESP_SHORT_BUSY 0x03
+
+#define RT_SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff))
+#define RT_SDHCI_GET_CMD(c)     ((c >> 8) & 0x3f)
+
+#define RT_SDHCI_RESPONSE 0x10
+
+#define RT_SDHCI_BUFFER 0x20
+
+#define RT_SDHCI_PRESENT_STATE   0x24
+#define RT_SDHCI_CMD_INHIBIT     0x00000001
+#define RT_SDHCI_DATA_INHIBIT    0x00000002
+#define RT_SDHCI_DOING_WRITE     0x00000100
+#define RT_SDHCI_DOING_READ      0x00000200
+#define RT_SDHCI_SPACE_AVAILABLE 0x00000400
+#define RT_SDHCI_DATA_AVAILABLE  0x00000800
+#define RT_SDHCI_CARD_PRESENT    0x00010000
+#define RT_SDHCI_CARD_PRES_SHIFT 16
+#define RT_SDHCI_CD_STABLE       0x00020000
+#define RT_SDHCI_CD_LVL          0x00040000
+#define RT_SDHCI_CD_LVL_SHIFT    18
+#define RT_SDHCI_WRITE_PROTECT   0x00080000
+#define RT_SDHCI_DATA_LVL_MASK   0x00F00000
+#define RT_SDHCI_DATA_LVL_SHIFT  20
+#define RT_SDHCI_DATA_0_LVL_MASK 0x00100000
+#define RT_SDHCI_CMD_LVL         0x01000000
+
+#define RT_SDHCI_HOST_CONTROL    0x28
+#define RT_SDHCI_CTRL_LED        0x01
+#define RT_SDHCI_CTRL_4BITBUS    0x02
+#define RT_SDHCI_CTRL_HISPD      0x04
+#define RT_SDHCI_CTRL_DMA_MASK   0x18
+#define RT_SDHCI_CTRL_SDMA       0x00
+#define RT_SDHCI_CTRL_ADMA1      0x08
+#define RT_SDHCI_CTRL_ADMA32     0x10
+#define RT_SDHCI_CTRL_ADMA64     0x18
+#define RT_SDHCI_CTRL_ADMA3      0x18
+#define RT_SDHCI_CTRL_8BITBUS    0x20
+#define RT_SDHCI_CTRL_CDTEST_INS 0x40
+#define RT_SDHCI_CTRL_CDTEST_EN  0x80
+
+#define RT_SDHCI_POWER_CONTROL 0x29
+#define RT_SDHCI_POWER_ON      0x01
+#define RT_SDHCI_POWER_180     0x0A
+#define RT_SDHCI_POWER_300     0x0C
+#define RT_SDHCI_POWER_330     0x0E
+/*
+ * VDD2 - UHS2 or PCIe/NVMe
+ * VDD2 power on/off and voltage select
+ */
+#define RT_SDHCI_VDD2_POWER_ON  0x10
+#define RT_SDHCI_VDD2_POWER_120 0x80
+#define RT_SDHCI_VDD2_POWER_180 0xA0
+
+#define RT_SDHCI_BLOCK_GAP_CONTROL 0x2A
+
+#define RT_SDHCI_WAKE_UP_CONTROL 0x2B
+#define RT_SDHCI_WAKE_ON_INT     0x01
+#define RT_SDHCI_WAKE_ON_INSERT  0x02
+#define RT_SDHCI_WAKE_ON_REMOVE  0x04
+
+#define RT_SDHCI_CLOCK_CONTROL    0x2C
+#define RT_SDHCI_DIVIDER_SHIFT    8
+#define RT_SDHCI_DIVIDER_HI_SHIFT 6
+#define RT_SDHCI_DIV_MASK         0xFF
+#define RT_SDHCI_DIV_MASK_LEN     8
+#define RT_SDHCI_DIV_HI_MASK      0x300
+#define RT_SDHCI_PROG_CLOCK_MODE  0x0020
+#define RT_SDHCI_CLOCK_CARD_EN    0x0004
+#define RT_SDHCI_CLOCK_PLL_EN     0x0008
+#define RT_SDHCI_CLOCK_INT_STABLE 0x0002
+#define RT_SDHCI_CLOCK_INT_EN     0x0001
+
+#define RT_SDHCI_TIMEOUT_CONTROL 0x2E
+
+#define RT_SDHCI_SOFTWARE_RESET 0x2F
+#define RT_SDHCI_RESET_ALL      0x01
+#define RT_SDHCI_RESET_CMD      0x02
+#define RT_SDHCI_RESET_DATA     0x04
+
+#define RT_SDHCI_INT_STATUS       0x30
+#define RT_SDHCI_INT_ENABLE       0x34
+#define RT_SDHCI_SIGNAL_ENABLE    0x38
+#define RT_SDHCI_INT_RESPONSE     0x00000001
+#define RT_SDHCI_INT_DATA_END     0x00000002
+#define RT_SDHCI_INT_BLK_GAP      0x00000004
+#define RT_SDHCI_INT_DMA_END      0x00000008
+#define RT_SDHCI_INT_SPACE_AVAIL  0x00000010
+#define RT_SDHCI_INT_DATA_AVAIL   0x00000020
+#define RT_SDHCI_INT_CARD_INSERT  0x00000040
+#define RT_SDHCI_INT_CARD_REMOVE  0x00000080
+#define RT_SDHCI_INT_CARD_INT     0x00000100
+#define RT_SDHCI_INT_RETUNE       0x00001000
+#define RT_SDHCI_INT_CQE          0x00004000
+#define RT_SDHCI_INT_ERROR        0x00008000
+#define RT_SDHCI_INT_TIMEOUT      0x00010000
+#define RT_SDHCI_INT_CRC          0x00020000
+#define RT_SDHCI_INT_END_BIT      0x00040000
+#define RT_SDHCI_INT_INDEX        0x00080000
+#define RT_SDHCI_INT_DATA_TIMEOUT 0x00100000
+#define RT_SDHCI_INT_DATA_CRC     0x00200000
+#define RT_SDHCI_INT_DATA_END_BIT 0x00400000
+#define RT_SDHCI_INT_BUS_POWER    0x00800000
+#define RT_SDHCI_INT_AUTO_CMD_ERR 0x01000000
+#define RT_SDHCI_INT_ADMA_ERROR   0x02000000
+
+#define RT_SDHCI_INT_NORMAL_MASK 0x00007FFF
+#define RT_SDHCI_INT_ERROR_MASK  0xFFFF8000
+
+#define RT_SDHCI_INT_CMD_MASK  (RT_SDHCI_INT_RESPONSE | RT_SDHCI_INT_TIMEOUT | RT_SDHCI_INT_CRC | RT_SDHCI_INT_END_BIT | RT_SDHCI_INT_INDEX | RT_SDHCI_INT_AUTO_CMD_ERR)
+#define RT_SDHCI_INT_DATA_MASK (RT_SDHCI_INT_DATA_END | RT_SDHCI_INT_DMA_END | RT_SDHCI_INT_DATA_AVAIL | RT_SDHCI_INT_SPACE_AVAIL | RT_SDHCI_INT_DATA_TIMEOUT | RT_SDHCI_INT_DATA_CRC | RT_SDHCI_INT_DATA_END_BIT | RT_SDHCI_INT_ADMA_ERROR | RT_SDHCI_INT_BLK_GAP)
+#define RT_SDHCI_INT_ALL_MASK  ((unsigned int)-1)
+
+#define RT_SDHCI_CQE_INT_ERR_MASK ( \
+    RT_SDHCI_INT_ADMA_ERROR | RT_SDHCI_INT_BUS_POWER | RT_SDHCI_INT_DATA_END_BIT | RT_SDHCI_INT_DATA_CRC | RT_SDHCI_INT_DATA_TIMEOUT | RT_SDHCI_INT_INDEX | RT_SDHCI_INT_END_BIT | RT_SDHCI_INT_CRC | RT_SDHCI_INT_TIMEOUT)
+
+#define RT_SDHCI_CQE_INT_MASK (RT_SDHCI_CQE_INT_ERR_MASK | RT_SDHCI_INT_CQE)
+
+#define RT_SDHCI_AUTO_CMD_STATUS  0x3C
+#define RT_SDHCI_AUTO_CMD_TIMEOUT 0x00000002
+#define RT_SDHCI_AUTO_CMD_CRC     0x00000004
+#define RT_SDHCI_AUTO_CMD_END_BIT 0x00000008
+#define RT_SDHCI_AUTO_CMD_INDEX   0x00000010
+
+#define RT_SDHCI_HOST_CONTROL2          0x3E
+#define RT_SDHCI_CTRL_UHS_MASK          0x0007
+#define RT_SDHCI_CTRL_UHS_SDR12         0x0000
+#define RT_SDHCI_CTRL_UHS_SDR25         0x0001
+#define RT_SDHCI_CTRL_UHS_SDR50         0x0002
+#define RT_SDHCI_CTRL_UHS_SDR104        0x0003
+#define RT_SDHCI_CTRL_UHS_DDR50         0x0004
+#define RT_SDHCI_CTRL_HS400             0x0005 /* Non-standard */
+#define RT_SDHCI_CTRL_VDD_180           0x0008
+#define RT_SDHCI_CTRL_DRV_TYPE_MASK     0x0030
+#define RT_SDHCI_CTRL_DRV_TYPE_B        0x0000
+#define RT_SDHCI_CTRL_DRV_TYPE_A        0x0010
+#define RT_SDHCI_CTRL_DRV_TYPE_C        0x0020
+#define RT_SDHCI_CTRL_DRV_TYPE_D        0x0030
+#define RT_SDHCI_CTRL_EXEC_TUNING       0x0040
+#define RT_SDHCI_CTRL_TUNED_CLK         0x0080
+#define RT_SDHCI_CMD23_ENABLE           0x0800
+#define RT_SDHCI_CTRL_V4_MODE           0x1000
+#define RT_SDHCI_CTRL_64BIT_ADDR        0x2000
+#define RT_SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
+
+#define RT_SDHCI_CAPABILITIES       0x40
+#define RT_SDHCI_TIMEOUT_CLK_MASK   RT_GENMASK(5, 0)
+#define RT_SDHCI_TIMEOUT_CLK_SHIFT  0
+#define RT_SDHCI_TIMEOUT_CLK_UNIT   0x00000080
+#define RT_SDHCI_CLOCK_BASE_MASK    RT_GENMASK(13, 8)
+#define RT_SDHCI_CLOCK_BASE_SHIFT   8
+#define RT_SDHCI_CLOCK_V3_BASE_MASK RT_GENMASK(15, 8)
+#define RT_SDHCI_MAX_BLOCK_MASK     0x00030000
+#define RT_SDHCI_MAX_BLOCK_SHIFT    16
+#define RT_SDHCI_CAN_DO_8BIT        0x00040000
+#define RT_SDHCI_CAN_DO_ADMA2       0x00080000
+#define RT_SDHCI_CAN_DO_ADMA1       0x00100000
+#define RT_SDHCI_CAN_DO_HISPD       0x00200000
+#define RT_SDHCI_CAN_DO_SDMA        0x00400000
+#define RT_SDHCI_CAN_DO_SUSPEND     0x00800000
+#define RT_SDHCI_CAN_VDD_330        0x01000000
+#define RT_SDHCI_CAN_VDD_300        0x02000000
+#define RT_SDHCI_CAN_VDD_180        0x04000000
+#define RT_SDHCI_CAN_64BIT_V4       0x08000000
+#define RT_SDHCI_CAN_64BIT          0x10000000
+
+#define RT_SDHCI_CAPABILITIES_1            0x44
+#define RT_SDHCI_SUPPORT_SDR50             0x00000001
+#define RT_SDHCI_SUPPORT_SDR104            0x00000002
+#define RT_SDHCI_SUPPORT_DDR50             0x00000004
+#define RT_SDHCI_DRIVER_TYPE_A             0x00000010
+#define RT_SDHCI_DRIVER_TYPE_C             0x00000020
+#define RT_SDHCI_DRIVER_TYPE_D             0x00000040
+#define RT_SDHCI_RETUNING_TIMER_COUNT_MASK RT_GENMASK(11, 8)
+#define RT_SDHCI_USE_SDR50_TUNING          0x00002000
+#define RT_SDHCI_RETUNING_MODE_MASK        RT_GENMASK(15, 14)
+#define RT_SDHCI_CLOCK_MUL_MASK            RT_GENMASK(23, 16)
+#define RT_SDHCI_CAN_DO_ADMA3              0x08000000
+#define RT_SDHCI_SUPPORT_HS400             0x80000000 /* Non-standard */
+
+#define RT_SDHCI_MAX_CURRENT            0x48
+#define RT_SDHCI_MAX_CURRENT_LIMIT      RT_GENMASK(7, 0)
+#define RT_SDHCI_MAX_CURRENT_330_MASK   RT_GENMASK(7, 0)
+#define RT_SDHCI_MAX_CURRENT_300_MASK   RT_GENMASK(15, 8)
+#define RT_SDHCI_MAX_CURRENT_180_MASK   RT_GENMASK(23, 16)
+#define RT_SDHCI_MAX_CURRENT_MULTIPLIER 4
+
+/* 4C-4F reserved for more max current */
+
+#define RT_SDHCI_SET_ACMD12_ERROR 0x50
+#define RT_SDHCI_SET_INT_ERROR    0x52
+
+#define RT_SDHCI_ADMA_ERROR 0x54
+
+/* 55-57 reserved */
+
+#define RT_SDHCI_ADMA_ADDRESS    0x58
+#define RT_SDHCI_ADMA_ADDRESS_HI 0x5C
+
+/* 60-FB reserved */
+
+#define RT_SDHCI_PRESET_FOR_HIGH_SPEED 0x64
+#define RT_SDHCI_PRESET_FOR_SDR12      0x66
+#define RT_SDHCI_PRESET_FOR_SDR25      0x68
+#define RT_SDHCI_PRESET_FOR_SDR50      0x6A
+#define RT_SDHCI_PRESET_FOR_SDR104     0x6C
+#define RT_SDHCI_PRESET_FOR_DDR50      0x6E
+#define RT_SDHCI_PRESET_FOR_HS400      0x74 /* Non-standard */
+#define RT_SDHCI_PRESET_DRV_MASK       RT_GENMASK(15, 14)
+#define BIT(nr)                     ((1) << (nr))
+
+#define RT_SDHCI_PRESET_CLKGEN_SEL      BIT(10)
+#define RT_SDHCI_PRESET_SDCLK_FREQ_MASK RT_GENMASK(9, 0)
+
+#define RT_SDHCI_SLOT_INT_STATUS 0xFC
+
+#define RT_SDHCI_HOST_VERSION     0xFE
+#define RT_SDHCI_VENDOR_VER_MASK  0xFF00
+#define RT_SDHCI_VENDOR_VER_SHIFT 8
+#define RT_SDHCI_SPEC_VER_MASK    0x00FF
+#define RT_SDHCI_SPEC_VER_SHIFT   0
+#define RT_SDHCI_SPEC_100         0
+#define RT_SDHCI_SPEC_200         1
+#define RT_SDHCI_SPEC_300         2
+#define RT_SDHCI_SPEC_400         3
+#define RT_SDHCI_SPEC_410         4
+#define RT_SDHCI_SPEC_420         5
+
+/*
+ * End of controller registers.
+ */
+
+#define RT_SDHCI_MAX_DIV_SPEC_200 256
+#define RT_SDHCI_MAX_DIV_SPEC_300 2046
+
+/*
+ * Host SDMA buffer boundary. Valid values from 4K to 512K in powers of 2.
+ */
+#define RT_SDHCI_DEFAULT_BOUNDARY_SIZE (512 * 1024)
+#define ilog2(v)                    __rt_ffs(v)
+#define RT_SDHCI_DEFAULT_BOUNDARY_ARG  (ilog2(RT_SDHCI_DEFAULT_BOUNDARY_SIZE) - 12)
+#define RT_SDHCI_MAX_SEGS 128
+
+/* Allow for a command request and a data request at the same time */
+#define RT_SDHCI_MAX_MRQS 2
+#define MMC_CMD_TRANSFER_TIME (10 * 1000000L) /* max 10 ms */
+
+
+enum rt_sdhci_cookie
+{
+    COOKIE_UNMAPPED,
+    COOKIE_PRE_MAPPED, /* mapped by sdhci_pre_req() */
+    COOKIE_MAPPED,     /* mapped by sdhci_prepare_data() */
+};
+
+struct rt_sdhci_host
+{
+    const char *hw_name; /* Hardware bus name */
+
+    unsigned int quirks; /* Deviations from spec. */
+
+    void *data_buf;
+/* Controller doesn't honor resets unless we touch the clock register */
+#define RT_SDHCI_QUIRK_CLOCK_BEFORE_RESET (1 << 0)
+/* Controller has bad caps bits, but really supports DMA */
+#define RT_SDHCI_QUIRK_FORCE_DMA (1 << 1)
+/* Controller doesn't like to be reset when there is no card inserted. */
+#define RT_SDHCI_QUIRK_NO_CARD_NO_RESET (1 << 2)
+/* Controller doesn't like clearing the power reg before a change */
+#define RT_SDHCI_QUIRK_SINGLE_POWER_WRITE (1 << 3)
+/* Controller has an unusable DMA engine */
+#define RT_SDHCI_QUIRK_BROKEN_DMA (1 << 5)
+/* Controller has an unusable ADMA engine */
+#define RT_SDHCI_QUIRK_BROKEN_ADMA (1 << 6)
+/* Controller can only DMA from 32-bit aligned addresses */
+#define RT_SDHCI_QUIRK_32BIT_DMA_ADDR (1 << 7)
+/* Controller can only DMA chunk sizes that are a multiple of 32 bits */
+#define RT_SDHCI_QUIRK_32BIT_DMA_SIZE (1 << 8)
+/* Controller can only ADMA chunks that are a multiple of 32 bits */
+#define RT_SDHCI_QUIRK_32BIT_ADMA_SIZE (1 << 9)
+/* Controller needs to be reset after each request to stay stable */
+#define RT_SDHCI_QUIRK_RESET_AFTER_REQUEST (1 << 10)
+/* Controller needs voltage and power writes to happen separately */
+#define RT_SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1 << 11)
+/* Controller provides an incorrect timeout value for transfers */
+#define RT_SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1 << 12)
+/* Controller has an issue with buffer bits for small transfers */
+#define RT_SDHCI_QUIRK_BROKEN_SMALL_PIO (1 << 13)
+/* Controller does not provide transfer-complete interrupt when not busy */
+#define RT_SDHCI_QUIRK_NO_BUSY_IRQ (1 << 14)
+/* Controller has unreliable card detection */
+#define RT_SDHCI_QUIRK_BROKEN_CARD_DETECTION (1 << 15)
+/* Controller reports inverted write-protect state */
+#define RT_SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1 << 16)
+/* Controller has unusable command queue engine */
+#define RT_SDHCI_QUIRK_BROKEN_CQE (1 << 17)
+/* Controller does not like fast PIO transfers */
+#define RT_SDHCI_QUIRK_PIO_NEEDS_DELAY (1 << 18)
+/* Controller does not have a LED */
+#define RT_SDHCI_QUIRK_NO_LED (1 << 19)
+/* Controller has to be forced to use block size of 2048 bytes */
+#define RT_SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1 << 20)
+/* Controller cannot do multi-block transfers */
+#define RT_SDHCI_QUIRK_NO_MULTIBLOCK (1 << 21)
+/* Controller can only handle 1-bit data transfers */
+#define RT_SDHCI_QUIRK_FORCE_1_BIT_DATA (1 << 22)
+/* Controller needs 10ms delay between applying power and clock */
+#define RT_SDHCI_QUIRK_DELAY_AFTER_POWER (1 << 23)
+/* Controller uses SDCLK instead of TMCLK for data timeouts */
+#define RT_SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK (1 << 24)
+/* Controller reports wrong base clock capability */
+#define RT_SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1 << 25)
+/* Controller cannot support End Attribute in NOP ADMA descriptor */
+#define RT_SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1 << 26)
+/* Controller uses Auto CMD12 command to stop the transfer */
+#define RT_SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1 << 28)
+/* Controller doesn't have HISPD bit field in HI-SPEED SD card */
+#define RT_SDHCI_QUIRK_NO_HISPD_BIT (1 << 29)
+/* Controller treats ADMA descriptors with length 0000h incorrectly */
+#define RT_SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC (1 << 30)
+/* The read-only detection via RT_SDHCI_PRESENT_STATE register is unstable */
+#define RT_SDHCI_QUIRK_UNSTABLE_RO_DETECT (1 << 31)
+
+    unsigned int quirks2; /* More deviations from spec. */
+
+#define RT_SDHCI_QUIRK2_HOST_OFF_CARD_ON (1 << 0)
+#define RT_SDHCI_QUIRK2_HOST_NO_CMD23    (1 << 1)
+/* The system physically doesn't support 1.8v, even if the host does */
+#define RT_SDHCI_QUIRK2_NO_1_8_V             (1 << 2)
+#define RT_SDHCI_QUIRK2_PRESET_VALUE_BROKEN  (1 << 3)
+#define RT_SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON (1 << 4)
+/* Controller has a non-standard host control register */
+#define RT_SDHCI_QUIRK2_BROKEN_HOST_CONTROL (1 << 5)
+/* Controller does not support HS200 */
+#define RT_SDHCI_QUIRK2_BROKEN_HS200 (1 << 6)
+/* Controller does not support DDR50 */
+#define RT_SDHCI_QUIRK2_BROKEN_DDR50 (1 << 7)
+/* Stop command (CMD12) can set Transfer Complete when not using MMC_RSP_BUSY */
+#define RT_SDHCI_QUIRK2_STOP_WITH_TC (1 << 8)
+/* Controller does not support 64-bit DMA */
+#define RT_SDHCI_QUIRK2_BROKEN_64_BIT_DMA (1 << 9)
+/* need clear transfer mode register before send cmd */
+#define RT_SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD (1 << 10)
+/* Capability register bit-63 indicates HS400 support */
+#define RT_SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 (1 << 11)
+/* forced tuned clock */
+#define RT_SDHCI_QUIRK2_TUNING_WORK_AROUND (1 << 12)
+/* disable the block count for single block transactions */
+#define RT_SDHCI_QUIRK2_SUPPORT_SINGLE (1 << 13)
+/* Controller broken with using ACMD23 */
+#define RT_SDHCI_QUIRK2_ACMD23_BROKEN (1 << 14)
+/* Broken Clock divider zero in controller */
+#define RT_SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN (1 << 15)
+/* Controller has CRC in 136 bit Command Response */
+#define RT_SDHCI_QUIRK2_RSP_136_HAS_CRC (1 << 16)
+
+#define RT_SDHCI_QUIRK2_DISABLE_HW_TIMEOUT (1 << 17)
+
+#define RT_SDHCI_QUIRK2_USE_32BIT_BLK_CNT (1 << 18)
+/* Issue CMD and DATA reset together */
+#define RT_SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER (1 << 19)
+
+    int          irq;           /* Device IRQ */
+    void        *ioaddr;        /* Mapped address */
+    char        *bounce_buffer; /* For packing SDMA reads/writes */
+    rt_uint64_t  bounce_addr;
+    unsigned int bounce_buffer_size;
+
+    const struct rt_sdhci_ops *ops; /* Low level hw interface */
+
+    /* Internal data */
+    struct rt_mmc_host    *mmc;          /* MMC structure */
+    struct mmc_host_ops mmc_host_ops; /* MMC host ops */
+    rt_uint64_t         dma_mask;     /* custom DMA mask */
+
+    rt_spinlock_t lock;
+    int           flags;                             /* Host attributes */
+#define RT_SDHCI_USE_SDMA           (1 << 0)            /* Host is SDMA capable */
+#define RT_SDHCI_USE_ADMA           (1 << 1)            /* Host is ADMA capable */
+#define RT_SDHCI_REQ_USE_DMA        (1 << 2)            /* Use DMA for this req. */
+#define RT_SDHCI_DEVICE_DEAD        (1 << 3)            /* Device unresponsive */
+#define RT_SDHCI_SDR50_NEEDS_TUNING (1 << 4)            /* SDR50 needs tuning */
+#define RT_SDHCI_AUTO_CMD12         (1 << 6)            /* Auto CMD12 support */
+#define RT_SDHCI_AUTO_CMD23         (1 << 7)            /* Auto CMD23 support */
+#define RT_SDHCI_PV_ENABLED         (1 << 8)            /* Preset value enabled */
+#define RT_SDHCI_USE_64_BIT_DMA     (1 << 12)           /* Use 64-bit DMA */
+#define RT_SDHCI_HS400_TUNING       (1 << 13)           /* Tuning for HS400 */
+#define RT_SDHCI_SIGNALING_330      (1 << 14)           /* Host is capable of 3.3V signaling */
+#define RT_SDHCI_SIGNALING_180      (1 << 15)           /* Host is capable of 1.8V signaling */
+#define RT_SDHCI_SIGNALING_120      (1 << 16)           /* Host is capable of 1.2V signaling */
+
+    unsigned int version;                            /* RT_SDHCI spec. version */
+
+    unsigned int max_clk;                            /* Max possible freq (MHz) */
+    unsigned int timeout_clk;                        /* Timeout freq (KHz) */
+    rt_uint8_t   max_timeout_count;                  /* Vendor specific max timeout count */
+    unsigned int clk_mul;                            /* Clock Muliplier value */
+
+    unsigned int clock;                              /* Current clock (MHz) */
+    rt_uint8_t   pwr;                                /* Current voltage */
+    rt_uint8_t   drv_type;                           /* Current UHS-I driver type */
+    rt_bool_t    reinit_uhs;                         /* Force UHS-related re-initialization */
+
+    rt_bool_t runtime_suspended;                     /* Host is runtime suspended */
+    rt_bool_t bus_on;                                /* Bus power prevents runtime suspend */
+    rt_bool_t preset_enabled;                        /* Preset is enabled */
+    rt_bool_t pending_reset;                         /* Cmd/data reset is pending */
+    rt_bool_t irq_wake_enabled;                      /* IRQ wakeup is enabled */
+    rt_bool_t v4_mode;                               /* Host Version 4 Enable */
+    rt_bool_t always_defer_done;                     /* Always defer to complete requests */
+
+    struct rt_mmcsd_req  *mrqs_done[RT_SDHCI_MAX_MRQS]; /* Requests done */
+    struct rt_mmcsd_cmd  *cmd;                       /* Current command */
+    struct rt_mmcsd_cmd  *data_cmd;                  /* Current data command */
+    struct rt_mmcsd_cmd  *deferred_cmd;              /* Deferred command */
+    struct rt_mmcsd_data *data;                      /* Current data request */
+    unsigned int          data_early : 1;            /* Data finished before cmd */
+
+    unsigned int blocks;                /* remaining PIO blocks */
+    size_t align_buffer_sz;             /* Bounce buffer size */
+    rt_uint64_t align_addr;             /* Mapped bounce buffer */
+
+    struct rt_workqueue *complete_wq;   /* Request completion wq */
+    struct rt_work       complete_work; /* Request completion work */
+
+    struct rt_workqueue *irq_wq;
+    struct rt_work       irq_work;
+
+    struct rt_timer timer;                    /* Timer for timeouts */
+    struct rt_timer data_timer;               /* Timer for data timeouts */
+
+    rt_uint32_t caps;                         /* CAPABILITY_0 */
+    rt_uint32_t caps1;                        /* CAPABILITY_1 */
+    rt_bool_t   read_caps;                    /* Capability flags have been read */
+
+    rt_bool_t    sdhci_core_to_disable_vqmmc; /* sdhci core can disable vqmmc */
+    unsigned int ocr_avail_sdio;              /* OCR bit masks */
+    unsigned int ocr_avail_sd;
+    unsigned int ocr_avail_mmc;
+    rt_uint32_t  ocr_mask; /* available voltages */
+
+    unsigned timing;       /* Current timing */
+
+    rt_uint32_t thread_isr;
+
+    /* cached registers */
+    rt_uint32_t ier;
+
+    rt_bool_t   cqe_on;         /* CQE is operating */
+    rt_uint32_t cqe_ier;        /* CQE interrupt mask */
+    rt_uint32_t cqe_err_ier;    /* CQE error interrupt mask */
+
+    rt_wqueue_t  buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */
+    unsigned int tuning_done;   /* Condition flag set when CMD19 succeeds */
+
+    unsigned int tuning_count;  /* Timer count for re-tuning */
+    unsigned int tuning_mode;   /* Re-tuning mode supported by host */
+    unsigned int tuning_err;    /* Error code for re-tuning */
+#define RT_SDHCI_TUNING_MODE_1 0
+#define RT_SDHCI_TUNING_MODE_2 1
+#define RT_SDHCI_TUNING_MODE_3 2
+    /* Delay (ms) between tuning commands */
+    int tuning_delay;
+    int tuning_loop_count;
+
+    /* Host SDMA buffer boundary. */
+    rt_uint32_t sdma_boundary;
+    rt_uint64_t data_timeout;
+
+    unsigned long private[];
+};
+
+static inline rt_uint8_t u8_read(const volatile void *addr)
+{
+    return *(const volatile rt_uint8_t *)addr;
+}
+
+static inline rt_uint16_t u16_read(const volatile void *addr)
+{
+    return *(const volatile rt_uint16_t *)addr;
+}
+
+static inline rt_uint32_t u32_read(const volatile void *addr)
+{
+    return *(const volatile rt_uint32_t *)addr;
+}
+
+static inline void u8_write(rt_uint8_t value, volatile void *addr)
+{
+    *(volatile rt_uint8_t *)addr = value;
+}
+
+static inline void u16_write(rt_uint16_t value, volatile void *addr)
+{
+    *(volatile rt_uint16_t *)addr = value;
+}
+
+static inline void u32_write(rt_uint32_t value, volatile void *addr)
+{
+    *(volatile rt_uint32_t *)addr = value;
+}
+
+#define readb(c)        u8_read(c)
+#define readw(c)        u16_read(c)
+#define readl(c)        u32_read(c)
+#define readsb(p, d, l) ({ __raw_readsb(p,d,l); __iormb(); })
+#define readsw(p, d, l) ({ __raw_readsw(p,d,l); __iormb(); })
+#define readsl(p, d, l) ({ __raw_readsl(p,d,l); __iormb(); })
+
+#define writeb(v, c)     u8_write(v, c)
+#define writew(v, c)     u16_write(v, c)
+#define writel(v, c)     u32_write(v, c)
+#define writesb(p, d, l) ({ __iowmb(); __raw_writesb(p,d,l); })
+#define writesw(p, d, l) ({ __iowmb(); __raw_writesw(p,d,l); })
+#define writesl(p, d, l) ({ __iowmb(); __raw_writesl(p,d,l); })
+
+static inline void rt_sdhci_writel(struct rt_sdhci_host *host, rt_uint32_t val, int reg)
+{
+    writel(val, host->ioaddr + reg);
+}
+
+static inline void rt_sdhci_writew(struct rt_sdhci_host *host, rt_uint16_t val, int reg)
+{
+    writew(val, host->ioaddr + reg);
+}
+
+static inline void rt_sdhci_writeb(struct rt_sdhci_host *host, rt_uint8_t val, int reg)
+{
+    writeb(val, host->ioaddr + reg);
+}
+
+static inline rt_uint32_t rt_sdhci_readl(struct rt_sdhci_host *host, int reg)
+{
+    return readl(host->ioaddr + reg);
+}
+
+static inline rt_uint16_t rt_sdhci_readw(struct rt_sdhci_host *host, int reg)
+{
+    return readw(host->ioaddr + reg);
+}
+
+static inline rt_uint8_t rt_sdhci_readb(struct rt_sdhci_host *host, int reg)
+{
+    return readb(host->ioaddr + reg);
+}
+
+
+struct rt_sdhci_ops
+{
+    void (*set_clock)(struct rt_sdhci_host *host, unsigned int clock);
+    void (*set_power)(struct rt_sdhci_host *host, unsigned char mode,
+                      unsigned short vdd);
+    rt_uint32_t (*irq)(struct rt_sdhci_host *host, rt_uint32_t intmask);
+    int (*set_dma_mask)(struct rt_sdhci_host *host);
+    int (*enable_dma)(struct rt_sdhci_host *host);
+    unsigned int (*get_max_clock)(struct rt_sdhci_host *host);
+    unsigned int (*get_min_clock)(struct rt_sdhci_host *host);
+    unsigned int (*get_timeout_clock)(struct rt_sdhci_host *host);
+    unsigned int (*get_max_timeout_count)(struct rt_sdhci_host *host);
+    void (*set_timeout)(struct rt_sdhci_host   *host,
+                        struct rt_mmcsd_cmd *cmd);
+    void (*set_bus_width)(struct rt_sdhci_host *host, int width);
+    unsigned int (*get_ro)(struct rt_sdhci_host *host);
+    void (*reset)(struct rt_sdhci_host *host, rt_uint8_t mask);
+    int (*platform_execute_tuning)(struct rt_sdhci_host *host, rt_uint32_t opcode);
+    void (*set_uhs_signaling)(struct rt_sdhci_host *host, unsigned int uhs);
+    void (*hw_reset)(struct rt_sdhci_host *host);
+    void (*card_event)(struct rt_sdhci_host *host);
+    void (*voltage_switch)(struct rt_sdhci_host *host);
+    void (*request_done)(struct rt_sdhci_host   *host,
+                         struct rt_mmcsd_req *mrq);
+};
+
+
+struct rt_sdhci_host *rt_sdhci_alloc_host(struct rt_device *dev, size_t priv_size);
+void               rt_sdhci_free_host(struct rt_sdhci_host *host);
+
+static inline void *sdhci_priv(struct rt_sdhci_host *host)
+{
+    return host->private;
+}
+
+void        rt_sdhci_read_caps(struct rt_sdhci_host *host, const rt_uint16_t *ver,
+                       const rt_uint32_t *caps, const rt_uint32_t *caps1);
+int         rt_sdhci_setup_host(struct rt_sdhci_host *host);
+void        rt_sdhci_cleanup_host(struct rt_sdhci_host *host);
+int         rt_sdhci_set_and_add_host(struct rt_sdhci_host *host);
+int         rt_sdhci_init_host(struct rt_sdhci_host *host);
+void        rt_sdhci_uninit_host(struct rt_sdhci_host *host, int dead);
+
+rt_uint16_t rt_sdhci_clk_set(struct rt_sdhci_host *host, unsigned int clock,
+                           unsigned int *actual_clock);
+void        rt_sdhci_set_clock(struct rt_sdhci_host *host, unsigned int clock);
+void        rt_sdhci_clk_enable(struct rt_sdhci_host *host, rt_uint16_t clk);
+void        rt_sdhci_set_power(struct rt_sdhci_host *host, unsigned char mode,unsigned short vdd);
+void        rt_read_reg(struct rt_sdhci_host* host);
+
+void        rt_sdhci_set_power_with_noreg(struct rt_sdhci_host *host, unsigned char mode,
+                                  unsigned short vdd);
+void        rt_sdhci_start_request(struct rt_mmc_host *mmc, struct rt_mmcsd_req *mrq);
+int         rt_sdhci_start_request_atomic(struct rt_mmc_host *mmc, struct rt_mmcsd_req *mrq);
+void        rt_sdhci_set_bus_width(struct rt_sdhci_host *host, int width);
+void        rt_sdhci_reset(struct rt_sdhci_host *host, rt_uint8_t mask);
+void        rt_sdhci_set_uhs(struct rt_sdhci_host *host, unsigned timing);
+int         rt_sdhci_execute_tuning(struct rt_mmc_host *mmc, rt_uint32_t opcode);
+int         __sdhci_execute_tuning(struct rt_sdhci_host *host, rt_uint32_t opcode);
+void        rt_sdhci_ios_set(struct rt_mmc_host *mmc, struct rt_mmcsd_io_cfg *ios);
+int         rt_sdhci_start_signal_voltage_switch(struct rt_mmc_host        *mmc,
+                                              struct rt_mmcsd_io_cfg *ios);
+void        rt_sdhci_enable_io_irq(struct rt_mmc_host *mmc, int enable);
+void        rt_sdhci_start_tuning(struct rt_sdhci_host *host);
+void        rt_sdhci_end_tuning(struct rt_sdhci_host *host);
+void        rt_sdhci_reset_tuning(struct rt_sdhci_host *host);
+void        rt_sdhci_send_tuning(struct rt_sdhci_host *host, rt_uint32_t opcode);
+void        rt_sdhci_abort_tuning(struct rt_sdhci_host *host, rt_uint32_t opcode);
+void        rt_sdhci_data_irq_timeout(struct rt_sdhci_host *host, rt_bool_t enable);
+void        rt_sdhci_timeout_set(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd);
+void        rt_read_reg_debug(struct rt_sdhci_host* host);
+
+#endif /* __RT_SDHCI_HW_H */

+ 345 - 0
components/drivers/sdio/sdhci/include/sdhci_host.h

@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2006-2024 RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author        Notes
+ * 2024-08-16     zhujiale     first version
+ */
+#ifndef __RT_SDHCI_MMC_H__
+#define __RT_SDHCI_MMC_H__
+
+#include <drivers/dev_mmcsd_core.h>
+#include <rtthread.h>
+#include <drivers/mmcsd_cmd.h>
+#include <drivers/dev_mmcsd_core.h>
+#include <drivers/mmcsd_host.h>
+#define mmc_dev(x) ((x)->parent)
+
+#define MMC_SEND_TUNING_BLOCK_HS200 SEND_TUNING_BLOCK_HS200
+#define MMC_SEND_TUNING_BLOCK       SEND_TUNING_BLOCK
+#define MMC_STOP_TRANSMISSION       STOP_TRANSMISSION
+#define MMC_BUS_TEST_R              14 /* adtc                    R1  */
+#define MMC_WRITE_MULTIPLE_BLOCK    WRITE_MULTIPLE_BLOCK
+#define MMC_READ_MULTIPLE_BLOCK     READ_MULTIPLE_BLOCK
+
+#define MMC_TIMING_UHS_DDR50  MMCSD_TIMING_UHS_DDR50
+#define MMC_TIMING_UHS_SDR50  MMCSD_TIMING_UHS_SDR50
+#define MMC_TIMING_MMC_HS200  MMCSD_TIMING_MMC_HS200
+#define MMC_TIMING_MMC_HS400  MMCSD_TIMING_MMC_HS400
+#define MMC_TIMING_UHS_SDR104 MMCSD_TIMING_UHS_SDR104
+#define MMC_TIMING_UHS_SDR25  MMCSD_TIMING_UHS_SDR25
+#define MMC_TIMING_MMC_DDR52  MMCSD_TIMING_MMC_DDR52
+#define MMC_TIMING_UHS_SDR12  MMCSD_TIMING_UHS_SDR12
+#define MMC_TIMING_SD_HS      MMCSD_TIMING_SD_HS
+#define MMC_TIMING_MMC_HS     MMCSD_TIMING_MMC_HS
+
+#define MMC_POWER_OFF       MMCSD_POWER_OFF
+#define MMC_POWER_UP        MMCSD_POWER_UP
+#define MMC_POWER_ON        MMCSD_POWER_ON
+#define MMC_POWER_UNDEFINED 3
+
+#define MMC_SET_DRIVER_TYPE_B 0
+#define MMC_SET_DRIVER_TYPE_A 1
+#define MMC_SET_DRIVER_TYPE_C 2
+#define MMC_SET_DRIVER_TYPE_D 3
+
+#define MMC_SIGNAL_VOLTAGE_330 0
+#define MMC_SIGNAL_VOLTAGE_180 1
+#define MMC_SIGNAL_VOLTAGE_120 2
+
+#define MMC_RSP_PRESENT (1 << 16)
+#define MMC_RSP_136     (1 << 17) /* 136 bit response */
+#define MMC_RSP_CRC     (1 << 18) /* expect valid crc */
+#define MMC_RSP_BUSY    (1 << 19) /* card may send busy */
+#define MMC_RSP_OPCODE  (1 << 20) /* response contains opcode */
+
+#define MMC_RSP_NONE (0)
+#define MMC_RSP_R1   (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
+#define MMC_RSP_R1B  (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE | MMC_RSP_BUSY)
+#define MMC_RSP_R2   (MMC_RSP_PRESENT | MMC_RSP_136 | MMC_RSP_CRC)
+#define MMC_RSP_R3   (MMC_RSP_PRESENT)
+#define MMC_RSP_R4   (MMC_RSP_PRESENT)
+#define MMC_RSP_R5   (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
+#define MMC_RSP_R6   (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
+#define MMC_RSP_R7   (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
+
+#define MMC_CMD_ADTC CMD_ADTC
+
+#define MMC_BUS_WIDTH_8 MMCSD_BUS_WIDTH_8
+#define MMC_BUS_WIDTH_4 MMCSD_BUS_WIDTH_4
+#define MMC_BUS_WIDTH_1 MMCSD_BUS_WIDTH_1
+
+#define MMC_PM_KEEP_POWER    (1 << 0) /* preserve card power during suspend */
+#define MMC_PM_WAKE_SDIO_IRQ (1 << 1) /* wake up host system on SDIO IRQ assertion */
+enum mmc_blk_status
+{
+    MMC_BLK_SUCCESS = 0,
+    MMC_BLK_PARTIAL,
+    MMC_BLK_CMD_ERR,
+    MMC_BLK_RETRY,
+    MMC_BLK_ABORT,
+    MMC_BLK_DATA_ERR,
+    MMC_BLK_ECC_ERR,
+    MMC_BLK_NOMEDIUM,
+    MMC_BLK_NEW_REQUEST,
+};
+
+#define MMC_NUM_CLK_PHASES (MMC_TIMING_MMC_HS400 + 1)
+
+struct rt_mmc_host ;
+
+struct mmc_host_ops
+{
+    void (*request)(struct rt_mmc_host *host, struct rt_mmcsd_req *req);
+    void (*set_ios)(struct rt_mmc_host *host, struct rt_mmcsd_io_cfg *ios);
+    int (*get_ro)(struct rt_mmc_host *host);
+    int (*get_cd)(struct rt_mmc_host *host);
+    void (*enable_sdio_irq)(struct rt_mmc_host *host, int enable);
+    void (*ack_sdio_irq)(struct rt_mmc_host *host);
+    int (*start_signal_voltage_switch)(struct rt_mmc_host *host, struct rt_mmcsd_io_cfg *ios);
+    int (*card_busy)(struct rt_mmc_host *host);
+    int (*execute_tuning)(struct rt_mmc_host *host, unsigned opcode);
+    int (*prepare_hs400_tuning)(struct rt_mmc_host *host, struct rt_mmcsd_io_cfg *ios);
+    int (*hs400_prepare_ddr)(struct rt_mmc_host *host);
+    void (*hs400_downgrade)(struct rt_mmc_host *host);
+    void (*hs400_complete)(struct rt_mmc_host *host);
+    void (*hs400_enhanced_strobe)(struct rt_mmc_host        *host,
+        struct rt_mmcsd_io_cfg* ios);
+    void (*hw_reset)(struct rt_mmc_host* host);
+    void (*card_event)(struct rt_mmc_host* host);
+};
+
+struct regulator;
+struct mmc_pwrseq;
+
+struct mmc_supply
+{
+    struct regulator *vmmc;  /* Card power supply */
+    struct regulator *vqmmc; /* Optional Vccq supply */
+};
+
+struct mmc_ctx
+{
+    struct task_struct *task;
+};
+
+/* VDD voltage 3.3 ~ 3.4 */
+#define MMC_VDD_34_35 0x00400000 /* VDD voltage 3.4 ~ 3.5 */
+#define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */
+
+#define MMC_CAP2_HS200_1_8V_SDR MMCSD_SUP_HS200_1V8
+#define MMC_CAP_4_BIT_DATA      MMCSD_BUSWIDTH_4
+#define MMC_CAP_8_BIT_DATA      MMCSD_BUSWIDTH_8
+#define MMC_CAP2_HS200          MMCSD_SUP_HS200
+#define MMC_CAP_MMC_HIGHSPEED   MMCSD_SUP_HIGHSPEED
+#define MMC_CAP_SD_HIGHSPEED    MMCSD_SUP_HIGHSPEED
+#define MMC_CAP_1_8V_DDR        MMCSD_SUP_DDR_1V8
+#define MMC_CAP_3_3V_DDR        MMCSD_SUP_DDR_3V3
+#define MMC_CAP_1_2V_DDR        MMCSD_SUP_DDR_1V2
+#define MMC_CAP_NONREMOVABLE    MMCSD_SUP_NONREMOVABLE
+
+
+#define MMC_CAP_UHS_DDR50          0
+#define MMC_CAP2_HS400             0
+#define MMC_CAP_UHS_SDR50          0
+#define MMC_CAP_UHS_SDR25          0
+#define MMC_CAP_UHS_SDR12          0
+#define MMC_CAP_UHS_SDR104         0
+#define MMC_CAP_UHS                0
+#define MMC_CAP2_HSX00_1_8V        0
+#define MMC_CAP2_HS400_ES          0
+#define MMC_CAP_NEEDS_POLL         0
+#define MMC_CAP2_HSX00_1_2V        0
+#define MMC_CAP2_HS400_1_8V        0
+#define MMC_CAP_DRIVER_TYPE_D      0
+#define MMC_CAP_DRIVER_TYPE_C      0
+#define MMC_SET_DRIVER_TYPE_B      0
+#define MMC_CAP_DRIVER_TYPE_A      0
+#define MMC_CAP2_SDIO_IRQ_NOTHREAD 0
+#define MMC_CAP_CMD23              0
+#define MMC_CAP_SDIO_IRQ           0
+
+#define MMC_CAP2_NO_SDIO (1 << 19)
+#define MMC_CAP2_NO_SD   (1 << 21)
+#define MMC_CAP2_NO_MMC  (1 << 22)
+#define MMC_CAP2_CQE     (1 << 23)
+
+#define MMC_VDD_165_195 VDD_165_195
+#define MMC_VDD_20_21   VDD_20_21
+#define MMC_VDD_29_30   VDD_29_30
+#define MMC_VDD_30_31   VDD_30_31
+#define MMC_VDD_32_33   VDD_32_33
+#define MMC_VDD_33_34   VDD_33_34
+
+
+struct rt_mmc_host
+{
+    struct rt_mmcsd_host       rthost;
+    struct rt_device          *parent;
+    int                        index;
+    const struct mmc_host_ops *ops;
+    unsigned int               f_min;
+    unsigned int               f_max;
+    unsigned int               f_init;
+    rt_uint32_t                ocr_avail;
+    rt_uint32_t                ocr_avail_sdio; /* SDIO-specific OCR */
+    rt_uint32_t                ocr_avail_sd;   /* SD-specific OCR */
+    rt_uint32_t                ocr_avail_mmc;  /* MMC-specific OCR */
+    struct wakeup_source      *ws;             /* Enable consume of uevents */
+    rt_uint32_t                max_current_330;
+    rt_uint32_t                max_current_300;
+    rt_uint32_t                max_current_180;
+    rt_uint32_t                caps; /* Host capabilities */
+
+    rt_uint32_t caps2;               /* More host capabilities */
+
+
+    /* host specific block data */
+    unsigned int           max_seg_size;     /* see blk_queue_max_segment_size */
+    unsigned short         max_segs;         /* see blk_queue_max_segments */
+    unsigned short         unused;
+    unsigned int           max_req_size;     /* maximum number of bytes in one req */
+    unsigned int           max_blk_size;     /* maximum size of one mmc block */
+    unsigned int           max_blk_count;    /* maximum number of blocks in one req */
+    unsigned int           max_busy_timeout; /* max busy timeout in ms */
+    struct rt_mmcsd_io_cfg ios;              /* current io bus settings */
+    unsigned int           retune_period;
+    /* group bitfields together to minimize padding */
+    unsigned int use_spi_crc        : 1;
+    unsigned int claimed            : 1; /* host exclusively claimed */
+    unsigned int doing_init_tune    : 1; /* initial tuning in progress */
+    unsigned int can_retune         : 1; /* re-tuning can be used */
+    unsigned int doing_retune       : 1; /* re-tuning in progress */
+    unsigned int retune_now         : 1; /* do re-tuning at next req */
+    unsigned int retune_paused      : 1; /* re-tuning is temporarily disabled */
+    unsigned int retune_crc_disable : 1; /* don't trigger retune upon crc */
+    unsigned int can_dma_map_merge  : 1; /* merging can be used */
+    unsigned int vqmmc_enabled      : 1; /* vqmmc regulator is enabled */
+
+    int          need_retune;            /* re-tuning is needed */
+    int          hold_retune;            /* hold off re-tuning */
+    rt_bool_t    trigger_card_event;     /* card_event necessary */
+    unsigned int sdio_irqs;
+    rt_bool_t    sdio_irq_pending;
+
+    struct led_trigger *led; /* activity led */
+
+    struct mmc_supply supply;
+
+
+    /* Ongoing data transfer that allows commands during transfer */
+    struct rt_mmcsd_req *ongoing_mrq;
+
+
+    unsigned int actual_clock; /* Actual HC clock rate */
+    rt_uint32_t  pm_caps;
+    unsigned long private[];
+};
+
+
+static inline int mmc_card_is_removable(struct rt_mmc_host *host)
+{
+    return !(host->caps & MMC_CAP_NONREMOVABLE);
+}
+
+struct device_node;
+struct rt_mmc_host *rt_mmc_alloc_host(int extra, struct rt_device *);
+int              rt_mmc_add_host(struct rt_mmc_host *);
+void             rt_mmc_remove_host(struct rt_mmc_host *);
+void             rt_mmc_free_host(struct rt_mmc_host *);
+int              rt_mmc_of_parse(struct rt_mmc_host *host);
+int              rt_mmc_of_parse_voltage(struct rt_mmc_host *host, rt_uint32_t *mask);
+
+static inline void *mmc_priv(struct rt_mmc_host *host)
+{
+    return (void *)host->private;
+}
+
+
+#define mmc_host_is_spi(host) ((host)->caps & MMC_CAP_SPI)
+
+#define mmc_dev(x)      ((x)->parent)
+#define mmc_classdev(x) (&(x)->class_dev)
+#define mmc_hostname(x) (x->parent->parent.name)
+
+void rt_mmc_detect_change(struct rt_mmc_host *, unsigned long delay);
+void rt_mmc_request_done(struct rt_mmc_host *, struct rt_mmcsd_req *);
+void mmc_command_done(struct rt_mmc_host *host, struct rt_mmcsd_req *mrq);
+
+void mmc_cqe_request_done(struct rt_mmc_host *host, struct rt_mmcsd_req *mrq);
+
+static inline rt_bool_t sdio_irq_claimed(struct rt_mmc_host *host)
+{
+    return host->sdio_irqs > 0;
+}
+
+static inline int mmc_regulator_set_ocr(struct rt_mmc_host  *mmc,
+                                        struct regulator *supply,
+                                        unsigned short    vdd_bit)
+{
+    return 0;
+}
+
+int  mmc_regulator_get_supply(struct rt_mmc_host *mmc);
+int  mmc_regulator_enable_vqmmc(struct rt_mmc_host *mmc);
+void mmc_regulator_disable_vqmmc(struct rt_mmc_host *mmc);
+
+void mmc_retune_timer_stop(struct rt_mmc_host* host);
+
+enum dma_data_direction
+{
+    DMA_BIDIRECTIONAL = 0,
+    DMA_TO_DEVICE     = 1,
+    DMA_FROM_DEVICE   = 2,
+    DMA_NONE          = 3,
+};
+#define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : ((1ULL << (n)) - 1))
+static inline void mmc_retune_needed(struct rt_mmc_host *host)
+{
+    if (host->can_retune)
+        host->need_retune = 1;
+}
+
+static inline rt_bool_t mmc_can_retune(struct rt_mmc_host *host)
+{
+    return host->can_retune == 1;
+}
+
+static inline rt_bool_t mmc_doing_retune(struct rt_mmc_host *host)
+{
+    return host->doing_retune == 1;
+}
+
+static inline rt_bool_t mmc_doing_tune(struct rt_mmc_host *host)
+{
+    return host->doing_retune == 1 || host->doing_init_tune == 1;
+}
+
+static inline int mmc_get_dma_dir(struct rt_mmcsd_data *data)
+{
+    return data->flags & DATA_DIR_WRITE ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+}
+
+static inline rt_bool_t mmc_op_multi(rt_uint32_t opcode)
+{
+    return opcode == MMC_WRITE_MULTIPLE_BLOCK || opcode == MMC_READ_MULTIPLE_BLOCK;
+}
+
+static inline rt_bool_t mmc_op_tuning(rt_uint32_t opcode)
+{
+    return opcode == MMC_SEND_TUNING_BLOCK || opcode == MMC_SEND_TUNING_BLOCK_HS200;
+}
+
+int       rt_mmc_gpio_get_cd(struct rt_mmc_host *host);
+void      rt_mmc_detect_change(struct rt_mmc_host *host, unsigned long delay);
+int       rt_mmc_regulator_set_vqmmc(struct rt_mmc_host *mmc, struct rt_mmcsd_io_cfg *ios);
+rt_bool_t rt_mmc_can_gpio_ro(struct rt_mmc_host *host);
+int       rt_mmc_gpio_get_ro(struct rt_mmc_host *host);
+
+int rt_mmc_send_abort_tuning(struct rt_mmc_host *host, rt_uint32_t opcode);
+int rt_mmc_of_parse(struct rt_mmc_host *host);
+
+
+#endif

+ 70 - 0
components/drivers/sdio/sdhci/include/sdhci_misc.h

@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2006-2024 RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author        Notes
+ * 2024-08-16     zhujiale     first version
+ */
+#ifndef __RT_SDHCI_MISC_H__
+#define __RT_SDHCI_MISC_H__
+
+
+#define __BF_FIELD_CHECK(...)
+#define __bf_shf(x) (__builtin_ffsll(x) - 1)
+#define FIELD_GET(_mask, _reg)                                  \
+    ({                                                          \
+        __BF_FIELD_CHECK(_mask, _reg, 0U, "FIELD_GET: ");       \
+        (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \
+    })
+
+#define FIELD_PREP(_mask, _val)                               \
+    ({                                                        \
+        __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: ");  \
+        ((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \
+    })
+
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+
+#define min_t(type, x, y) (((type)x < (type)y) ? x : y)
+#define max_t(type, x, y) (((type)x > (type)y) ? x : y)
+#define min(x, y)         ((x) < (y) ? (x) : (y))
+
+#define from_timer(var, callback_timer, timer_fieldname) \
+    container_of(callback_timer, typeof(*var), timer_fieldname)
+
+
+#define le32_to_cpu(x)   (x)
+#define le16_to_cpu(x)   (x)
+#define cpu_to_le16(x)   (x)
+#define cpu_to_le32(x)   (x)
+#define lower_32_bits(n) ((rt_uint32_t)((n) & 0xffffffff))
+#define upper_32_bits(n) ((rt_uint32_t)(((n) >> 16) >> 16))
+
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+
+#define do_div(n, base) ({            \
+    uint32_t __base = (base);         \
+    uint32_t __rem;                   \
+    __rem = ((uint64_t)(n)) % __base; \
+    (n)   = ((uint64_t)(n)) / __base; \
+    __rem;                            \
+})
+
+#define fallthrough \
+    do {            \
+    } while (0)
+
+int       regulator_is_supported_voltage(struct regulator *regulator,
+                                         int min_uV, int max_uV);
+rt_bool_t rt_mmc_can_gpio_cd(struct rt_mmc_host *host);
+
+struct regulator
+{
+    const char *supply_name;
+};
+
+int regulator_get_current_limit(struct regulator *regulator);
+
+#endif

+ 125 - 0
components/drivers/sdio/sdhci/sdhci-platform.c

@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2006-2024 RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author        Notes
+ * 2024-08-16     zhujiale     first version
+ */
+#include "sdhci-platform.h"
+
+static const struct rt_sdhci_ops sdhci_pltfm_ops = {
+    .set_clock         = rt_sdhci_set_clock,
+    .set_bus_width     = rt_sdhci_set_bus_width,
+    .reset             = rt_sdhci_reset,
+    .set_uhs_signaling = rt_sdhci_set_uhs,
+};
+
+void rt_sdhci_get_property(struct rt_platform_device *pdev)
+{
+    struct rt_device        *dev        = &pdev->parent;
+    struct rt_sdhci_host       *host       = pdev->priv;
+    struct rt_sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+    rt_uint32_t              bus_width;
+
+    if (rt_dm_dev_prop_read_bool(dev, "sdhci,auto-cmd12"))
+        host->quirks |= RT_SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
+
+    if (rt_dm_dev_prop_read_bool(dev, "sdhci,1-bit-only") || (rt_dm_dev_prop_read_u32(dev, "bus-width", &bus_width) == 0 && bus_width == 1))
+        host->quirks |= RT_SDHCI_QUIRK_FORCE_1_BIT_DATA;
+
+    if (rt_dm_dev_prop_read_bool(dev, "broken-cd"))
+        host->quirks |= RT_SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+
+    if (rt_dm_dev_prop_read_bool(dev, "no-1-8-v"))
+        host->quirks2 |= RT_SDHCI_QUIRK2_NO_1_8_V;
+
+    rt_dm_dev_prop_read_u32(dev, "clock-frequency", &pltfm_host->clock);
+
+    if (rt_dm_dev_prop_read_bool(dev, "keep-power-in-suspend"))
+        host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
+
+    if (rt_dm_dev_prop_read_bool(dev, "wakeup-source") || rt_dm_dev_prop_read_bool(dev, "enable-sdio-wakeup")) /* legacy */
+        host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
+}
+
+struct rt_sdhci_host *rt_sdhci_pltfm_init(struct rt_platform_device     *pdev,
+                                    const struct rt_sdhci_pltfm_data *pdata,
+                                    size_t                         priv_size)
+{
+    struct rt_sdhci_host      *host;
+    struct rt_device       *dev = &pdev->parent;
+    void                   *ioaddr;
+    int                     irq;
+
+    ioaddr = rt_dm_dev_iomap(dev, 0);
+    if (!ioaddr)
+    {
+        return RT_NULL;
+    }
+
+    irq = rt_dm_dev_get_irq(dev, 0);
+    if (irq < 0)
+    {
+        return RT_NULL;
+    }
+    host = rt_sdhci_alloc_host(dev,sizeof(struct rt_sdhci_pltfm_host) + priv_size);
+    if (!host)
+    {
+        return RT_NULL;
+    }
+    host->irq     = irq;
+    host->ioaddr  = ioaddr;
+    host->hw_name = rt_dm_dev_get_name(dev);
+
+    if (pdata && pdata->ops)
+        host->ops = pdata->ops;
+    else
+        host->ops = &sdhci_pltfm_ops;
+    if (pdata)
+    {
+        host->quirks  = pdata->quirks;
+        host->quirks2 = pdata->quirks2;
+    }
+
+    pdev->priv = host;
+
+    return host;
+}
+
+int rt_sdhci_pltfm_init_and_add_host(struct rt_platform_device     *pdev,
+                                  const struct rt_sdhci_pltfm_data *pdata,
+                                  size_t                         priv_size)
+{
+    struct rt_sdhci_host *host;
+    int                ret = 0;
+
+    host = rt_sdhci_pltfm_init(pdev, pdata, priv_size);
+    if (!host)
+        return -RT_ERROR;
+
+    rt_sdhci_get_property(pdev);
+
+    ret = rt_sdhci_init_host(host);
+    if (ret)
+        rt_sdhci_pltfm_free(pdev);
+
+    return ret;
+}
+
+void rt_sdhci_pltfm_free(struct rt_platform_device *pdev)
+{
+    struct rt_sdhci_host *host = pdev->priv;
+
+    rt_sdhci_free_host(host);
+}
+
+void rt_sdhci_pltfm_remove(struct rt_platform_device *pdev)
+{
+    struct rt_sdhci_host *host = pdev->priv;
+    int dead = (readl(host->ioaddr + RT_SDHCI_INT_STATUS) == 0xffffffff);
+
+    rt_sdhci_uninit_host(host, dead);
+    rt_sdhci_pltfm_free(pdev);
+}

+ 3152 - 0
components/drivers/sdio/sdhci/sdhci.c

@@ -0,0 +1,3152 @@
+/*
+ * Copyright (c) 2006-2024 RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author        Notes
+ * 2024-08-16     zhujiale     first version
+ */
+
+#include <rtthread.h>
+#include <mm_aspace.h>
+#include "sdhci.h"
+#include <string.h>
+#define DBG_TAG "RT_SDHCI"
+#ifdef DRV_DEBUG
+#define DBG_LVL DBG_LOG
+#else
+#define DBG_LVL DBG_INFO
+#endif /* DRV_DEBUG */
+#include <rtdbg.h>
+static unsigned int debug_quirks = 0;
+static unsigned int debug_quirks2;
+/********************************************************* */
+/*                        cmd                              */
+/********************************************************* */
+
+
+void rt_read_reg_debug(struct rt_sdhci_host *host)
+{
+    rt_kprintf("0x00 addddddddddddd              = %x \n", rt_sdhci_readl(host, 0x00));
+    rt_kprintf("0x04 EMMC_BLOCKSIZE              = %x \n", rt_sdhci_readw(host, 0x04));
+    rt_kprintf("0x06 EMMC_BLOCKCOUNT             = %x \n", rt_sdhci_readw(host, 0x06));
+    rt_kprintf("0x08 RT_SDHCI_ARGUMENT           = %x \n", rt_sdhci_readl(host, 0x08));
+    rt_kprintf("0x0c EMMC_XFER_MODE              = %x \n", rt_sdhci_readw(host, 0x0c));
+    rt_kprintf("0x0e RT_SDHCI_COMMAND            = %x \n", rt_sdhci_readw(host, 0x0e));
+    rt_kprintf("0x24 RT_SDHCI_PRESENT_STATE      = %x \n", rt_sdhci_readl(host, 0x24));
+    rt_kprintf("0x28 RT_SDHCI_HOST_CONTROL       = %x \n", rt_sdhci_readb(host, 0x28));
+    rt_kprintf("0x29 RT_SDHCI_POWER_CONTROL      = %x \n", rt_sdhci_readb(host, 0x29));
+    rt_kprintf("0x2a EMMC_BGAP_CTRL              = %x \n", rt_sdhci_readb(host, 0x2a));
+    rt_kprintf("0x2c EMMC_CLK_CTRL               = %x \n", rt_sdhci_readw(host, 0x2c));
+    rt_kprintf("0x2e EMMC_TOUT_CTRL              = %x \n", rt_sdhci_readb(host, 0x2e));
+    rt_kprintf("0x2f EMMC_SW_RST                 = %x \n", rt_sdhci_readb(host, 0x2f));
+    rt_kprintf("0x30 RT_SDHCI_INT_STATUS         = %x \n", rt_sdhci_readw(host, 0x30));
+    rt_kprintf("0x32 RT_SDHCI_ERR_INT_STATUS     = %x \n", rt_sdhci_readw(host, 0x32));
+    rt_kprintf("0x34 RT_SDHCI_INT_ENABLE         = %x \n", rt_sdhci_readw(host, 0x34));
+    rt_kprintf("0x36 EMMC ERROR INT STATEN       = %x \n", rt_sdhci_readw(host, 0x36));
+    rt_kprintf("0x38 EMMC NORMAL INT SIGNAL EN   = %x \n", rt_sdhci_readw(host, 0x38));
+    rt_kprintf("0x3a EMMC ERROR INT SIGNAL EN    = %x \n", rt_sdhci_readw(host, 0x3a));
+    rt_kprintf("0x3c EMMC_AUTO_CMD_STAT          = %x \n", rt_sdhci_readw(host, 0x3c));
+    rt_kprintf("0x3e EMMC_HOST_CTRL2             = %x \n", rt_sdhci_readw(host, 0x3e));
+    rt_kprintf("0x40 EMMC_CAPABILITIES1          = %x \n", rt_sdhci_readl(host, 0x40));
+    rt_kprintf("0x44 EMMC_CAPABILITIES2          = %x \n", rt_sdhci_readl(host, 0x44));
+    rt_kprintf("0x52 EMMC_FORC_ERR_INT_STAT      = %x \n", rt_sdhci_readw(host, 0x52));
+    rt_kprintf("0x54 EMMC_ADMA_ERR_STAT          = %x \n", rt_sdhci_readb(host, 0x54));
+    rt_kprintf("0x58 EMMC_ADMA_SA                = %x \n", rt_sdhci_readl(host, 0x58));
+    rt_kprintf("0x66 EMMC_PRESET_SDR12           = %x \n", rt_sdhci_readw(host, 0x66));
+    rt_kprintf("0x68 EMMC_PRESET_SDR25           = %x \n", rt_sdhci_readw(host, 0x68));
+    rt_kprintf("0x6a EMMC_PRESET_SDR50           = %x \n", rt_sdhci_readw(host, 0x6a));
+    rt_kprintf("0x6c EMMC_PRESET_SDR104          = %x \n", rt_sdhci_readw(host, 0x6c));
+    rt_kprintf("0x6e EMMC_PRESET_DDR50           = %x \n", rt_sdhci_readw(host, 0x6e));
+    rt_kprintf("0x78 EMMC_ADMA_ID                = %x \n", rt_sdhci_readl(host, 0x78));
+    rt_kprintf("0xfe EMMC_HOST_CNTRL_VERS        = %x \n", rt_sdhci_readw(host, 0xfe));
+
+}
+static inline rt_bool_t sdhci_has_requests(struct rt_sdhci_host *host)
+{
+    return host->cmd || host->data_cmd;
+}
+
+static inline rt_bool_t sdhci_auto_cmd23(struct rt_sdhci_host   *host,
+                                         struct rt_mmcsd_req *mrq)
+{
+    return mrq->sbc && (host->flags & RT_SDHCI_AUTO_CMD23);
+}
+
+static inline rt_bool_t sdhci_auto_cmd12(struct rt_sdhci_host   *host,
+                                         struct rt_mmcsd_req *mrq)
+{
+    return !mrq->sbc && (host->flags & RT_SDHCI_AUTO_CMD12) && !mrq->cap_cmd_during_tfr;
+}
+
+static inline rt_bool_t sdhci_manual_cmd23(struct rt_sdhci_host   *host,
+                                           struct rt_mmcsd_req *mrq)
+{
+    return mrq->sbc && !(host->flags & RT_SDHCI_AUTO_CMD23);
+}
+
+static inline rt_bool_t sdhci_data_line_cmd(struct rt_mmcsd_cmd *cmd)
+{
+    return cmd->data || cmd->flags & MMC_RSP_BUSY;
+}
+
+void rt_sdhci_data_irq_timeout(struct rt_sdhci_host *host, rt_bool_t enable)
+{
+    if (enable)
+        host->ier |= RT_SDHCI_INT_DATA_TIMEOUT;
+    else
+        host->ier &= ~RT_SDHCI_INT_DATA_TIMEOUT;
+    rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE);
+    rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE);
+}
+
+void rt_sdhci_set_uhs(struct rt_sdhci_host *host, unsigned timing)
+{
+    rt_uint16_t ctrl_2;
+
+    ctrl_2 = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2);
+
+    ctrl_2 &= ~RT_SDHCI_CTRL_UHS_MASK;
+    if ((timing == MMC_TIMING_MMC_HS200) || (timing == MMC_TIMING_UHS_SDR104))
+        ctrl_2 |= RT_SDHCI_CTRL_UHS_SDR104;
+    else if (timing == MMC_TIMING_UHS_SDR12)
+        ctrl_2 |= RT_SDHCI_CTRL_UHS_SDR12;
+    else if (timing == MMC_TIMING_UHS_SDR25)
+        ctrl_2 |= RT_SDHCI_CTRL_UHS_SDR25;
+    else if (timing == MMC_TIMING_UHS_SDR50)
+        ctrl_2 |= RT_SDHCI_CTRL_UHS_SDR50;
+    else if ((timing == MMC_TIMING_UHS_DDR50) || (timing == MMC_TIMING_MMC_DDR52))
+        ctrl_2 |= RT_SDHCI_CTRL_UHS_DDR50;
+    else if (timing == MMC_TIMING_MMC_HS400)
+        ctrl_2 |= RT_SDHCI_CTRL_HS400; /* Non-standard */
+    rt_sdhci_writew(host, ctrl_2, RT_SDHCI_HOST_CONTROL2);
+}
+
+void rt_sdhci_set_bus_width(struct rt_sdhci_host *host, int width)
+{
+    rt_uint8_t ctrl;
+
+    ctrl = rt_sdhci_readb(host, RT_SDHCI_HOST_CONTROL);
+    if (width == MMC_BUS_WIDTH_8)
+    {
+        ctrl &= ~RT_SDHCI_CTRL_4BITBUS;
+        ctrl |= RT_SDHCI_CTRL_8BITBUS;
+    }
+    else
+    {
+        if (host->mmc->caps & MMC_CAP_8_BIT_DATA)
+            ctrl &= ~RT_SDHCI_CTRL_8BITBUS;
+        if (width == MMC_BUS_WIDTH_4)
+            ctrl |= RT_SDHCI_CTRL_4BITBUS;
+        else
+            ctrl &= ~RT_SDHCI_CTRL_4BITBUS;
+    }
+    rt_sdhci_writeb(host, ctrl, RT_SDHCI_HOST_CONTROL);
+}
+
+static inline rt_bool_t sdhci_can_64bit_dma(struct rt_sdhci_host *host)
+{
+    if (host->version >= RT_SDHCI_SPEC_410 && host->v4_mode)
+        return host->caps & RT_SDHCI_CAN_64BIT_V4;
+
+    return host->caps & RT_SDHCI_CAN_64BIT;
+}
+
+static void sdhci_do_enable_v4_mode(struct rt_sdhci_host *host)
+{
+    rt_uint16_t ctrl2;
+
+    ctrl2 = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2);
+    if (ctrl2 & RT_SDHCI_CTRL_V4_MODE)
+        return;
+
+    ctrl2 |= RT_SDHCI_CTRL_V4_MODE;
+    rt_sdhci_writew(host, ctrl2, RT_SDHCI_HOST_CONTROL2);
+}
+
+void rt_sdhci_cleanup_host(struct rt_sdhci_host *host)
+{
+    return;
+}
+
+static void sdhci_set_default_irqs(struct rt_sdhci_host *host)
+{
+    host->ier = RT_SDHCI_INT_BUS_POWER | RT_SDHCI_INT_DATA_END_BIT | RT_SDHCI_INT_DATA_CRC | RT_SDHCI_INT_DATA_TIMEOUT | RT_SDHCI_INT_INDEX | RT_SDHCI_INT_END_BIT | RT_SDHCI_INT_CRC | RT_SDHCI_INT_TIMEOUT | RT_SDHCI_INT_DATA_END | RT_SDHCI_INT_RESPONSE;
+
+    if (host->tuning_mode == RT_SDHCI_TUNING_MODE_2 || host->tuning_mode == RT_SDHCI_TUNING_MODE_3)
+        host->ier |= RT_SDHCI_INT_RETUNE;
+
+    rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE);
+    rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE);
+}
+
+
+static inline void sdhci_auto_cmd_select(struct rt_sdhci_host   *host,
+                                         struct rt_mmcsd_cmd *cmd,
+                                         rt_uint16_t         *mode)
+{
+    rt_bool_t   use_cmd12 = sdhci_auto_cmd12(host, cmd->mrq) && (cmd->cmd_code != SD_IO_RW_EXTENDED);
+    rt_bool_t   use_cmd23 = sdhci_auto_cmd23(host, cmd->mrq);
+    rt_uint16_t ctrl2;
+
+    if (host->version >= RT_SDHCI_SPEC_410 && host->v4_mode && (use_cmd12 || use_cmd23))
+    {
+        *mode |= RT_SDHCI_TRNS_AUTO_SEL;
+
+        ctrl2 = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2);
+        if (use_cmd23)
+            ctrl2 |= RT_SDHCI_CMD23_ENABLE;
+        else
+            ctrl2 &= ~RT_SDHCI_CMD23_ENABLE;
+        rt_sdhci_writew(host, ctrl2, RT_SDHCI_HOST_CONTROL2);
+
+        return;
+    }
+
+    if (use_cmd12)
+        *mode |= RT_SDHCI_TRNS_AUTO_CMD12;
+    else if (use_cmd23)
+        *mode |= RT_SDHCI_TRNS_AUTO_CMD23;
+}
+
+
+static rt_bool_t sdhci_present_error(struct rt_sdhci_host   *host,
+                                     struct rt_mmcsd_cmd *cmd, rt_bool_t present)
+{
+    if (!present || host->flags & RT_SDHCI_DEVICE_DEAD)
+    {
+        cmd->err = -ENOMEDIUM;
+        return RT_TRUE;
+    }
+
+    return RT_FALSE;
+}
+
+static rt_uint16_t sdhci_get_preset_value(struct rt_sdhci_host *host)
+{
+    rt_uint16_t preset = 0;
+
+    switch (host->timing)
+    {
+    case MMC_TIMING_MMC_HS:
+    case MMC_TIMING_SD_HS:
+        preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_HIGH_SPEED);
+        break;
+    case MMC_TIMING_UHS_SDR12:
+        preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_SDR12);
+        break;
+    case MMC_TIMING_UHS_SDR25:
+        preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_SDR25);
+        break;
+    case MMC_TIMING_UHS_SDR50:
+        preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_SDR50);
+        break;
+    case MMC_TIMING_UHS_SDR104:
+    case MMC_TIMING_MMC_HS200:
+        preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_SDR104);
+        break;
+    case MMC_TIMING_UHS_DDR50:
+    case MMC_TIMING_MMC_DDR52:
+        preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_DDR50);
+        break;
+    case MMC_TIMING_MMC_HS400:
+        preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_HS400);
+        break;
+    default:
+        preset = rt_sdhci_readw(host, RT_SDHCI_PRESET_FOR_SDR12);
+        break;
+    }
+    return preset;
+}
+
+static void sdhci_set_card_detection(struct rt_sdhci_host *host, rt_bool_t enable)
+{
+    rt_uint32_t present;
+
+    if ((host->quirks & RT_SDHCI_QUIRK_BROKEN_CARD_DETECTION) || !mmc_card_is_removable(host->mmc))
+        return;
+
+    if (enable)
+    {
+        present = rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE) & RT_SDHCI_CARD_PRESENT;
+
+        host->ier |= present ? RT_SDHCI_INT_CARD_REMOVE : RT_SDHCI_INT_CARD_INSERT;
+    }
+    else
+    {
+        host->ier &= ~(RT_SDHCI_INT_CARD_REMOVE | RT_SDHCI_INT_CARD_INSERT);
+    }
+
+    rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE);
+    rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE);
+}
+
+static void sdhci_enable_card_detection(struct rt_sdhci_host *host)
+{
+    sdhci_set_card_detection(host, RT_TRUE);
+}
+
+/********************************************************* */
+/*                     reset                               */
+/********************************************************* */
+enum sdhci_reset_reason
+{
+    RT_SDHCI_RESET_FOR_INIT,
+    RT_SDHCI_RESET_FOR_REQUEST_ERROR,
+    RT_SDHCI_RESET_FOR_REQUEST_ERROR_DATA_ONLY,
+    RT_SDHCI_RESET_FOR_TUNING_ABORT,
+    RT_SDHCI_RESET_FOR_CARD_REMOVED,
+    RT_SDHCI_RESET_FOR_CQE_RECOVERY,
+};
+
+static rt_bool_t sdhci_needs_reset(struct rt_sdhci_host *host, struct rt_mmcsd_req *mrq)
+{
+    return (!(host->flags & RT_SDHCI_DEVICE_DEAD) && ((mrq->cmd && mrq->cmd->err) || (mrq->sbc && mrq->sbc->err) || (mrq->data && mrq->data->stop && mrq->data->stop->err) || (host->quirks & RT_SDHCI_QUIRK_RESET_AFTER_REQUEST)));
+}
+
+static rt_bool_t sdhci_do_reset(struct rt_sdhci_host *host, rt_uint8_t mask)
+{
+    if (host->quirks & RT_SDHCI_QUIRK_NO_CARD_NO_RESET)
+    {
+        struct rt_mmc_host *mmc = host->mmc;
+
+        if (!mmc->ops->get_cd(mmc))
+            return RT_FALSE;
+    }
+    if (host->ops->reset)
+    {
+        host->ops->reset(host, mask);
+    }
+    return RT_TRUE;
+}
+
+static void sdhci_reset_for_reason(struct rt_sdhci_host *host, enum sdhci_reset_reason reason)
+{
+    if (host->quirks2 & RT_SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER)
+    {
+        sdhci_do_reset(host, RT_SDHCI_RESET_CMD | RT_SDHCI_RESET_DATA);
+        return;
+    }
+
+    switch (reason)
+    {
+    case RT_SDHCI_RESET_FOR_INIT:
+        sdhci_do_reset(host, RT_SDHCI_RESET_CMD | RT_SDHCI_RESET_DATA);
+        break;
+    case RT_SDHCI_RESET_FOR_REQUEST_ERROR:
+    case RT_SDHCI_RESET_FOR_TUNING_ABORT:
+    case RT_SDHCI_RESET_FOR_CARD_REMOVED:
+    case RT_SDHCI_RESET_FOR_CQE_RECOVERY:
+        sdhci_do_reset(host, RT_SDHCI_RESET_CMD);
+        sdhci_do_reset(host, RT_SDHCI_RESET_DATA);
+        break;
+    case RT_SDHCI_RESET_FOR_REQUEST_ERROR_DATA_ONLY:
+        sdhci_do_reset(host, RT_SDHCI_RESET_DATA);
+        break;
+    }
+}
+
+#define sdhci_reset_for(h, r) sdhci_reset_for_reason((h), RT_SDHCI_RESET_FOR_##r)
+
+static void sdhci_reset_for_all(struct rt_sdhci_host *host)
+{
+    if (sdhci_do_reset(host, RT_SDHCI_RESET_ALL))
+    {
+        if (host->flags & (RT_SDHCI_USE_SDMA))
+        {
+            if (host->ops->enable_dma)
+                host->ops->enable_dma(host);
+        }
+        host->preset_enabled = RT_FALSE;
+    }
+}
+
+
+static void sdhci_runtime_pm_bus_on(struct rt_sdhci_host *host)
+{
+    if (host->bus_on)
+        return;
+    host->bus_on = RT_TRUE;
+}
+
+static void sdhci_runtime_pm_bus_off(struct rt_sdhci_host *host)
+{
+    if (!host->bus_on)
+        return;
+    host->bus_on = RT_FALSE;
+}
+
+void rt_sdhci_reset(struct rt_sdhci_host *host, rt_uint8_t mask)
+{
+    ssize_t timeout;
+
+    rt_sdhci_writeb(host, mask, RT_SDHCI_SOFTWARE_RESET);
+
+    if (mask & RT_SDHCI_RESET_ALL)
+    {
+        host->clock = 0;
+        if (host->quirks2 & RT_SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
+            sdhci_runtime_pm_bus_off(host);
+    }
+
+    timeout = rt_tick_from_millisecond(150);
+    while (1)
+    {
+        timeout = timeout - rt_tick_get();
+
+
+        if (!(rt_sdhci_readb(host, RT_SDHCI_SOFTWARE_RESET) & mask))
+            break;
+        if (timeout < 0)
+        {
+            rt_kprintf("%s: Reset 0x%x never completed.\n",
+                       mmc_hostname(host->mmc), (int)mask);
+            rt_read_reg_debug(host);
+            return;
+        }
+        rt_hw_us_delay(10);
+    }
+}
+
+/********************************************************* */
+/*                      data                               */
+/********************************************************* */
+static rt_ubase_t sdhci_sdma_address(struct rt_sdhci_host *host)
+{
+    return (rt_ubase_t)rt_kmem_v2p(host->data->buf);
+}
+
+static void sdhci_set_adma_addr(struct rt_sdhci_host *host, rt_uint32_t addr)
+{
+    rt_sdhci_writel(host, lower_32_bits(addr), RT_SDHCI_ADMA_ADDRESS);
+    if (host->flags & RT_SDHCI_USE_64_BIT_DMA)
+        rt_sdhci_writel(host, upper_32_bits(addr), RT_SDHCI_ADMA_ADDRESS_HI);
+}
+
+static void sdhci_set_sdma_addr(struct rt_sdhci_host *host, rt_uint32_t addr)
+{
+    if (host->v4_mode)
+        sdhci_set_adma_addr(host, addr);
+    else
+        rt_sdhci_writel(host, addr, RT_SDHCI_DMA_ADDRESS);
+}
+
+static void sdhci_config_dma(struct rt_sdhci_host *host)
+{
+    rt_uint8_t  ctrl;
+    rt_uint16_t ctrl2;
+
+    if (host->version < RT_SDHCI_SPEC_200)
+        return;
+
+    ctrl = rt_sdhci_readb(host, RT_SDHCI_HOST_CONTROL);
+
+    ctrl &= ~RT_SDHCI_CTRL_DMA_MASK;
+    if (!(host->flags & RT_SDHCI_REQ_USE_DMA))
+        goto out;
+
+    /* Note if DMA Select is zero then SDMA is selected */
+    if (host->flags & RT_SDHCI_USE_64_BIT_DMA)
+    {
+
+        if (host->v4_mode)
+        {
+            ctrl2  = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2);
+            ctrl2 |= RT_SDHCI_CTRL_64BIT_ADDR;
+            rt_sdhci_writew(host, ctrl2, RT_SDHCI_HOST_CONTROL2);
+        }
+    }
+
+out:
+    rt_sdhci_writeb(host, ctrl, RT_SDHCI_HOST_CONTROL);
+}
+
+static inline void sdhci_set_block_info(struct rt_sdhci_host    *host,
+                                        struct rt_mmcsd_data *data)
+{
+    int boundary;
+    size_t total_size = data->blks * data->blksize;
+
+    if (total_size <= 512)
+        boundary = 0;  /* 4k bytes*/
+    else if (total_size <= 1024)
+        boundary = 1;  /* 8 KB*/
+    else if (total_size <= 2048)
+        boundary = 2;  /* 16 KB*/
+    else if (total_size <= 4096)
+        boundary = 3;  /* 32 KB*/
+    else if (total_size <= 8192)
+        boundary = 4;  /* 64 KB*/
+    else if (total_size <= 16384)
+        boundary = 5;  /* 128 KB*/
+    else if (total_size <= 32768)
+        boundary = 6;  /* 256 KB*/
+    else
+        boundary = 7;  /* 512 KB*/
+    rt_sdhci_writew(host,
+                 RT_SDHCI_MAKE_BLKSZ(boundary, data->blksize),
+                 RT_SDHCI_BLOCK_SIZE);
+
+    if (host->version >= RT_SDHCI_SPEC_410 && host->v4_mode && (host->quirks2 & RT_SDHCI_QUIRK2_USE_32BIT_BLK_CNT))
+    {
+        if (rt_sdhci_readw(host, RT_SDHCI_BLOCK_COUNT))
+            rt_sdhci_writew(host, 0, RT_SDHCI_BLOCK_COUNT);
+        rt_sdhci_writew(host, data->blks, RT_SDHCI_32BIT_BLK_CNT);
+    }
+    else
+    {
+        rt_sdhci_writew(host, data->blks, RT_SDHCI_BLOCK_COUNT);
+    }
+}
+
+static void sdhci_set_transfer_irqs(struct rt_sdhci_host *host)
+{
+    rt_uint32_t pio_irqs = RT_SDHCI_INT_DATA_AVAIL | RT_SDHCI_INT_SPACE_AVAIL;
+    rt_uint32_t dma_irqs = RT_SDHCI_INT_DMA_END;
+
+    if (host->flags & RT_SDHCI_REQ_USE_DMA)
+        host->ier = (host->ier & ~pio_irqs) | dma_irqs;
+    else
+        host->ier = (host->ier & ~dma_irqs) | pio_irqs;
+
+    if (host->flags & (RT_SDHCI_AUTO_CMD23 | RT_SDHCI_AUTO_CMD12))
+        host->ier |= RT_SDHCI_INT_AUTO_CMD_ERR;
+    else
+        host->ier &= ~RT_SDHCI_INT_AUTO_CMD_ERR;
+    rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE);
+    rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE);
+}
+
+static void sdhci_prepare_data(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd)
+{
+    struct rt_mmcsd_data *data = cmd->data;
+
+    LOG_D(data->blksize * data->blks > 524288);
+    LOG_D(data->blksize > host->mmc->max_blk_size);
+    LOG_D(data->blks > 65535);
+
+    host->data               = data;
+    host->data_early         = 0;
+    host->data->bytes_xfered = 0;
+
+    if (host->flags & RT_SDHCI_USE_SDMA)
+    {
+        unsigned int length_mask, offset_mask;
+
+        host->flags |= RT_SDHCI_REQ_USE_DMA;
+
+        length_mask = 0;
+        offset_mask = 0;
+        if (host->quirks & RT_SDHCI_QUIRK_32BIT_DMA_SIZE)
+            length_mask = 3;
+        if (host->quirks & RT_SDHCI_QUIRK_32BIT_DMA_ADDR)
+            offset_mask = 3;
+
+        if ((data->blks * data->blksize) & length_mask)
+        {
+            host->flags &= ~RT_SDHCI_REQ_USE_DMA;
+        }
+        else if ((rt_ubase_t)rt_kmem_v2p(data->buf) & offset_mask)
+        {
+            host->flags &= ~RT_SDHCI_REQ_USE_DMA;
+        }
+    }
+
+    sdhci_config_dma(host);
+
+    if (host->flags & RT_SDHCI_REQ_USE_DMA)
+    {
+        if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE)
+            rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, data->buf, data->blks * data->blksize);
+        else
+            rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, data->buf, data->blks * data->blksize);
+
+        sdhci_set_sdma_addr(host, sdhci_sdma_address(host));
+    }
+
+    if (!(host->flags & RT_SDHCI_REQ_USE_DMA))
+    {
+        host->blocks = data->blks;
+    }
+
+    sdhci_set_transfer_irqs(host);
+
+    sdhci_set_block_info(host, data);
+}
+
+static void sdhci_set_mrq_done(struct rt_sdhci_host *host, struct rt_mmcsd_req *mrq)
+{
+    int i;
+
+    for (i = 0; i < RT_SDHCI_MAX_MRQS; i++)
+    {
+        if (host->mrqs_done[i] == mrq)
+        {
+            LOG_D(1);
+            return;
+        }
+    }
+
+    for (i = 0; i < RT_SDHCI_MAX_MRQS; i++)
+    {
+        if (!host->mrqs_done[i])
+        {
+            host->mrqs_done[i] = mrq;
+            break;
+        }
+    }
+
+    LOG_D(i >= RT_SDHCI_MAX_MRQS);
+}
+
+static inline rt_bool_t sdhci_defer_done(struct rt_sdhci_host   *host,
+                                         struct rt_mmcsd_req *mrq)
+{
+    struct rt_mmcsd_data *data = mrq->data;
+
+    return host->pending_reset || host->always_defer_done || ((host->flags & RT_SDHCI_REQ_USE_DMA) && data && data->host_cookie == COOKIE_MAPPED);
+}
+
+
+/********************************************************* */
+/*                        pio                              */
+/********************************************************* */
+
+static void rt_sdhci_read_block_pio(struct rt_sdhci_host *host,void **buf)
+{
+    rt_uint32_t scratch;
+    size_t      len;
+
+    rt_uint32_t blksize = host->data->blksize;
+    while (blksize)
+    {
+        len = min(4U, blksize);
+
+        scratch = rt_sdhci_readl(host, RT_SDHCI_BUFFER);
+        rt_memcpy(*buf, &scratch, len);
+
+        *buf     += len;
+        blksize -= len;
+    }
+}
+
+static void rt_sdhci_write_block_pio(struct rt_sdhci_host *host,void **buf)
+{
+    size_t      blksize, len;
+    rt_uint32_t scratch;
+    LOG_D("PIO writing\n");
+
+    blksize   = host->data->blksize;
+    scratch   = 0;
+
+    while (blksize)
+    {
+        len = min(4U, blksize);
+        rt_memcpy(&scratch, *buf, len);
+        *buf     += len;
+        blksize -= len;
+        rt_sdhci_writel(host, scratch, RT_SDHCI_BUFFER);
+    }
+}
+
+static void sdhci_transfer_pio(struct rt_sdhci_host *host)
+{
+    rt_uint32_t mask;
+
+    if (host->blocks == 0)
+        return;
+
+    if (host->data->flags & DATA_DIR_READ)
+        mask = RT_SDHCI_DATA_AVAILABLE;
+    else
+        mask = RT_SDHCI_SPACE_AVAILABLE;
+
+    if ((host->quirks & RT_SDHCI_QUIRK_BROKEN_SMALL_PIO) && (host->data->blks == 1))
+    {
+        mask = ~0;
+    }
+    void *buf = (void *)host->data->buf;
+    while (rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE) & mask)
+    {
+        if (host->quirks & RT_SDHCI_QUIRK_PIO_NEEDS_DELAY)
+            rt_hw_us_delay(100);
+
+        if (host->data->flags & DATA_DIR_READ)
+            rt_sdhci_read_block_pio(host,&buf);
+        else
+            rt_sdhci_write_block_pio(host,&buf);
+
+        host->data->blks--;
+        if (host->data->blks == 0)
+            break;
+    }
+}
+
+/********************************************************* */
+/*                   config                                */
+/********************************************************* */
+
+
+static rt_bool_t sdhci_timing_has_preset(unsigned char timing)
+{
+    switch (timing)
+    {
+    case MMC_TIMING_UHS_SDR12:
+    case MMC_TIMING_UHS_SDR25:
+    case MMC_TIMING_UHS_SDR50:
+    case MMC_TIMING_UHS_SDR104:
+    case MMC_TIMING_UHS_DDR50:
+    case MMC_TIMING_MMC_DDR52:
+        return RT_TRUE;
+    }
+    return RT_FALSE;
+}
+
+static rt_bool_t sdhci_preset_needed(struct rt_sdhci_host *host, unsigned char timing)
+{
+    return !(host->quirks2 & RT_SDHCI_QUIRK2_PRESET_VALUE_BROKEN) && sdhci_timing_has_preset(timing);
+}
+
+static rt_bool_t sdhci_presetable_values_change(struct rt_sdhci_host *host, struct rt_mmcsd_io_cfg *ios)
+{
+    return !host->preset_enabled && (sdhci_preset_needed(host, ios->timing) || host->drv_type != ios->drv_type);
+}
+
+
+static void sdhci_preset_value_enable(struct rt_sdhci_host *host, rt_bool_t enable)
+{
+    if (host->version < RT_SDHCI_SPEC_300)
+        return;
+
+    if (host->preset_enabled != enable)
+    {
+        rt_uint16_t ctrl = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2);
+
+        if (enable)
+            ctrl |= RT_SDHCI_CTRL_PRESET_VAL_ENABLE;
+        else
+            ctrl &= ~RT_SDHCI_CTRL_PRESET_VAL_ENABLE;
+
+        rt_sdhci_writew(host, ctrl, RT_SDHCI_HOST_CONTROL2);
+
+        if (enable)
+            host->flags |= RT_SDHCI_PV_ENABLED;
+        else
+            host->flags &= ~RT_SDHCI_PV_ENABLED;
+
+        host->preset_enabled = enable;
+    }
+}
+
+static void sdhci_set_power_reg(struct rt_sdhci_host *host, unsigned char mode,
+                                unsigned short vdd)
+{
+    struct rt_mmc_host *mmc = host->mmc;
+
+    mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
+
+    if (mode != MMC_POWER_OFF)
+        rt_sdhci_writeb(host, RT_SDHCI_POWER_ON, RT_SDHCI_POWER_CONTROL);
+    else
+        rt_sdhci_writeb(host, 0, RT_SDHCI_POWER_CONTROL);
+}
+
+void rt_sdhci_set_power_with_noreg(struct rt_sdhci_host *host, unsigned char mode,
+                           unsigned short vdd)
+{
+    rt_uint8_t pwr = 0;
+
+    if (mode != MMC_POWER_OFF)
+    {
+        switch (1 << vdd)
+        {
+        case MMC_VDD_165_195:
+        case MMC_VDD_20_21:
+            pwr = RT_SDHCI_POWER_180;
+            break;
+        case MMC_VDD_29_30:
+        case MMC_VDD_30_31:
+            pwr = RT_SDHCI_POWER_300;
+            break;
+        case MMC_VDD_32_33:
+        case MMC_VDD_33_34:
+        case MMC_VDD_34_35:
+        case MMC_VDD_35_36:
+            pwr = RT_SDHCI_POWER_330;
+            break;
+        default:
+            break;
+        }
+    }
+
+    if (host->pwr == pwr)
+        return;
+
+    host->pwr = pwr;
+
+    if (pwr == 0)
+    {
+        rt_sdhci_writeb(host, 0, RT_SDHCI_POWER_CONTROL);
+        if (host->quirks2 & RT_SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
+            sdhci_runtime_pm_bus_off(host);
+    }
+    else
+    {
+        if (!(host->quirks & RT_SDHCI_QUIRK_SINGLE_POWER_WRITE))
+            rt_sdhci_writeb(host, 0, RT_SDHCI_POWER_CONTROL);
+
+        if (host->quirks & RT_SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)
+            rt_sdhci_writeb(host, pwr, RT_SDHCI_POWER_CONTROL);
+
+        pwr |= RT_SDHCI_POWER_ON;
+
+        rt_sdhci_writeb(host, pwr, RT_SDHCI_POWER_CONTROL);
+
+        if (host->quirks2 & RT_SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
+            sdhci_runtime_pm_bus_on(host);
+
+        if (host->quirks & RT_SDHCI_QUIRK_DELAY_AFTER_POWER)
+            rt_thread_mdelay(10);
+    }
+}
+
+void rt_sdhci_set_power(struct rt_sdhci_host *host, unsigned char mode,
+                     unsigned short vdd)
+{
+    if (!host->mmc->supply.vmmc)
+        rt_sdhci_set_power_with_noreg(host, mode, vdd);
+    else
+        sdhci_set_power_reg(host, mode, vdd);
+}
+
+
+int rt_sdhci_start_signal_voltage_switch(struct rt_mmc_host        *mmc,
+                                      struct rt_mmcsd_io_cfg *ios)
+{
+    struct rt_sdhci_host *host = mmc_priv(mmc);
+    rt_uint16_t        ctrl;
+    int                ret;
+
+    if (host->version < RT_SDHCI_SPEC_300)
+        return 0;
+
+    ctrl = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2);
+
+    switch (ios->signal_voltage)
+    {
+    case MMC_SIGNAL_VOLTAGE_330:
+        if (!(host->flags & RT_SDHCI_SIGNALING_330))
+            return -EINVAL;
+        ctrl &= ~RT_SDHCI_CTRL_VDD_180;
+        rt_sdhci_writew(host, ctrl, RT_SDHCI_HOST_CONTROL2);
+
+        if (!mmc->supply.vqmmc)
+        {
+            ret = rt_mmc_regulator_set_vqmmc(mmc, ios);
+            if (ret < 0)
+            {
+                return -EIO;
+            }
+        }
+        rt_thread_mdelay(5);
+
+        ctrl = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2);
+        if (!(ctrl & RT_SDHCI_CTRL_VDD_180))
+            return 0;
+        return -EAGAIN;
+    case MMC_SIGNAL_VOLTAGE_180:
+        if (!(host->flags & RT_SDHCI_SIGNALING_180))
+            return -EINVAL;
+        if (!mmc->supply.vqmmc)
+        {
+            ret = rt_mmc_regulator_set_vqmmc(mmc, ios);
+            if (ret < 0)
+            {
+                LOG_D("%s: Switching to 1.8V signalling voltage failed\n",
+                      mmc_hostname(mmc));
+                return -EIO;
+            }
+        }
+
+        ctrl |= RT_SDHCI_CTRL_VDD_180;
+        rt_sdhci_writew(host, ctrl, RT_SDHCI_HOST_CONTROL2);
+
+        if (host->ops->voltage_switch)
+            host->ops->voltage_switch(host);
+
+        ctrl = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2);
+        if (ctrl & RT_SDHCI_CTRL_VDD_180)
+            return 0;
+
+        LOG_D("%s: 1.8V regulator output did not become stable\n",
+              mmc_hostname(mmc));
+
+        return -EAGAIN;
+    case MMC_SIGNAL_VOLTAGE_120:
+        if (!(host->flags & RT_SDHCI_SIGNALING_120))
+            return -EINVAL;
+        if (!mmc->supply.vqmmc)
+        {
+            ret = rt_mmc_regulator_set_vqmmc(mmc, ios);
+            if (ret < 0)
+            {
+                LOG_D("%s: Switching to 1.2V signalling voltage failed\n",
+                      mmc_hostname(mmc));
+                return -EIO;
+            }
+        }
+        return 0;
+    default:
+        return 0;
+    }
+}
+
+
+static int sdhci_get_cd(struct rt_mmc_host *mmc)
+{
+    struct rt_sdhci_host *host    = mmc_priv(mmc);
+    int                gpio_cd = rt_mmc_gpio_get_cd(mmc);
+
+    if (host->flags & RT_SDHCI_DEVICE_DEAD)
+        return 0;
+
+    if (!mmc_card_is_removable(mmc))
+        return 1;
+
+    if (gpio_cd >= 0)
+        return !!gpio_cd;
+
+    if (host->quirks & RT_SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+        return 1;
+
+    return !!(rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE) & RT_SDHCI_CARD_PRESENT);
+}
+
+static int sdhci_check_ro(struct rt_sdhci_host *host)
+{
+    int       is_readonly;
+    rt_base_t flags;
+    flags = rt_spin_lock_irqsave(&host->lock);
+
+    if (host->flags & RT_SDHCI_DEVICE_DEAD)
+        is_readonly = 0;
+    else if (host->ops->get_ro)
+        is_readonly = host->ops->get_ro(host);
+    else if (rt_mmc_can_gpio_ro(host->mmc))
+        is_readonly = rt_mmc_gpio_get_ro(host->mmc);
+    else
+        is_readonly = !(rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE)
+                        & RT_SDHCI_WRITE_PROTECT);
+
+    rt_spin_unlock_irqrestore(&host->lock, flags);
+
+    return host->quirks & RT_SDHCI_QUIRK_INVERTED_WRITE_PROTECT ? !is_readonly : is_readonly;
+}
+
+#define SAMPLE_COUNT 5
+static int rt_sdhci_ro_get(struct rt_mmc_host *mmc)
+{
+    struct rt_sdhci_host *host = mmc_priv(mmc);
+    int                i, ro_count;
+
+    if (!(host->quirks & RT_SDHCI_QUIRK_UNSTABLE_RO_DETECT))
+        return sdhci_check_ro(host);
+
+    ro_count = 0;
+    for (i = 0; i < SAMPLE_COUNT; i++)
+    {
+        if (sdhci_check_ro(host))
+        {
+            if (++ro_count > SAMPLE_COUNT / 2)
+                return 1;
+        }
+        rt_thread_mdelay(30);
+    }
+    return 0;
+}
+
+static void rt_sdhci_enable_io_irq_nolock(struct rt_sdhci_host *host, int enable)
+{
+    if (!(host->flags & RT_SDHCI_DEVICE_DEAD))
+    {
+        if (enable)
+            host->ier |= RT_SDHCI_INT_CARD_INT;
+        else
+            host->ier &= ~RT_SDHCI_INT_CARD_INT;
+
+        rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE);
+        rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE);
+    }
+}
+
+static void sdhci_ack_sdio_irq(struct rt_mmc_host *mmc)
+{
+    rt_base_t          flags;
+    struct rt_sdhci_host *host = mmc_priv(mmc);
+    flags                   = rt_spin_lock_irqsave(&host->lock);
+    rt_sdhci_enable_io_irq_nolock(host, RT_TRUE);
+    rt_spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void sdhci_del_timer(struct rt_sdhci_host *host, struct rt_mmcsd_req *mrq)
+{
+    if (sdhci_data_line_cmd(mrq->cmd))
+        rt_timer_stop(&host->data_timer);
+    else
+        rt_timer_stop(&host->timer);
+}
+
+static unsigned int sdhci_target_timeout(struct rt_sdhci_host    *host,
+                                         struct rt_mmcsd_cmd  *cmd,
+                                         struct rt_mmcsd_data *data)
+{
+    unsigned int target_timeout;
+
+    if (!data)
+    {
+        target_timeout = cmd->busy_timeout * 1000;
+    }
+    else
+    {
+        target_timeout = DIV_ROUND_UP(data->timeout_ns, 1000);
+        if (host->clock && data->timeout_clks)
+        {
+            rt_uint32_t val;
+
+            val = 1000000ULL * data->timeout_clks;
+            if (do_div(val, host->clock))
+                target_timeout++;
+            target_timeout += val;
+        }
+    }
+
+    return target_timeout;
+}
+
+static rt_uint8_t sdhci_calc_timeout(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd,
+                                     rt_bool_t *too_big)
+{
+    rt_uint8_t            count;
+    struct rt_mmcsd_data *data;
+    unsigned              target_timeout, current_timeout;
+
+    *too_big = RT_FALSE;
+
+    if (host->quirks & RT_SDHCI_QUIRK_BROKEN_TIMEOUT_VAL)
+        return host->max_timeout_count;
+
+    if (cmd == NULL)
+        return host->max_timeout_count;
+
+    data = cmd->data;
+    if (!data && !cmd->busy_timeout)
+        return host->max_timeout_count;
+
+    target_timeout = sdhci_target_timeout(host, cmd, data);
+
+    count           = 0;
+    current_timeout = (1 << 13) * 1000 / host->timeout_clk;
+    while (current_timeout < target_timeout)
+    {
+        count++;
+        current_timeout <<= 1;
+        if (count > host->max_timeout_count)
+        {
+            if (!(host->quirks2 & RT_SDHCI_QUIRK2_DISABLE_HW_TIMEOUT))
+                LOG_D("Too large timeout 0x%x requested for CMD%d!\n",
+                      count, cmd->cmd_code);
+            count    = host->max_timeout_count;
+            *too_big = RT_TRUE;
+            break;
+        }
+    }
+
+    return count;
+}
+
+static void sdhci_calc_sw_timeout(struct rt_sdhci_host   *host,
+                                  struct rt_mmcsd_cmd *cmd)
+{
+    struct rt_mmcsd_data   *data      = cmd->data;
+    struct rt_mmc_host        *mmc       = host->mmc;
+    struct rt_mmcsd_io_cfg *ios       = &mmc->ios;
+    unsigned char           bus_width = 1 << ios->bus_width;
+    unsigned int            blksz;
+    unsigned int            freq;
+    rt_uint64_t             target_timeout;
+    rt_uint64_t             transfer_time;
+
+    target_timeout  = sdhci_target_timeout(host, cmd, data);
+    target_timeout *= 1000L;
+
+    if (data)
+    {
+        blksz         = data->blksize;
+        freq          = mmc->actual_clock ?: host->clock;
+        transfer_time = (rt_uint64_t)blksz * 1000000000L * (8 / bus_width);
+        do_div(transfer_time, freq);
+        transfer_time = transfer_time * 2;
+        host->data_timeout = data->blks * target_timeout + transfer_time;
+    }
+    else
+    {
+        host->data_timeout = target_timeout;
+    }
+
+    if (host->data_timeout)
+        host->data_timeout += MMC_CMD_TRANSFER_TIME;
+}
+
+
+void rt_sdhci_timeout_set(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd)
+{
+    rt_bool_t  too_big = RT_FALSE;
+    rt_uint8_t count   = sdhci_calc_timeout(host, cmd, &too_big);
+
+    if (too_big && host->quirks2 & RT_SDHCI_QUIRK2_DISABLE_HW_TIMEOUT)
+    {
+        sdhci_calc_sw_timeout(host, cmd);
+        rt_sdhci_data_irq_timeout(host, RT_FALSE);
+    }
+    else if (!(host->ier & RT_SDHCI_INT_DATA_TIMEOUT))
+    {
+        rt_sdhci_data_irq_timeout(host, RT_FALSE);
+    }
+
+    rt_sdhci_writeb(host, count, RT_SDHCI_TIMEOUT_CONTROL);
+}
+
+static void sdhci_set_timeout(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd)
+{
+    if (host->ops->set_timeout)
+        host->ops->set_timeout(host, cmd);
+    else
+        rt_sdhci_timeout_set(host, cmd);
+}
+
+static void sdhci_start_timer(struct rt_sdhci_host *host, struct rt_mmcsd_req *mrq,
+                            unsigned long timeout)
+{
+    if (sdhci_data_line_cmd(mrq->cmd))
+    {
+        rt_tick_t tick = rt_tick_get();
+
+        if (timeout < tick)
+        {
+            timeout = tick;
+        }
+        tick = timeout - tick;
+
+        rt_timer_stop(&host->data_timer);
+        rt_timer_control(&host->data_timer, RT_TIMER_CTRL_SET_TIME, &tick);
+        rt_timer_start(&host->data_timer);
+    }
+    else
+    {
+        rt_tick_t tick = rt_tick_get();
+
+        if (timeout < tick)
+        {
+            timeout = tick;
+        }
+        tick = timeout - tick;
+
+        rt_timer_stop(&host->timer);
+        rt_timer_control(&host->timer, RT_TIMER_CTRL_SET_TIME, &tick);
+        rt_timer_start(&host->timer);
+    }
+}
+
+static void __sdhci_finish_mrq(struct rt_sdhci_host *host, struct rt_mmcsd_req *mrq)
+{
+    if (host->cmd && host->cmd->mrq == mrq)
+        host->cmd = NULL;
+
+    if (host->data_cmd && host->data_cmd->mrq == mrq)
+        host->data_cmd = NULL;
+
+    if (host->deferred_cmd && host->deferred_cmd->mrq == mrq)
+        host->deferred_cmd = NULL;
+
+    if (host->data && host->data->mrq == mrq)
+        host->data = NULL;
+
+    if (sdhci_needs_reset(host, mrq))
+        host->pending_reset = RT_TRUE;
+
+    sdhci_set_mrq_done(host, mrq);
+
+    sdhci_del_timer(host, mrq);
+}
+
+static void sdhci_finish_mrq(struct rt_sdhci_host *host, struct rt_mmcsd_req *mrq)
+{
+    __sdhci_finish_mrq(host, mrq);
+
+    rt_workqueue_submit_work(host->complete_wq, &host->complete_work, 0);
+}
+
+static void sdhci_error_out_mrqs(struct rt_sdhci_host *host, int err)
+{
+    if (host->data_cmd)
+    {
+        host->data_cmd->err = err;
+        sdhci_finish_mrq(host, host->data_cmd->mrq);
+    }
+
+    if (host->cmd)
+    {
+        host->cmd->err = err;
+        sdhci_finish_mrq(host, host->cmd->mrq);
+    }
+}
+
+static void sdhci_card_event(struct rt_mmc_host *mmc)
+{
+    struct rt_sdhci_host *host = mmc_priv(mmc);
+    rt_uint32_t        flags;
+    int                present;
+
+    if (host->ops->card_event)
+        host->ops->card_event(host);
+
+    present = mmc->ops->get_cd(mmc);
+
+    flags = rt_spin_lock_irqsave(&host->lock);
+
+    if (sdhci_has_requests(host) && !present)
+    {
+        rt_kprintf("%s: Card removed during transfer!\n",
+                   mmc_hostname(mmc));
+        rt_kprintf("%s: Resetting controller.\n",
+                   mmc_hostname(mmc));
+
+        sdhci_do_reset(host, RT_SDHCI_RESET_CMD);
+        sdhci_do_reset(host, RT_SDHCI_RESET_DATA);
+        sdhci_error_out_mrqs(host, -ENOMEDIUM);
+    }
+
+    rt_spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static int sdhci_card_busy(struct rt_mmc_host *mmc)
+{
+    struct rt_sdhci_host *host = mmc_priv(mmc);
+    rt_uint32_t        present_state;
+
+    present_state = rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE);
+
+    return !(present_state & RT_SDHCI_DATA_0_LVL_MASK);
+}
+
+
+static int sdhci_prepare_hs400_tuning(struct rt_mmc_host *mmc, struct rt_mmcsd_io_cfg *ios)
+{
+    struct rt_sdhci_host *host = mmc_priv(mmc);
+    rt_uint32_t        flags;
+
+    flags        = rt_spin_lock_irqsave(&host->lock);
+    host->flags |= RT_SDHCI_HS400_TUNING;
+    rt_spin_unlock_irqrestore(&host->lock, flags);
+
+    return 0;
+}
+
+
+static void sdhci_set_transfer_mode(struct rt_sdhci_host   *host,
+                                    struct rt_mmcsd_cmd *cmd)
+{
+    rt_uint16_t           mode = 0;
+    struct rt_mmcsd_data *data = cmd->data;
+
+    if (data == NULL)
+    {
+        if (host->quirks2 & RT_SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD)
+        {
+            if (!mmc_op_tuning(cmd->cmd_code))
+                rt_sdhci_writew(host, 0x0, RT_SDHCI_TRANSFER_MODE);
+        }
+        else
+        {
+            mode = rt_sdhci_readw(host, RT_SDHCI_TRANSFER_MODE);
+            rt_sdhci_writew(host, mode & ~(RT_SDHCI_TRNS_AUTO_CMD12 | RT_SDHCI_TRNS_AUTO_CMD23), RT_SDHCI_TRANSFER_MODE);
+        }
+        return;
+    }
+
+    if (!(host->quirks2 & RT_SDHCI_QUIRK2_SUPPORT_SINGLE))
+        mode = RT_SDHCI_TRNS_BLK_CNT_EN;
+
+    if (mmc_op_multi(cmd->cmd_code) || data->blks > 1)
+    {
+        mode = RT_SDHCI_TRNS_BLK_CNT_EN | RT_SDHCI_TRNS_MULTI;
+        sdhci_auto_cmd_select(host, cmd, &mode);
+        if (sdhci_auto_cmd23(host, cmd->mrq))
+            rt_sdhci_writel(host, cmd->mrq->sbc->arg, RT_SDHCI_ARGUMENT2);
+    }
+
+    if (data->flags & DATA_DIR_READ)
+        mode |= RT_SDHCI_TRNS_READ;
+    if (host->flags & RT_SDHCI_REQ_USE_DMA)
+        mode |= RT_SDHCI_TRNS_DMA;
+
+    rt_sdhci_writew(host, mode, RT_SDHCI_TRANSFER_MODE);
+}
+
+static rt_bool_t sdhci_send_command(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd)
+{
+    int           flags;
+    rt_uint32_t   mask;
+    unsigned long timeout;
+    cmd->err = 0;
+
+    if ((host->quirks2 & RT_SDHCI_QUIRK2_STOP_WITH_TC) && cmd->cmd_code == MMC_STOP_TRANSMISSION)
+        cmd->flags |= MMC_RSP_BUSY;
+
+    mask = RT_SDHCI_CMD_INHIBIT;
+    if (sdhci_data_line_cmd(cmd))
+        mask |= RT_SDHCI_DATA_INHIBIT;
+
+    if (cmd->mrq->data && (cmd == cmd->mrq->data->stop))
+        mask &= ~RT_SDHCI_DATA_INHIBIT;
+
+    if (rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE) & mask)
+        return RT_FALSE;
+
+    host->cmd          = cmd;
+    host->data_timeout = 0;
+    if (sdhci_data_line_cmd(cmd))
+    {
+        host->data_cmd = cmd;
+        sdhci_set_timeout(host, cmd);
+    }
+
+    if (cmd->data)
+    {
+        sdhci_prepare_data(host, cmd);
+    }
+    rt_sdhci_writel(host, cmd->arg, RT_SDHCI_ARGUMENT);
+
+    sdhci_set_transfer_mode(host, cmd);
+
+    if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY))
+    {
+        cmd->flags &= ~MMC_RSP_BUSY;
+    }
+
+    if (!(cmd->flags & MMC_RSP_PRESENT))
+        flags = RT_SDHCI_CMD_RESP_NONE;
+    else if (cmd->flags & MMC_RSP_136)
+        flags = RT_SDHCI_CMD_RESP_LONG;
+    else if (cmd->flags & MMC_RSP_BUSY)
+        flags = RT_SDHCI_CMD_RESP_SHORT_BUSY;
+    else
+        flags = RT_SDHCI_CMD_RESP_SHORT;
+
+    if (cmd->flags & MMC_RSP_CRC)
+        flags |= RT_SDHCI_CMD_CRC;
+    if (cmd->flags & MMC_RSP_OPCODE)
+        flags |= RT_SDHCI_CMD_INDEX;
+
+    if (cmd->data || mmc_op_tuning(cmd->cmd_code))
+        flags |= RT_SDHCI_CMD_DATA;
+
+    timeout = rt_tick_get();
+    if (host->data_timeout)
+        timeout += rt_tick_from_millisecond(host->data_timeout * 1000);
+    else if (!cmd->data && cmd->busy_timeout > 9000)
+        timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * RT_TICK_PER_SECOND + RT_TICK_PER_SECOND;
+    else
+        timeout += 10 * RT_TICK_PER_SECOND;
+    sdhci_start_timer(host, cmd->mrq, timeout);
+
+    rt_sdhci_writew(host, RT_SDHCI_MAKE_CMD(cmd->cmd_code, flags), RT_SDHCI_COMMAND);
+    return RT_TRUE;
+}
+
+/********************************************************* */
+/*                        dma                              */
+/********************************************************* */
+static void __sdhci_finish_data(struct rt_sdhci_host *host, rt_bool_t sw_data_timeout)
+{
+    struct rt_mmcsd_cmd  *data_cmd = host->data_cmd;
+    struct rt_mmcsd_data *data     = host->data;
+
+    host->data     = NULL;
+    host->data_cmd = NULL;
+
+    if (data->err)
+    {
+        if (!host->cmd || host->cmd == data_cmd)
+            sdhci_reset_for(host, REQUEST_ERROR);
+        else
+            sdhci_reset_for(host, REQUEST_ERROR_DATA_ONLY);
+    }
+
+    if (data->err)
+    {
+        data->bytes_xfered = 0;
+    }
+    else
+    {
+        data->bytes_xfered = data->blksize * data->blks;
+    }
+
+    if (data->stop && ((!data->mrq->sbc && !sdhci_auto_cmd12(host, data->mrq)) || data->err))
+    {
+        if (data->mrq->cap_cmd_during_tfr)
+        {
+            __sdhci_finish_mrq(host, data->mrq);
+        }
+        else
+        {
+            host->cmd = NULL;
+            if (!sdhci_send_command(host, data->stop))
+            {
+                if (sw_data_timeout)
+                {
+                    data->stop->err = -EIO;
+                    __sdhci_finish_mrq(host, data->mrq);
+                }
+                else
+                {
+                    host->deferred_cmd = data->stop;
+                }
+            }
+        }
+    }
+    else
+    {
+        __sdhci_finish_mrq(host, data->mrq);
+    }
+}
+
+static void sdhci_finish_data(struct rt_sdhci_host *host)
+{
+    __sdhci_finish_data(host, RT_FALSE);
+}
+
+
+/********************************************************* */
+/*                     irq                                 */
+/********************************************************* */
+static void sdhci_data_irq(struct rt_sdhci_host *host, rt_uint32_t intmask)
+{
+    rt_uint32_t command;
+
+    if (intmask & RT_SDHCI_INT_DATA_AVAIL && !host->data)
+    {
+        command = RT_SDHCI_GET_CMD(rt_sdhci_readw(host, RT_SDHCI_COMMAND));
+        if (command == MMC_SEND_TUNING_BLOCK || command == MMC_SEND_TUNING_BLOCK_HS200)
+        {
+            host->tuning_done = 1;
+            rt_wqueue_wakeup(&host->buf_ready_int, 0);
+            return;
+        }
+    }
+
+    if (!host->data)
+    {
+        struct rt_mmcsd_cmd *data_cmd = host->data_cmd;
+
+        if (data_cmd && (data_cmd->flags & MMC_RSP_BUSY))
+        {
+            if (intmask & RT_SDHCI_INT_DATA_TIMEOUT)
+            {
+                host->data_cmd = NULL;
+                data_cmd->err  = -ETIMEDOUT;
+                __sdhci_finish_mrq(host, data_cmd->mrq);
+                return;
+            }
+            if (intmask & RT_SDHCI_INT_DATA_END)
+            {
+                host->data_cmd = NULL;
+
+                if (host->cmd == data_cmd)
+                    return;
+
+                __sdhci_finish_mrq(host, data_cmd->mrq);
+                return;
+            }
+        }
+
+
+        if (host->pending_reset)
+            return;
+        rt_kprintf("%s: Got data interrupt 0x%08x even though no data operation was in progress.\n",
+                   mmc_hostname(host->mmc), (unsigned)intmask);
+        rt_read_reg_debug(host);
+
+        return;
+    }
+
+    if (intmask & RT_SDHCI_INT_DATA_TIMEOUT)
+        host->data->err = -ETIMEDOUT;
+    else if (intmask & RT_SDHCI_INT_DATA_END_BIT)
+        host->data->err = -EILSEQ;
+    else if ((intmask & RT_SDHCI_INT_DATA_CRC) && RT_SDHCI_GET_CMD(rt_sdhci_readw(host, RT_SDHCI_COMMAND)) != MMC_BUS_TEST_R)
+    {
+        host->data->err = -EILSEQ;
+    }
+
+
+    if (host->data->err)
+    {
+        sdhci_finish_data(host);
+    }
+    else
+    {
+        if (intmask & (RT_SDHCI_INT_DATA_AVAIL | RT_SDHCI_INT_SPACE_AVAIL))
+            sdhci_transfer_pio(host);
+
+        if (intmask & RT_SDHCI_INT_DMA_END)
+        {
+            rt_uint32_t dmastart, dmanow;
+
+            dmastart = sdhci_sdma_address(host);
+            dmanow   = dmastart + host->data->bytes_xfered;
+            dmanow                   = (dmanow & ~((rt_uint32_t)RT_SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) + RT_SDHCI_DEFAULT_BOUNDARY_SIZE;
+            host->data->bytes_xfered = dmanow - dmastart;
+            LOG_D("DMA base %pad, transferred 0x%06x bytes, next %pad\n",
+                  &dmastart, host->data->bytes_xfered, &dmanow);
+            sdhci_set_sdma_addr(host, dmanow);
+        }
+
+        if (intmask & RT_SDHCI_INT_DATA_END)
+        {
+            struct rt_mmcsd_data *data = host->data;
+            if (data->buf)
+            {
+                if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE)
+                {
+                    rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, data->buf, data->blks * data->blksize);
+                } else {
+                    rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, data->buf, data->blks * data->blksize);
+                }
+            }
+            if (host->cmd == host->data_cmd)
+            {
+                host->data_early = 1;
+            }
+            else
+            {
+                sdhci_finish_data(host);
+            }
+        }
+    }
+}
+
+static void rt_sdhci_read_rsp_136(struct rt_sdhci_host *host, struct rt_mmcsd_cmd *cmd)
+{
+    int i, reg;
+
+    for (i = 0; i < 4; i++)
+    {
+        reg          = RT_SDHCI_RESPONSE + (3 - i) * 4;
+        cmd->resp[i] = rt_sdhci_readl(host, reg);
+    }
+
+    if (host->quirks2 & RT_SDHCI_QUIRK2_RSP_136_HAS_CRC)
+        return;
+
+    for (i = 0; i < 4; i++)
+    {
+        cmd->resp[i] <<= 8;
+        if (i != 3)
+            cmd->resp[i] |= cmd->resp[i + 1] >> 24;
+    }
+}
+
+static void sdhci_command_end(struct rt_sdhci_host *host)
+{
+    struct rt_mmcsd_cmd *cmd = host->cmd;
+
+    host->cmd = NULL;
+
+    if (cmd->flags & MMC_RSP_PRESENT)
+    {
+        if (cmd->flags & MMC_RSP_136)
+        {
+            rt_sdhci_read_rsp_136(host, cmd);
+        }
+        else
+        {
+            cmd->resp[0] = rt_sdhci_readl(host, RT_SDHCI_RESPONSE);
+        }
+    }
+
+    if (cmd->flags & MMC_RSP_BUSY)
+    {
+        if (cmd->data)
+        {
+            LOG_D("Cannot wait for busy signal when also doing a data transfer");
+        }
+        else if (!(host->quirks & RT_SDHCI_QUIRK_NO_BUSY_IRQ) && cmd == host->data_cmd)
+        {
+            return;
+        }
+    }
+
+    if (cmd == cmd->mrq->sbc)
+    {
+        if (!sdhci_send_command(host, cmd->mrq->cmd))
+        {
+            host->deferred_cmd = cmd->mrq->cmd;
+        }
+    }
+    else
+    {
+        if (host->data && host->data_early)
+            sdhci_finish_data(host);
+
+        if (!cmd->data)
+            __sdhci_finish_mrq(host, cmd->mrq);
+    }
+}
+
+
+static void sdhci_cmd_irq(struct rt_sdhci_host *host, rt_uint32_t intmask, rt_uint32_t *intmask_p)
+{
+    if (intmask & RT_SDHCI_INT_AUTO_CMD_ERR && host->data_cmd)
+    {
+        struct rt_mmcsd_req *mrq             = host->data_cmd->mrq;
+        rt_uint16_t          auto_cmd_status = rt_sdhci_readw(host, RT_SDHCI_AUTO_CMD_STATUS);
+        int                  data_err_bit    = (auto_cmd_status & RT_SDHCI_AUTO_CMD_TIMEOUT) ? RT_SDHCI_INT_DATA_TIMEOUT : RT_SDHCI_INT_DATA_CRC;
+
+        if (!mrq->sbc && (host->flags & RT_SDHCI_AUTO_CMD12))
+        {
+            *intmask_p |= data_err_bit;
+            return;
+        }
+    }
+
+    if (!host->cmd)
+    {
+        if (host->pending_reset)
+            return;
+        rt_kprintf("%s: Got command interrupt 0x%08x even though no command operation was in progress.\n",
+                   mmc_hostname(host->mmc), (unsigned)intmask);
+        rt_read_reg_debug(host);
+        return;
+    }
+    if (intmask & (RT_SDHCI_INT_TIMEOUT | RT_SDHCI_INT_CRC | RT_SDHCI_INT_END_BIT | RT_SDHCI_INT_INDEX))
+    {
+        if (intmask & RT_SDHCI_INT_TIMEOUT)
+            host->cmd->err = -ETIMEDOUT;
+        else
+            host->cmd->err = -EILSEQ;
+
+        /* Treat data command CRC error the same as data CRC error */
+        if (host->cmd->data && (intmask & (RT_SDHCI_INT_CRC | RT_SDHCI_INT_TIMEOUT)) == RT_SDHCI_INT_CRC)
+        {
+            host->cmd   = NULL;
+            *intmask_p |= RT_SDHCI_INT_DATA_CRC;
+            return;
+        }
+
+        __sdhci_finish_mrq(host, host->cmd->mrq);
+        return;
+    }
+
+    if (intmask & RT_SDHCI_INT_AUTO_CMD_ERR)
+    {
+        struct rt_mmcsd_req *mrq             = host->cmd->mrq;
+        rt_uint16_t          auto_cmd_status = rt_sdhci_readw(host, RT_SDHCI_AUTO_CMD_STATUS);
+        int                  err             = (auto_cmd_status & RT_SDHCI_AUTO_CMD_TIMEOUT) ? -ETIMEDOUT : -EILSEQ;
+
+        if (mrq->sbc && (host->flags & RT_SDHCI_AUTO_CMD23))
+        {
+            mrq->sbc->err = err;
+            __sdhci_finish_mrq(host, mrq);
+            return;
+        }
+    }
+
+    if (intmask & RT_SDHCI_INT_RESPONSE)
+        sdhci_command_end(host);
+}
+
+static void sdhci_irq(int irq, void *dev_id)
+{
+#define IRQ_NONE 0
+#define IRQ_WAIT 1
+#define IRQ_DONE 2
+
+    struct rt_mmcsd_req* mrqs_done[RT_SDHCI_MAX_MRQS] = { 0 };
+    struct rt_sdhci_host   *host                      = dev_id;
+    rt_uint32_t          intmask, mask, unexpected = 0;
+    int                  max_loops = 16;
+    int                  i, result= IRQ_NONE ;
+    rt_spin_lock(&host->lock);
+
+    if (host->runtime_suspended)
+    {
+        rt_spin_unlock(&host->lock);
+        return;
+    }
+
+    intmask = rt_sdhci_readl(host, RT_SDHCI_INT_STATUS);
+    if (!intmask || intmask == 0xffffffff)
+    {
+        result = IRQ_NONE;
+        goto out;
+    }
+
+    do {
+        LOG_D("IRQ status 0x%08x\n", intmask);
+
+        if (host->ops->irq)
+        {
+            intmask = host->ops->irq(host, intmask);
+            if (!intmask)
+                goto cont;
+        }
+
+        /* Clear selected interrupts. */
+        mask = intmask & (RT_SDHCI_INT_CMD_MASK | RT_SDHCI_INT_DATA_MASK | RT_SDHCI_INT_BUS_POWER);
+        rt_sdhci_writel(host, mask, RT_SDHCI_INT_STATUS);
+
+        if (intmask & (RT_SDHCI_INT_CARD_INSERT | RT_SDHCI_INT_CARD_REMOVE))
+        {
+            rt_uint32_t present = rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE) & RT_SDHCI_CARD_PRESENT;
+
+            host->ier &= ~(RT_SDHCI_INT_CARD_INSERT | RT_SDHCI_INT_CARD_REMOVE);
+            host->ier |= present ? RT_SDHCI_INT_CARD_REMOVE : RT_SDHCI_INT_CARD_INSERT;
+            rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE);
+            rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE);
+
+            rt_sdhci_writel(host, intmask & (RT_SDHCI_INT_CARD_INSERT | RT_SDHCI_INT_CARD_REMOVE), RT_SDHCI_INT_STATUS);
+
+            host->thread_isr |= intmask & (RT_SDHCI_INT_CARD_INSERT | RT_SDHCI_INT_CARD_REMOVE);
+            result            = IRQ_WAIT;
+        }
+
+        if (intmask & RT_SDHCI_INT_CMD_MASK)
+            sdhci_cmd_irq(host, intmask & RT_SDHCI_INT_CMD_MASK, &intmask);
+
+        if (intmask & RT_SDHCI_INT_DATA_MASK)
+            sdhci_data_irq(host, intmask & RT_SDHCI_INT_DATA_MASK);
+
+        if (intmask & RT_SDHCI_INT_BUS_POWER)
+            rt_kprintf("%s: Card is consuming too much power!\n",
+                       mmc_hostname(host->mmc));
+
+        intmask &= ~(RT_SDHCI_INT_CARD_INSERT | RT_SDHCI_INT_CARD_REMOVE | RT_SDHCI_INT_CMD_MASK | RT_SDHCI_INT_DATA_MASK | RT_SDHCI_INT_ERROR | RT_SDHCI_INT_BUS_POWER | RT_SDHCI_INT_RETUNE | RT_SDHCI_INT_CARD_INT);
+
+        if (intmask)
+        {
+            unexpected |= intmask;
+            rt_sdhci_writel(host, intmask, RT_SDHCI_INT_STATUS);
+        }
+    cont:
+        if (result == IRQ_NONE)
+            result = IRQ_WAIT;
+        intmask = rt_sdhci_readl(host, RT_SDHCI_INT_STATUS);
+    } while (intmask && --max_loops);
+
+    for (i = 0; i < RT_SDHCI_MAX_MRQS; i++)
+    {
+        struct rt_mmcsd_req *mrq = host->mrqs_done[i];
+
+        if (!mrq)
+            continue;
+
+        if (sdhci_defer_done(host, mrq))
+        {
+            result = IRQ_WAIT;
+        }
+        else
+        {
+            mrqs_done[i]       = mrq;
+            host->mrqs_done[i] = NULL;
+        }
+    }
+out:
+    if (host->deferred_cmd)
+        result = IRQ_WAIT;
+
+    rt_spin_unlock(&host->lock);
+
+    for (i = 0; i < RT_SDHCI_MAX_MRQS; i++)
+    {
+        if (!mrqs_done[i])
+            continue;
+
+        if (host->ops->request_done)
+            host->ops->request_done(host, mrqs_done[i]);
+        else
+            rt_mmc_request_done(host->mmc, mrqs_done[i]);
+    }
+
+    if (unexpected)
+    {
+        rt_kprintf("%s: Unexpected interrupt 0x%08x.\n",
+               mmc_hostname(host->mmc), unexpected);
+        rt_read_reg_debug(host);
+    }
+
+    if (result == IRQ_WAIT)
+    {
+        rt_workqueue_submit_work(host->irq_wq, &host->irq_work, 0);
+    }
+}
+
+static rt_bool_t sdhci_send_command_retry(struct rt_sdhci_host   *host,
+                                          struct rt_mmcsd_cmd *cmd,
+                                          unsigned long        flags)
+{
+    struct rt_mmcsd_cmd *deferred_cmd = host->deferred_cmd;
+    int                  timeout      = 10; /* Approx. 10 ms */
+    rt_bool_t            present;
+    while (!sdhci_send_command(host, cmd))
+    {
+        if (!timeout--)
+        {
+            rt_kprintf("%s: Controller never released inhibit bit(s).\n",
+                       mmc_hostname(host->mmc));
+            rt_read_reg_debug(host);
+            cmd->err = -EIO;
+            return RT_FALSE;
+        }
+
+        rt_spin_unlock_irqrestore(&host->lock, flags);
+
+        rt_thread_mdelay(1);
+
+        present = host->mmc->ops->get_cd(host->mmc);
+
+        flags = rt_spin_lock_irqsave(&host->lock);
+
+        if (cmd == deferred_cmd && cmd != host->deferred_cmd)
+            return RT_TRUE;
+
+        if (sdhci_present_error(host, cmd, present))
+            return RT_FALSE;
+    }
+
+    if (cmd == host->deferred_cmd)
+        host->deferred_cmd = NULL;
+
+    return RT_TRUE;
+}
+
+static rt_bool_t rt_sdhci_start_request_done(struct rt_sdhci_host *host)
+{
+    rt_base_t            flags;
+    struct rt_mmcsd_req *mrq;
+    int                  i;
+
+    flags = rt_spin_lock_irqsave(&host->lock);
+
+    for (i = 0; i < RT_SDHCI_MAX_MRQS; i++)
+    {
+        mrq = host->mrqs_done[i];
+        if (mrq)
+            break;
+    }
+
+    if (!mrq)
+    {
+        rt_spin_unlock_irqrestore(&host->lock, flags);
+        return RT_TRUE;
+    }
+
+    if (sdhci_needs_reset(host, mrq))
+    {
+        if (host->cmd || host->data_cmd)
+        {
+            rt_spin_unlock_irqrestore(&host->lock, flags);
+            return RT_TRUE;
+        }
+
+        /* Some controllers need this kick or reset won't work here */
+        if (host->quirks & RT_SDHCI_QUIRK_CLOCK_BEFORE_RESET)
+            /* This is to force an update */
+            host->ops->set_clock(host, host->clock);
+
+        sdhci_reset_for(host, REQUEST_ERROR);
+
+        host->pending_reset = RT_FALSE;
+    }
+
+    if (host->flags & RT_SDHCI_REQ_USE_DMA)
+    {
+        struct rt_mmcsd_data *data = mrq->data;
+
+        if (data && data->host_cookie == COOKIE_MAPPED)
+        {
+            if (host->bounce_buffer)
+            {
+                /*
+                 * On reads, copy the bounced data into the
+                 * sglist
+                 */
+                if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE)
+                {
+                    unsigned int length = data->bytes_xfered;
+
+                    if (length > host->bounce_buffer_size)
+                    {
+                        LOG_E("%s: bounce buffer is %u bytes but DMA claims to have transferred %u bytes\n",
+                               mmc_hostname(host->mmc),
+                               host->bounce_buffer_size,
+                               data->bytes_xfered);
+                        /* Cap it down and continue */
+                        length = host->bounce_buffer_size;
+                    }
+                        rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, data->buf, data->blks * data->blksize);
+                } else {
+                    /* No copying, just switch ownership */
+                        rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, data->buf, data->blks * data->blksize);
+                }
+            }
+            data->host_cookie = COOKIE_UNMAPPED;
+        }
+        else {
+                if (mmc_get_dma_dir(data) == DMA_FROM_DEVICE)
+                {
+                    rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, data->buf, data->blks * data->blksize);
+                } else {
+                    /* No copying, just switch ownership */
+                    rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, data->buf, data->blks * data->blksize);
+                }
+        }
+    }
+
+    host->mrqs_done[i] = NULL;
+
+    rt_spin_unlock_irqrestore(&host->lock, flags);
+
+    if (host->ops->request_done)
+        host->ops->request_done(host, mrq);
+    else
+        rt_mmc_request_done(host->mmc, mrq);
+
+    return RT_FALSE;
+}
+
+
+static void sdhci_thread_irq(struct rt_work *work, void *work_data)
+{
+    struct rt_sdhci_host* host = work_data;
+    struct rt_mmcsd_cmd *cmd;
+    rt_base_t            flags;
+    rt_uint32_t          isr;
+
+    while (!rt_sdhci_start_request_done(host));
+
+    flags = rt_spin_lock_irqsave(&host->lock);
+
+    isr              = host->thread_isr;
+    host->thread_isr = 0;
+
+    cmd = host->deferred_cmd;
+    if (cmd && !sdhci_send_command_retry(host, cmd, flags))
+        sdhci_finish_mrq(host, cmd->mrq);
+
+    rt_spin_unlock_irqrestore(&host->lock, flags);
+
+    if (isr & (RT_SDHCI_INT_CARD_INSERT | RT_SDHCI_INT_CARD_REMOVE))
+    {
+        struct rt_mmc_host *mmc = host->mmc;
+
+        mmc->ops->card_event(mmc);
+    }
+}
+
+
+void rt_sdhci_enable_io_irq(struct rt_mmc_host *mmc, int enable)
+{
+    struct rt_sdhci_host *host = mmc_priv(mmc);
+    rt_uint32_t        flags;
+
+    flags = rt_spin_lock_irqsave(&host->lock);
+    rt_sdhci_enable_io_irq_nolock(host, enable);
+    rt_spin_unlock_irqrestore(&host->lock, flags);
+}
+
+
+/********************************************************* */
+/*                     request                             */
+/********************************************************* */
+
+void rt_sdhci_start_request(struct rt_mmc_host *mmc, struct rt_mmcsd_req *mrq)
+{
+    struct rt_sdhci_host   *host = mmc_priv(mmc);
+    struct rt_mmcsd_cmd *cmd;
+    rt_base_t            flags;
+    rt_bool_t            present;
+
+    /* Firstly check card presence */
+    present = mmc->ops->get_cd(mmc);
+
+    flags = rt_spin_lock_irqsave(&host->lock);
+
+    if (sdhci_present_error(host, mrq->cmd, present))
+        goto out_finish;
+
+    cmd = sdhci_manual_cmd23(host, mrq) ? mrq->sbc : mrq->cmd;
+
+    if (!sdhci_send_command_retry(host, cmd, flags))
+        goto out_finish;
+
+    rt_spin_unlock_irqrestore(&host->lock, flags);
+
+    return;
+
+out_finish:
+    sdhci_finish_mrq(host, mrq);
+    rt_spin_unlock_irqrestore(&host->lock, flags);
+}
+
+
+static void sdhci_complete_work(struct rt_work *work, void *work_data)
+{
+    struct rt_sdhci_host *host = work_data;
+
+    while (!rt_sdhci_start_request_done(host));
+}
+
+
+/********************************************************* */
+/*                     timer                               */
+/********************************************************* */
+static void sdhci_timeout_timer(void *parameter)
+{
+    struct rt_sdhci_host *host = parameter;
+    rt_base_t          flags;
+
+    flags = rt_spin_lock_irqsave(&host->lock);
+
+    if (host->cmd && !sdhci_data_line_cmd(host->cmd))
+    {
+        rt_kprintf("%s: Timeout waiting for hardware cmd interrupt.\n",
+                   mmc_hostname(host->mmc));
+        rt_read_reg_debug(host);
+
+        host->cmd->err = -ETIMEDOUT;
+        sdhci_finish_mrq(host, host->cmd->mrq);
+    }
+
+    rt_spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void sdhci_timeout_data_timer(void *parameter)
+{
+    struct rt_sdhci_host *host = parameter;
+    rt_base_t          flags;
+
+    flags = rt_spin_lock_irqsave(&host->lock);
+
+    if (host->data || host->data_cmd || (host->cmd && sdhci_data_line_cmd(host->cmd)))
+    {
+        rt_kprintf("%s: Timeout waiting for hardware interrupt.\n",
+                   mmc_hostname(host->mmc));
+        rt_read_reg_debug(host);
+
+        if (host->data)
+        {
+            host->data->err = -ETIMEDOUT;
+            __sdhci_finish_data(host, RT_TRUE);
+            rt_workqueue_submit_work(host->complete_wq, &host->complete_work, 0);
+        }
+        else if (host->data_cmd)
+        {
+            host->data_cmd->err = -ETIMEDOUT;
+            sdhci_finish_mrq(host, host->data_cmd->mrq);
+        }
+        else
+        {
+            host->cmd->err = -ETIMEDOUT;
+            sdhci_finish_mrq(host, host->cmd->mrq);
+        }
+    }
+
+    rt_spin_unlock_irqrestore(&host->lock, flags);
+}
+
+
+/********************************************************* */
+/*                     tuning                              */
+/********************************************************* */
+int rt_sdhci_execute_tuning(struct rt_mmc_host *mmc, rt_uint32_t opcode)
+{
+    struct rt_sdhci_host *host         = mmc_priv(mmc);
+    int                err          = 0;
+    unsigned int       tuning_count = 0;
+    rt_bool_t          hs400_tuning;
+
+    hs400_tuning = host->flags & RT_SDHCI_HS400_TUNING;
+
+    if (host->tuning_mode == RT_SDHCI_TUNING_MODE_1)
+        tuning_count = host->tuning_count;
+
+    switch (host->timing)
+    {
+    /* HS400 tuning is done in HS200 mode */
+    case MMC_TIMING_MMC_HS400:
+        err = -EINVAL;
+        goto out;
+
+    case MMC_TIMING_MMC_HS200:
+        if (hs400_tuning)
+            tuning_count = 0;
+        break;
+
+    case MMC_TIMING_UHS_SDR104:
+    case MMC_TIMING_UHS_DDR50:
+        break;
+
+    case MMC_TIMING_UHS_SDR50:
+        if (host->flags & RT_SDHCI_SDR50_NEEDS_TUNING)
+            break;
+        fallthrough;
+
+    default:
+        goto out;
+    }
+
+    if (host->ops->platform_execute_tuning)
+    {
+        err = host->ops->platform_execute_tuning(host, opcode);
+        goto out;
+    }
+
+    mmc->retune_period = tuning_count;
+
+    if (host->tuning_delay < 0)
+        host->tuning_delay = opcode == MMC_SEND_TUNING_BLOCK;
+
+    rt_sdhci_start_tuning(host);
+
+    host->tuning_err = __sdhci_execute_tuning(host, opcode);
+
+    rt_sdhci_end_tuning(host);
+out:
+    host->flags &= ~RT_SDHCI_HS400_TUNING;
+
+    return err;
+}
+
+int __sdhci_execute_tuning(struct rt_sdhci_host *host, rt_uint32_t opcode)
+{
+    int i;
+
+    for (i = 0; i < host->tuning_loop_count; i++)
+    {
+        rt_uint16_t ctrl;
+
+        rt_sdhci_send_tuning(host, opcode);
+
+        if (!host->tuning_done)
+        {
+            rt_sdhci_abort_tuning(host, opcode);
+            return -ETIMEDOUT;
+        }
+
+        if (host->tuning_delay > 0)
+            rt_thread_mdelay(host->tuning_delay);
+
+        ctrl = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2);
+        if (!(ctrl & RT_SDHCI_CTRL_EXEC_TUNING))
+        {
+            if (ctrl & RT_SDHCI_CTRL_TUNED_CLK)
+                return 0; /* Success! */
+            break;
+        }
+    }
+
+    LOG_D("%s: Tuning failed, falling back to fixed sampling clock\n",
+          mmc_hostname(host->mmc));
+    rt_sdhci_reset_tuning(host);
+    return -EAGAIN;
+}
+
+void rt_sdhci_start_tuning(struct rt_sdhci_host *host)
+{
+    rt_uint16_t ctrl;
+
+    ctrl  = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2);
+    ctrl |= RT_SDHCI_CTRL_EXEC_TUNING;
+    if (host->quirks2 & RT_SDHCI_QUIRK2_TUNING_WORK_AROUND)
+        ctrl |= RT_SDHCI_CTRL_TUNED_CLK;
+    rt_sdhci_writew(host, ctrl, RT_SDHCI_HOST_CONTROL2);
+
+    rt_sdhci_writel(host, RT_SDHCI_INT_DATA_AVAIL, RT_SDHCI_INT_ENABLE);
+    rt_sdhci_writel(host, RT_SDHCI_INT_DATA_AVAIL, RT_SDHCI_SIGNAL_ENABLE);
+}
+
+void rt_sdhci_end_tuning(struct rt_sdhci_host *host)
+{
+    rt_sdhci_writel(host, host->ier, RT_SDHCI_INT_ENABLE);
+    rt_sdhci_writel(host, host->ier, RT_SDHCI_SIGNAL_ENABLE);
+}
+
+void rt_sdhci_abort_tuning(struct rt_sdhci_host *host, rt_uint32_t opcode)
+{
+    rt_sdhci_reset_tuning(host);
+
+    sdhci_reset_for(host, TUNING_ABORT);
+
+    rt_sdhci_end_tuning(host);
+}
+
+void rt_sdhci_send_tuning(struct rt_sdhci_host *host, rt_uint32_t opcode)
+{
+    struct rt_mmc_host    *mmc = host->mmc;
+    struct rt_mmcsd_cmd cmd = {};
+    struct rt_mmcsd_req mrq = {};
+    unsigned long       flags;
+    rt_uint32_t         b = host->sdma_boundary;
+
+    flags = rt_spin_lock_irqsave(&host->lock);
+
+    cmd.cmd_code = opcode;
+    cmd.flags    = MMC_RSP_R1 | MMC_CMD_ADTC;
+    cmd.mrq      = &mrq;
+
+    mrq.cmd = &cmd;
+
+    if (cmd.cmd_code == MMC_SEND_TUNING_BLOCK_HS200 && mmc->ios.bus_width == MMC_BUS_WIDTH_8)
+        rt_sdhci_writew(host, RT_SDHCI_MAKE_BLKSZ(b, 128), RT_SDHCI_BLOCK_SIZE);
+    else
+        rt_sdhci_writew(host, RT_SDHCI_MAKE_BLKSZ(b, 64), RT_SDHCI_BLOCK_SIZE);
+
+    rt_sdhci_writew(host, RT_SDHCI_TRNS_READ, RT_SDHCI_TRANSFER_MODE);
+
+    if (!sdhci_send_command_retry(host, &cmd, flags))
+    {
+        rt_spin_unlock_irqrestore(&host->lock, flags);
+        host->tuning_done = 0;
+        return;
+    }
+
+    host->cmd = NULL;
+
+    sdhci_del_timer(host, &mrq);
+
+    host->tuning_done = 0;
+
+    rt_spin_unlock_irqrestore(&host->lock, flags);
+}
+
+void rt_sdhci_reset_tuning(struct rt_sdhci_host *host)
+{
+    rt_uint16_t ctrl;
+
+    ctrl  = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2);
+    ctrl &= ~RT_SDHCI_CTRL_TUNED_CLK;
+    ctrl &= ~RT_SDHCI_CTRL_EXEC_TUNING;
+    rt_sdhci_writew(host, ctrl, RT_SDHCI_HOST_CONTROL2);
+}
+
+
+/********************************************************* */
+/*                     error                               */
+/********************************************************* */
+static const struct mmc_host_ops rt_sdhci_ops = {
+    .request                     = rt_sdhci_start_request,
+    .set_ios                     = rt_sdhci_ios_set,
+    .get_cd                      = sdhci_get_cd,
+    .get_ro                      = rt_sdhci_ro_get,
+    .enable_sdio_irq             = rt_sdhci_enable_io_irq,
+    .ack_sdio_irq                = sdhci_ack_sdio_irq,
+    .start_signal_voltage_switch = rt_sdhci_start_signal_voltage_switch,
+    .prepare_hs400_tuning        = sdhci_prepare_hs400_tuning,
+    .execute_tuning              = rt_sdhci_execute_tuning,
+    .card_event                  = sdhci_card_event,
+    .card_busy                   = sdhci_card_busy,
+};
+
+
+void rt_sdhci_uninit_host(struct rt_sdhci_host *host, int dead)
+{
+    struct rt_mmc_host *mmc = host->mmc;
+    unsigned long    flags;
+
+    if (dead)
+    {
+        flags = rt_spin_lock_irqsave(&host->lock);
+
+        host->flags |= RT_SDHCI_DEVICE_DEAD;
+
+        if (sdhci_has_requests(host))
+        {
+            rt_kprintf("%s: Controller removed during "
+                       " transfer!\n",
+                       mmc_hostname(mmc));
+            sdhci_error_out_mrqs(host, -ENOMEDIUM);
+        }
+
+        rt_spin_unlock_irqrestore(&host->lock, flags);
+    }
+
+    sdhci_set_card_detection(host, RT_FALSE);
+
+    rt_mmc_remove_host(mmc);
+
+
+    if (!dead)
+        sdhci_reset_for_all(host);
+
+    rt_sdhci_writel(host, 0, RT_SDHCI_INT_ENABLE);
+    rt_sdhci_writel(host, 0, RT_SDHCI_SIGNAL_ENABLE);
+
+    rt_timer_delete(&host->timer);
+    rt_timer_delete(&host->data_timer);
+
+    rt_workqueue_destroy(host->complete_wq);
+
+}
+
+rt_uint16_t rt_sdhci_clk_set(struct rt_sdhci_host *host, unsigned int clock,
+                           unsigned int *actual_clock)
+{
+    int         div      = 0; /* Initialized for compiler warning */
+    int         real_div = div, clk_mul = 1;
+    rt_uint16_t clk             = 0;
+    rt_bool_t   switch_base_clk = RT_FALSE;
+
+    if (host->version >= RT_SDHCI_SPEC_300)
+    {
+        if (host->preset_enabled)
+        {
+            rt_uint16_t pre_val;
+
+            clk     = rt_sdhci_readw(host, RT_SDHCI_CLOCK_CONTROL);
+            pre_val = sdhci_get_preset_value(host);
+            div     = FIELD_GET(RT_SDHCI_PRESET_SDCLK_FREQ_MASK, pre_val);
+            if (host->clk_mul && (pre_val & RT_SDHCI_PRESET_CLKGEN_SEL))
+            {
+                clk      = RT_SDHCI_PROG_CLOCK_MODE;
+                real_div = div + 1;
+                clk_mul  = host->clk_mul;
+            }
+            else
+            {
+                real_div = max_t(int, 1, div << 1);
+            }
+            goto clock_set;
+        }
+
+        if (host->clk_mul)
+        {
+            for (div = 1; div <= 1024; div++)
+            {
+                if ((host->max_clk * host->clk_mul / div)
+                    <= clock)
+                    break;
+            }
+            if ((host->max_clk * host->clk_mul / div) <= clock)
+            {
+
+                clk      = RT_SDHCI_PROG_CLOCK_MODE;
+                real_div = div;
+                clk_mul  = host->clk_mul;
+                div--;
+            }
+            else
+            {
+
+                switch_base_clk = RT_TRUE;
+            }
+        }
+
+        if (!host->clk_mul || switch_base_clk)
+        {
+            if (host->max_clk <= clock)
+                div = 1;
+            else
+            {
+                for (div  = 2; div < RT_SDHCI_MAX_DIV_SPEC_300;
+                     div += 2)
+                {
+                    if ((host->max_clk / div) <= clock)
+                        break;
+                }
+            }
+            real_div   = div;
+            div      >>= 1;
+            if ((host->quirks2 & RT_SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN)
+                && !div && host->max_clk <= 25000000)
+                div = 1;
+        }
+    }
+    else
+    {
+        for (div = 1; div < RT_SDHCI_MAX_DIV_SPEC_200; div *= 2)
+        {
+            if ((host->max_clk / div) <= clock)
+                break;
+        }
+        real_div   = div;
+        div      >>= 1;
+    }
+
+clock_set:
+    if (real_div)
+        *actual_clock = (host->max_clk * clk_mul) / real_div;
+    clk |= (div & RT_SDHCI_DIV_MASK) << RT_SDHCI_DIVIDER_SHIFT;
+    clk |= ((div & RT_SDHCI_DIV_HI_MASK) >> RT_SDHCI_DIV_MASK_LEN)
+           << RT_SDHCI_DIVIDER_HI_SHIFT;
+
+    return clk;
+}
+
+void rt_sdhci_clk_enable(struct rt_sdhci_host *host, rt_uint16_t clk)
+{
+    long timeout;
+
+    clk |= RT_SDHCI_CLOCK_INT_EN;
+    rt_sdhci_writew(host, clk, RT_SDHCI_CLOCK_CONTROL);
+
+    timeout = rt_tick_from_millisecond(150);
+    while (1)
+    {
+        timeout = timeout - rt_tick_get();
+
+        clk = rt_sdhci_readw(host, RT_SDHCI_CLOCK_CONTROL);
+        if (clk & RT_SDHCI_CLOCK_INT_STABLE)
+            break;
+        if (timeout < 0)
+        {
+            rt_kprintf("%s: Internal clock never stabilised.\n",
+                       mmc_hostname(host->mmc));
+            rt_read_reg_debug(host);
+            return;
+        }
+        rt_hw_us_delay(10);
+    }
+
+    if (host->version >= RT_SDHCI_SPEC_410 && host->v4_mode)
+    {
+        clk |= RT_SDHCI_CLOCK_PLL_EN;
+        clk &= ~RT_SDHCI_CLOCK_INT_STABLE;
+        rt_sdhci_writew(host, clk, RT_SDHCI_CLOCK_CONTROL);
+
+        timeout = rt_tick_from_millisecond(150);
+        while (1)
+        {
+            timeout = timeout - rt_tick_get();
+
+            clk = rt_sdhci_readw(host, RT_SDHCI_CLOCK_CONTROL);
+            if (clk & RT_SDHCI_CLOCK_INT_STABLE)
+                break;
+            if (timeout < 0)
+            {
+                rt_kprintf("%s: PLL clock never stabilised.\n",
+                           mmc_hostname(host->mmc));
+                rt_read_reg_debug(host);
+                return;
+            }
+            rt_hw_us_delay(10);
+        }
+    }
+
+    clk |= RT_SDHCI_CLOCK_CARD_EN;
+    rt_sdhci_writew(host, clk, RT_SDHCI_CLOCK_CONTROL);
+}
+
+void rt_sdhci_set_clock(struct rt_sdhci_host *host, unsigned int clock)
+{
+    rt_uint16_t clk;
+
+    host->mmc->actual_clock = 0;
+
+    rt_sdhci_writew(host, 0, RT_SDHCI_CLOCK_CONTROL);
+
+    if (clock == 0)
+        return;
+
+    clk = rt_sdhci_clk_set(host, clock, &host->mmc->actual_clock);
+    rt_sdhci_clk_enable(host, clk);
+}
+
+void rt_sdhci_read_caps(struct rt_sdhci_host *host, const rt_uint16_t *ver,
+                       const rt_uint32_t *caps, const rt_uint32_t *caps1)
+{
+    rt_uint16_t v;
+    rt_uint64_t dt_caps_mask = 0;
+    rt_uint64_t dt_caps      = 0;
+
+    if (host->read_caps)
+        return;
+
+    host->read_caps = RT_TRUE;
+
+    if (debug_quirks)
+        host->quirks = debug_quirks;
+
+    if (debug_quirks2)
+        host->quirks2 = debug_quirks2;
+
+    sdhci_reset_for_all(host);
+
+    if (host->v4_mode)
+        sdhci_do_enable_v4_mode(host);
+#ifdef RT_USING_OFW
+    rt_ofw_prop_read_u64(mmc_dev(host->mmc)->ofw_node,
+                         "sdhci-caps-mask", &dt_caps_mask);
+    rt_ofw_prop_read_u64(mmc_dev(host->mmc)->ofw_node,
+                         "sdhci-caps", &dt_caps);
+#endif
+    v             = ver ? *ver : rt_sdhci_readw(host, RT_SDHCI_HOST_VERSION);
+    host->version = (v & RT_SDHCI_SPEC_VER_MASK) >> RT_SDHCI_SPEC_VER_SHIFT;
+
+    if (caps)
+    {
+        host->caps = *caps;
+    }
+    else
+    {
+        host->caps  = rt_sdhci_readl(host, RT_SDHCI_CAPABILITIES);
+        host->caps &= ~lower_32_bits(dt_caps_mask);
+        host->caps |= lower_32_bits(dt_caps);
+    }
+
+    if (host->version < RT_SDHCI_SPEC_300)
+        return;
+
+    if (caps1)
+    {
+        host->caps1 = *caps1;
+    }
+    else
+    {
+        host->caps1  = rt_sdhci_readl(host, RT_SDHCI_CAPABILITIES_1);
+        host->caps1 &= ~upper_32_bits(dt_caps_mask);
+        host->caps1 |= upper_32_bits(dt_caps);
+    }
+}
+
+struct rt_sdhci_host *rt_sdhci_alloc_host(struct rt_device *dev,
+                                    size_t            priv_size)
+{
+    struct rt_mmc_host   *mmc;
+    struct rt_sdhci_host *host;
+
+    mmc = rt_mmc_alloc_host(sizeof(struct rt_sdhci_host) + priv_size, dev);
+    if (!mmc)
+        return NULL;
+
+    host               = mmc_priv(mmc);
+    host->mmc          = mmc;
+    host->mmc_host_ops = rt_sdhci_ops;
+    mmc->ops           = &host->mmc_host_ops;
+
+    host->flags = RT_SDHCI_SIGNALING_330;
+
+    host->cqe_ier     = RT_SDHCI_CQE_INT_MASK;
+    host->cqe_err_ier = RT_SDHCI_CQE_INT_ERR_MASK;
+
+    host->tuning_delay      = -1;
+    host->tuning_loop_count = MAX_TUNING_LOOP;
+
+    host->sdma_boundary = RT_SDHCI_DEFAULT_BOUNDARY_ARG;
+
+    host->max_timeout_count = 0xE;
+
+    return host;
+}
+
+int rt_sdhci_setup_host(struct rt_sdhci_host *host)
+{
+    struct rt_mmc_host *mmc;
+    size_t           max_current_caps;
+    unsigned int     ocr_avail;
+    unsigned int     override_timeout_clk;
+    size_t           max_clk;
+    int              ret          = 0;
+    bool             enable_vqmmc = RT_FALSE;
+
+    RT_ASSERT(host != NULL);
+
+
+    mmc = host->mmc;
+
+    if (!mmc->supply.vqmmc)
+    {
+        if (ret)
+            return ret;
+        enable_vqmmc = RT_TRUE;
+    }
+
+    LOG_D("Version:   0x%08x | Present:  0x%08x\n",
+          rt_sdhci_readw(host, RT_SDHCI_HOST_VERSION),
+          rt_sdhci_readl(host, RT_SDHCI_PRESENT_STATE));
+    LOG_D("Caps:      0x%08x | Caps_1:   0x%08x\n",
+          rt_sdhci_readl(host, RT_SDHCI_CAPABILITIES),
+          rt_sdhci_readl(host, RT_SDHCI_CAPABILITIES_1));
+
+    rt_sdhci_read_caps(host,RT_NULL,RT_NULL,RT_NULL);
+
+    override_timeout_clk = host->timeout_clk;
+
+    if (host->version > RT_SDHCI_SPEC_420)
+    {
+        rt_kprintf("%s: Unknown controller version (%d). You may experience problems.\n",
+                   mmc_hostname(mmc), host->version);
+    }
+
+    if (host->quirks & RT_SDHCI_QUIRK_FORCE_DMA)
+        host->flags |= RT_SDHCI_USE_SDMA;
+    else if (!(host->caps & RT_SDHCI_CAN_DO_SDMA))
+        LOG_D("Controller doesn't have SDMA capability\n");
+    else
+        host->flags |= RT_SDHCI_USE_SDMA;
+
+    if ((host->quirks & RT_SDHCI_QUIRK_BROKEN_DMA) && (host->flags & RT_SDHCI_USE_SDMA))
+    {
+        LOG_D("Disabling DMA as it is marked broken\n");
+        host->flags &= ~RT_SDHCI_USE_SDMA;
+    }
+
+    if (sdhci_can_64bit_dma(host))
+        host->flags |= RT_SDHCI_USE_64_BIT_DMA;
+
+    if (host->flags & RT_SDHCI_USE_SDMA)
+    {
+        if (host->ops->set_dma_mask)
+            ret = host->ops->set_dma_mask(host);
+
+        if (!ret && host->ops->enable_dma)
+            ret = host->ops->enable_dma(host);
+
+        if (ret)
+        {
+            rt_kprintf("%s: No suitable DMA available - falling back to PIO\n",
+                       mmc_hostname(mmc));
+            host->flags &= ~RT_SDHCI_USE_SDMA;
+
+            ret = 0;
+        }
+    }
+
+    if ((host->flags & RT_SDHCI_USE_64_BIT_DMA) && !host->v4_mode)
+        host->flags &= ~RT_SDHCI_USE_SDMA;
+
+    if (!(host->flags & RT_SDHCI_USE_SDMA))
+    {
+        host->dma_mask = DMA_BIT_MASK(64);
+    }
+    if (host->version >= RT_SDHCI_SPEC_300)
+        host->max_clk = FIELD_GET(RT_SDHCI_CLOCK_V3_BASE_MASK, host->caps);
+    else
+        host->max_clk = FIELD_GET(RT_SDHCI_CLOCK_BASE_MASK, host->caps);
+
+    host->max_clk *= 1000000;
+    if (host->max_clk == 0 || host->quirks & RT_SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN)
+    {
+        if (!host->ops->get_max_clock)
+        {
+            rt_kprintf("%s: Hardware doesn't specify base clock frequency.  %p \n",
+                       mmc_hostname(mmc), host->ops->get_max_clock);
+            ret = -ENODEV;
+            goto undma;
+        }
+        host->max_clk = host->ops->get_max_clock(host);
+    }
+
+    host->clk_mul = FIELD_GET(RT_SDHCI_CLOCK_MUL_MASK, host->caps1);
+
+    if (host->clk_mul)
+        host->clk_mul += 1;
+
+    max_clk = host->max_clk;
+
+    if (host->ops->get_min_clock)
+        mmc->f_min = host->ops->get_min_clock(host);
+    else if (host->version >= RT_SDHCI_SPEC_300)
+    {
+        if (host->clk_mul)
+            max_clk = host->max_clk * host->clk_mul;
+
+        mmc->f_min = host->max_clk / RT_SDHCI_MAX_DIV_SPEC_300;
+    }
+    else
+        mmc->f_min = host->max_clk / RT_SDHCI_MAX_DIV_SPEC_200;
+
+    if (!mmc->f_max || mmc->f_max > max_clk)
+        mmc->f_max = max_clk;
+
+    if (!(host->quirks & RT_SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK))
+    {
+        host->timeout_clk = FIELD_GET(RT_SDHCI_TIMEOUT_CLK_MASK, host->caps);
+
+        if (host->caps & RT_SDHCI_TIMEOUT_CLK_UNIT)
+            host->timeout_clk *= 1000;
+
+        if (host->timeout_clk == 0)
+        {
+            if (!host->ops->get_timeout_clock)
+            {
+                rt_kprintf("%s: Hardware doesn't specify timeout clock frequency.\n",
+                           mmc_hostname(mmc));
+                ret = -ENODEV;
+                goto undma;
+            }
+
+            host->timeout_clk =
+                DIV_ROUND_UP(host->ops->get_timeout_clock(host),
+                             1000);
+        }
+
+        if (override_timeout_clk)
+            host->timeout_clk = override_timeout_clk;
+
+        mmc->max_busy_timeout  = host->ops->get_max_timeout_count ? host->ops->get_max_timeout_count(host) : 1 << 27;
+        mmc->max_busy_timeout /= host->timeout_clk;
+    }
+
+    if (host->quirks2 & RT_SDHCI_QUIRK2_DISABLE_HW_TIMEOUT && !host->ops->get_max_timeout_count)
+        mmc->max_busy_timeout = 0;
+
+    mmc->caps  |= MMC_CAP_SDIO_IRQ | MMC_CAP_CMD23;
+    mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
+
+    if (host->quirks & RT_SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
+        host->flags |= RT_SDHCI_AUTO_CMD12;
+
+    if ((host->version >= RT_SDHCI_SPEC_300) && (!(host->flags & RT_SDHCI_USE_SDMA) || host->v4_mode) && !(host->quirks2 & RT_SDHCI_QUIRK2_ACMD23_BROKEN))
+    {
+        host->flags |= RT_SDHCI_AUTO_CMD23;
+        LOG_D("Auto-CMD23 available\n");
+    }
+    else
+    {
+        LOG_D("Auto-CMD23 unavailable\n");
+    }
+
+    if (!(host->quirks & RT_SDHCI_QUIRK_FORCE_1_BIT_DATA))
+        mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+    if (host->quirks2 & RT_SDHCI_QUIRK2_HOST_NO_CMD23)
+        mmc->caps &= ~MMC_CAP_CMD23;
+
+    if (host->caps & RT_SDHCI_CAN_DO_HISPD)
+        mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
+    if ((host->quirks & RT_SDHCI_QUIRK_BROKEN_CARD_DETECTION) && mmc_card_is_removable(mmc) && rt_mmc_gpio_get_cd(mmc) < 0)
+        mmc->caps |= MMC_CAP_NEEDS_POLL;
+
+    if (mmc->supply.vqmmc)
+    {
+        if (enable_vqmmc)
+        {
+            host->sdhci_core_to_disable_vqmmc = !ret;
+        }
+
+        if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 1700000,
+                                            1950000))
+            host->caps1 &= ~(RT_SDHCI_SUPPORT_SDR104 | RT_SDHCI_SUPPORT_SDR50 | RT_SDHCI_SUPPORT_DDR50);
+
+        if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 2700000,
+                                            3600000))
+            host->flags &= ~RT_SDHCI_SIGNALING_330;
+
+        if (ret)
+        {
+            rt_kprintf("%s: Failed to enable vqmmc regulator: %d\n",
+                       mmc_hostname(mmc), ret);
+            mmc->supply.vqmmc = (void *)-EINVAL;
+        }
+    }
+    if (host->quirks2 & RT_SDHCI_QUIRK2_NO_1_8_V)
+    {
+        host->caps1 &= ~(RT_SDHCI_SUPPORT_SDR104 | RT_SDHCI_SUPPORT_SDR50 | RT_SDHCI_SUPPORT_DDR50);
+        mmc->caps2 &= ~(MMC_CAP2_HSX00_1_8V | MMC_CAP2_HS400_ES);
+        mmc->caps  &= ~(MMC_CAP_1_8V_DDR | MMC_CAP_UHS);
+    }
+
+    if (host->caps1 & (RT_SDHCI_SUPPORT_SDR104 | RT_SDHCI_SUPPORT_SDR50 | RT_SDHCI_SUPPORT_DDR50))
+        mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
+
+    if (host->caps1 & RT_SDHCI_SUPPORT_SDR104)
+    {
+        mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50;
+
+        if (!(host->quirks2 & RT_SDHCI_QUIRK2_BROKEN_HS200))
+            mmc->caps2 |= MMC_CAP2_HS200;
+    }
+    else if (host->caps1 & RT_SDHCI_SUPPORT_SDR50)
+    {
+        mmc->caps |= MMC_CAP_UHS_SDR50;
+    }
+
+    if (host->quirks2 & RT_SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 && (host->caps1 & RT_SDHCI_SUPPORT_HS400))
+        mmc->caps2 |= MMC_CAP2_HS400;
+    if ((mmc->caps2 & MMC_CAP2_HSX00_1_2V) && (!mmc->supply.vqmmc || !regulator_is_supported_voltage(mmc->supply.vqmmc, 1100000, 1300000)))
+        mmc->caps2 &= ~MMC_CAP2_HSX00_1_2V;
+    if ((host->caps1 & RT_SDHCI_SUPPORT_DDR50) && !(host->quirks2 & RT_SDHCI_QUIRK2_BROKEN_DDR50))
+        mmc->caps |= MMC_CAP_UHS_DDR50;
+
+    if (host->caps1 & RT_SDHCI_USE_SDR50_TUNING)
+        host->flags |= RT_SDHCI_SDR50_NEEDS_TUNING;
+
+    if (host->caps1 & RT_SDHCI_DRIVER_TYPE_A)
+        mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
+    if (host->caps1 & RT_SDHCI_DRIVER_TYPE_C)
+        mmc->caps |= MMC_CAP_DRIVER_TYPE_C;
+    if (host->caps1 & RT_SDHCI_DRIVER_TYPE_D)
+        mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
+
+    host->tuning_count = FIELD_GET(RT_SDHCI_RETUNING_TIMER_COUNT_MASK,
+                                   host->caps1);
+
+    if (host->tuning_count)
+        host->tuning_count = 1 << (host->tuning_count - 1);
+
+    /* Re-tuning mode supported by the Host Controller */
+    host->tuning_mode = FIELD_GET(RT_SDHCI_RETUNING_MODE_MASK, host->caps1);
+
+    ocr_avail = 0;
+
+    max_current_caps = rt_sdhci_readl(host, RT_SDHCI_MAX_CURRENT);
+
+    if (!max_current_caps && mmc->supply.vmmc)
+    {
+        int curr = regulator_get_current_limit(mmc->supply.vmmc);
+        if (curr > 0)
+        {
+            curr = curr / 1000; /* convert to mA */
+            curr = curr / RT_SDHCI_MAX_CURRENT_MULTIPLIER;
+
+            curr = min_t(rt_uint32_t, curr, RT_SDHCI_MAX_CURRENT_LIMIT);
+            max_current_caps =
+                FIELD_PREP(RT_SDHCI_MAX_CURRENT_330_MASK, curr) | FIELD_PREP(RT_SDHCI_MAX_CURRENT_300_MASK, curr) | FIELD_PREP(RT_SDHCI_MAX_CURRENT_180_MASK, curr);
+        }
+    }
+
+    if (host->caps & RT_SDHCI_CAN_VDD_330)
+    {
+        ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
+
+        mmc->max_current_330 = FIELD_GET(RT_SDHCI_MAX_CURRENT_330_MASK,
+                                         max_current_caps)
+                               * RT_SDHCI_MAX_CURRENT_MULTIPLIER;
+    }
+    if (host->caps & RT_SDHCI_CAN_VDD_300)
+    {
+        ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
+
+        mmc->max_current_300 = FIELD_GET(RT_SDHCI_MAX_CURRENT_300_MASK,
+                                         max_current_caps)
+                               * RT_SDHCI_MAX_CURRENT_MULTIPLIER;
+    }
+    if (host->caps & RT_SDHCI_CAN_VDD_180)
+    {
+        ocr_avail |= MMC_VDD_165_195;
+
+        mmc->max_current_180 = FIELD_GET(RT_SDHCI_MAX_CURRENT_180_MASK,
+                                         max_current_caps)
+                               * RT_SDHCI_MAX_CURRENT_MULTIPLIER;
+    }
+
+    if (host->ocr_mask)
+        ocr_avail = host->ocr_mask;
+
+    if (mmc->ocr_avail)
+        ocr_avail = mmc->ocr_avail;
+
+    mmc->ocr_avail      = ocr_avail;
+    mmc->ocr_avail_sdio = ocr_avail;
+    if (host->ocr_avail_sdio)
+        mmc->ocr_avail_sdio &= host->ocr_avail_sdio;
+    mmc->ocr_avail_sd = ocr_avail;
+    if (host->ocr_avail_sd)
+        mmc->ocr_avail_sd &= host->ocr_avail_sd;
+    else
+        mmc->ocr_avail_sd &= ~MMC_VDD_165_195;
+    mmc->ocr_avail_mmc = ocr_avail;
+    if (host->ocr_avail_mmc)
+        mmc->ocr_avail_mmc &= host->ocr_avail_mmc;
+
+    if (mmc->ocr_avail == 0)
+    {
+        rt_kprintf("%s: Hardware doesn't report any support voltages.\n",
+                   mmc_hostname(mmc));
+        ret = -ENODEV;
+        goto unreg;
+    }
+
+    if ((mmc->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR)) || (mmc->caps2 & (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS400_1_8V)))
+        host->flags |= RT_SDHCI_SIGNALING_180;
+
+    if (mmc->caps2 & MMC_CAP2_HSX00_1_2V)
+        host->flags |= RT_SDHCI_SIGNALING_120;
+
+    rt_spin_lock_init(&host->lock);
+
+    mmc->max_req_size = 524288;
+    if (host->flags & RT_SDHCI_USE_SDMA)
+    {
+        mmc->max_segs = 1;
+    }
+    else
+    { /* PIO */
+        mmc->max_segs = RT_SDHCI_MAX_SEGS;
+    }
+
+    mmc->max_seg_size = mmc->max_req_size;
+
+    if (host->quirks & RT_SDHCI_QUIRK_FORCE_BLK_SZ_2048)
+    {
+        mmc->max_blk_size = 2;
+    }
+    else
+    {
+        mmc->max_blk_size = (host->caps & RT_SDHCI_MAX_BLOCK_MASK) >> RT_SDHCI_MAX_BLOCK_SHIFT;
+        if (mmc->max_blk_size >= 3)
+        {
+            rt_kprintf("%s: Invalid maximum block size, assuming 512 bytes\n",
+                       mmc_hostname(mmc));
+            mmc->max_blk_size = 0;
+        }
+    }
+
+    mmc->max_blk_size = 512 << mmc->max_blk_size;
+
+    /*
+     * Maximum block count.
+     */
+    mmc->max_blk_count = (host->quirks & RT_SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535;
+    return 0;
+
+unreg:
+undma:
+    return ret;
+}
+
+static void sdhci_init(struct rt_sdhci_host *host, int soft)
+{
+    struct rt_mmc_host *mmc = host->mmc;
+    rt_base_t        flags;
+
+    if (soft)
+    {
+        sdhci_do_reset(host, RT_SDHCI_RESET_CMD | RT_SDHCI_RESET_DATA);
+    }
+    else
+    {
+        sdhci_do_reset(host, RT_SDHCI_RESET_ALL);
+    }
+    if (host->v4_mode)
+    {
+        sdhci_do_enable_v4_mode(host);
+    }
+    flags = rt_spin_lock_irqsave(&host->lock);
+    sdhci_set_default_irqs(host);
+    rt_spin_unlock_irqrestore(&host->lock, flags);
+
+    host->cqe_on = RT_FALSE;
+
+    if (soft)
+    {
+        /* force clock reconfiguration */
+        host->clock      = 0;
+        host->reinit_uhs = RT_TRUE;
+        mmc->ops->set_ios(mmc, &mmc->ios);
+    }
+}
+
+static void sdhci_reinit(struct rt_sdhci_host *host)
+{
+    rt_uint32_t cd = host->ier & (RT_SDHCI_INT_CARD_REMOVE | RT_SDHCI_INT_CARD_INSERT);
+
+    sdhci_init(host, 0);
+    sdhci_enable_card_detection(host);
+
+    if (cd != (host->ier & (RT_SDHCI_INT_CARD_REMOVE | RT_SDHCI_INT_CARD_INSERT)))
+        rt_mmc_detect_change(host->mmc, rt_tick_from_millisecond(200));
+}
+
+int rt_sdhci_init_host(struct rt_sdhci_host *host)
+{
+    struct rt_mmc_host *mmc = host->mmc;
+    int              ret;
+
+    if ((mmc->caps2 & MMC_CAP2_CQE) && (host->quirks & RT_SDHCI_QUIRK_BROKEN_CQE))
+    {
+        mmc->caps2 &= ~MMC_CAP2_CQE;
+    }
+
+    host->complete_wq = rt_workqueue_create("sdhci", 4096, 20);
+    if (!host->complete_wq)
+        return -ENOMEM;
+
+    rt_work_init(&host->complete_work, sdhci_complete_work, host);
+
+    rt_timer_init(&host->timer, "sdhci_timer", sdhci_timeout_timer, host, 0, RT_TIMER_FLAG_SOFT_TIMER);
+    rt_timer_init(&host->data_timer, "sdhci_data_timer", sdhci_timeout_data_timer, host, 0, RT_TIMER_FLAG_SOFT_TIMER);
+
+    rt_wqueue_init(&host->buf_ready_int);
+
+    sdhci_init(host, 0);
+
+    host->irq_wq = rt_workqueue_create("sdhci_irq", 8192, 1);
+    rt_work_init(&host->irq_work, sdhci_thread_irq, host);
+    rt_hw_interrupt_install(host->irq, sdhci_irq, host, mmc_hostname(mmc));
+    rt_pic_irq_unmask(host->irq);
+    ret = rt_mmc_add_host(mmc);
+    if (ret)
+        goto unirq;
+
+    LOG_D("%s: RT_SDHCI controller on %s [%s] using %s\n",
+               mmc_hostname(mmc), host->hw_name, mmc_dev(mmc)->parent.name,
+               (host->flags & RT_SDHCI_USE_SDMA) ? "DMA" : "PIO");
+
+    sdhci_enable_card_detection(host);
+
+    return 0;
+
+unirq:
+    sdhci_reset_for_all(host);
+    rt_sdhci_writel(host, 0, RT_SDHCI_INT_ENABLE);
+    rt_sdhci_writel(host, 0, RT_SDHCI_SIGNAL_ENABLE);
+
+    return ret;
+}
+
+int rt_sdhci_set_and_add_host(struct rt_sdhci_host *host)
+{
+    int ret;
+    ret = rt_sdhci_setup_host(host);
+    if (ret)
+        return ret;
+
+    ret = rt_sdhci_init_host(host);
+    if (ret)
+        goto cleanup;
+
+    return 0;
+
+cleanup:
+    rt_sdhci_cleanup_host(host);
+
+    return ret;
+}
+
+void rt_sdhci_ios_set(struct rt_mmc_host *mmc, struct rt_mmcsd_io_cfg *ios)
+{
+    struct rt_sdhci_host *host           = mmc_priv(mmc);
+    rt_bool_t          reinit_uhs     = host->reinit_uhs;
+    rt_bool_t          turning_on_clk = RT_FALSE;
+    rt_uint8_t         ctrl;
+
+    host->reinit_uhs = RT_FALSE;
+
+    if (ios->power_mode == MMC_POWER_UNDEFINED)
+        return;
+
+    if (host->flags & RT_SDHCI_DEVICE_DEAD)
+    {
+        if (mmc->supply.vmmc && ios->power_mode == MMC_POWER_OFF)
+            mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+        return;
+    }
+
+    if (ios->power_mode == MMC_POWER_OFF)
+    {
+        rt_sdhci_writel(host, 0, RT_SDHCI_SIGNAL_ENABLE);
+        sdhci_reinit(host);
+    }
+
+    if (host->version >= RT_SDHCI_SPEC_300 && (ios->power_mode == MMC_POWER_UP) && !(host->quirks2 & RT_SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
+        sdhci_preset_value_enable(host, RT_FALSE);
+
+    if (!ios->clock || ios->clock != host->clock)
+    {
+        turning_on_clk = ios->clock && !host->clock;
+
+        host->ops->set_clock(host, ios->clock);
+        host->clock = ios->clock;
+
+        if (host->quirks & RT_SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK && host->clock)
+        {
+            host->timeout_clk = mmc->actual_clock ? mmc->actual_clock / 1000 : host->clock / 1000;
+            mmc->max_busy_timeout =
+                host->ops->get_max_timeout_count ? host->ops->get_max_timeout_count(host) : 1 << 27;
+            mmc->max_busy_timeout /= host->timeout_clk;
+        }
+    }
+
+    if (host->ops->set_power)
+        host->ops->set_power(host, ios->power_mode, ios->vdd);
+    else
+        rt_sdhci_set_power(host, ios->power_mode, ios->vdd);
+
+    host->ops->set_bus_width(host, ios->bus_width);
+
+    if (!reinit_uhs && turning_on_clk && host->timing == ios->timing && host->version >= RT_SDHCI_SPEC_300 && !sdhci_presetable_values_change(host, ios))
+        return;
+
+    ctrl = rt_sdhci_readb(host, RT_SDHCI_HOST_CONTROL);
+
+    if (!(host->quirks & RT_SDHCI_QUIRK_NO_HISPD_BIT))
+    {
+        if (ios->timing == MMC_TIMING_SD_HS || ios->timing == MMC_TIMING_MMC_HS || ios->timing == MMC_TIMING_MMC_HS400 || ios->timing == MMC_TIMING_MMC_HS200 || ios->timing == MMC_TIMING_MMC_DDR52 || ios->timing == MMC_TIMING_UHS_SDR50 || ios->timing == MMC_TIMING_UHS_SDR104 || ios->timing == MMC_TIMING_UHS_DDR50 || ios->timing == MMC_TIMING_UHS_SDR25)
+            ctrl |= RT_SDHCI_CTRL_HISPD;
+        else
+            ctrl &= ~RT_SDHCI_CTRL_HISPD;
+    }
+
+    if (host->version >= RT_SDHCI_SPEC_300)
+    {
+        rt_uint16_t clk, ctrl_2;
+
+        clk = rt_sdhci_readw(host, RT_SDHCI_CLOCK_CONTROL);
+        if (clk & RT_SDHCI_CLOCK_CARD_EN)
+        {
+            clk &= ~RT_SDHCI_CLOCK_CARD_EN;
+            rt_sdhci_writew(host, clk, RT_SDHCI_CLOCK_CONTROL);
+        }
+
+        rt_sdhci_writeb(host, ctrl, RT_SDHCI_HOST_CONTROL);
+
+        if (!host->preset_enabled)
+        {
+            ctrl_2  = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2);
+            ctrl_2 &= ~RT_SDHCI_CTRL_DRV_TYPE_MASK;
+            if (ios->drv_type == MMC_SET_DRIVER_TYPE_A)
+                ctrl_2 |= RT_SDHCI_CTRL_DRV_TYPE_A;
+            else if (ios->drv_type == MMC_SET_DRIVER_TYPE_B)
+                ctrl_2 |= RT_SDHCI_CTRL_DRV_TYPE_B;
+            else if (ios->drv_type == MMC_SET_DRIVER_TYPE_C)
+                ctrl_2 |= RT_SDHCI_CTRL_DRV_TYPE_C;
+            else if (ios->drv_type == MMC_SET_DRIVER_TYPE_D)
+                ctrl_2 |= RT_SDHCI_CTRL_DRV_TYPE_D;
+            else
+            {
+                LOG_D("%s: invalid driver type, default to driver type B\n",
+                      mmc_hostname(mmc));
+                ctrl_2 |= RT_SDHCI_CTRL_DRV_TYPE_B;
+            }
+
+            rt_sdhci_writew(host, ctrl_2, RT_SDHCI_HOST_CONTROL2);
+            host->drv_type = ios->drv_type;
+        }
+
+        host->ops->set_uhs_signaling(host, ios->timing);
+        host->timing = ios->timing;
+
+        if (sdhci_preset_needed(host, ios->timing))
+        {
+            rt_uint16_t preset;
+
+            sdhci_preset_value_enable(host, RT_TRUE);
+            preset         = sdhci_get_preset_value(host);
+            ios->drv_type  = FIELD_GET(RT_SDHCI_PRESET_DRV_MASK,
+                                       preset);
+            host->drv_type = ios->drv_type;
+        }
+
+        host->ops->set_clock(host, host->clock);
+    }
+    else
+        rt_sdhci_writeb(host, ctrl, RT_SDHCI_HOST_CONTROL);
+}
+void rt_sdhci_free_host(struct rt_sdhci_host *host)
+{
+    rt_sdhci_cleanup_host(host);
+    rt_free(host);
+}