diff mbox series

CMDQ feature is introduced to eMMC standard in v5.1, which can be used to improve performance.

Message ID 20230616063731.17591-1-jyanchou@realtek.com
State New
Headers show
Series CMDQ feature is introduced to eMMC standard in v5.1, which can be used to improve performance. | expand

Commit Message

Jyan Chou [周芷安] June 16, 2023, 6:37 a.m. UTC
We add the mmc driver for the Synopsys DesignWare mmc host controller
with cmdq support that can implement this feature.

Signed-off-by: Jyan Chou <jyanchou@realtek.com>
---
 drivers/mmc/host/Kconfig      |   11 +
 drivers/mmc/host/Makefile     |    1 +
 drivers/mmc/host/dw_mmc_cqe.c | 1823 +++++++++++++++++++++++++++++++++
 drivers/mmc/host/dw_mmc_cqe.h |  444 ++++++++
 4 files changed, 2279 insertions(+)
 create mode 100644 drivers/mmc/host/dw_mmc_cqe.c
 create mode 100644 drivers/mmc/host/dw_mmc_cqe.h

Comments

Adrian Hunter June 20, 2023, 6:45 a.m. UTC | #1
On 16/06/23 09:37, Jyan Chou wrote:
> We add the mmc driver for the Synopsys DesignWare mmc host controller
> with cmdq support that can implement this feature.

Why not add CQHCI support to dw_mmc.c ?

It does not usually require that many changes, for example:

commit 88bd652b3c74997bb436adf6131acf445066243e
Author: Chun-Hung Wu <chun-hung.wu@mediatek.com>
Date:   Mon Jul 20 08:42:38 2020 +0800

    mmc: mediatek: command queue support
    
    Support command queue for mt6779 platform.
    a. Add msdc_set_busy_timeout() to calculate emmc write timeout.
    b. Connect mtk msdc driver to cqhci driver through
       host->cq_host->ops = &msdc_cmdq_ops;
    c. msdc_cmdq_irq() will link up with cqchi_irq(). Besides, it provides
       more irq error messages like RSPCRCERR/CMDTO/DATACRCERR/DATTMO.
    d. Select kernel config MMC_CQHCI for MMC_MTK
    
    Signed-off-by: Chun-Hung Wu <chun-hung.wu@mediatek.com>
    Acked-by: Yong Mao <yong.mao@mediatek.com>
    Link: https://lore.kernel.org/r/1595205759-5825-4-git-send-email-chun-hung.wu@mediatek.com
    Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>



> 
> Signed-off-by: Jyan Chou <jyanchou@realtek.com>
> ---
>  drivers/mmc/host/Kconfig      |   11 +
>  drivers/mmc/host/Makefile     |    1 +
>  drivers/mmc/host/dw_mmc_cqe.c | 1823 +++++++++++++++++++++++++++++++++
>  drivers/mmc/host/dw_mmc_cqe.h |  444 ++++++++
>  4 files changed, 2279 insertions(+)
>  create mode 100644 drivers/mmc/host/dw_mmc_cqe.c
>  create mode 100644 drivers/mmc/host/dw_mmc_cqe.h
> 
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 9f793892123c..b8c7727b1897 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -762,6 +762,17 @@ config MMC_DW_PLTFM
>  
>  	  If unsure, say Y.
>  
> +config MMC_DW_CQE
> +	tristate "Synopsys DesignWare Memory Card with CQE Interface"
> +	depends on ARC || ARM || ARM64 || MIPS || COMPILE_TEST
> +	select MMC_CQHCI
> +	help
> +	  This selects support for the Synopsys DesignWare Mobile Storage IP
> +	  block. It provides host support for SD and MMC interfaces, and adds
> +	  the support of cmdq.
> +
> +	  If unsure, say N.
> +
>  config MMC_DW_BLUEFIELD
>  	tristate "BlueField specific extensions for Synopsys DW Memory Card Interface"
>  	depends on MMC_DW
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index a693fa3d3f1c..7fa1411692e8 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -55,6 +55,7 @@ obj-$(CONFIG_MMC_DW_K3)		+= dw_mmc-k3.o
>  obj-$(CONFIG_MMC_DW_PCI)	+= dw_mmc-pci.o
>  obj-$(CONFIG_MMC_DW_ROCKCHIP)	+= dw_mmc-rockchip.o
>  obj-$(CONFIG_MMC_DW_STARFIVE)	+= dw_mmc-starfive.o
> +obj-$(CONFIG_MMC_DW_CQE)                += dw_mmc_cqe.o
>  obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
>  obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
>  obj-$(CONFIG_MMC_VUB300)	+= vub300.o
> diff --git a/drivers/mmc/host/dw_mmc_cqe.c b/drivers/mmc/host/dw_mmc_cqe.c
> new file mode 100644
> index 000000000000..c50a6c71a362
> --- /dev/null
> +++ b/drivers/mmc/host/dw_mmc_cqe.c
> @@ -0,0 +1,1823 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Synopsys DesignWare Multimedia Card Interface driver with CMDQ support
> + *  (Based on Synopsys DesignWare Multimedia Card Interface driver)
> + *
> + * Copyright (c) 2023 Realtek Semiconductor Corp
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/blkdev.h>
> +#include <linux/clk.h>
> +#include <linux/debugfs.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/iopoll.h>
> +#include <linux/ioport.h>
> +#include <linux/irq.h>
> +#include <linux/mmc/card.h>
> +#include <linux/mmc/host.h>
> +#include <linux/mmc/mmc.h>
> +#include <linux/mmc/sd.h>
> +#include <linux/mmc/sdio.h>
> +#include <linux/mmc/slot-gpio.h>
> +#include <linux/module.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/seq_file.h>
> +#include <linux/sizes.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +
> +#include "dw_mmc_cqe.h"
> +#include "cqhci.h"
> +
> +#define DW_MCI_FREQ_MAX	200000000	/* unit: HZ */
> +#define DW_MCI_FREQ_MIN	100000		/* unit: HZ */
> +#define DW_MCI_CMDQ_DISABLED	0x30f0001
> +#define DW_MCI_CMDQ_ENABLED	0x30f0101
> +#define DW_MCI_POWEROFF		0x3220301
> +#define DW_MCI_DESC_LEN		0x100000
> +#define DW_MCI_MAX_SCRIPT_BLK	128
> +#define DW_MCI_TIMEOUT_MS	3000
> +#define DW_MCI_TIMEOUT_us	3000000
> +#define TUNING_ERR		531
> +#define DW_MCI_NOT_READY	9999
> +
> +DECLARE_COMPLETION(dw_mci_wait);
> +
> +
> +#if defined(CONFIG_DEBUG_FS)
> +static int dw_mci_cqe_req_show(struct seq_file *s, void *v)
> +{
> +	struct dw_mci_slot *slot = s->private;
> +	struct mmc_request *mrq;
> +	struct mmc_command *cmd;
> +	struct mmc_command *stop;
> +	struct mmc_data	*data;
> +
> +	/* Make sure we get a consistent snapshot */
> +	spin_lock_bh(&slot->host->lock);
> +	mrq = slot->mrq;
> +
> +	if (mrq) {
> +		cmd = mrq->cmd;
> +		data = mrq->data;
> +		stop = mrq->stop;
> +
> +		if (cmd)
> +			seq_printf(s,
> +				   "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
> +				   cmd->opcode, cmd->arg, cmd->flags,
> +				   cmd->resp[0], cmd->resp[1], cmd->resp[2],
> +				   cmd->resp[2], cmd->error);
> +		if (data)
> +			seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
> +				   data->bytes_xfered, data->blocks,
> +				   data->blksz, data->flags, data->error);
> +		if (stop)
> +			seq_printf(s,
> +				   "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
> +				   stop->opcode, stop->arg, stop->flags,
> +				   stop->resp[0], stop->resp[1], stop->resp[2],
> +				   stop->resp[2], stop->error);
> +	}
> +
> +	spin_unlock_bh(&slot->host->lock);
> +
> +	return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(dw_mci_cqe_req);
> +#endif /* defined(CONFIG_DEBUG_FS) */
> +
> +static int dw_mci_cqe_regs_show(struct dw_mci *host,
> +				struct mmc_command *cmd, u32 cmd_flags)
> +{
> +	dev_err(host->dev, "opcode = %d, arg = 0x%x, cmdflags = 0x%x\n",
> +				cmd->opcode, cmd->arg, cmd_flags);
> +	dev_err(host->dev, "status_int = 0x%x\n", host->normal_interrupt);
> +	dev_err(host->dev, "error_int = 0x%x\n", host->error_interrupt);
> +	dev_err(host->dev, "auto_error_int = 0x%x\n", host->auto_error_interrupt);
> +	dev_err(host->dev, "pstate_reg = 0x%x\n", mci_readl(host, PSTATE_REG));
> +	dev_err(host->dev, "host_ctrl_1 = 0x%x\n", mci_readb(host, HOST_CTRL1_R));
> +	dev_err(host->dev, "xfer_mode_r = 0x%x\n", mci_readw(host, XFER_MODE_R));
> +
> +	return 0;
> +}
> +
> +static void dw_mci_cqe_dumpregs(struct mmc_host *mmc)
> +{
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct dw_mci *host = slot->host;
> +
> +	dev_info(host->dev, "%s: cmd idx 0x%08x\n", __func__, mci_readw(host, CMD_R));
> +}
> +
> +static void dw_mci_cqe_set_tran_desc(u8 *desc,
> +					dma_addr_t addr,
> +					int len,
> +					bool end,
> +					bool dma64)
> +{
> +	__le32 *attr = (__le32 __force *)desc;
> +
> +	*attr = (CQHCI_VALID(1) |
> +		 CQHCI_END(end ? 1 : 0) |
> +		 CQHCI_INT(0) |
> +		 CQHCI_ACT(0x4) |
> +		 CQHCI_DAT_LENGTH(len));
> +
> +	if (dma64) {
> +		__le64 *dataddr = (__le64 __force *)(desc + 4);
> +
> +		dataddr[0] = cpu_to_le64(addr);
> +	} else {
> +		__le32 *dataddr = (__le32 __force *)(desc + 4);
> +
> +		dataddr[0] = cpu_to_le32(addr);
> +	}
> +}
> +
> +static void dw_mci_cqe_setup_tran_desc(struct mmc_data *data,
> +				      struct cqhci_host *cq_host,
> +				      u8 *desc,
> +				      int sg_count)
> +{
> +	struct scatterlist *sg;
> +	u32 cur_blk_cnt, remain_blk_cnt;
> +	unsigned int begin, end;
> +	int i, len;
> +	bool last = false;
> +	bool dma64 = cq_host->dma64;
> +	dma_addr_t addr;
> +
> +	for_each_sg(data->sg, sg, sg_count, i) {
> +		addr = sg_dma_address(sg);
> +		len = sg_dma_len(sg);
> +		remain_blk_cnt  = len >> 9;
> +
> +		while (remain_blk_cnt) {
> +			/*DW_MCI_MAX_SCRIPT_BLK is tha max for each descriptor record*/
> +			if (remain_blk_cnt > DW_MCI_MAX_SCRIPT_BLK)
> +				cur_blk_cnt = DW_MCI_MAX_SCRIPT_BLK;
> +			else
> +				cur_blk_cnt = remain_blk_cnt;
> +
> +			/* In Synopsys DesignWare Databook Page 84,
> +			 * They mentioned the DMA 128MB restriction
> +			 */
> +			begin = addr / SZ_128M;
> +			end = (addr + cur_blk_cnt * SZ_512) / SZ_128M;
> +
> +			if (begin != end)
> +				cur_blk_cnt = (end * SZ_128M - addr) / SZ_512;
> +
> +			if ((i+1) == sg_count && (remain_blk_cnt == cur_blk_cnt))
> +				last = true;
> +
> +			dw_mci_cqe_set_tran_desc(desc, addr,
> +					(cur_blk_cnt << 9), last, dma64);
> +
> +			addr = addr + (cur_blk_cnt << 9);
> +			remain_blk_cnt -= cur_blk_cnt;
> +			desc += cq_host->trans_desc_len;
> +		}
> +	}
> +}
> +
> +static void dw_mci_cqe_enable(struct mmc_host *mmc)
> +{
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct dw_mci *host = slot->host;
> +
> +	/*clear data path SW_RST_R.SW_RST_DAT = 1*/
> +	mci_writeb(host, SW_RST_R, SDMMC_RST_DAT);
> +	/*0x9801200c*/
> +	mci_writew(host, XFER_MODE_R,
> +		((1 << SDMMC_MULTI_BLK_SEL) | SDMMC_BLOCK_COUNT_ENABLE | SDMMC_DMA_ENABLE));
> +
> +	/*Set DMA_SEL to ADMA2 only mode in the HOST_CTRL1_R*/
> +	mci_writeb(host, HOST_CTRL1_R,
> +		(mci_readb(host, HOST_CTRL1_R) & 0xe7) | (SDMMC_ADMA2_32 << SDMMC_DMA_SEL));
> +	mci_writew(host, BLOCKSIZE_R, 0x200);
> +	mci_writew(host, BLOCKCOUNT_R, 0);
> +
> +	/*Set SDMASA_R (while using 32 bits) to 0*/
> +	mci_writel(host, SDMASA_R, 0);
> +	/*we set this register additionally to enhance the IO perofrmance*/
> +
> +	cqhci_writel(host->cqe, 0x10, CQHCI_SSC1);
> +	cqhci_writel(host->cqe, 0, CQHCI_CTL);
> +
> +	if (cqhci_readl(host->cqe, CQHCI_CTL) && CQHCI_HALT) {
> +		dev_err(host->dev, "%s: cqhci: CQE failed to exit halt state\n",
> +			mmc_hostname(mmc));
> +	}
> +
> +	/*cmdq interrupt mode*/
> +	dw_mci_clr_signal_int(host);
> +	dw_mci_en_cqe_int(host);
> +}
> +
> +static const struct cqhci_host_ops dw_mci_cqhci_host_ops = {
> +	.enable = dw_mci_cqe_enable,
> +	.dumpregs = dw_mci_cqe_dumpregs,
> +	.setup_tran_desc = dw_mci_cqe_setup_tran_desc,
> +};
> +
> +void dw_mci_cqe_wait_done(struct dw_mci *host, u32 *addr,
> +		      u32 mask, u32 value)
> +{
> +	int n = 0;
> +
> +	while (1) {
> +		if (((*addr) & mask) == value)
> +			break;
> +
> +		/*error interrupt detected*/
> +		if ((mci_readw(host, NORMAL_INT_STAT_R) & SDMMC_ERR_INTERRUPT) != 0)
> +			break;
> +
> +		if (n++ > DW_MCI_TIMEOUT_us) {
> +			dev_err(host->dev, "opcode = %d, *addr = 0x%x, mask = 0x%x, value = 0x%x\n",
> +				host->opcode, readl(addr), mask, value);
> +			break;
> +		}
> +		udelay(1);
> +	}
> +}
> +EXPORT_SYMBOL(dw_mci_cqe_wait_done);
> +
> +static void dw_mci_cqe_reset(struct dw_mci *host)
> +{
> +	/*check the cmd line*/
> +	if (mci_readw(host, ERROR_INT_STAT_R) & SDMMC_CMD_ERR) {
> +		/*Perform a software reset*/
> +		mci_writeb(host, SW_RST_R, SDMMC_RST_CMD);
> +		dw_mci_cqe_wait_done(host, (u32 *)(host->regs + SDMMC_CLK_CTRL_R), BIT(25), 0);
> +	}
> +	/*check data line*/
> +	if (mci_readw(host, ERROR_INT_STAT_R) & SDMMC_DATA_ERR) {
> +		mci_writeb(host, SW_RST_R, SDMMC_RST_DAT);
> +		dw_mci_cqe_wait_done(host, (u32 *)(host->regs + SDMMC_CLK_CTRL_R), BIT(26), 0);
> +	}
> +}
> +
> +static void dw_mci_cqe_read_rsp(struct dw_mci *host, struct mmc_command *cmd, u32 *rsp)
> +{
> +	const struct dw_mci_drv_data *drv_data = host->drv_data;
> +
> +	if (cmd->flags & MMC_RSP_PRESENT) {
> +		if (cmd->flags & MMC_RSP_136) {
> +			if (drv_data && drv_data->shift_rsp) {
> +				drv_data->shift_rsp(host, cmd, cmd->resp);
> +			} else {
> +				/*R2 long response*/
> +				u32 rsp_tmp[4];
> +
> +				rsp_tmp[3] = mci_readl(host, RESP01_R);
> +				rsp_tmp[2] = mci_readl(host, RESP23_R);
> +				rsp_tmp[1] = mci_readl(host, RESP45_R);
> +				rsp_tmp[0] = mci_readl(host, RESP67_R);
> +			}
> +		} else {
> +			/*Short response*/
> +			rsp[0] = rsp[1] = rsp[2] = rsp[3] = 0;
> +			rsp[0] = mci_readl(host, RESP01_R);
> +		}
> +	}
> +}
> +
> +static u32 dw_mci_cqe_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
> +{
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct dw_mci *host = slot->host;
> +	u32 cmdr;
> +
> +	cmd->error = -EINPROGRESS;
> +
> +    /* our ip design puts resp in bit 8-135, so we need to shift 8 bits*/
> +	if (host->shift)
> +		cmdr = (cmd->opcode << 8);
> +	else
> +		cmdr = cmd->opcode;
> +
> +	if (cmd->flags & MMC_RSP_PRESENT) {
> +		if (cmd->flags & MMC_RSP_136)
> +			cmdr |= SDMMC_RESP_LEN_136;
> +		else {
> +			if (cmd->flags & MMC_RSP_BUSY)
> +				cmdr |= SDMMC_RESP_LEN_48B;
> +			else
> +				cmdr |= SDMMC_RESP_LEN_48;
> +		}
> +	}
> +
> +	cmdr |= SDMMC_CMD_CHK_RESP_CRC;
> +	if (cmd->opcode == MMC_GO_IDLE_STATE ||
> +	   cmd->opcode == MMC_SEND_OP_COND ||
> +	   (cmd->opcode == MMC_SELECT_CARD && cmd->flags == (MMC_RSP_NONE | MMC_CMD_AC)))
> +		cmdr &= ~SDMMC_CMD_CHK_RESP_CRC;
> +
> +	cmdr |= SDMMC_CMD_IDX_CHK_ENABLE;
> +	if (cmd->opcode == MMC_GO_IDLE_STATE ||
> +	   cmd->opcode == MMC_SEND_OP_COND ||
> +	   cmd->opcode == MMC_SEND_CSD ||
> +	   cmd->opcode == MMC_SEND_CID ||
> +	   cmd->opcode == MMC_ALL_SEND_CID ||
> +	   (cmd->opcode == MMC_SELECT_CARD && cmd->flags == (MMC_RSP_NONE | MMC_CMD_AC)))
> +		cmdr &= ~SDMMC_CMD_IDX_CHK_ENABLE;
> +
> +	if (cmd->data)
> +		cmdr |= SDMMC_DATA;
> +
> +	if (cmd->opcode == MMC_STOP_TRANSMISSION)
> +		cmdr |= (SDMMC_ABORT_CMD << 6);
> +
> +	return cmdr;
> +}
> +
> +static int dw_mci_cqe_start_command(struct dw_mci *host,
> +				 struct mmc_command *cmd, u32 cmd_flags)
> +{
> +	int err = 0;
> +	unsigned long end = 0;
> +	unsigned long flags;
> +	bool xfer_flag = false;
> +
> +	host->cmd = cmd;
> +
> +	switch (cmd->opcode) {
> +	case MMC_READ_SINGLE_BLOCK:
> +	case MMC_READ_MULTIPLE_BLOCK:
> +	case MMC_WRITE_BLOCK:
> +	case MMC_WRITE_MULTIPLE_BLOCK:
> +	case MMC_SEND_EXT_CSD:
> +	case MMC_GEN_CMD:
> +	case MMC_SLEEP_AWAKE:
> +	case MMC_SWITCH:
> +	case MMC_SET_WRITE_PROT:
> +	case MMC_CLR_WRITE_PROT:
> +	case MMC_SEND_WRITE_PROT:
> +	case MMC_ERASE:
> +	case MMC_SEND_TUNING_BLOCK_HS200:
> +		xfer_flag = true;
> +		break;
> +	default:
> +		xfer_flag = false;
> +	}
> +
> +	host->int_waiting = &dw_mci_wait;
> +	end = jiffies + msecs_to_jiffies(DW_MCI_TIMEOUT_MS);
> +	mod_timer(&host->timer, end);
> +
> +	if (host->int_waiting) {
> +		dw_mci_clr_signal_int(host);
> +		dw_mci_clr_int(host);
> +
> +		/*command with data, r1b case*/
> +		if (xfer_flag == 1)
> +			dw_mci_en_xfer_int(host);
> +		else
> +			dw_mci_en_cd_int(host);
> +
> +		/*If we use cmd23, we cannot send auto stop command*/
> +		if (cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK ||
> +		    cmd->opcode == MMC_READ_MULTIPLE_BLOCK) {
> +			if (host->is_sbc) {
> +				mci_writew(host, XFER_MODE_R,
> +					mci_readw(host, XFER_MODE_R) & ~BIT(SDMMC_AUTO_CMD_ENABLE));
> +					host->is_sbc = 0;
> +			}
> +		}
> +
> +		host->opcode = cmd->opcode;
> +		host->arg = cmd->arg;
> +
> +		spin_lock_irqsave(&host->irq_lock, flags);
> +		mci_writew(host, CMD_R, cmd_flags);
> +		spin_unlock_irqrestore(&host->irq_lock, flags);
> +
> +		wait_for_completion(host->int_waiting);
> +
> +		if (xfer_flag == 1)
> +			dw_mci_cqe_wait_done(host,
> +				(u32 *)(host->regs + SDMMC_NORMAL_INT_STAT_R),
> +				SDMMC_XFER_COMPLETE, SDMMC_XFER_COMPLETE);
> +		else
> +			dw_mci_cqe_wait_done(host,
> +				(u32 *)(host->regs + SDMMC_NORMAL_INT_STAT_R),
> +				SDMMC_CMD_COMPLETE, SDMMC_CMD_COMPLETE);
> +
> +		if (host->normal_interrupt & SDMMC_ERR_INTERRUPT) {
> +			if (host->tuning == 1)
> +				dev_info(host->dev, "Tuning error ... keep tuning\n");
> +			else
> +				dw_mci_cqe_regs_show(host, cmd, cmd_flags);
> +			err = -1;
> +		}
> +	}
> +
> +	return err;
> +}
> +
> +static void dw_mci_cqe_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd)
> +{
> +
> +	struct mmc_command stop;
> +	u32 cmdr;
> +	/*Stop command only use after data command*/
> +	if (!cmd->data)
> +		return;
> +
> +	memset(&stop, 0, sizeof(struct mmc_command));
> +
> +	if (cmd->opcode == MMC_READ_SINGLE_BLOCK ||
> +	    cmd->opcode == MMC_READ_MULTIPLE_BLOCK ||
> +	    cmd->opcode == MMC_WRITE_BLOCK ||
> +	    cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK ||
> +	    cmd->opcode == MMC_SEND_TUNING_BLOCK ||
> +	    cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200) {
> +		stop.opcode = MMC_STOP_TRANSMISSION;
> +		stop.arg = 0;
> +		stop.flags = MMC_RSP_R1 | MMC_CMD_AC;
> +	} else if (cmd->opcode == SD_IO_RW_EXTENDED) {
> +		stop.opcode = SD_IO_RW_DIRECT;
> +		stop.arg |= (1 << 31) | (0 << 28) | (SDIO_CCCR_ABORT << 9) |
> +			    ((cmd->arg >> 28) & 0x7);
> +		stop.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC;
> +	} else {
> +		return;
> +	}
> +
> +	cmdr = (stop.opcode << 8) | SDMMC_RESP_LEN_48 |
> +		SDMMC_CMD_CHK_RESP_CRC | SDMMC_CMD_IDX_CHK_ENABLE;
> +	cmdr |= (SDMMC_ABORT_CMD << 6);
> +	mci_writew(host, XFER_MODE_R, 0);
> +	mci_writel(host, ARGUMENT_R, stop.arg);
> +	dw_mci_cqe_start_command(host, &stop, cmdr);
> +}
> +
> +static int dw_mci_cqe_wait_while_busy(struct dw_mci *host, u32 *status)
> +{
> +	struct mmc_command cmd;
> +	u32 cmdr;
> +	u32 cur_state;
> +	unsigned long timeend;
> +	int err = 0;
> +
> +	memset(&cmd, 0, sizeof(struct mmc_command));
> +
> +	timeend = jiffies + msecs_to_jiffies(600);
> +
> +	do {
> +		cmd.opcode = MMC_SEND_STATUS;
> +		cmd.arg = 1 << 16;
> +		cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
> +		cmd.data = NULL;
> +
> +		cmdr = (cmd.opcode << 8) | SDMMC_RESP_LEN_48 |
> +			SDMMC_CMD_CHK_RESP_CRC | SDMMC_CMD_IDX_CHK_ENABLE;
> +
> +		mci_writew(host, XFER_MODE_R, 0);
> +		mci_writel(host, ARGUMENT_R, cmd.arg);
> +
> +		err = dw_mci_cqe_start_command(host, &cmd, cmdr);
> +		if (err) {
> +			dw_mci_cqe_reset(host);
> +			break;
> +		}
> +		dw_mci_cqe_read_rsp(host, &cmd, cmd.resp);
> +
> +		*status = cmd.resp[0];
> +		cur_state = R1_CURRENT_STATE(cmd.resp[0]);
> +		err = -DW_MCI_NOT_READY;
> +		if (cur_state == R1_STATE_TRAN) {
> +			if (cmd.resp[0] & R1_READY_FOR_DATA) {
> +				err = 0;
> +				break;
> +			}
> +		}
> +	} while (time_before(jiffies, timeend));
> +
> +	return err;
> +
> +}
> +
> +static void dw_mci_cqe_stop_dma(struct dw_mci *host, struct mmc_data *data)
> +{
> +	u32 dir = 0;
> +
> +	if (data->flags & MMC_DATA_READ)
> +		dir = DMA_FROM_DEVICE;
> +	else
> +		dir = DMA_TO_DEVICE;
> +
> +	dma_unmap_sg(mmc_dev(host->slot->mmc), data->sg, data->sg_len, dir);
> +	host->sg = NULL;
> +}
> +
> +static void dw_mci_cqe_prepare_desc64(struct dw_mci *host, struct mmc_data *data,
> +					struct scatterlist *sg)
> +{
> +	dev_info(host->dev, "Currently, the 64bit DMA mode is not implemented yet.\n");
> +}
> +
> +
> +static void dw_mci_cqe_prepare_desc32(struct dw_mci *host, struct mmc_data *data,
> +					struct scatterlist *sg)
> +{
> +	u32  blk_cnt, cur_blk_cnt, remain_blk_cnt;
> +	u32  tmp_val;
> +	u32 *desc_base = host->sg_cpu;
> +	u32  dma_len = 0;
> +	u32  dma_addr;
> +	u32  i;
> +	unsigned int begin, end;
> +
> +	for (i = 0; i < host->dma_nents; i++, sg++) {
> +		dma_len = sg_dma_len(sg);
> +
> +		/*blk_cnt must be the multiple of 512(0x200)*/
> +		if (dma_len < SZ_512)
> +			blk_cnt = 1;
> +		else
> +			blk_cnt  = dma_len >> 9;
> +
> +		remain_blk_cnt  = blk_cnt;
> +		dma_addr = sg_dma_address(sg);
> +
> +		while (remain_blk_cnt) {
> +			/*DW_MCI_MAX_SCRIPT_BLK is the max
> +			 * for each descriptor record
> +			 */
> +			if (remain_blk_cnt > DW_MCI_MAX_SCRIPT_BLK)
> +				cur_blk_cnt = DW_MCI_MAX_SCRIPT_BLK;
> +			else
> +				cur_blk_cnt = remain_blk_cnt;
> +
> +			/* In Synopsys DesignWare Databook Page 84,
> +			 * They mentioned the DMA 128MB restriction
> +			 */
> +			begin = dma_addr / SZ_128M;
> +			end = (dma_addr + cur_blk_cnt * SZ_512) / SZ_128M;
> +
> +			/*If begin and end in the different 128MB memory zone*/
> +			if (begin != end)
> +				cur_blk_cnt = (end * SZ_128M - dma_addr) / SZ_512;
> +
> +			if (dma_len < SZ_512)
> +				tmp_val = ((dma_len) << 16) | VALID(0x1) | ACT(0x4);
> +			else
> +				tmp_val = ((cur_blk_cnt & 0x7f) << 25) | VALID(0x1) | ACT(0x4);
> +
> +			/*Last descriptor*/
> +			if (i == host->dma_nents - 1 && remain_blk_cnt == cur_blk_cnt)
> +				tmp_val |= END(0x1);
> +
> +			desc_base[0] =  tmp_val;
> +			desc_base[1] =  dma_addr;
> +
> +			dma_addr = dma_addr + (cur_blk_cnt << 9);
> +			remain_blk_cnt -= cur_blk_cnt;
> +			desc_base += 2;
> +		}
> +	}
> +}
> +
> +static void dw_mci_cqe_pre_req(struct mmc_host *mmc,
> +			   struct mmc_request *mrq)
> +{
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct dw_mci *host = slot->host;
> +	struct mmc_data *data = mrq->data;
> +	unsigned int sg_len;
> +
> +	if (!slot->host->use_dma || !data)
> +		return;
> +
> +	/* This data might be unmapped at this time */
> +	data->host_cookie = COOKIE_UNMAPPED;
> +
> +	sg_len = dma_map_sg(host->dev,
> +			    data->sg,
> +			    data->sg_len,
> +			    mmc_get_dma_dir(data));
> +	if (sg_len < 0)
> +		data->host_cookie = COOKIE_UNMAPPED;
> +}
> +
> +static void dw_mci_cqe_post_req(struct mmc_host *mmc,
> +			    struct mmc_request *mrq,
> +			    int err)
> +{
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct mmc_data *data = mrq->data;
> +
> +	if (!slot->host->use_dma || !data)
> +		return;
> +
> +	if (data->host_cookie != COOKIE_UNMAPPED)
> +		dma_unmap_sg(slot->host->dev,
> +			     data->sg,
> +			     data->sg_len,
> +			     mmc_get_dma_dir(data));
> +	data->host_cookie = COOKIE_UNMAPPED;
> +}
> +
> +static int dw_mci_cqe_get_cd(struct mmc_host *mmc)
> +{
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct dw_mci *host = slot->host;
> +	int gpio_cd = mmc_gpio_get_cd(mmc);
> +	int present = -1;
> +
> +	/* Use platform get_cd function, else try onboard card detect */
> +	if (((mmc->caps & MMC_CAP_NEEDS_POLL)
> +		|| !mmc_card_is_removable(mmc))) {
> +		present = 1;
> +
> +		if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
> +			if (mmc->caps & MMC_CAP_NEEDS_POLL) {
> +				dev_info(&mmc->class_dev,
> +					"card is polling.\n");
> +			} else {
> +				dev_info(&mmc->class_dev,
> +					"card is non-removable.\n");
> +			}
> +			set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
> +		}
> +
> +		return present;
> +	} else if (gpio_cd >= 0) {
> +		present = gpio_cd;
> +	} else {
> +		/*SD card detect using IP regs is todo*/
> +		dev_err(&mmc->class_dev, "SD card detect using IP regs is ToDo.\n");
> +	}
> +
> +	spin_lock_bh(&host->lock);
> +
> +	if (present && !test_and_set_bit(DW_MMC_CARD_PRESENT, &slot->flags))
> +		dev_dbg(&mmc->class_dev, "card is present\n");
> +	else if (!present &&
> +		!test_and_clear_bit(DW_MMC_CARD_PRESENT, &slot->flags))
> +		dev_dbg(&mmc->class_dev, "card is not present\n");
> +
> +	spin_unlock_bh(&host->lock);
> +
> +	return present;
> +}
> +
> +static void dw_mci_cqe_submit_data_dma(struct dw_mci *host)
> +{
> +	if (host->dma_64bit_address == 1)
> +		dw_mci_cqe_prepare_desc64(host, host->data, host->sg);
> +	else
> +		dw_mci_cqe_prepare_desc32(host, host->data, host->sg);
> +
> +}
> +
> +static void dw_mci_cqe_submit_data(struct dw_mci *host, struct mmc_data *data)
> +{
> +	u32 dir = 0;
> +
> +	host->sg = NULL;
> +	host->data = data;
> +
> +	if (data->flags & MMC_DATA_READ)
> +		dir = DMA_FROM_DEVICE;
> +	else
> +		dir = DMA_TO_DEVICE;
> +
> +	host->dma_nents = dma_map_sg(mmc_dev(host->slot->mmc),
> +					data->sg, data->sg_len, dir);
> +	host->sg = data->sg;
> +
> +	host->using_dma = 1;
> +
> +	dw_mci_cqe_submit_data_dma(host);
> +}
> +
> +static void dw_mci_cqe_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
> +{
> +	struct dw_mci *host = slot->host;
> +	const struct dw_mci_drv_data *drv_data = host->drv_data;
> +	unsigned int clock = slot->clock;
> +	u32 div = 0;
> +
> +	slot->mmc->actual_clock = 0;
> +
> +	if (clock != host->current_speed || force_clkinit) {
> +		div = host->bus_hz / clock;
> +		if (host->bus_hz % clock)
> +			div += 1;
> +
> +		if (clock != slot->__clk_old) {
> +			/* Silent the verbose log if calling from PM context */
> +			dev_info(&slot->mmc->class_dev,
> +				"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n",
> +				slot->id, host->bus_hz, clock, host->bus_hz / div, div);
> +		}
> +
> +		slot->__clk_old = clock;
> +		slot->mmc->actual_clock = host->bus_hz / div;
> +
> +		if (drv_data && drv_data->set_ios)
> +			drv_data->set_ios(slot, &slot->mmc->ios);
> +	}
> +}
> +
> +
> +static void dw_mci_cqe_err_handle(struct dw_mci *host, struct mmc_command *cmd)
> +{
> +	u32 status = 0;
> +	int err = 0;
> +	int rty_cnt = 0;
> +	int pstat_rty = 0;
> +
> +	do {
> +		mci_writew(host, ERROR_INT_STAT_R,
> +			mci_readw(host, ERROR_INT_STAT_R) & 0xffff);
> +		/*synchronous abort: stop host dma*/
> +		mci_writeb(host, BGAP_CTRL_R, SDMMC_STOP_BG_REQ);
> +		dw_mci_cqe_wait_done(host,
> +			(u32 *)(host->regs + SDMMC_NORMAL_INT_STAT_R),
> +				SDMMC_XFER_COMPLETE, SDMMC_XFER_COMPLETE);
> +
> +		mci_writew(host, NORMAL_INT_STAT_R, SDMMC_XFER_COMPLETE);
> +
> +		do {
> +			if (cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200) {
> +				dw_mci_cqe_prep_stop_abort(host, cmd);
> +				mdelay(1);
> +
> +				err = dw_mci_cqe_wait_while_busy(host, &status);
> +
> +				rty_cnt++;
> +				if (rty_cnt > 100) {
> +					if (err == -DW_MCI_NOT_READY)
> +						dev_err(host->dev, "status check failed, err = %d, status = 0x%x\n",
> +							err, status);
> +						break;
> +				}
> +			} else {
> +				break;
> +			}
> +		} while (err);
> +
> +		mci_writeb(host, SW_RST_R, SDMMC_RST_CMD | SDMMC_RST_DAT);
> +		dw_mci_cqe_wait_done(host, (u32 *)(host->regs + SDMMC_CLK_CTRL_R),
> +				(BIT(25)|BIT(26)), 0);
> +		dw_mci_cqe_wait_done(host, (u32 *)(host->regs + SDMMC_PSTATE_REG),
> +				(SDMMC_CMD_INHIBIT | SDMMC_CMD_INHIBIT_DAT), 0);
> +		udelay(40);
> +
> +		pstat_rty++;
> +		if (pstat_rty > 5000) {
> +			dev_err(host->dev, "wait pstate register data line ready timeout\n");
> +			break;
> +		}
> +	} while ((mci_readl(host, PSTATE_REG) & 0xf00000) != 0xf00000 ||
> +		(mci_readl(host, PSTATE_REG) & 0xf0) != 0xf0);
> +}
> +
> +static void dw_mci_cqe_send_stop_abort(struct dw_mci *host,
> +			      struct dw_mci_slot *slot,
> +			      struct mmc_command *cmd)
> +{
> +	dw_mci_cqe_reset(host);
> +
> +	if (cmd->data)
> +		dw_mci_cqe_err_handle(host, cmd);
> +	else
> +		return;
> +}
> +
> +static u32 dw_mci_cqe_prepare_data_flags(struct mmc_command *cmd)
> +{
> +	u32 dataflags;
> +	int read_flag = 1;
> +	int mul_blk_flag = 0;
> +	int auto_stop_flag = 0;
> +
> +	if (cmd->opcode == MMC_WRITE_BLOCK ||
> +	   cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK ||
> +	   cmd->opcode == MMC_LOCK_UNLOCK ||
> +	   (cmd->opcode == MMC_GEN_CMD && cmd->arg == 0))
> +		read_flag = 0;
> +
> +	if (cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK ||
> +	   cmd->opcode == MMC_READ_MULTIPLE_BLOCK) {
> +		mul_blk_flag = 1;
> +		auto_stop_flag = 1;
> +	}
> +
> +	dataflags = (mul_blk_flag << SDMMC_MULTI_BLK_SEL) |
> +		    (read_flag << SDMMC_DATA_XFER_DIR) |
> +		    (auto_stop_flag << SDMMC_AUTO_CMD_ENABLE) |
> +		    (SDMMC_BLOCK_COUNT_ENABLE) |
> +		    (SDMMC_DMA_ENABLE);
> +
> +	return dataflags;
> +}
> +
> +static int dw_mci_cqe_command_complete(struct dw_mci *host, u16 interrupt,
> +					int *cmd_error)
> +{
> +	if (interrupt & (SDMMC_CMD_IDX_ERR | SDMMC_CMD_END_BIT_ERR
> +		| SDMMC_CMD_CRC_ERR)) {
> +		if (host->tuning)
> +			*cmd_error = -TUNING_ERR;
> +		else
> +			*cmd_error = -EILSEQ;
> +	} else if (interrupt & SDMMC_CMD_TOUT_ERR) {
> +		if (host->tuning)
> +			*cmd_error = -TUNING_ERR;
> +		else
> +			*cmd_error = -ETIMEDOUT;
> +	} else {
> +		*cmd_error = 0;
> +	}
> +
> +	return *cmd_error;
> +}
> +
> +static int dw_mci_cqe_data_complete(struct dw_mci *host, u16 interrupt,
> +					int *data_error)
> +{
> +	if (interrupt & (SDMMC_DATA_END_BIT_ERR | SDMMC_DATA_CRC_ERR)) {
> +		if (host->tuning)
> +			*data_error = -TUNING_ERR;
> +		else
> +			*data_error = -EILSEQ;
> +	} else if (interrupt & SDMMC_DATA_TOUT_ERR) {
> +		if (host->tuning)
> +			*data_error = -TUNING_ERR;
> +		else
> +			*data_error = -ETIMEDOUT;
> +	} else if (interrupt & SDMMC_ADMA_ERR) {
> +		*data_error = -EIO;
> +	} else {
> +		*data_error = 0;
> +	}
> +
> +	return *data_error;
> +}
> +
> +static void __dw_mci_cqe_start_request(struct dw_mci *host,
> +				   struct dw_mci_slot *slot,
> +				   struct mmc_command *cmd)
> +{
> +	struct mmc_data *data;
> +	u32 cmdflags;
> +	u32 dataflags;
> +	int ret = 0;
> +
> +	data = cmd->data;
> +
> +	if (data) {
> +		mci_writew(host, BLOCKCOUNT_R, data->blocks);
> +		mci_writel(host, BLOCKSIZE_R, data->blksz);
> +		mci_writel(host, ADMA_SA_LOW_R, host->sg_dma);
> +
> +		dataflags = dw_mci_cqe_prepare_data_flags(cmd);
> +
> +		mci_writew(host, XFER_MODE_R, dataflags);
> +	} else {
> +		if (cmd->opcode == MMC_SET_BLOCK_COUNT)
> +			host->is_sbc = 1;
> +		else
> +			host->is_sbc = 0;
> +
> +		mci_writew(host, XFER_MODE_R, 0);
> +	}
> +
> +	mci_writel(host, ARGUMENT_R, cmd->arg);
> +
> +	cmdflags = dw_mci_cqe_prepare_command(slot->mmc, cmd);
> +
> +	if (data) {
> +		data->bytes_xfered = 0;
> +		if (host->use_dma == TRANS_MODE_DMA) {
> +			dw_mci_cqe_submit_data(host, data);
> +			wmb(); /* drain writebuffer */
> +		} else {
> +			/*Using PIO mode*/
> +			dev_err(host->dev, "pio mode is not supported currently\n");
> +		}
> +	}
> +
> +	ret = dw_mci_cqe_start_command(host, cmd, cmdflags);
> +
> +	if (ret == 0) {
> +		dw_mci_cqe_read_rsp(host, cmd, cmd->resp);
> +
> +		if (data)
> +			data->bytes_xfered += (data->blocks * data->blksz);
> +	}
> +
> +	dw_mci_cqe_command_complete(host, host->error_interrupt, &cmd->error);
> +	if (data) {
> +		dw_mci_cqe_data_complete(host, host->error_interrupt, &data->error);
> +		if (host->use_dma == TRANS_MODE_DMA)
> +			dw_mci_cqe_stop_dma(host, data);
> +		else {
> +			/*Using PIO mode*/
> +			dev_err(host->dev, "pio mode is not supported currently\n");
> +		}
> +	}
> +
> +	if (ret != 0)
> +		dw_mci_cqe_send_stop_abort(host, slot, cmd);
> +
> +	if (cmd->opcode == SD_SWITCH_VOLTAGE) {
> +		/*
> +		 * If cmd11 needs to be dealt with specially, put in here.
> +		 */
> +	}
> +}
> +
> +static void dw_mci_cqe_start_request(struct dw_mci *host,
> +				 struct dw_mci_slot *slot)
> +{
> +	struct mmc_request *mrq = slot->mrq;
> +
> +	if (mrq->sbc)
> +		__dw_mci_cqe_start_request(host, slot, mrq->sbc);
> +
> +	if (mrq->cmd)
> +		__dw_mci_cqe_start_request(host, slot, mrq->cmd);
> +}
> +
> +static int dw_mci_switch(struct mmc_host *mmc,
> +			 u8 set,
> +			 u8 index,
> +			 u8 value,
> +			 unsigned int timeout_ms)
> +{
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct dw_mci *host = slot->host;
> +	struct mmc_command cmd;
> +	int err = 0;
> +	u32 cmdr;
> +
> +	memset(&cmd, 0, sizeof(struct mmc_command));
> +
> +	cmd.opcode		= MMC_SWITCH;
> +	cmd.arg			= (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
> +				(index << 16) |
> +				(value << 8) |
> +				set;
> +	cmd.flags		= MMC_CMD_AC|MMC_RSP_SPI_R1B | MMC_RSP_R1B;
> +	cmd.data		= NULL;
> +
> +	cmdr = (cmd.opcode << 8) | SDMMC_RESP_LEN_48B |
> +		SDMMC_CMD_CHK_RESP_CRC | SDMMC_CMD_IDX_CHK_ENABLE;
> +
> +	mci_writew(host, XFER_MODE_R, 0);
> +	mci_writel(host, ARGUMENT_R, cmd.arg);
> +
> +	err = dw_mci_cqe_start_command(host, &cmd, cmdr);
> +
> +	if (err) {
> +		dev_err(host->dev, "interrupt status reg :0x%x, error reg : 0x%x\n",
> +			host->normal_interrupt, host->error_interrupt);
> +	}
> +
> +	return err;
> +}
> +
> +static int dw_mci_cqe_switch(struct mmc_host *mmc, bool enable)
> +{
> +	struct mmc_card *card = mmc->card;
> +	int ret = 0;
> +	u8 val = enable ? EXT_CSD_CMDQ_MODE_ENABLED : 0;
> +
> +	if (!card->ext_csd.cmdq_support) {
> +		dev_err(&mmc->class_dev, "The device card does not support cqe mode\n");
> +		return 0;
> +	}
> +
> +	ret = dw_mci_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
> +			    EXT_CSD_CMDQ_MODE_EN, val,
> +			    card->ext_csd.generic_cmd6_time);
> +	if (ret) {
> +		dev_err(&mmc->class_dev, "cmdq mode %sable failed %d\n",
> +			enable ? "en" : "dis", ret);
> +		goto out;
> +	} else {
> +		card->ext_csd.cmdq_en = enable;
> +	}
> +
> +out:
> +	return ret;
> +}
> +
> +static void dw_mci_cqe_request(struct mmc_host *mmc, struct mmc_request *mrq)
> +{
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct dw_mci *host = slot->host;
> +	int ret;
> +	u32 status = 0;
> +
> +	WARN_ON(slot->mrq);
> +
> +	/*
> +	 * The check for card presence and queueing of the request must be
> +	 * atomic, otherwise the card could be removed in between and the
> +	 * request wouldn't fail until another card was inserted.
> +	 */
> +
> +	if (!dw_mci_cqe_get_cd(mmc)) {
> +		mrq->cmd->error = -ENOMEDIUM;
> +		mmc_request_done(mmc, mrq);
> +		return;
> +	}
> +
> +	down_write(&host->cr_rw_sem);
> +
> +	/*cmdq case needs extra check*/
> +	if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE)) {
> +		if ((host->cqe) == NULL) {
> +			dev_err(host->dev, "dw_mci_request_cqe not done yet\n");
> +			mdelay(2);
> +		}
> +
> +		if (mmc->cqe_on == false && host->cqe->activated == true
> +			&& slot->switch_partition == 0)
> +			cqhci_deactivate(mmc);
> +
> +		if (mrq->cmd->opcode == MMC_SWITCH && mrq->cmd->arg == DW_MCI_CMDQ_DISABLED)
> +			slot->switch_partition = 1;
> +
> +		/* we do not need to disable cmdq if it is rpmb request
> +		 * because rpmb has been changed to rpmb partition in block.c
> +		 * Also, we do not need to disable cmdq if this command is disable/enable cmdq
> +		 */
> +		if (mmc->card && mmc->card->ext_csd.cmdq_en == 1
> +			&& slot->switch_partition == 0
> +			&& host->cmd_atomic == false) {
> +			ret = dw_mci_cqe_switch(mmc, false);
> +
> +			if (mrq->cmd->opcode == MMC_SLEEP_AWAKE ||
> +				(mrq->cmd->opcode == MMC_SELECT_CARD
> +					&& mrq->cmd->arg == 0) || (mrq->cmd->opcode == MMC_SWITCH
> +					&& mrq->cmd->arg == DW_MCI_POWEROFF))
> +				host->cqe_reenable = 0;
> +			else
> +				host->cqe_reenable = 1;
> +
> +			if (ret)
> +				dev_err(host->dev, "disable cmdq failed !\n");
> +
> +			dw_mci_cqe_wait_while_busy(host, &status);
> +		}
> +
> +		if (mrq->cmd->opcode == MMC_SWITCH
> +			&& mrq->cmd->arg == DW_MCI_CMDQ_ENABLED)
> +			slot->switch_partition = 0;
> +
> +		if (mrq->cmd->opcode == MMC_ERASE_GROUP_START) {
> +			host->cmd_atomic = true;
> +			host->cqe_reenable = 0;
> +		}
> +
> +		if (host->cmd_atomic == true
> +			&& mrq->cmd->opcode == MMC_SEND_STATUS) {
> +			host->cmd_atomic = false;
> +			host->cqe_reenable = 1;
> +		}
> +	}
> +
> +	slot->mrq = mrq;
> +	host->mrq = mrq;
> +
> +	dw_mci_cqe_start_request(host, slot);
> +
> +	tasklet_schedule(&host->tasklet);
> +
> +	/*cmdq case needs extra check*/
> +	if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE) &&
> +		host->cqe_reenable == 1) {
> +		if (mmc->card && mmc->card->ext_csd.cmdq_en == 0) {
> +			ret = dw_mci_cqe_switch(mmc, true);
> +			host->cqe_reenable = 0;
> +			if (ret)
> +				dev_err(host->dev, "switch cmdq failed !\n");
> +			dw_mci_cqe_wait_while_busy(host, &status);
> +		}
> +	}
> +
> +	up_write(&host->cr_rw_sem);
> +}
> +
> +static void dw_mci_cqe_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> +{
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct dw_mci *host = slot->host;
> +	const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
> +
> +	switch (ios->timing) {
> +	case MMC_TIMING_MMC_HS400:
> +		mci_writew(host, HOST_CTRL2_R,
> +			(mci_readw(host, HOST_CTRL2_R)
> +				& SDMMC_UHS_MODE_SEL_MASK) | SDMMC_HS400);
> +		break;
> +	case MMC_TIMING_MMC_HS200:
> +		mci_writew(host, HOST_CTRL2_R,
> +			(mci_readw(host, HOST_CTRL2_R)
> +				& SDMMC_UHS_MODE_SEL_MASK) | SDMMC_HS200);
> +		break;
> +	case MMC_TIMING_MMC_HS:
> +		mci_writew(host, HOST_CTRL2_R,
> +			(mci_readw(host, HOST_CTRL2_R)
> +				& SDMMC_UHS_MODE_SEL_MASK) | SDMMC_SDR);
> +		break;
> +	default:
> +		/*MMC_TIMING_LEGACY case*/
> +		mci_writew(host, HOST_CTRL2_R,
> +			(mci_readw(host, HOST_CTRL2_R)
> +				& SDMMC_UHS_MODE_SEL_MASK) | SDMMC_LEGACY);
> +	}
> +
> +	slot->clock = ios->clock;
> +
> +	if (drv_data && drv_data->set_ios)
> +		drv_data->set_ios(slot, ios);
> +
> +	switch (ios->bus_width) {
> +	case MMC_BUS_WIDTH_4:
> +		mci_writeb(host, HOST_CTRL1_R,
> +			(mci_readb(host, HOST_CTRL1_R) &
> +			(SDMMC_EXT_DAT_XFER_MASK & SDMMC_DAT_XFER_WIDTH_MASK))
> +				|SDMMC_BUS_WIDTH_4);
> +		break;
> +	case MMC_BUS_WIDTH_8:
> +		mci_writeb(host, HOST_CTRL1_R,
> +			(mci_readb(host, HOST_CTRL1_R) &
> +				SDMMC_EXT_DAT_XFER_MASK) | SDMMC_BUS_WIDTH_8);
> +		break;
> +	default:
> +		/* set default 1 bit mode */
> +		mci_writeb(host, HOST_CTRL1_R,
> +			(mci_readb(host, HOST_CTRL1_R) &
> +				(SDMMC_EXT_DAT_XFER_MASK &
> +				SDMMC_DAT_XFER_WIDTH_MASK)) | SDMMC_BUS_WIDTH_1);
> +	}
> +}
> +
> +static int dw_mci_cqe_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
> +{
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct dw_mci *host = slot->host;
> +	const struct dw_mci_drv_data *drv_data = host->drv_data;
> +
> +	if (drv_data && drv_data->switch_voltage)
> +		return drv_data->switch_voltage(mmc, ios);
> +
> +	return 0;
> +}
> +
> +static int dw_mci_cqe_get_ro(struct mmc_host *mmc)
> +{
> +	int read_only;
> +	int gpio_ro = mmc_gpio_get_ro(mmc);
> +
> +	/* Use platform get_ro function, else try on board write protect */
> +	if (gpio_ro >= 0)
> +		read_only = gpio_ro;
> +	else
> +		/*Need to read the IP register to judge if ro*/
> +		dev_err(&mmc->class_dev, "IP get_ro feature is not implemented currently.\n");
> +
> +	dev_dbg(&mmc->class_dev, "card is %s\n",
> +		read_only ? "read-only" : "read-write");
> +
> +	return read_only;
> +}
> +
> +static int dw_mci_cqe_execute_tuning(struct mmc_host *mmc, u32 opcode)
> +{
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct dw_mci *host = slot->host;
> +	const struct dw_mci_drv_data *drv_data = host->drv_data;
> +	int err = -EINVAL;
> +
> +	if (drv_data && drv_data->execute_tuning)
> +		err = drv_data->execute_tuning(slot, opcode);
> +	return err;
> +
> +}
> +
> +static int dw_mci_cqe_prepare_hs400_tuning(struct mmc_host *mmc,
> +				       struct mmc_ios *ios)
> +{
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct dw_mci *host = slot->host;
> +	const struct dw_mci_drv_data *drv_data = host->drv_data;
> +
> +	if (drv_data && drv_data->prepare_hs400_tuning)
> +		return drv_data->prepare_hs400_tuning(host, ios);
> +
> +	return 0;
> +}
> +
> +static void dw_mci_cqe_hs400_complete(struct mmc_host *mmc)
> +{
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct dw_mci *host = slot->host;
> +	const struct dw_mci_drv_data *drv_data = host->drv_data;
> +
> +	if (drv_data && drv_data->hs400_complete)
> +		drv_data->hs400_complete(mmc);
> +}
> +
> +static void dw_mci_cqe_init_card(struct mmc_host *mmc, struct mmc_card *card)
> +{
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct dw_mci *host = slot->host;
> +	const struct dw_mci_drv_data *drv_data = host->drv_data;
> +
> +	/*
> +	 * Add any quirks for this synopsys IP here or
> +	 * deal with something special for some specific
> +	 * vendors' SOC platform by calling drv_data->init_card().
> +	 */
> +	if (drv_data && drv_data->init_card)
> +		drv_data->init_card(mmc, card);
> +}
> +
> +static const struct mmc_host_ops dw_mci_ops = {
> +	.request		= dw_mci_cqe_request,
> +	.pre_req		= dw_mci_cqe_pre_req,
> +	.post_req		= dw_mci_cqe_post_req,
> +	.set_ios		= dw_mci_cqe_set_ios,
> +	.get_ro			= dw_mci_cqe_get_ro,
> +	.get_cd			= dw_mci_cqe_get_cd,
> +	.execute_tuning		= dw_mci_cqe_execute_tuning,
> +	.start_signal_voltage_switch = dw_mci_cqe_switch_voltage,
> +	.init_card		= dw_mci_cqe_init_card,
> +	.prepare_hs400_tuning	= dw_mci_cqe_prepare_hs400_tuning,
> +	.hs400_complete         = dw_mci_cqe_hs400_complete,
> +};
> +
> +static void dw_mci_cqe_tasklet_func(unsigned long priv)
> +{
> +	struct dw_mci *host = (struct dw_mci *)priv;
> +	struct mmc_host *prev_mmc = host->slot->mmc;
> +	struct mmc_request *mrq;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&host->irq_lock, flags);
> +
> +	host->cmd = NULL;
> +	host->data = NULL;
> +	mrq = host->mrq;
> +	host->slot->mrq = NULL;
> +	host->mrq = NULL;
> +
> +	spin_unlock_irqrestore(&host->irq_lock, flags);
> +
> +	mmc_request_done(prev_mmc, mrq);
> +}
> +
> +static irqreturn_t dw_mci_cqe_interrupt(int irq, void *dev_id)
> +{
> +	struct dw_mci *host = dev_id;
> +	struct mmc_host *mmc = host->slot->mmc;
> +	struct cqhci_host *cq_host = NULL;
> +	int cmd_error = 0, data_error = 0;
> +
> +	if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE))
> +		cq_host = mmc->cqe_private;
> +
> +	dw_mci_get_int(host);
> +
> +	if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE)) {
> +		if (mmc->cqe_on == false && cq_host->activated == false)
> +			dw_mci_clr_signal_int(host);
> +	} else {
> +		dw_mci_clr_signal_int(host);
> +	}
> +	/*if run the cmdq mode*/
> +	if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE) &&
> +		mmc->cqe_on == true && cq_host->activated == true) {
> +		if (host->normal_interrupt & SDMMC_ERR_INTERRUPT) {
> +			dev_err(host->dev, "cmdq error: interrupt status=%08x, error interrupt=0x%08x, CQIS=0x%x, CQTCN=0x%x\n",
> +				host->normal_interrupt, host->error_interrupt,
> +				readl(host->cqe->mmio + CQHCI_IS),
> +				readl(host->cqe->mmio + CQHCI_TCN));
> +
> +			dw_mci_cqe_command_complete(host, host->error_interrupt, &cmd_error);
> +			dw_mci_cqe_data_complete(host, host->error_interrupt, &data_error);
> +		}
> +		cqhci_irq(mmc, (u32)(host->normal_interrupt), cmd_error, data_error);
> +		dw_mci_clr_int(host);
> +
> +		return IRQ_HANDLED;
> +	}
> +
> +	if (host->int_waiting) {
> +		del_timer(&host->timer);
> +		complete(host->int_waiting);
> +	}
> +
> +	return IRQ_HANDLED;
> +
> +}
> +
> +static void dw_mci_cqe_setup(struct dw_mci *host)
> +{
> +	mci_writeb(host, SW_RST_R, (SDMMC_RST_ALL|SDMMC_RST_CMD|SDMMC_RST_DAT));
> +	mci_writeb(host, TOUT_CTRL_R, 0xe);
> +	mci_writew(host, HOST_CTRL2_R, SDMMC_HOST_VER4_ENABLE|SDMMC_SIGNALING_EN);
> +	mci_writew(host, NORMAL_INT_STAT_EN_R, 0xffff);
> +	mci_writew(host, ERROR_INT_STAT_EN_R, SDMMC_ALL_ERR_STAT_EN);
> +	/*Card detect will be enabled in the last*/
> +	mci_writew(host, NORMAL_INT_SIGNAL_EN_R,
> +		(~(SDMMC_CARD_INSERTION_SIGNAL_EN | SDMMC_CARD_REMOVAL_SIGNAL_EN |
> +			SDMMC_CARD_INTERRUPT_SIGNAL_EN) & 0xffff));
> +	mci_writew(host, ERROR_INT_SIGNAL_EN_R, SDMMC_ALL_ERR_SIGNAL_EN);
> +	mci_writeb(host, CTRL_R, SDMMC_RST_N_OE|SDMMC_RST_N|SDMMC_CARD_IS_EMMC);
> +	mci_writeb(host, HOST_CTRL1_R,
> +		(mci_readb(host, HOST_CTRL1_R)&0xe7) | (SDMMC_ADMA2_32 << SDMMC_DMA_SEL));
> +	mci_writeb(host, MSHC_CTRL_R, mci_readb(host, MSHC_CTRL_R) & (~SDMMC_CMD_CONFLICT_CHECK));
> +	mci_writew(host, CLK_CTRL_R, mci_readw(host, CLK_CTRL_R)|SDMMC_INTERNAL_CLK_EN);
> +}
> +
> +static int dw_mci_cqe_init_slot_caps(struct dw_mci_slot *slot)
> +{
> +	struct dw_mci *host = slot->host;
> +	const struct dw_mci_drv_data *drv_data = host->drv_data;
> +	struct mmc_host *mmc = slot->mmc;
> +	int ctrl_id;
> +
> +	if (host->pdata->caps)
> +		mmc->caps = host->pdata->caps;
> +
> +	if (host->pdata->pm_caps)
> +		mmc->pm_caps = host->pdata->pm_caps;
> +
> +	if (host->dev->of_node) {
> +		ctrl_id = of_alias_get_id(host->dev->of_node, "mshc");
> +		if (ctrl_id < 0)
> +			ctrl_id = 0;
> +	} else {
> +		ctrl_id = to_platform_device(host->dev)->id;
> +	}
> +
> +	if (drv_data && drv_data->caps) {
> +		if (ctrl_id >= drv_data->num_caps) {
> +			dev_err(host->dev, "invalid controller id %d\n",
> +				ctrl_id);
> +			return -EINVAL;
> +		}
> +		mmc->caps |= drv_data->caps[ctrl_id];
> +	}
> +
> +	if (drv_data && drv_data->shift_rsp)
> +		host->shift = 1;
> +	else
> +		host->shift = 0;
> +
> +	if (host->pdata->caps2)
> +		mmc->caps2 = host->pdata->caps2;
> +
> +	mmc->f_min = DW_MCI_FREQ_MIN;
> +	if (!mmc->f_max)
> +		mmc->f_max = DW_MCI_FREQ_MAX;
> +
> +	/* Process SDIO IRQs through the sdio_irq_work. */
> +	if (mmc->caps & MMC_CAP_SDIO_IRQ)
> +		mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
> +
> +	return 0;
> +}
> +
> +static int dw_mci_cqe_init_slot(struct dw_mci *host)
> +{
> +	struct mmc_host *mmc;
> +	struct dw_mci_slot *slot;
> +	int ret;
> +
> +	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
> +	if (!mmc)
> +		return -ENOMEM;
> +
> +	slot = mmc_priv(mmc);
> +	slot->id = 0;
> +	slot->sdio_id = host->sdio_id0 + slot->id;
> +	slot->mmc = mmc;
> +	slot->switch_partition = 0;
> +	slot->host = host;
> +	host->slot = slot;
> +
> +	mmc->ops = &dw_mci_ops;
> +
> +	/*if there are external regulators, get them*/
> +	ret = mmc_regulator_get_supply(mmc);
> +	if (ret)
> +		goto err_host_allocated;
> +
> +	if (!mmc->ocr_avail)
> +		mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
> +
> +	dev_info(host->dev, "regulator support volage ocr_avail=0x%x\n",
> +			mmc->ocr_avail);
> +
> +	ret = mmc_of_parse(mmc);
> +	if (ret)
> +		goto err_host_allocated;
> +
> +	ret = dw_mci_cqe_init_slot_caps(slot);
> +	if (ret)
> +		goto err_host_allocated;
> +
> +	/* Useful defaults if platform data is unset. */
> +	if (host->use_dma == TRANS_MODE_DMA) {
> +		mmc->max_segs = 256;
> +		mmc->max_blk_size = 512;
> +		mmc->max_seg_size = 0x1000;
> +		mmc->max_req_size = mmc->max_seg_size * mmc->max_segs;
> +		mmc->max_blk_count = mmc->max_req_size / 512;
> +	} else {
> +		dev_info(host->dev, "dw-mmc-cqe pio mode is ToDo.\n");
> +		/* To DO, TRANS_MODE_PIO */
> +	}
> +
> +	dw_mci_cqe_get_cd(mmc);
> +
> +	ret = mmc_add_host(mmc);
> +	if (ret)
> +		goto err_host_allocated;
> +
> +	return 0;
> +
> +err_host_allocated:
> +	mmc_free_host(mmc);
> +	return ret;
> +}
> +
> +static void dw_mci_cqe_cleanup_slot(struct dw_mci_slot *slot)
> +{
> +	/* Debugfs stuff is cleaned up by mmc core */
> +	mmc_remove_host(slot->mmc);
> +	slot->host->slot = NULL;
> +	mmc_free_host(slot->mmc);
> +}
> +
> +static void dw_mci_cqe_init_dma(struct dw_mci *host)
> +{
> +	host->use_dma = TRANS_MODE_DMA;
> +
> +	/* Determine which DMA interface to use */
> +	/* using 32bit DMA by default,
> +	 * user can modify this setting by drv_data->init()
> +	 */
> +	if (host->use_dma == TRANS_MODE_DMA) {
> +		host->dma_64bit_address = 0;
> +		dev_info(host->dev, "IDMAC supports 32-bit address mode.\n");
> +	}
> +
> +	/* Alloc memory for sg translation */
> +	host->sg_cpu = dma_alloc_coherent(host->dev,
> +						DW_MCI_DESC_LEN,
> +						&host->sg_dma, GFP_KERNEL);
> +	if (!host->sg_cpu) {
> +		dev_err(host->dev,
> +			"%s: could not alloc DMA memory\n",
> +			__func__);
> +		goto no_dma;
> +	}
> +
> +	return;
> +
> +no_dma:
> +	dev_info(host->dev, "Using PIO mode.\n");
> +	host->use_dma = TRANS_MODE_PIO;
> +}
> +
> +#ifdef CONFIG_OF
> +static struct dw_mci_board *dw_mci_cqe_parse_dt(struct dw_mci *host)
> +{
> +	struct dw_mci_board *pdata;
> +	struct device *dev = host->dev;
> +	const struct dw_mci_drv_data *drv_data = host->drv_data;
> +	int ret;
> +	u32 clock_frequency;
> +
> +	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
> +	if (!pdata)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* find reset controller when exist */
> +	pdata->rstc = devm_reset_control_get_optional(dev, "reset");
> +	if (IS_ERR(pdata->rstc)) {
> +		if (PTR_ERR(pdata->rstc) == -EPROBE_DEFER)
> +			return ERR_PTR(-EPROBE_DEFER);
> +	}
> +
> +	device_property_read_u32(dev, "card-detect-delay",
> +		&pdata->detect_delay_ms);
> +
> +	if (!device_property_read_u32(dev, "clock-frequency", &clock_frequency))
> +		pdata->bus_hz = clock_frequency;
> +
> +	if (drv_data && drv_data->parse_dt) {
> +		ret = drv_data->parse_dt(host);
> +		if (ret)
> +			return ERR_PTR(ret);
> +	}
> +
> +	return pdata;
> +}
> +
> +#else /* CONFIG_OF */
> +static struct dw_mci_board *dw_mci_cqe_parse_dt(struct dw_mci *host)
> +{
> +	return ERR_PTR(-EINVAL);
> +}
> +#endif /* CONFIG_OF */
> +
> +static void dw_mci_cqe_cto_timer(struct timer_list *t)
> +{
> +	struct dw_mci *host = from_timer(host, t, timer);
> +
> +	if (host->int_waiting) {
> +		dev_err(host->dev, "fired, opcode=%d, arg=0x%x, irq status=0x%x, err irq=0x%x, auto err irq=0x%x\n",
> +				host->opcode, host->arg,
> +			host->normal_interrupt, host->error_interrupt,
> +			host->auto_error_interrupt);
> +
> +		dw_mci_clr_signal_int(host);
> +		dw_mci_get_int(host);
> +
> +		complete(host->int_waiting);
> +	}
> +}
> +
> +
> +static void dw_mci_cqe_enable_cd(struct dw_mci *host)
> +{
> +	/*
> +	 * No need for CD if all slots have a non-error GPIO
> +	 * as well as broken card detection is found.
> +	 */
> +	if (host->slot->mmc->caps & MMC_CAP_NEEDS_POLL
> +		|| !mmc_card_is_removable(host->slot->mmc))
> +		return;
> +}
> +
> +static void dw_mci_cqhci_init(struct dw_mci *host)
> +{
> +	if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE)) {
> +		host->cqe = cqhci_pltfm_init(host->pdev);
> +		if (PTR_ERR(host->cqe) == -EINVAL ||
> +		   PTR_ERR(host->cqe) == -ENOMEM ||
> +		   PTR_ERR(host->cqe) == -EBUSY) {
> +			dev_err(host->dev,
> +				"Unable to get the cmdq related attribute,err = %ld\n",
> +				PTR_ERR(host->cqe));
> +			host->cqe = 0;
> +			host->pdata->caps2 &= ~(MMC_CAP2_CQE|MMC_CAP2_CQE_DCMD);
> +		} else {
> +			host->cqe->ops = &dw_mci_cqhci_host_ops;
> +			cqhci_init(host->cqe, host->slot->mmc, 0);
> +		}
> +	}
> +}
> +
> +int dw_mci_cqe_probe(struct dw_mci *host)
> +{
> +	const struct dw_mci_drv_data *drv_data = host->drv_data;
> +	int ret = 0;
> +
> +	if (!host->pdata) {
> +		host->pdata = dw_mci_cqe_parse_dt(host);
> +		if (PTR_ERR(host->pdata) == -EPROBE_DEFER) {
> +			return -EPROBE_DEFER;
> +		} else if (IS_ERR(host->pdata)) {
> +			dev_err(host->dev, "platform data not available\n");
> +			return -EINVAL;
> +		}
> +	}
> +
> +	host->biu_clk = devm_clk_get(host->dev, "biu");
> +	if (IS_ERR(host->biu_clk)) {
> +		dev_dbg(host->dev, "biu clock not available\n");
> +	} else {
> +		ret = clk_prepare_enable(host->biu_clk);
> +		if (ret) {
> +			dev_err(host->dev, "failed to enable biu clock\n");
> +			return ret;
> +		}
> +	}
> +
> +	host->ciu_clk = devm_clk_get(host->dev, "ciu");
> +	if (IS_ERR(host->ciu_clk)) {
> +		dev_dbg(host->dev, "ciu clock not available\n");
> +		host->bus_hz = host->pdata->bus_hz;
> +	} else {
> +		ret = clk_prepare_enable(host->ciu_clk);
> +		if (ret) {
> +			dev_err(host->dev, "failed to enable ciu clock\n");
> +			goto err_clk_biu;
> +		}
> +
> +		if (host->pdata->bus_hz) {
> +			ret = clk_set_rate(host->ciu_clk, host->pdata->bus_hz);
> +			if (ret)
> +				dev_warn(host->dev,
> +					"Unable to set bus rate to %uHz\n",
> +					 host->pdata->bus_hz);
> +		}
> +		host->bus_hz = clk_get_rate(host->ciu_clk);
> +	}
> +
> +	if (!host->bus_hz) {
> +		dev_err(host->dev,
> +			"Platform data must supply bus speed\n");
> +		ret = -ENODEV;
> +		goto err_clk_ciu;
> +	}
> +
> +	if (!IS_ERR(host->pdata->rstc)) {
> +		reset_control_assert(host->pdata->rstc);
> +		usleep_range(10, 50);
> +		reset_control_deassert(host->pdata->rstc);
> +	}
> +
> +	timer_setup(&host->timer, dw_mci_cqe_cto_timer, 0);
> +
> +	spin_lock_init(&host->lock);
> +	spin_lock_init(&host->irq_lock);
> +	init_rwsem(&host->cr_rw_sem);
> +	tasklet_init(&host->tasklet, dw_mci_cqe_tasklet_func, (unsigned long)host);
> +
> +	host->cqe_reenable = 0;
> +	host->cmd_atomic = false;
> +
> +	/*pio mode's parameters should be initialized here*/
> +
> +	/*Initialize the eMMC IP related attribute*/
> +	dw_mci_cqe_setup(host);
> +
> +	dw_mci_cqe_init_dma(host);
> +
> +	/* This flag will be set 1 when doing tuning,
> +	 * we add this flag because
> +	 * some vendors might use other cmd instead of 21
> +	 * to tune phase under high speed interface.
> +	 * we use this flag to recognize if the system is under tuning stage.
> +	 */
> +	host->tuning = 0;
> +
> +	/*Timing_setting is to avoid sending command
> +	 *before setting phase in hs200, hs400
> +	 */
> +	host->current_speed = 0;
> +
> +	/*Do the rest of init for specific*/
> +	if (drv_data && drv_data->init) {
> +		ret = drv_data->init(host);
> +		if (ret) {
> +			dev_err(host->dev,
> +				"implementation specific init failed\n");
> +			goto err_dmaunmap;
> +		}
> +	}
> +
> +	ret = dw_mci_cqe_init_slot(host);
> +	if (ret) {
> +		dev_err(host->dev, "slot 0 init failed\n");
> +		goto err_dmaunmap;
> +	}
> +
> +	ret = devm_request_irq(host->dev, host->irq, dw_mci_cqe_interrupt,
> +				host->irq_flags, "dw-mci-cqe", host);
> +	if (ret)
> +		goto err_dmaunmap;
> +
> +	/*After the slot initialization,
> +	 *now we have mmc data and can initialize cmdq if user enabled
> +	 */
> +	dw_mci_cqhci_init(host);
> +
> +	/* Now that slots are all setup, we can enable card detect */
> +	dw_mci_cqe_enable_cd(host);
> +
> +	return 0;
> +
> +err_dmaunmap:
> +	if (!IS_ERR(host->pdata->rstc))
> +		reset_control_assert(host->pdata->rstc);
> +err_clk_ciu:
> +	clk_disable_unprepare(host->ciu_clk);
> +
> +err_clk_biu:
> +	clk_disable_unprepare(host->biu_clk);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(dw_mci_cqe_probe);
> +
> +void dw_mci_cqe_remove(struct dw_mci *host)
> +{
> +	dev_dbg(host->dev, "remove slot\n");
> +	if (host->slot)
> +		dw_mci_cqe_cleanup_slot(host->slot);
> +
> +	if (!IS_ERR(host->pdata->rstc))
> +		reset_control_assert(host->pdata->rstc);
> +
> +	clk_disable_unprepare(host->ciu_clk);
> +	clk_disable_unprepare(host->biu_clk);
> +
> +}
> +EXPORT_SYMBOL(dw_mci_cqe_remove);
> +
> +#ifdef CONFIG_PM
> +int dw_mci_cqe_runtime_suspend(struct device *dev)
> +{
> +	struct dw_mci *host = dev_get_drvdata(dev);
> +	int ret = 0;
> +
> +	if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE)) {
> +		if (host->slot) {
> +			dev_info(host->dev, "cqe suspend\n");
> +			ret = cqhci_suspend(host->slot->mmc);
> +			if (ret) {
> +				dev_err(host->dev, "cqe suspend failed\n");
> +				return ret;
> +			}
> +		}
> +	}
> +
> +	ret = pm_runtime_force_suspend(dev);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(dw_mci_cqe_runtime_suspend);
> +
> +int dw_mci_cqe_runtime_resume(struct device *dev)
> +{
> +	struct dw_mci *host = dev_get_drvdata(dev);
> +	const struct dw_mci_drv_data *drv_data = host->drv_data;
> +	int ret = 0;
> +
> +	ret = pm_runtime_force_resume(dev);
> +	if (ret) {
> +		dev_err(host->dev, "pm_runtime_force_resume failed\n");
> +		return ret;
> +	}
> +
> +	dw_mci_cqe_setup(host);
> +	if (drv_data && drv_data->init) {
> +		ret = drv_data->init(host);
> +		if (ret)
> +			dev_err(host->dev, "implementation specific init failed\n");
> +	}
> +
> +	init_completion(host->int_waiting);
> +
> +	if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE)) {
> +		if (host->slot) {
> +			dev_info(host->dev, "cqe resume\n");
> +			ret = cqhci_resume(host->slot->mmc);
> +			if (ret)
> +				dev_err(host->dev, "cqe resume failed\n");
> +		}
> +	}
> +
> +	dw_mci_cqe_setup_bus(host->slot, true);
> +	dw_mci_cqe_enable_cd(host);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(dw_mci_cqe_runtime_resume);
> +#endif /* CONFIG_PM */
> +
> +static int __init dw_mci_cqe_init(void)
> +{
> +	pr_info("Synopsys Designware Multimedia Card Interface Driver\n");
> +	return 0;
> +}
> +
> +static void __exit dw_mci_cqe_exit(void)
> +{
> +}
> +
> +module_init(dw_mci_cqe_init);
> +module_exit(dw_mci_cqe_exit);
> +
> +MODULE_DESCRIPTION("DW Multimedia Card CMDQ Interface driver");
> +MODULE_AUTHOR("<jyanchou@realtek.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/mmc/host/dw_mmc_cqe.h b/drivers/mmc/host/dw_mmc_cqe.h
> new file mode 100644
> index 000000000000..ef52b67aceb6
> --- /dev/null
> +++ b/drivers/mmc/host/dw_mmc_cqe.h
> @@ -0,0 +1,444 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + *  Copyright (C) 2023 Realtek Semiconductors, All Rights Reserved.
> + *
> + */
> +
> +#ifndef __DW_MMC_CQE_H
> +#define __DW_MMC_CQE_H
> +
> +#include <linux/dmaengine.h>
> +#include <linux/interrupt.h>
> +#include <linux/mmc/core.h>
> +#include <linux/reset.h>
> +#include <linux/scatterlist.h>
> +
> +struct dw_mci {
> +	spinlock_t              lock;
> +	spinlock_t              irq_lock;
> +	struct tasklet_struct   tasklet;
> +	struct rw_semaphore     cr_rw_sem;
> +
> +	void __iomem            *regs;
> +	resource_size_t         phy_regs;
> +
> +	struct mmc_request      *mrq;
> +	struct mmc_command      *cmd;
> +	struct mmc_command	stop_abort;
> +	struct mmc_data         *data;
> +
> +	struct clk              *biu_clk;
> +	struct clk              *ciu_clk;
> +	struct dw_mci_slot      *slot;
> +	struct timer_list       timer;
> +	struct completion	*int_waiting;
> +
> +	unsigned int		*desc_vaddr;
> +	unsigned int            *sg_cpu;
> +	dma_addr_t              sg_dma;
> +	int                     use_dma;
> +
> +	struct platform_device  *pdev;
> +	struct device           *dev;
> +	struct dw_mci_board     *pdata;
> +	const struct dw_mci_drv_data    *drv_data;
> +	void                    *priv;
> +
> +	u32			opcode;
> +	u32			arg;
> +	u16                     normal_interrupt;
> +	u16                     error_interrupt;
> +	u16                     auto_error_interrupt;
> +
> +	u32                     bus_hz;
> +	u32			current_speed;
> +	u32			stop_cmdr;
> +	bool			is_sbc;
> +	int			dma_64bit_address;
> +	int			using_dma;
> +
> +	unsigned long           irq_flags; /* IRQ flags */
> +	int                     irq;
> +	int                     sdio_id0;
> +
> +	struct scatterlist	*sg;
> +	u32			dma_nents;
> +
> +	u8			tuning;
> +	u8			cqe_reenable;
> +	bool			cmd_atomic;
> +	bool			shift;
> +	struct cqhci_host       *cqe;
> +};
> +
> +enum {
> +	TRANS_MODE_PIO = 0,
> +	TRANS_MODE_DMA,
> +};
> +
> +enum dw_mci_cookie {
> +	COOKIE_UNMAPPED,
> +	COOKIE_PRE_MAPPED,	/* mapped by pre_req() of dwmmc */
> +	COOKIE_MAPPED,		/* mapped by prepare_data() of dwmmc */
> +};
> +/* eMMC control register definition */
> +#define SDMMC_SDMASA_R				0x000
> +#define SDMMC_BLOCKSIZE_R			0x004
> +#define SDMMC_BLOCKCOUNT_R			0x006
> +#define SDMMC_ARGUMENT_R			0x008
> +#define SDMMC_XFER_MODE_R			0x00c
> +#define SDMMC_CMD_R				0x00e
> +#define SDMMC_RESP01_R				0x010
> +#define SDMMC_RESP23_R				0x014
> +#define SDMMC_RESP45_R				0x018
> +#define SDMMC_RESP67_R				0x01c
> +#define SDMMC_BUF_DATA_R			0x020
> +#define SDMMC_PSTATE_REG			0x024
> +#define SDMMC_HOST_CTRL1_R			0x028
> +#define SDMMC_PWR_CTRL_R			0x029
> +#define SDMMC_BGAP_CTRL_R			0x02a
> +#define SDMMC_CLK_CTRL_R			0x02c
> +#define SDMMC_TOUT_CTRL_R			0x02e
> +#define SDMMC_SW_RST_R				0x02f
> +#define SDMMC_NORMAL_INT_STAT_R			0x030
> +#define SDMMC_ERROR_INT_STAT_R			0x032
> +#define SDMMC_NORMAL_INT_STAT_EN_R		0x034
> +#define SDMMC_ERROR_INT_STAT_EN_R		0x036
> +#define SDMMC_NORMAL_INT_SIGNAL_EN_R		0x038
> +#define SDMMC_ERROR_INT_SIGNAL_EN_R		0x03a
> +#define SDMMC_AUTO_CMD_STAT_R			0x03c
> +#define SDMMC_HOST_CTRL2_R			0x03e
> +#define SDMMC_ADMA_ERR_STAT_R			0x054
> +#define SDMMC_ADMA_SA_LOW_R			0x058
> +
> +#define SDMMC_MSHC_CTRL_R			0x208
> +#define SDMMC_CTRL_R				0x22c
> +
> +#define SDMMC_CMD_CONFLICT_CHECK		BIT(0)
> +#define CMD_IDX_MASK(x)				((x >> 8)&0x3f)
> +
> +/*0xc*/
> +#define SDMMC_MULTI_BLK_SEL			5
> +#define SDMMC_DATA_XFER_DIR			4
> +#define SDMMC_BLOCK_COUNT_ENABLE		BIT(1)
> +#define SDMMC_DMA_ENABLE			BIT(0)
> +#define SDMMC_AUTO_CMD_ENABLE			2
> +#define SDMMC_AUTO_CMD_DISABLED			0x0
> +#define SDMMC_AUTO_CMD12_ENABLED		0x1
> +#define SDMMC_AUTO_CMD23_ENABLED		0x2
> +#define SDMMC_AUTO_CMD_SEL			0x3
> +
> +/*0xe*/
> +#define SDMMC_RESP_TYPE_SELECT			0
> +#define SDMMC_CMD_TYPE				6
> +#define SDMMC_NO_RESP				0x0
> +#define SDMMC_RESP_LEN_136			0x1
> +#define SDMMC_RESP_LEN_48			0x2
> +#define SDMMC_RESP_LEN_48B			0x3
> +#define SDMMC_CMD_CHK_RESP_CRC			BIT(3)
> +#define SDMMC_CMD_IDX_CHK_ENABLE		BIT(4)
> +#define SDMMC_DATA				BIT(5)
> +#define SDMMC_ABORT_CMD				0x3
> +#define SDMMC_RESUME_CMD			0x2
> +#define SDMMC_SUSPEND_CMD			0x1
> +#define SDMMC_NORMAL_CMD			0x0
> +
> +/*0x24 PSTATE*/
> +#define SDMMC_CMD_INHIBIT			BIT(0)
> +#define SDMMC_CMD_INHIBIT_DAT			BIT(1)
> +#define SDMMC_DAT_3_0				(0xf << 20)
> +#define SDMMC_DAT_7_4				(0xf << 4)
> +
> +/*0x28*/
> +#define SDMMC_DMA_SEL				3
> +#define SDMMC_SDMA				(0x0)
> +#define SDMMC_ADMA2_32				(0x2)
> +#define SDMMC_ADMA2_64				(0x3)
> +#define SDMMC_EXT_DAT_XFER			BIT(5)
> +#define SDMMC_EXT_DAT_XFER_MASK			(~SDMMC_EXT_DAT_XFER & 0xff)
> +#define SDMMC_HIGH_SPEED_EN			BIT(2)
> +#define SDMMC_HIGH_SPEED_MASK			((~BIT(2)) & 0xff)
> +#define SDMMC_UHS_MODE_SEL_MASK			((~(BIT(0)|BIT(1)|BIT(2))) & 0xffff)
> +#define SDMMC_DAT_XFER_WIDTH			BIT(1)
> +#define SDMMC_DAT_XFER_WIDTH_MASK		(~SDMMC_DAT_XFER_WIDTH & 0xff)
> +#define SDMMC_BUS_WIDTH_8			SDMMC_EXT_DAT_XFER
> +#define SDMMC_BUS_WIDTH_4			SDMMC_DAT_XFER_WIDTH
> +#define SDMMC_BUS_WIDTH_1			0
> +#define SDMMC_DMA_SEL_CLR			(0xff & (~(0x3<<SDMMC_DMA_SEL)))
> +#define SDMMC_DATA_XFER_CLR			((0xff & (~SDMMC_EXT_DAT_XFER)) \
> +						& (~SDMMC_DAT_XFER_WIDTH))
> +
> +/*0x2a*/
> +#define SDMMC_STOP_BG_REQ			BIT(0)
> +
> +/*0x2f SW_RST_R*/
> +#define SDMMC_RST_DAT			BIT(2)
> +#define SDMMC_RST_CMD			BIT(1)
> +#define SDMMC_RST_ALL			BIT(0)
> +
> +/*0x30 status bitmap*/
> +#define SDMMC_STATUS_ALL			0xffff
> +#define SDMMC_ERR_INTERRUPT			BIT(15)
> +#define SDMMC_CQE_EVENT				BIT(14)
> +#define SDMMC_FX_EVENT				BIT(13)
> +#define SDMMC_RE_TUNE_EVENT			BIT(12)
> +#define SDMMC_INT_C				BIT(11)
> +#define SDMMC_INT_B				BIT(10)
> +#define SDMMC_INT_A				BIT(9)
> +#define SDMMC_CARD_INTERRUPT			BIT(8)
> +#define SDMMC_CARD_REMOVAL			BIT(7)
> +#define SDMMC_CARD_INSERTION			BIT(6)
> +#define SDMMC_BUF_RD_READY			BIT(5)
> +#define SDMMC_BUF_WR_READY			BIT(4)
> +#define SDMMC_DMA_INTERRPT			BIT(3)
> +#define SDMMC_BGAP_EVENT			BIT(2)
> +#define SDMMC_XFER_COMPLETE			BIT(1)
> +#define SDMMC_CMD_COMPLETE			BIT(0)
> +
> +/*0x32 error bitmap*/
> +#define SDMMC_VENDOR_ERR3			BIT(15)
> +#define SDMMC_VENDOR_ERR2			BIT(14)
> +#define SDMMC_VENDOR_ERR1			BIT(13)
> +#define SDMMC_BOOT_ACK_ERR			BIT(12)
> +#define SDMMC_RESP_ERR				BIT(11)
> +#define SDMMC_TUNING_ERR			BIT(10)
> +#define SDMMC_ADMA_ERR				BIT(9)
> +#define SDMMC_AUTO_CMD_ERR			BIT(8)
> +#define SDMMC_CUR_LMT_ERR			BIT(7)
> +#define SDMMC_DATA_END_BIT_ERR			BIT(6)
> +#define SDMMC_DATA_CRC_ERR			BIT(5)
> +#define SDMMC_DATA_TOUT_ERR			BIT(4)
> +#define SDMMC_CMD_IDX_ERR			BIT(3)
> +#define SDMMC_CMD_END_BIT_ERR			BIT(2)
> +#define SDMMC_CMD_CRC_ERR			BIT(1)
> +#define SDMMC_CMD_TOUT_ERR			BIT(0)
> +#define SDMMC_CMD_ERR                           (SDMMC_AUTO_CMD_ERR| \
> +						SDMMC_CMD_IDX_ERR|SDMMC_CMD_END_BIT_ERR| \
> +						SDMMC_CMD_CRC_ERR|SDMMC_CMD_TOUT_ERR)
> +#define SDMMC_DATA_ERR				(SDMMC_ADMA_ERR| \
> +						SDMMC_DATA_END_BIT_ERR| \
> +						SDMMC_DATA_CRC_ERR|SDMMC_DATA_TOUT_ERR)
> +
> +/*0x34 status enable bitmap*/
> +#define SDMMC_CQE_EVENT_STAT_EN			BIT(14)
> +#define SDMMC_FX_EVENT_STAT_EN			BIT(13)
> +#define SDMMC_RE_TUNE_EVENT_STAT_EN		BIT(12)
> +#define SDMMC_INT_C_STAT_EN			BIT(11)
> +#define SDMMC_INT_B_STAT_EN			BIT(10)
> +#define SDMMC_INT_A_STAT_EN			BIT(9)
> +#define SDMMC_CARD_INTERRUPT_STAT_EN		BIT(8)
> +#define SDMMC_CARD_REMOVAL_STAT_EN		BIT(7)
> +#define SDMMC_CARD_INSERTION_STAT_EN		BIT(6)
> +#define SDMMC_BUF_RD_READY_STAT_EN		BIT(5)
> +#define SDMMC_BUF_WR_READY_STAT_EN		BIT(4)
> +#define SDMMC_DMA_INTERRPT_STAT_EN		BIT(3)
> +#define SDMMC_BGAP_EVENT_STAT_EN		BIT(2)
> +#define SDMMC_XFER_COMPLETE_STAT_EN		BIT(1)
> +#define SDMMC_CMD_COMPLETE_STAT_EN		BIT(0)
> +
> +/*0x36 error status enable bitmap*/
> +#define SDMMC_VENDOR_ERR_STAT_EN3		BIT(15)
> +#define SDMMC_VENDOR_ERR_STAT_EN2		BIT(14)
> +#define SDMMC_VENDOR_ERR_STAT_EN1		BIT(13)
> +#define SDMMC_BOOT_ACK_ERR_STAT_EN		BIT(12)
> +#define SDMMC_RESP_ERR_STAT_EN			BIT(11)
> +#define SDMMC_TUNING_ERR_STAT_EN		BIT(10)
> +#define SDMMC_ADMA_ERR_STAT_EN			BIT(9)
> +#define SDMMC_AUTO_CMD_ERR_STAT_EN		BIT(8)
> +#define SDMMC_CUR_LMT_ERR_STAT_EN		BIT(7)
> +#define SDMMC_DATA_END_BIT_ERR_STAT_EN		BIT(6)
> +#define SDMMC_DATA_CRC_ERR_STAT_EN		BIT(5)
> +#define SDMMC_DATA_TOUT_ERR_STAT_EN		BIT(4)
> +#define SDMMC_CMD_IDX_ERR_STAT_EN		BIT(3)
> +#define SDMMC_CMD_END_BIT_ERR_STAT_EN		BIT(2)
> +#define SDMMC_CMD_CRC_ERR_STAT_EN		BIT(1)
> +#define SDMMC_CMD_TOUT_ERR_STAT_EN		BIT(0)
> +
> +/*0x38 signal interrupt enable*/
> +#define SDMMC_CQE_EVENT_SIGNAL_EN		BIT(14)
> +#define SDMMC_FX_EVENT_SIGNAL_EN		BIT(13)
> +#define SDMMC_RE_TUNE_EVENT_SIGNAL_EN		BIT(12)
> +#define SDMMC_INT_C_SIGNAL_EN			BIT(11)
> +#define SDMMC_INT_B_SIGNAL_EN			BIT(10)
> +#define SDMMC_INT_A_SIGNAL_EN			BIT(9)
> +#define SDMMC_CARD_INTERRUPT_SIGNAL_EN		BIT(8)
> +#define SDMMC_CARD_REMOVAL_SIGNAL_EN		BIT(7)
> +#define SDMMC_CARD_INSERTION_SIGNAL_EN		BIT(6)
> +#define SDMMC_BUF_RD_READY_SIGNAL_EN		BIT(5)
> +#define SDMMC_BUF_WR_READY_SIGNAL_EN		BIT(4)
> +#define SDMMC_DMA_INTERRPT_SIGNAL_EN		BIT(3)
> +#define SDMMC_BGAP_EVENT_SIGNAL_EN		BIT(2)
> +#define SDMMC_XFER_COMPLETE_SIGNAL_EN		BIT(1)
> +#define SDMMC_CMD_COMPLETE_SIGNAL_EN		BIT(0)
> +#define SDMMC_NORMAL_INT_SIGNAL_CMD_EN_R	(~(BIT(6) | BIT(7) | BIT(8) | BIT(1)) & 0xffff)
> +#define SDMMC_NORMAL_INT_SIGNAL_DAT_EN_R	(~(BIT(6) | BIT(7) | BIT(8) | BIT(0)) & 0xffff)
> +#define SDMMC_NORMAL_INT_SIGNAL_CQE_EN_R	(~(BIT(6) | BIT(7) | BIT(8) | \
> +						BIT(1) | BIT(0)) & 0xffff)
> +
> +/*0x3a error ssignal enable bitmap*/
> +#define SDMMC_VENDOR_ERR_SIGNAL_EN3		BIT(15)
> +#define SDMMC_VENDOR_ERR_SIGNAL_EN2		BIT(14)
> +#define SDMMC_VENDOR_ERR_SIGNAL_EN1		BIT(13)
> +#define SDMMC_BOOT_ACK_ERR_SIGNAL_EN		BIT(12)
> +#define SDMMC_RESP_ERR_SIGNAL_EN		BIT(11)
> +#define SDMMC_TUNING_ERR_SIGNAL_EN		BIT(10)
> +#define SDMMC_ADMA_ERR_SIGNAL_EN		BIT(9)
> +#define SDMMC_AUTO_CMD_ERR_SIGNAL_EN		BIT(8)
> +#define SDMMC_CUR_LMT_ERR_SIGNAL_EN		BIT(7)
> +#define SDMMC_DATA_END_BIT_ERR_SIGNAL_EN	BIT(6)
> +#define SDMMC_DATA_CRC_ERR_SIGNAL_EN		BIT(5)
> +#define SDMMC_DATA_TOUT_ERR_SIGNAL_EN		BIT(4)
> +#define SDMMC_CMD_IDX_ERR_SIGNAL_EN		BIT(3)
> +#define SDMMC_CMD_END_BIT_ERR_SIGNAL_EN		BIT(2)
> +#define SDMMC_CMD_CRC_ERR_SIGNAL_EN		BIT(1)
> +#define SDMMC_CMD_TOUT_ERR_STAT_EN		BIT(0)
> +
> +#define SDMMC_ALL_NORMAL_STAT_EN		(0xfeff)
> +#define SDMMC_ALL_ERR_STAT_EN			(0xffff)
> +					/*enablle all error initerrupt in 0x36*/
> +#define SDMMC_ALL_SIGNAL_STAT_EN                (0xfeff)
> +#define SDMMC_ALL_ERR_SIGNAL_EN			(0xffff)
> +					/*enable all signal error interrupt in 0x3a*/
> +
> +/*0x3e*/
> +#define SDMMC_LEGACY				0x0
> +#define SDMMC_SDR				0x1
> +#define SDMMC_HS200				0x3
> +#define SDMMC_DDR				0x4
> +#define SDMMC_HS400				0x7
> +#define SDMMC_HOST_VER4_ENABLE			BIT(12)
> +#define SDMMC_SIGNALING_EN			BIT(3)
> +
> +/*0x22c*/
> +#define SDMMC_RST_N_OE				BIT(3)
> +#define SDMMC_RST_N				BIT(2)
> +#define SDMMC_CARD_IS_EMMC			BIT(0)
> +
> +#define SDMMC_INTERNAL_CLK_EN			BIT(0)
> +#define SDMMC_PLL_USABLE			BIT(0)
> +
> +#define VALID(x)			((x & 1) << 0)
> +#define END(x)				((x & 1) << 1)
> +#define INT(x)				((x & 1) << 2)
> +#define ACT(x)				((x & 0x7) << 3)
> +#define DAT_LENGTH(x)			((x & 0xFFFF) << 16)
> +
> +
> +/* Register access macros */
> +#define mci_readl(dev, reg)                     \
> +	readl_relaxed((dev)->regs + SDMMC_##reg)
> +#define mci_writel(dev, reg, value)                     \
> +	writel_relaxed((value), (dev)->regs + SDMMC_##reg)
> +
> +#define mci_readw(dev, reg)                     \
> +	readw_relaxed((dev)->regs + SDMMC_##reg)
> +#define mci_writew(dev, reg, value)                     \
> +	writew_relaxed((value), (dev)->regs + SDMMC_##reg)
> +
> +#define mci_readb(dev, reg)                     \
> +	readb_relaxed((dev)->regs + SDMMC_##reg)
> +#define mci_writeb(dev, reg, value)                     \
> +	writeb_relaxed((value), (dev)->regs + SDMMC_##reg)
> +
> +#define dw_mci_get_int(dev)    \
> +	do {    \
> +		dev->normal_interrupt = mci_readw(dev, NORMAL_INT_STAT_R);   \
> +		dev->error_interrupt = mci_readw(dev, ERROR_INT_STAT_R);   \
> +		dev->auto_error_interrupt = mci_readw(dev, AUTO_CMD_STAT_R);     \
> +	} while (0)
> +
> +/*clear status register, we always keep the card interrupt*/
> +#define dw_mci_clr_int(dev)                                             \
> +	do {                                                            \
> +		mci_writew(dev, ERROR_INT_STAT_R, mci_readw(dev, ERROR_INT_STAT_R) & 0xffff); \
> +		mci_writew(dev, NORMAL_INT_STAT_R, mci_readw(dev, NORMAL_INT_STAT_R) & 0xffff); \
> +	} while (0)
> +
> +/*mask all emmc interrupts*/
> +#define dw_mci_clr_signal_int(dev)    \
> +	do {      \
> +		mci_writew(dev, NORMAL_INT_SIGNAL_EN_R, \
> +			mci_readw(dev, NORMAL_INT_SIGNAL_EN_R) & (BIT(6)|BIT(7))); \
> +		mci_writew(dev, ERROR_INT_SIGNAL_EN_R, 0); \
> +	} while (0)
> +
> +/*for cmdq, we do not need cmd and xfer done, only cqe event*/
> +#define dw_mci_en_cqe_int(dev)  \
> +	do { \
> +		mci_writew(dev, NORMAL_INT_SIGNAL_EN_R, \
> +			mci_readw(dev, NORMAL_INT_SIGNAL_EN_R)|SDMMC_NORMAL_INT_SIGNAL_CQE_EN_R); \
> +		mci_writew(dev, ERROR_INT_SIGNAL_EN_R, SDMMC_ALL_ERR_SIGNAL_EN); \
> +	} while (0)
> +
> +/*used for data, r1b case, we mask cmd done interrupt*/
> +#define dw_mci_en_xfer_int(dev)  \
> +	do {  \
> +		mci_writew(dev, NORMAL_INT_SIGNAL_EN_R, \
> +			mci_readw(dev, NORMAL_INT_SIGNAL_EN_R)|SDMMC_NORMAL_INT_SIGNAL_DAT_EN_R); \
> +		mci_writew(dev, ERROR_INT_SIGNAL_EN_R, SDMMC_ALL_ERR_SIGNAL_EN); \
> +	} while (0)
> +
> +/*used for none-stream case (cmd w/wo/ resp)*/
> +#define dw_mci_en_cd_int(dev)  \
> +	do {    \
> +		mci_writew(dev, NORMAL_INT_SIGNAL_EN_R, \
> +			mci_readw(dev, NORMAL_INT_SIGNAL_EN_R)|SDMMC_NORMAL_INT_SIGNAL_CMD_EN_R); \
> +		mci_writew(dev, ERROR_INT_SIGNAL_EN_R, SDMMC_ALL_ERR_SIGNAL_EN); \
> +	} while (0)
> +
> +extern int dw_mci_cqe_probe(struct dw_mci *host);
> +extern void dw_mci_cqe_remove(struct dw_mci *host);
> +#ifdef CONFIG_PM
> +extern int dw_mci_cqe_runtime_suspend(struct device *device);
> +extern int dw_mci_cqe_runtime_resume(struct device *device);
> +#endif
> +irqreturn_t cqhci_irq(struct mmc_host *mmc, u32 intmask, int cmd_error, int data_error);
> +
> +/* Board platform data */
> +struct dw_mci_board {
> +	unsigned int bus_hz; /* Clock speed at the cclk_in pad */
> +	u32 caps;       /* Capabilities */
> +	u32 caps2;      /* More capabilities */
> +	u32 pm_caps;    /* PM capabilities */
> +
> +	/* delay in mS before detecting cards after interrupt */
> +	u32 detect_delay_ms;
> +
> +	struct reset_control *rstc;
> +};
> +
> +struct dw_mci_slot {
> +	struct mmc_host         *mmc;
> +	struct dw_mci           *host;
> +
> +	struct mmc_request      *mrq;
> +
> +	unsigned int            clock;
> +	unsigned int            __clk_old;
> +
> +	unsigned long           flags;
> +#define DW_MMC_CARD_PRESENT     1
> +	int                     id;
> +	int                     sdio_id;
> +	u8                      switch_partition;
> +};
> +
> +struct dw_mci_drv_data {
> +	unsigned long   *caps;
> +	u32             num_caps;
> +	int             (*init)(struct dw_mci *host);
> +	void            (*set_ios)(struct dw_mci_slot *slot, struct mmc_ios *ios);
> +	int             (*parse_dt)(struct dw_mci *host);
> +	int             (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode);
> +	int             (*prepare_hs400_tuning)(struct dw_mci *host,
> +					struct mmc_ios *ios);
> +	int             (*switch_voltage)(struct mmc_host *mmc,
> +					struct mmc_ios *ios);
> +	void            (*hs400_complete)(struct mmc_host *mmc);
> +	void		(*init_card)(struct mmc_host *host,
> +					struct mmc_card *card);
> +	void            (*shift_rsp)(struct dw_mci *host, struct mmc_command *cmd, u32 *rsp);
> +};
> +
> +void dw_mci_cqe_wait_done(struct dw_mci *host, u32 *addr, u32 mask, u32 value);
> +#endif
kernel test robot June 20, 2023, 11:04 a.m. UTC | #2
Hi Jyan,

kernel test robot noticed the following build warnings:

[auto build test WARNING on linus/master]
[also build test WARNING on v6.4-rc7 next-20230620]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Jyan-Chou/CMDQ-feature-is-introduced-to-eMMC-standard-in-v5-1-which-can-be-used-to-improve-performance/20230616-143849
base:   linus/master
patch link:    https://lore.kernel.org/r/20230616063731.17591-1-jyanchou%40realtek.com
patch subject: [PATCH] CMDQ feature is introduced to eMMC standard in v5.1, which can be used to improve performance.
config: mips-allyesconfig (https://download.01.org/0day-ci/archive/20230620/202306201816.tTRpHo0c-lkp@intel.com/config)
compiler: mips-linux-gcc (GCC) 12.3.0
reproduce: (https://download.01.org/0day-ci/archive/20230620/202306201816.tTRpHo0c-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202306201816.tTRpHo0c-lkp@intel.com/

All warnings (new ones prefixed by >>):

   In file included from drivers/mmc/host/dw_mmc_cqe.c:39:
>> drivers/mmc/host/dw_mmc_cqe.h:321: warning: "END" redefined
     321 | #define END(x)                          ((x & 1) << 1)
         | 
   In file included from arch/mips/include/asm/bitops.h:19,
                    from include/linux/bitops.h:68,
                    from drivers/mmc/host/dw_mmc_cqe.c:9:
   arch/mips/include/asm/asm.h:69: note: this is the location of the previous definition
      69 | #define END(function)                                   \
         | 
   drivers/mmc/host/dw_mmc_cqe.c:232:10: error: 'const struct cqhci_host_ops' has no member named 'setup_tran_desc'
     232 |         .setup_tran_desc = dw_mci_cqe_setup_tran_desc,
         |          ^~~~~~~~~~~~~~~
   drivers/mmc/host/dw_mmc_cqe.c:232:28: error: positional initialization of field in 'struct' declared with 'designated_init' attribute [-Werror=designated-init]
     232 |         .setup_tran_desc = dw_mci_cqe_setup_tran_desc,
         |                            ^~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/mmc/host/dw_mmc_cqe.c:232:28: note: (near initialization for 'dw_mci_cqhci_host_ops')
   drivers/mmc/host/dw_mmc_cqe.c:232:28: error: initialization of 'void (*)(struct mmc_host *)' from incompatible pointer type 'void (*)(struct mmc_data *, struct cqhci_host *, u8 *, int)' {aka 'void (*)(struct mmc_data *, struct cqhci_host *, unsigned char *, int)'} [-Werror=incompatible-pointer-types]
   drivers/mmc/host/dw_mmc_cqe.c:232:28: note: (near initialization for 'dw_mci_cqhci_host_ops.pre_enable')
   drivers/mmc/host/dw_mmc_cqe.c: In function 'dw_mci_cqe_read_rsp':
>> drivers/mmc/host/dw_mmc_cqe.c:283:37: warning: variable 'rsp_tmp' set but not used [-Wunused-but-set-variable]
     283 |                                 u32 rsp_tmp[4];
         |                                     ^~~~~~~
   drivers/mmc/host/dw_mmc_cqe.c: In function 'dw_mci_cqe_err_handle':
>> drivers/mmc/host/dw_mmc_cqe.c:766:41: warning: this 'if' clause does not guard... [-Wmisleading-indentation]
     766 |                                         if (err == -DW_MCI_NOT_READY)
         |                                         ^~
   drivers/mmc/host/dw_mmc_cqe.c:769:49: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the 'if'
     769 |                                                 break;
         |                                                 ^~~~~
   In file included from include/linux/debugfs.h:16,
                    from drivers/mmc/host/dw_mmc_cqe.c:12:
   drivers/mmc/host/dw_mmc_cqe.c: At top level:
   drivers/mmc/host/dw_mmc_cqe.c:97:23: warning: 'dw_mci_cqe_req_fops' defined but not used [-Wunused-const-variable=]
      97 | DEFINE_SHOW_ATTRIBUTE(dw_mci_cqe_req);
         |                       ^~~~~~~~~~~~~~
   include/linux/seq_file.h:202:37: note: in definition of macro 'DEFINE_SHOW_ATTRIBUTE'
     202 | static const struct file_operations __name ## _fops = {                 \
         |                                     ^~~~~~
   cc1: some warnings being treated as errors


vim +/END +321 drivers/mmc/host/dw_mmc_cqe.h

   319	
   320	#define VALID(x)			((x & 1) << 0)
 > 321	#define END(x)				((x & 1) << 1)
   322	#define INT(x)				((x & 1) << 2)
   323	#define ACT(x)				((x & 0x7) << 3)
   324	#define DAT_LENGTH(x)			((x & 0xFFFF) << 16)
   325	
   326
kernel test robot June 20, 2023, 1:19 p.m. UTC | #3
Hi Jyan,

kernel test robot noticed the following build errors:

[auto build test ERROR on linus/master]
[also build test ERROR on v6.4-rc7 next-20230620]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Jyan-Chou/CMDQ-feature-is-introduced-to-eMMC-standard-in-v5-1-which-can-be-used-to-improve-performance/20230616-143849
base:   linus/master
patch link:    https://lore.kernel.org/r/20230616063731.17591-1-jyanchou%40realtek.com
patch subject: [PATCH] CMDQ feature is introduced to eMMC standard in v5.1, which can be used to improve performance.
config: arc-allyesconfig (https://download.01.org/0day-ci/archive/20230620/202306202123.jfw85iPk-lkp@intel.com/config)
compiler: arceb-elf-gcc (GCC) 12.3.0
reproduce: (https://download.01.org/0day-ci/archive/20230620/202306202123.jfw85iPk-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202306202123.jfw85iPk-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/mmc/host/dw_mmc_cqe.c:232:10: error: 'const struct cqhci_host_ops' has no member named 'setup_tran_desc'
     232 |         .setup_tran_desc = dw_mci_cqe_setup_tran_desc,
         |          ^~~~~~~~~~~~~~~
>> drivers/mmc/host/dw_mmc_cqe.c:232:28: error: initialization of 'void (*)(struct cqhci_host *, u32,  int)' {aka 'void (*)(struct cqhci_host *, unsigned int,  int)'} from incompatible pointer type 'void (*)(struct mmc_data *, struct cqhci_host *, u8 *, int)' {aka 'void (*)(struct mmc_data *, struct cqhci_host *, unsigned char *, int)'} [-Werror=incompatible-pointer-types]
     232 |         .setup_tran_desc = dw_mci_cqe_setup_tran_desc,
         |                            ^~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/mmc/host/dw_mmc_cqe.c:232:28: note: (near initialization for 'dw_mci_cqhci_host_ops.write_l')
   drivers/mmc/host/dw_mmc_cqe.c: In function 'dw_mci_cqe_read_rsp':
   drivers/mmc/host/dw_mmc_cqe.c:283:37: warning: variable 'rsp_tmp' set but not used [-Wunused-but-set-variable]
     283 |                                 u32 rsp_tmp[4];
         |                                     ^~~~~~~
   drivers/mmc/host/dw_mmc_cqe.c: In function 'dw_mci_cqe_err_handle':
   drivers/mmc/host/dw_mmc_cqe.c:766:41: warning: this 'if' clause does not guard... [-Wmisleading-indentation]
     766 |                                         if (err == -DW_MCI_NOT_READY)
         |                                         ^~
   drivers/mmc/host/dw_mmc_cqe.c:769:49: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the 'if'
     769 |                                                 break;
         |                                                 ^~~~~
   In file included from include/linux/debugfs.h:16,
                    from drivers/mmc/host/dw_mmc_cqe.c:12:
   drivers/mmc/host/dw_mmc_cqe.c: At top level:
   drivers/mmc/host/dw_mmc_cqe.c:97:23: warning: 'dw_mci_cqe_req_fops' defined but not used [-Wunused-const-variable=]
      97 | DEFINE_SHOW_ATTRIBUTE(dw_mci_cqe_req);
         |                       ^~~~~~~~~~~~~~
   include/linux/seq_file.h:202:37: note: in definition of macro 'DEFINE_SHOW_ATTRIBUTE'
     202 | static const struct file_operations __name ## _fops = {                 \
         |                                     ^~~~~~
   cc1: some warnings being treated as errors


vim +232 drivers/mmc/host/dw_mmc_cqe.c

   228	
   229	static const struct cqhci_host_ops dw_mci_cqhci_host_ops = {
   230		.enable = dw_mci_cqe_enable,
   231		.dumpregs = dw_mci_cqe_dumpregs,
 > 232		.setup_tran_desc = dw_mci_cqe_setup_tran_desc,
   233	};
   234
Adrian Hunter June 29, 2023, 1:31 p.m. UTC | #4
On 20/06/23 11:09, Jyan Chou [周芷安] wrote:
> Hello Adrian,
> 
>> Why not add CQHCI support to dw_mmc.c ?
> 
> The reason why we would like to commit dw_mmc_cqe.c is that we found 
> 
> the difference between synopsys' data book and user guide.
> 
> It seems that dw_mmc.c is based on former spec. 
> 
> In new spec, the programmable registers of the DWC_mobile_storage are different, 
> 
> some operate flow had changed, and also they add cmdq feature.

Please do not top-post:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/2.Process.rst?h=v6.4#n437

The patch needs a proper subject (look at other patches on the linux-mmc mailing list):

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/submitting-patches.rst?h=v6.4-rc7#n588

The commit message needs to contain a summary of what is different from dw_mmc.c

There needs to be a user. i.e. dw_mci_cqe_probe() is never used

> 
> Thanks a lot.
> 
> Best Regards,
> Jyan
> 
> -----Original Message-----
> From: Jyan Chou [周芷安] 
> Sent: Tuesday, June 20, 2023 4:04 PM
> To: 'Adrian Hunter' <adrian.hunter@intel.com>; jh80.chung@samsung.com; ulf.hansson@linaro.org
> Cc: linux-mmc@vger.kernel.org; linux-kernel@vger.kernel.org; James Tai [戴志峰] <james.tai@realtek.com>
> Subject: RE: [PATCH] CMDQ feature is introduced to eMMC standard in v5.1, which can be used to improve performance.
> 
> Hello Adrian,
> 
>> Why not add CQHCI support to dw_mmc.c ?
> 
> The reason why we would like to commit dw_mmc_cqe.c is that we found the difference between synopsys' data book and user guide.
> 
> It seems that dw_mmc.c is based on former spec. In new spec, the programmable registers of the DWC_mobile_storage are different,
> 
> some operate flow had changed, and also they add cmdq feature.
> 
> Thanks a lot.
> 
> Best Regards,
> Jyan
> 
> -----Original Message-----
> From: Adrian Hunter <adrian.hunter@intel.com> 
> Sent: Tuesday, June 20, 2023 2:46 PM
> To: Jyan Chou [周芷安] <jyanchou@realtek.com>; jh80.chung@samsung.com; ulf.hansson@linaro.org
> Cc: linux-mmc@vger.kernel.org; linux-kernel@vger.kernel.org; James Tai [戴志峰] <james.tai@realtek.com>
> Subject: Re: [PATCH] CMDQ feature is introduced to eMMC standard in v5.1, which can be used to improve performance.
> 
> 
> External mail.
> 
> 
> 
> On 16/06/23 09:37, Jyan Chou wrote:
>> We add the mmc driver for the Synopsys DesignWare mmc host controller
>> with cmdq support that can implement this feature.
> 
> Why not add CQHCI support to dw_mmc.c ?
> 
> It does not usually require that many changes, for example:
> 
> commit 88bd652b3c74997bb436adf6131acf445066243e
> Author: Chun-Hung Wu <chun-hung.wu@mediatek.com>
> Date:   Mon Jul 20 08:42:38 2020 +0800
> 
>     mmc: mediatek: command queue support
> 
>     Support command queue for mt6779 platform.
>     a. Add msdc_set_busy_timeout() to calculate emmc write timeout.
>     b. Connect mtk msdc driver to cqhci driver through
>        host->cq_host->ops = &msdc_cmdq_ops;
>     c. msdc_cmdq_irq() will link up with cqchi_irq(). Besides, it provides
>        more irq error messages like RSPCRCERR/CMDTO/DATACRCERR/DATTMO.
>     d. Select kernel config MMC_CQHCI for MMC_MTK
> 
>     Signed-off-by: Chun-Hung Wu <chun-hung.wu@mediatek.com>
>     Acked-by: Yong Mao <yong.mao@mediatek.com>
>     Link: https://lore.kernel.org/r/1595205759-5825-4-git-send-email-chun-hung.wu@mediatek.com
>     Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
> 
> 
> 
>>
>> Signed-off-by: Jyan Chou <jyanchou@realtek.com>
>> ---
>>  drivers/mmc/host/Kconfig      |   11 +
>>  drivers/mmc/host/Makefile     |    1 +
>>  drivers/mmc/host/dw_mmc_cqe.c | 1823 +++++++++++++++++++++++++++++++++
>>  drivers/mmc/host/dw_mmc_cqe.h |  444 ++++++++
>>  4 files changed, 2279 insertions(+)
>>  create mode 100644 drivers/mmc/host/dw_mmc_cqe.c
>>  create mode 100644 drivers/mmc/host/dw_mmc_cqe.h
>>
>> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
>> index 9f793892123c..b8c7727b1897 100644
>> --- a/drivers/mmc/host/Kconfig
>> +++ b/drivers/mmc/host/Kconfig
>> @@ -762,6 +762,17 @@ config MMC_DW_PLTFM
>>
>>         If unsure, say Y.
>>
>> +config MMC_DW_CQE
>> +     tristate "Synopsys DesignWare Memory Card with CQE Interface"
>> +     depends on ARC || ARM || ARM64 || MIPS || COMPILE_TEST
>> +     select MMC_CQHCI
>> +     help
>> +       This selects support for the Synopsys DesignWare Mobile Storage IP
>> +       block. It provides host support for SD and MMC interfaces, and adds
>> +       the support of cmdq.
>> +
>> +       If unsure, say N.
>> +
>>  config MMC_DW_BLUEFIELD
>>       tristate "BlueField specific extensions for Synopsys DW Memory Card Interface"
>>       depends on MMC_DW
>> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
>> index a693fa3d3f1c..7fa1411692e8 100644
>> --- a/drivers/mmc/host/Makefile
>> +++ b/drivers/mmc/host/Makefile
>> @@ -55,6 +55,7 @@ obj-$(CONFIG_MMC_DW_K3)             += dw_mmc-k3.o
>>  obj-$(CONFIG_MMC_DW_PCI)     += dw_mmc-pci.o
>>  obj-$(CONFIG_MMC_DW_ROCKCHIP)        += dw_mmc-rockchip.o
>>  obj-$(CONFIG_MMC_DW_STARFIVE)        += dw_mmc-starfive.o
>> +obj-$(CONFIG_MMC_DW_CQE)                += dw_mmc_cqe.o
>>  obj-$(CONFIG_MMC_SH_MMCIF)   += sh_mmcif.o
>>  obj-$(CONFIG_MMC_JZ4740)     += jz4740_mmc.o
>>  obj-$(CONFIG_MMC_VUB300)     += vub300.o
>> diff --git a/drivers/mmc/host/dw_mmc_cqe.c b/drivers/mmc/host/dw_mmc_cqe.c
>> new file mode 100644
>> index 000000000000..c50a6c71a362
>> --- /dev/null
>> +++ b/drivers/mmc/host/dw_mmc_cqe.c
>> @@ -0,0 +1,1823 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/*
>> + * Synopsys DesignWare Multimedia Card Interface driver with CMDQ support
>> + *  (Based on Synopsys DesignWare Multimedia Card Interface driver)
>> + *
>> + * Copyright (c) 2023 Realtek Semiconductor Corp
>> + */
>> +
>> +#include <linux/bitops.h>
>> +#include <linux/blkdev.h>
>> +#include <linux/clk.h>
>> +#include <linux/debugfs.h>
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/err.h>
>> +#include <linux/init.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/iopoll.h>
>> +#include <linux/ioport.h>
>> +#include <linux/irq.h>
>> +#include <linux/mmc/card.h>
>> +#include <linux/mmc/host.h>
>> +#include <linux/mmc/mmc.h>
>> +#include <linux/mmc/sd.h>
>> +#include <linux/mmc/sdio.h>
>> +#include <linux/mmc/slot-gpio.h>
>> +#include <linux/module.h>
>> +#include <linux/of_gpio.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/seq_file.h>
>> +#include <linux/sizes.h>
>> +#include <linux/slab.h>
>> +#include <linux/stat.h>
>> +
>> +#include "dw_mmc_cqe.h"
>> +#include "cqhci.h"
>> +
>> +#define DW_MCI_FREQ_MAX      200000000       /* unit: HZ */
>> +#define DW_MCI_FREQ_MIN      100000          /* unit: HZ */
>> +#define DW_MCI_CMDQ_DISABLED 0x30f0001
>> +#define DW_MCI_CMDQ_ENABLED  0x30f0101
>> +#define DW_MCI_POWEROFF              0x3220301
>> +#define DW_MCI_DESC_LEN              0x100000
>> +#define DW_MCI_MAX_SCRIPT_BLK        128
>> +#define DW_MCI_TIMEOUT_MS    3000
>> +#define DW_MCI_TIMEOUT_us    3000000
>> +#define TUNING_ERR           531
>> +#define DW_MCI_NOT_READY     9999
>> +
>> +DECLARE_COMPLETION(dw_mci_wait);
>> +
>> +
>> +#if defined(CONFIG_DEBUG_FS)
>> +static int dw_mci_cqe_req_show(struct seq_file *s, void *v)
>> +{
>> +     struct dw_mci_slot *slot = s->private;
>> +     struct mmc_request *mrq;
>> +     struct mmc_command *cmd;
>> +     struct mmc_command *stop;
>> +     struct mmc_data *data;
>> +
>> +     /* Make sure we get a consistent snapshot */
>> +     spin_lock_bh(&slot->host->lock);
>> +     mrq = slot->mrq;
>> +
>> +     if (mrq) {
>> +             cmd = mrq->cmd;
>> +             data = mrq->data;
>> +             stop = mrq->stop;
>> +
>> +             if (cmd)
>> +                     seq_printf(s,
>> +                                "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
>> +                                cmd->opcode, cmd->arg, cmd->flags,
>> +                                cmd->resp[0], cmd->resp[1], cmd->resp[2],
>> +                                cmd->resp[2], cmd->error);
>> +             if (data)
>> +                     seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
>> +                                data->bytes_xfered, data->blocks,
>> +                                data->blksz, data->flags, data->error);
>> +             if (stop)
>> +                     seq_printf(s,
>> +                                "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
>> +                                stop->opcode, stop->arg, stop->flags,
>> +                                stop->resp[0], stop->resp[1], stop->resp[2],
>> +                                stop->resp[2], stop->error);
>> +     }
>> +
>> +     spin_unlock_bh(&slot->host->lock);
>> +
>> +     return 0;
>> +}
>> +DEFINE_SHOW_ATTRIBUTE(dw_mci_cqe_req);
>> +#endif /* defined(CONFIG_DEBUG_FS) */
>> +
>> +static int dw_mci_cqe_regs_show(struct dw_mci *host,
>> +                             struct mmc_command *cmd, u32 cmd_flags)
>> +{
>> +     dev_err(host->dev, "opcode = %d, arg = 0x%x, cmdflags = 0x%x\n",
>> +                             cmd->opcode, cmd->arg, cmd_flags);
>> +     dev_err(host->dev, "status_int = 0x%x\n", host->normal_interrupt);
>> +     dev_err(host->dev, "error_int = 0x%x\n", host->error_interrupt);
>> +     dev_err(host->dev, "auto_error_int = 0x%x\n", host->auto_error_interrupt);
>> +     dev_err(host->dev, "pstate_reg = 0x%x\n", mci_readl(host, PSTATE_REG));
>> +     dev_err(host->dev, "host_ctrl_1 = 0x%x\n", mci_readb(host, HOST_CTRL1_R));
>> +     dev_err(host->dev, "xfer_mode_r = 0x%x\n", mci_readw(host, XFER_MODE_R));
>> +
>> +     return 0;
>> +}
>> +
>> +static void dw_mci_cqe_dumpregs(struct mmc_host *mmc)
>> +{
>> +     struct dw_mci_slot *slot = mmc_priv(mmc);
>> +     struct dw_mci *host = slot->host;
>> +
>> +     dev_info(host->dev, "%s: cmd idx 0x%08x\n", __func__, mci_readw(host, CMD_R));
>> +}
>> +
>> +static void dw_mci_cqe_set_tran_desc(u8 *desc,
>> +                                     dma_addr_t addr,
>> +                                     int len,
>> +                                     bool end,
>> +                                     bool dma64)
>> +{
>> +     __le32 *attr = (__le32 __force *)desc;
>> +
>> +     *attr = (CQHCI_VALID(1) |
>> +              CQHCI_END(end ? 1 : 0) |
>> +              CQHCI_INT(0) |
>> +              CQHCI_ACT(0x4) |
>> +              CQHCI_DAT_LENGTH(len));
>> +
>> +     if (dma64) {
>> +             __le64 *dataddr = (__le64 __force *)(desc + 4);
>> +
>> +             dataddr[0] = cpu_to_le64(addr);
>> +     } else {
>> +             __le32 *dataddr = (__le32 __force *)(desc + 4);
>> +
>> +             dataddr[0] = cpu_to_le32(addr);
>> +     }
>> +}
>> +
>> +static void dw_mci_cqe_setup_tran_desc(struct mmc_data *data,
>> +                                   struct cqhci_host *cq_host,
>> +                                   u8 *desc,
>> +                                   int sg_count)
>> +{
>> +     struct scatterlist *sg;
>> +     u32 cur_blk_cnt, remain_blk_cnt;
>> +     unsigned int begin, end;
>> +     int i, len;
>> +     bool last = false;
>> +     bool dma64 = cq_host->dma64;
>> +     dma_addr_t addr;
>> +
>> +     for_each_sg(data->sg, sg, sg_count, i) {
>> +             addr = sg_dma_address(sg);
>> +             len = sg_dma_len(sg);
>> +             remain_blk_cnt  = len >> 9;
>> +
>> +             while (remain_blk_cnt) {
>> +                     /*DW_MCI_MAX_SCRIPT_BLK is tha max for each descriptor record*/
>> +                     if (remain_blk_cnt > DW_MCI_MAX_SCRIPT_BLK)
>> +                             cur_blk_cnt = DW_MCI_MAX_SCRIPT_BLK;
>> +                     else
>> +                             cur_blk_cnt = remain_blk_cnt;
>> +
>> +                     /* In Synopsys DesignWare Databook Page 84,
>> +                      * They mentioned the DMA 128MB restriction
>> +                      */
>> +                     begin = addr / SZ_128M;
>> +                     end = (addr + cur_blk_cnt * SZ_512) / SZ_128M;
>> +
>> +                     if (begin != end)
>> +                             cur_blk_cnt = (end * SZ_128M - addr) / SZ_512;
>> +
>> +                     if ((i+1) == sg_count && (remain_blk_cnt == cur_blk_cnt))
>> +                             last = true;
>> +
>> +                     dw_mci_cqe_set_tran_desc(desc, addr,
>> +                                     (cur_blk_cnt << 9), last, dma64);
>> +
>> +                     addr = addr + (cur_blk_cnt << 9);
>> +                     remain_blk_cnt -= cur_blk_cnt;
>> +                     desc += cq_host->trans_desc_len;
>> +             }
>> +     }
>> +}
>> +
>> +static void dw_mci_cqe_enable(struct mmc_host *mmc)
>> +{
>> +     struct dw_mci_slot *slot = mmc_priv(mmc);
>> +     struct dw_mci *host = slot->host;
>> +
>> +     /*clear data path SW_RST_R.SW_RST_DAT = 1*/
>> +     mci_writeb(host, SW_RST_R, SDMMC_RST_DAT);
>> +     /*0x9801200c*/
>> +     mci_writew(host, XFER_MODE_R,
>> +             ((1 << SDMMC_MULTI_BLK_SEL) | SDMMC_BLOCK_COUNT_ENABLE | SDMMC_DMA_ENABLE));
>> +
>> +     /*Set DMA_SEL to ADMA2 only mode in the HOST_CTRL1_R*/
>> +     mci_writeb(host, HOST_CTRL1_R,
>> +             (mci_readb(host, HOST_CTRL1_R) & 0xe7) | (SDMMC_ADMA2_32 << SDMMC_DMA_SEL));
>> +     mci_writew(host, BLOCKSIZE_R, 0x200);
>> +     mci_writew(host, BLOCKCOUNT_R, 0);
>> +
>> +     /*Set SDMASA_R (while using 32 bits) to 0*/
>> +     mci_writel(host, SDMASA_R, 0);
>> +     /*we set this register additionally to enhance the IO perofrmance*/
>> +
>> +     cqhci_writel(host->cqe, 0x10, CQHCI_SSC1);
>> +     cqhci_writel(host->cqe, 0, CQHCI_CTL);
>> +
>> +     if (cqhci_readl(host->cqe, CQHCI_CTL) && CQHCI_HALT) {
>> +             dev_err(host->dev, "%s: cqhci: CQE failed to exit halt state\n",
>> +                     mmc_hostname(mmc));
>> +     }
>> +
>> +     /*cmdq interrupt mode*/
>> +     dw_mci_clr_signal_int(host);
>> +     dw_mci_en_cqe_int(host);
>> +}
>> +
>> +static const struct cqhci_host_ops dw_mci_cqhci_host_ops = {
>> +     .enable = dw_mci_cqe_enable,
>> +     .dumpregs = dw_mci_cqe_dumpregs,
>> +     .setup_tran_desc = dw_mci_cqe_setup_tran_desc,
>> +};
>> +
>> +void dw_mci_cqe_wait_done(struct dw_mci *host, u32 *addr,
>> +                   u32 mask, u32 value)
>> +{
>> +     int n = 0;
>> +
>> +     while (1) {
>> +             if (((*addr) & mask) == value)
>> +                     break;
>> +
>> +             /*error interrupt detected*/
>> +             if ((mci_readw(host, NORMAL_INT_STAT_R) & SDMMC_ERR_INTERRUPT) != 0)
>> +                     break;
>> +
>> +             if (n++ > DW_MCI_TIMEOUT_us) {
>> +                     dev_err(host->dev, "opcode = %d, *addr = 0x%x, mask = 0x%x, value = 0x%x\n",
>> +                             host->opcode, readl(addr), mask, value);
>> +                     break;
>> +             }
>> +             udelay(1);
>> +     }
>> +}
>> +EXPORT_SYMBOL(dw_mci_cqe_wait_done);
>> +
>> +static void dw_mci_cqe_reset(struct dw_mci *host)
>> +{
>> +     /*check the cmd line*/
>> +     if (mci_readw(host, ERROR_INT_STAT_R) & SDMMC_CMD_ERR) {
>> +             /*Perform a software reset*/
>> +             mci_writeb(host, SW_RST_R, SDMMC_RST_CMD);
>> +             dw_mci_cqe_wait_done(host, (u32 *)(host->regs + SDMMC_CLK_CTRL_R), BIT(25), 0);
>> +     }
>> +     /*check data line*/
>> +     if (mci_readw(host, ERROR_INT_STAT_R) & SDMMC_DATA_ERR) {
>> +             mci_writeb(host, SW_RST_R, SDMMC_RST_DAT);
>> +             dw_mci_cqe_wait_done(host, (u32 *)(host->regs + SDMMC_CLK_CTRL_R), BIT(26), 0);
>> +     }
>> +}
>> +
>> +static void dw_mci_cqe_read_rsp(struct dw_mci *host, struct mmc_command *cmd, u32 *rsp)
>> +{
>> +     const struct dw_mci_drv_data *drv_data = host->drv_data;
>> +
>> +     if (cmd->flags & MMC_RSP_PRESENT) {
>> +             if (cmd->flags & MMC_RSP_136) {
>> +                     if (drv_data && drv_data->shift_rsp) {
>> +                             drv_data->shift_rsp(host, cmd, cmd->resp);
>> +                     } else {
>> +                             /*R2 long response*/
>> +                             u32 rsp_tmp[4];
>> +
>> +                             rsp_tmp[3] = mci_readl(host, RESP01_R);
>> +                             rsp_tmp[2] = mci_readl(host, RESP23_R);
>> +                             rsp_tmp[1] = mci_readl(host, RESP45_R);
>> +                             rsp_tmp[0] = mci_readl(host, RESP67_R);
>> +                     }
>> +             } else {
>> +                     /*Short response*/
>> +                     rsp[0] = rsp[1] = rsp[2] = rsp[3] = 0;
>> +                     rsp[0] = mci_readl(host, RESP01_R);
>> +             }
>> +     }
>> +}
>> +
>> +static u32 dw_mci_cqe_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
>> +{
>> +     struct dw_mci_slot *slot = mmc_priv(mmc);
>> +     struct dw_mci *host = slot->host;
>> +     u32 cmdr;
>> +
>> +     cmd->error = -EINPROGRESS;
>> +
>> +    /* our ip design puts resp in bit 8-135, so we need to shift 8 bits*/
>> +     if (host->shift)
>> +             cmdr = (cmd->opcode << 8);
>> +     else
>> +             cmdr = cmd->opcode;
>> +
>> +     if (cmd->flags & MMC_RSP_PRESENT) {
>> +             if (cmd->flags & MMC_RSP_136)
>> +                     cmdr |= SDMMC_RESP_LEN_136;
>> +             else {
>> +                     if (cmd->flags & MMC_RSP_BUSY)
>> +                             cmdr |= SDMMC_RESP_LEN_48B;
>> +                     else
>> +                             cmdr |= SDMMC_RESP_LEN_48;
>> +             }
>> +     }
>> +
>> +     cmdr |= SDMMC_CMD_CHK_RESP_CRC;
>> +     if (cmd->opcode == MMC_GO_IDLE_STATE ||
>> +        cmd->opcode == MMC_SEND_OP_COND ||
>> +        (cmd->opcode == MMC_SELECT_CARD && cmd->flags == (MMC_RSP_NONE | MMC_CMD_AC)))
>> +             cmdr &= ~SDMMC_CMD_CHK_RESP_CRC;
>> +
>> +     cmdr |= SDMMC_CMD_IDX_CHK_ENABLE;
>> +     if (cmd->opcode == MMC_GO_IDLE_STATE ||
>> +        cmd->opcode == MMC_SEND_OP_COND ||
>> +        cmd->opcode == MMC_SEND_CSD ||
>> +        cmd->opcode == MMC_SEND_CID ||
>> +        cmd->opcode == MMC_ALL_SEND_CID ||
>> +        (cmd->opcode == MMC_SELECT_CARD && cmd->flags == (MMC_RSP_NONE | MMC_CMD_AC)))
>> +             cmdr &= ~SDMMC_CMD_IDX_CHK_ENABLE;
>> +
>> +     if (cmd->data)
>> +             cmdr |= SDMMC_DATA;
>> +
>> +     if (cmd->opcode == MMC_STOP_TRANSMISSION)
>> +             cmdr |= (SDMMC_ABORT_CMD << 6);
>> +
>> +     return cmdr;
>> +}
>> +
>> +static int dw_mci_cqe_start_command(struct dw_mci *host,
>> +                              struct mmc_command *cmd, u32 cmd_flags)
>> +{
>> +     int err = 0;
>> +     unsigned long end = 0;
>> +     unsigned long flags;
>> +     bool xfer_flag = false;
>> +
>> +     host->cmd = cmd;
>> +
>> +     switch (cmd->opcode) {
>> +     case MMC_READ_SINGLE_BLOCK:
>> +     case MMC_READ_MULTIPLE_BLOCK:
>> +     case MMC_WRITE_BLOCK:
>> +     case MMC_WRITE_MULTIPLE_BLOCK:
>> +     case MMC_SEND_EXT_CSD:
>> +     case MMC_GEN_CMD:
>> +     case MMC_SLEEP_AWAKE:
>> +     case MMC_SWITCH:
>> +     case MMC_SET_WRITE_PROT:
>> +     case MMC_CLR_WRITE_PROT:
>> +     case MMC_SEND_WRITE_PROT:
>> +     case MMC_ERASE:
>> +     case MMC_SEND_TUNING_BLOCK_HS200:
>> +             xfer_flag = true;
>> +             break;
>> +     default:
>> +             xfer_flag = false;
>> +     }
>> +
>> +     host->int_waiting = &dw_mci_wait;
>> +     end = jiffies + msecs_to_jiffies(DW_MCI_TIMEOUT_MS);
>> +     mod_timer(&host->timer, end);
>> +
>> +     if (host->int_waiting) {
>> +             dw_mci_clr_signal_int(host);
>> +             dw_mci_clr_int(host);
>> +
>> +             /*command with data, r1b case*/
>> +             if (xfer_flag == 1)
>> +                     dw_mci_en_xfer_int(host);
>> +             else
>> +                     dw_mci_en_cd_int(host);
>> +
>> +             /*If we use cmd23, we cannot send auto stop command*/
>> +             if (cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK ||
>> +                 cmd->opcode == MMC_READ_MULTIPLE_BLOCK) {
>> +                     if (host->is_sbc) {
>> +                             mci_writew(host, XFER_MODE_R,
>> +                                     mci_readw(host, XFER_MODE_R) & ~BIT(SDMMC_AUTO_CMD_ENABLE));
>> +                                     host->is_sbc = 0;
>> +                     }
>> +             }
>> +
>> +             host->opcode = cmd->opcode;
>> +             host->arg = cmd->arg;
>> +
>> +             spin_lock_irqsave(&host->irq_lock, flags);
>> +             mci_writew(host, CMD_R, cmd_flags);
>> +             spin_unlock_irqrestore(&host->irq_lock, flags);
>> +
>> +             wait_for_completion(host->int_waiting);
>> +
>> +             if (xfer_flag == 1)
>> +                     dw_mci_cqe_wait_done(host,
>> +                             (u32 *)(host->regs + SDMMC_NORMAL_INT_STAT_R),
>> +                             SDMMC_XFER_COMPLETE, SDMMC_XFER_COMPLETE);
>> +             else
>> +                     dw_mci_cqe_wait_done(host,
>> +                             (u32 *)(host->regs + SDMMC_NORMAL_INT_STAT_R),
>> +                             SDMMC_CMD_COMPLETE, SDMMC_CMD_COMPLETE);
>> +
>> +             if (host->normal_interrupt & SDMMC_ERR_INTERRUPT) {
>> +                     if (host->tuning == 1)
>> +                             dev_info(host->dev, "Tuning error ... keep tuning\n");
>> +                     else
>> +                             dw_mci_cqe_regs_show(host, cmd, cmd_flags);
>> +                     err = -1;
>> +             }
>> +     }
>> +
>> +     return err;
>> +}
>> +
>> +static void dw_mci_cqe_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd)
>> +{
>> +
>> +     struct mmc_command stop;
>> +     u32 cmdr;
>> +     /*Stop command only use after data command*/
>> +     if (!cmd->data)
>> +             return;
>> +
>> +     memset(&stop, 0, sizeof(struct mmc_command));
>> +
>> +     if (cmd->opcode == MMC_READ_SINGLE_BLOCK ||
>> +         cmd->opcode == MMC_READ_MULTIPLE_BLOCK ||
>> +         cmd->opcode == MMC_WRITE_BLOCK ||
>> +         cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK ||
>> +         cmd->opcode == MMC_SEND_TUNING_BLOCK ||
>> +         cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200) {
>> +             stop.opcode = MMC_STOP_TRANSMISSION;
>> +             stop.arg = 0;
>> +             stop.flags = MMC_RSP_R1 | MMC_CMD_AC;
>> +     } else if (cmd->opcode == SD_IO_RW_EXTENDED) {
>> +             stop.opcode = SD_IO_RW_DIRECT;
>> +             stop.arg |= (1 << 31) | (0 << 28) | (SDIO_CCCR_ABORT << 9) |
>> +                         ((cmd->arg >> 28) & 0x7);
>> +             stop.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC;
>> +     } else {
>> +             return;
>> +     }
>> +
>> +     cmdr = (stop.opcode << 8) | SDMMC_RESP_LEN_48 |
>> +             SDMMC_CMD_CHK_RESP_CRC | SDMMC_CMD_IDX_CHK_ENABLE;
>> +     cmdr |= (SDMMC_ABORT_CMD << 6);
>> +     mci_writew(host, XFER_MODE_R, 0);
>> +     mci_writel(host, ARGUMENT_R, stop.arg);
>> +     dw_mci_cqe_start_command(host, &stop, cmdr);
>> +}
>> +
>> +static int dw_mci_cqe_wait_while_busy(struct dw_mci *host, u32 *status)
>> +{
>> +     struct mmc_command cmd;
>> +     u32 cmdr;
>> +     u32 cur_state;
>> +     unsigned long timeend;
>> +     int err = 0;
>> +
>> +     memset(&cmd, 0, sizeof(struct mmc_command));
>> +
>> +     timeend = jiffies + msecs_to_jiffies(600);
>> +
>> +     do {
>> +             cmd.opcode = MMC_SEND_STATUS;
>> +             cmd.arg = 1 << 16;
>> +             cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
>> +             cmd.data = NULL;
>> +
>> +             cmdr = (cmd.opcode << 8) | SDMMC_RESP_LEN_48 |
>> +                     SDMMC_CMD_CHK_RESP_CRC | SDMMC_CMD_IDX_CHK_ENABLE;
>> +
>> +             mci_writew(host, XFER_MODE_R, 0);
>> +             mci_writel(host, ARGUMENT_R, cmd.arg);
>> +
>> +             err = dw_mci_cqe_start_command(host, &cmd, cmdr);
>> +             if (err) {
>> +                     dw_mci_cqe_reset(host);
>> +                     break;
>> +             }
>> +             dw_mci_cqe_read_rsp(host, &cmd, cmd.resp);
>> +
>> +             *status = cmd.resp[0];
>> +             cur_state = R1_CURRENT_STATE(cmd.resp[0]);
>> +             err = -DW_MCI_NOT_READY;
>> +             if (cur_state == R1_STATE_TRAN) {
>> +                     if (cmd.resp[0] & R1_READY_FOR_DATA) {
>> +                             err = 0;
>> +                             break;
>> +                     }
>> +             }
>> +     } while (time_before(jiffies, timeend));
>> +
>> +     return err;
>> +
>> +}
>> +
>> +static void dw_mci_cqe_stop_dma(struct dw_mci *host, struct mmc_data *data)
>> +{
>> +     u32 dir = 0;
>> +
>> +     if (data->flags & MMC_DATA_READ)
>> +             dir = DMA_FROM_DEVICE;
>> +     else
>> +             dir = DMA_TO_DEVICE;
>> +
>> +     dma_unmap_sg(mmc_dev(host->slot->mmc), data->sg, data->sg_len, dir);
>> +     host->sg = NULL;
>> +}
>> +
>> +static void dw_mci_cqe_prepare_desc64(struct dw_mci *host, struct mmc_data *data,
>> +                                     struct scatterlist *sg)
>> +{
>> +     dev_info(host->dev, "Currently, the 64bit DMA mode is not implemented yet.\n");
>> +}
>> +
>> +
>> +static void dw_mci_cqe_prepare_desc32(struct dw_mci *host, struct mmc_data *data,
>> +                                     struct scatterlist *sg)
>> +{
>> +     u32  blk_cnt, cur_blk_cnt, remain_blk_cnt;
>> +     u32  tmp_val;
>> +     u32 *desc_base = host->sg_cpu;
>> +     u32  dma_len = 0;
>> +     u32  dma_addr;
>> +     u32  i;
>> +     unsigned int begin, end;
>> +
>> +     for (i = 0; i < host->dma_nents; i++, sg++) {
>> +             dma_len = sg_dma_len(sg);
>> +
>> +             /*blk_cnt must be the multiple of 512(0x200)*/
>> +             if (dma_len < SZ_512)
>> +                     blk_cnt = 1;
>> +             else
>> +                     blk_cnt  = dma_len >> 9;
>> +
>> +             remain_blk_cnt  = blk_cnt;
>> +             dma_addr = sg_dma_address(sg);
>> +
>> +             while (remain_blk_cnt) {
>> +                     /*DW_MCI_MAX_SCRIPT_BLK is the max
>> +                      * for each descriptor record
>> +                      */
>> +                     if (remain_blk_cnt > DW_MCI_MAX_SCRIPT_BLK)
>> +                             cur_blk_cnt = DW_MCI_MAX_SCRIPT_BLK;
>> +                     else
>> +                             cur_blk_cnt = remain_blk_cnt;
>> +
>> +                     /* In Synopsys DesignWare Databook Page 84,
>> +                      * They mentioned the DMA 128MB restriction
>> +                      */
>> +                     begin = dma_addr / SZ_128M;
>> +                     end = (dma_addr + cur_blk_cnt * SZ_512) / SZ_128M;
>> +
>> +                     /*If begin and end in the different 128MB memory zone*/
>> +                     if (begin != end)
>> +                             cur_blk_cnt = (end * SZ_128M - dma_addr) / SZ_512;
>> +
>> +                     if (dma_len < SZ_512)
>> +                             tmp_val = ((dma_len) << 16) | VALID(0x1) | ACT(0x4);
>> +                     else
>> +                             tmp_val = ((cur_blk_cnt & 0x7f) << 25) | VALID(0x1) | ACT(0x4);
>> +
>> +                     /*Last descriptor*/
>> +                     if (i == host->dma_nents - 1 && remain_blk_cnt == cur_blk_cnt)
>> +                             tmp_val |= END(0x1);
>> +
>> +                     desc_base[0] =  tmp_val;
>> +                     desc_base[1] =  dma_addr;
>> +
>> +                     dma_addr = dma_addr + (cur_blk_cnt << 9);
>> +                     remain_blk_cnt -= cur_blk_cnt;
>> +                     desc_base += 2;
>> +             }
>> +     }
>> +}
>> +
>> +static void dw_mci_cqe_pre_req(struct mmc_host *mmc,
>> +                        struct mmc_request *mrq)
>> +{
>> +     struct dw_mci_slot *slot = mmc_priv(mmc);
>> +     struct dw_mci *host = slot->host;
>> +     struct mmc_data *data = mrq->data;
>> +     unsigned int sg_len;
>> +
>> +     if (!slot->host->use_dma || !data)
>> +             return;
>> +
>> +     /* This data might be unmapped at this time */
>> +     data->host_cookie = COOKIE_UNMAPPED;
>> +
>> +     sg_len = dma_map_sg(host->dev,
>> +                         data->sg,
>> +                         data->sg_len,
>> +                         mmc_get_dma_dir(data));
>> +     if (sg_len < 0)
>> +             data->host_cookie = COOKIE_UNMAPPED;
>> +}
>> +
>> +static void dw_mci_cqe_post_req(struct mmc_host *mmc,
>> +                         struct mmc_request *mrq,
>> +                         int err)
>> +{
>> +     struct dw_mci_slot *slot = mmc_priv(mmc);
>> +     struct mmc_data *data = mrq->data;
>> +
>> +     if (!slot->host->use_dma || !data)
>> +             return;
>> +
>> +     if (data->host_cookie != COOKIE_UNMAPPED)
>> +             dma_unmap_sg(slot->host->dev,
>> +                          data->sg,
>> +                          data->sg_len,
>> +                          mmc_get_dma_dir(data));
>> +     data->host_cookie = COOKIE_UNMAPPED;
>> +}
>> +
>> +static int dw_mci_cqe_get_cd(struct mmc_host *mmc)
>> +{
>> +     struct dw_mci_slot *slot = mmc_priv(mmc);
>> +     struct dw_mci *host = slot->host;
>> +     int gpio_cd = mmc_gpio_get_cd(mmc);
>> +     int present = -1;
>> +
>> +     /* Use platform get_cd function, else try onboard card detect */
>> +     if (((mmc->caps & MMC_CAP_NEEDS_POLL)
>> +             || !mmc_card_is_removable(mmc))) {
>> +             present = 1;
>> +
>> +             if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
>> +                     if (mmc->caps & MMC_CAP_NEEDS_POLL) {
>> +                             dev_info(&mmc->class_dev,
>> +                                     "card is polling.\n");
>> +                     } else {
>> +                             dev_info(&mmc->class_dev,
>> +                                     "card is non-removable.\n");
>> +                     }
>> +                     set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
>> +             }
>> +
>> +             return present;
>> +     } else if (gpio_cd >= 0) {
>> +             present = gpio_cd;
>> +     } else {
>> +             /*SD card detect using IP regs is todo*/
>> +             dev_err(&mmc->class_dev, "SD card detect using IP regs is ToDo.\n");
>> +     }
>> +
>> +     spin_lock_bh(&host->lock);
>> +
>> +     if (present && !test_and_set_bit(DW_MMC_CARD_PRESENT, &slot->flags))
>> +             dev_dbg(&mmc->class_dev, "card is present\n");
>> +     else if (!present &&
>> +             !test_and_clear_bit(DW_MMC_CARD_PRESENT, &slot->flags))
>> +             dev_dbg(&mmc->class_dev, "card is not present\n");
>> +
>> +     spin_unlock_bh(&host->lock);
>> +
>> +     return present;
>> +}
>> +
>> +static void dw_mci_cqe_submit_data_dma(struct dw_mci *host)
>> +{
>> +     if (host->dma_64bit_address == 1)
>> +             dw_mci_cqe_prepare_desc64(host, host->data, host->sg);
>> +     else
>> +             dw_mci_cqe_prepare_desc32(host, host->data, host->sg);
>> +
>> +}
>> +
>> +static void dw_mci_cqe_submit_data(struct dw_mci *host, struct mmc_data *data)
>> +{
>> +     u32 dir = 0;
>> +
>> +     host->sg = NULL;
>> +     host->data = data;
>> +
>> +     if (data->flags & MMC_DATA_READ)
>> +             dir = DMA_FROM_DEVICE;
>> +     else
>> +             dir = DMA_TO_DEVICE;
>> +
>> +     host->dma_nents = dma_map_sg(mmc_dev(host->slot->mmc),
>> +                                     data->sg, data->sg_len, dir);
>> +     host->sg = data->sg;
>> +
>> +     host->using_dma = 1;
>> +
>> +     dw_mci_cqe_submit_data_dma(host);
>> +}
>> +
>> +static void dw_mci_cqe_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
>> +{
>> +     struct dw_mci *host = slot->host;
>> +     const struct dw_mci_drv_data *drv_data = host->drv_data;
>> +     unsigned int clock = slot->clock;
>> +     u32 div = 0;
>> +
>> +     slot->mmc->actual_clock = 0;
>> +
>> +     if (clock != host->current_speed || force_clkinit) {
>> +             div = host->bus_hz / clock;
>> +             if (host->bus_hz % clock)
>> +                     div += 1;
>> +
>> +             if (clock != slot->__clk_old) {
>> +                     /* Silent the verbose log if calling from PM context */
>> +                     dev_info(&slot->mmc->class_dev,
>> +                             "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n",
>> +                             slot->id, host->bus_hz, clock, host->bus_hz / div, div);
>> +             }
>> +
>> +             slot->__clk_old = clock;
>> +             slot->mmc->actual_clock = host->bus_hz / div;
>> +
>> +             if (drv_data && drv_data->set_ios)
>> +                     drv_data->set_ios(slot, &slot->mmc->ios);
>> +     }
>> +}
>> +
>> +
>> +static void dw_mci_cqe_err_handle(struct dw_mci *host, struct mmc_command *cmd)
>> +{
>> +     u32 status = 0;
>> +     int err = 0;
>> +     int rty_cnt = 0;
>> +     int pstat_rty = 0;
>> +
>> +     do {
>> +             mci_writew(host, ERROR_INT_STAT_R,
>> +                     mci_readw(host, ERROR_INT_STAT_R) & 0xffff);
>> +             /*synchronous abort: stop host dma*/
>> +             mci_writeb(host, BGAP_CTRL_R, SDMMC_STOP_BG_REQ);
>> +             dw_mci_cqe_wait_done(host,
>> +                     (u32 *)(host->regs + SDMMC_NORMAL_INT_STAT_R),
>> +                             SDMMC_XFER_COMPLETE, SDMMC_XFER_COMPLETE);
>> +
>> +             mci_writew(host, NORMAL_INT_STAT_R, SDMMC_XFER_COMPLETE);
>> +
>> +             do {
>> +                     if (cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200) {
>> +                             dw_mci_cqe_prep_stop_abort(host, cmd);
>> +                             mdelay(1);
>> +
>> +                             err = dw_mci_cqe_wait_while_busy(host, &status);
>> +
>> +                             rty_cnt++;
>> +                             if (rty_cnt > 100) {
>> +                                     if (err == -DW_MCI_NOT_READY)
>> +                                             dev_err(host->dev, "status check failed, err = %d, status = 0x%x\n",
>> +                                                     err, status);
>> +                                             break;
>> +                             }
>> +                     } else {
>> +                             break;
>> +                     }
>> +             } while (err);
>> +
>> +             mci_writeb(host, SW_RST_R, SDMMC_RST_CMD | SDMMC_RST_DAT);
>> +             dw_mci_cqe_wait_done(host, (u32 *)(host->regs + SDMMC_CLK_CTRL_R),
>> +                             (BIT(25)|BIT(26)), 0);
>> +             dw_mci_cqe_wait_done(host, (u32 *)(host->regs + SDMMC_PSTATE_REG),
>> +                             (SDMMC_CMD_INHIBIT | SDMMC_CMD_INHIBIT_DAT), 0);
>> +             udelay(40);
>> +
>> +             pstat_rty++;
>> +             if (pstat_rty > 5000) {
>> +                     dev_err(host->dev, "wait pstate register data line ready timeout\n");
>> +                     break;
>> +             }
>> +     } while ((mci_readl(host, PSTATE_REG) & 0xf00000) != 0xf00000 ||
>> +             (mci_readl(host, PSTATE_REG) & 0xf0) != 0xf0);
>> +}
>> +
>> +static void dw_mci_cqe_send_stop_abort(struct dw_mci *host,
>> +                           struct dw_mci_slot *slot,
>> +                           struct mmc_command *cmd)
>> +{
>> +     dw_mci_cqe_reset(host);
>> +
>> +     if (cmd->data)
>> +             dw_mci_cqe_err_handle(host, cmd);
>> +     else
>> +             return;
>> +}
>> +
>> +static u32 dw_mci_cqe_prepare_data_flags(struct mmc_command *cmd)
>> +{
>> +     u32 dataflags;
>> +     int read_flag = 1;
>> +     int mul_blk_flag = 0;
>> +     int auto_stop_flag = 0;
>> +
>> +     if (cmd->opcode == MMC_WRITE_BLOCK ||
>> +        cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK ||
>> +        cmd->opcode == MMC_LOCK_UNLOCK ||
>> +        (cmd->opcode == MMC_GEN_CMD && cmd->arg == 0))
>> +             read_flag = 0;
>> +
>> +     if (cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK ||
>> +        cmd->opcode == MMC_READ_MULTIPLE_BLOCK) {
>> +             mul_blk_flag = 1;
>> +             auto_stop_flag = 1;
>> +     }
>> +
>> +     dataflags = (mul_blk_flag << SDMMC_MULTI_BLK_SEL) |
>> +                 (read_flag << SDMMC_DATA_XFER_DIR) |
>> +                 (auto_stop_flag << SDMMC_AUTO_CMD_ENABLE) |
>> +                 (SDMMC_BLOCK_COUNT_ENABLE) |
>> +                 (SDMMC_DMA_ENABLE);
>> +
>> +     return dataflags;
>> +}
>> +
>> +static int dw_mci_cqe_command_complete(struct dw_mci *host, u16 interrupt,
>> +                                     int *cmd_error)
>> +{
>> +     if (interrupt & (SDMMC_CMD_IDX_ERR | SDMMC_CMD_END_BIT_ERR
>> +             | SDMMC_CMD_CRC_ERR)) {
>> +             if (host->tuning)
>> +                     *cmd_error = -TUNING_ERR;
>> +             else
>> +                     *cmd_error = -EILSEQ;
>> +     } else if (interrupt & SDMMC_CMD_TOUT_ERR) {
>> +             if (host->tuning)
>> +                     *cmd_error = -TUNING_ERR;
>> +             else
>> +                     *cmd_error = -ETIMEDOUT;
>> +     } else {
>> +             *cmd_error = 0;
>> +     }
>> +
>> +     return *cmd_error;
>> +}
>> +
>> +static int dw_mci_cqe_data_complete(struct dw_mci *host, u16 interrupt,
>> +                                     int *data_error)
>> +{
>> +     if (interrupt & (SDMMC_DATA_END_BIT_ERR | SDMMC_DATA_CRC_ERR)) {
>> +             if (host->tuning)
>> +                     *data_error = -TUNING_ERR;
>> +             else
>> +                     *data_error = -EILSEQ;
>> +     } else if (interrupt & SDMMC_DATA_TOUT_ERR) {
>> +             if (host->tuning)
>> +                     *data_error = -TUNING_ERR;
>> +             else
>> +                     *data_error = -ETIMEDOUT;
>> +     } else if (interrupt & SDMMC_ADMA_ERR) {
>> +             *data_error = -EIO;
>> +     } else {
>> +             *data_error = 0;
>> +     }
>> +
>> +     return *data_error;
>> +}
>> +
>> +static void __dw_mci_cqe_start_request(struct dw_mci *host,
>> +                                struct dw_mci_slot *slot,
>> +                                struct mmc_command *cmd)
>> +{
>> +     struct mmc_data *data;
>> +     u32 cmdflags;
>> +     u32 dataflags;
>> +     int ret = 0;
>> +
>> +     data = cmd->data;
>> +
>> +     if (data) {
>> +             mci_writew(host, BLOCKCOUNT_R, data->blocks);
>> +             mci_writel(host, BLOCKSIZE_R, data->blksz);
>> +             mci_writel(host, ADMA_SA_LOW_R, host->sg_dma);
>> +
>> +             dataflags = dw_mci_cqe_prepare_data_flags(cmd);
>> +
>> +             mci_writew(host, XFER_MODE_R, dataflags);
>> +     } else {
>> +             if (cmd->opcode == MMC_SET_BLOCK_COUNT)
>> +                     host->is_sbc = 1;
>> +             else
>> +                     host->is_sbc = 0;
>> +
>> +             mci_writew(host, XFER_MODE_R, 0);
>> +     }
>> +
>> +     mci_writel(host, ARGUMENT_R, cmd->arg);
>> +
>> +     cmdflags = dw_mci_cqe_prepare_command(slot->mmc, cmd);
>> +
>> +     if (data) {
>> +             data->bytes_xfered = 0;
>> +             if (host->use_dma == TRANS_MODE_DMA) {
>> +                     dw_mci_cqe_submit_data(host, data);
>> +                     wmb(); /* drain writebuffer */
>> +             } else {
>> +                     /*Using PIO mode*/
>> +                     dev_err(host->dev, "pio mode is not supported currently\n");
>> +             }
>> +     }
>> +
>> +     ret = dw_mci_cqe_start_command(host, cmd, cmdflags);
>> +
>> +     if (ret == 0) {
>> +             dw_mci_cqe_read_rsp(host, cmd, cmd->resp);
>> +
>> +             if (data)
>> +                     data->bytes_xfered += (data->blocks * data->blksz);
>> +     }
>> +
>> +     dw_mci_cqe_command_complete(host, host->error_interrupt, &cmd->error);
>> +     if (data) {
>> +             dw_mci_cqe_data_complete(host, host->error_interrupt, &data->error);
>> +             if (host->use_dma == TRANS_MODE_DMA)
>> +                     dw_mci_cqe_stop_dma(host, data);
>> +             else {
>> +                     /*Using PIO mode*/
>> +                     dev_err(host->dev, "pio mode is not supported currently\n");
>> +             }
>> +     }
>> +
>> +     if (ret != 0)
>> +             dw_mci_cqe_send_stop_abort(host, slot, cmd);
>> +
>> +     if (cmd->opcode == SD_SWITCH_VOLTAGE) {
>> +             /*
>> +              * If cmd11 needs to be dealt with specially, put in here.
>> +              */
>> +     }
>> +}
>> +
>> +static void dw_mci_cqe_start_request(struct dw_mci *host,
>> +                              struct dw_mci_slot *slot)
>> +{
>> +     struct mmc_request *mrq = slot->mrq;
>> +
>> +     if (mrq->sbc)
>> +             __dw_mci_cqe_start_request(host, slot, mrq->sbc);
>> +
>> +     if (mrq->cmd)
>> +             __dw_mci_cqe_start_request(host, slot, mrq->cmd);
>> +}
>> +
>> +static int dw_mci_switch(struct mmc_host *mmc,
>> +                      u8 set,
>> +                      u8 index,
>> +                      u8 value,
>> +                      unsigned int timeout_ms)
>> +{
>> +     struct dw_mci_slot *slot = mmc_priv(mmc);
>> +     struct dw_mci *host = slot->host;
>> +     struct mmc_command cmd;
>> +     int err = 0;
>> +     u32 cmdr;
>> +
>> +     memset(&cmd, 0, sizeof(struct mmc_command));
>> +
>> +     cmd.opcode              = MMC_SWITCH;
>> +     cmd.arg                 = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
>> +                             (index << 16) |
>> +                             (value << 8) |
>> +                             set;
>> +     cmd.flags               = MMC_CMD_AC|MMC_RSP_SPI_R1B | MMC_RSP_R1B;
>> +     cmd.data                = NULL;
>> +
>> +     cmdr = (cmd.opcode << 8) | SDMMC_RESP_LEN_48B |
>> +             SDMMC_CMD_CHK_RESP_CRC | SDMMC_CMD_IDX_CHK_ENABLE;
>> +
>> +     mci_writew(host, XFER_MODE_R, 0);
>> +     mci_writel(host, ARGUMENT_R, cmd.arg);
>> +
>> +     err = dw_mci_cqe_start_command(host, &cmd, cmdr);
>> +
>> +     if (err) {
>> +             dev_err(host->dev, "interrupt status reg :0x%x, error reg : 0x%x\n",
>> +                     host->normal_interrupt, host->error_interrupt);
>> +     }
>> +
>> +     return err;
>> +}
>> +
>> +static int dw_mci_cqe_switch(struct mmc_host *mmc, bool enable)
>> +{
>> +     struct mmc_card *card = mmc->card;
>> +     int ret = 0;
>> +     u8 val = enable ? EXT_CSD_CMDQ_MODE_ENABLED : 0;
>> +
>> +     if (!card->ext_csd.cmdq_support) {
>> +             dev_err(&mmc->class_dev, "The device card does not support cqe mode\n");
>> +             return 0;
>> +     }
>> +
>> +     ret = dw_mci_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
>> +                         EXT_CSD_CMDQ_MODE_EN, val,
>> +                         card->ext_csd.generic_cmd6_time);
>> +     if (ret) {
>> +             dev_err(&mmc->class_dev, "cmdq mode %sable failed %d\n",
>> +                     enable ? "en" : "dis", ret);
>> +             goto out;
>> +     } else {
>> +             card->ext_csd.cmdq_en = enable;
>> +     }
>> +
>> +out:
>> +     return ret;
>> +}
>> +
>> +static void dw_mci_cqe_request(struct mmc_host *mmc, struct mmc_request *mrq)
>> +{
>> +     struct dw_mci_slot *slot = mmc_priv(mmc);
>> +     struct dw_mci *host = slot->host;
>> +     int ret;
>> +     u32 status = 0;
>> +
>> +     WARN_ON(slot->mrq);
>> +
>> +     /*
>> +      * The check for card presence and queueing of the request must be
>> +      * atomic, otherwise the card could be removed in between and the
>> +      * request wouldn't fail until another card was inserted.
>> +      */
>> +
>> +     if (!dw_mci_cqe_get_cd(mmc)) {
>> +             mrq->cmd->error = -ENOMEDIUM;
>> +             mmc_request_done(mmc, mrq);
>> +             return;
>> +     }
>> +
>> +     down_write(&host->cr_rw_sem);
>> +
>> +     /*cmdq case needs extra check*/
>> +     if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE)) {
>> +             if ((host->cqe) == NULL) {
>> +                     dev_err(host->dev, "dw_mci_request_cqe not done yet\n");
>> +                     mdelay(2);
>> +             }
>> +
>> +             if (mmc->cqe_on == false && host->cqe->activated == true
>> +                     && slot->switch_partition == 0)
>> +                     cqhci_deactivate(mmc);
>> +
>> +             if (mrq->cmd->opcode == MMC_SWITCH && mrq->cmd->arg == DW_MCI_CMDQ_DISABLED)
>> +                     slot->switch_partition = 1;
>> +
>> +             /* we do not need to disable cmdq if it is rpmb request
>> +              * because rpmb has been changed to rpmb partition in block.c
>> +              * Also, we do not need to disable cmdq if this command is disable/enable cmdq
>> +              */
>> +             if (mmc->card && mmc->card->ext_csd.cmdq_en == 1
>> +                     && slot->switch_partition == 0
>> +                     && host->cmd_atomic == false) {
>> +                     ret = dw_mci_cqe_switch(mmc, false);
>> +
>> +                     if (mrq->cmd->opcode == MMC_SLEEP_AWAKE ||
>> +                             (mrq->cmd->opcode == MMC_SELECT_CARD
>> +                                     && mrq->cmd->arg == 0) || (mrq->cmd->opcode == MMC_SWITCH
>> +                                     && mrq->cmd->arg == DW_MCI_POWEROFF))
>> +                             host->cqe_reenable = 0;
>> +                     else
>> +                             host->cqe_reenable = 1;
>> +
>> +                     if (ret)
>> +                             dev_err(host->dev, "disable cmdq failed !\n");
>> +
>> +                     dw_mci_cqe_wait_while_busy(host, &status);
>> +             }
>> +
>> +             if (mrq->cmd->opcode == MMC_SWITCH
>> +                     && mrq->cmd->arg == DW_MCI_CMDQ_ENABLED)
>> +                     slot->switch_partition = 0;
>> +
>> +             if (mrq->cmd->opcode == MMC_ERASE_GROUP_START) {
>> +                     host->cmd_atomic = true;
>> +                     host->cqe_reenable = 0;
>> +             }
>> +
>> +             if (host->cmd_atomic == true
>> +                     && mrq->cmd->opcode == MMC_SEND_STATUS) {
>> +                     host->cmd_atomic = false;
>> +                     host->cqe_reenable = 1;
>> +             }
>> +     }
>> +
>> +     slot->mrq = mrq;
>> +     host->mrq = mrq;
>> +
>> +     dw_mci_cqe_start_request(host, slot);
>> +
>> +     tasklet_schedule(&host->tasklet);
>> +
>> +     /*cmdq case needs extra check*/
>> +     if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE) &&
>> +             host->cqe_reenable == 1) {
>> +             if (mmc->card && mmc->card->ext_csd.cmdq_en == 0) {
>> +                     ret = dw_mci_cqe_switch(mmc, true);
>> +                     host->cqe_reenable = 0;
>> +                     if (ret)
>> +                             dev_err(host->dev, "switch cmdq failed !\n");
>> +                     dw_mci_cqe_wait_while_busy(host, &status);
>> +             }
>> +     }
>> +
>> +     up_write(&host->cr_rw_sem);
>> +}
>> +
>> +static void dw_mci_cqe_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>> +{
>> +     struct dw_mci_slot *slot = mmc_priv(mmc);
>> +     struct dw_mci *host = slot->host;
>> +     const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
>> +
>> +     switch (ios->timing) {
>> +     case MMC_TIMING_MMC_HS400:
>> +             mci_writew(host, HOST_CTRL2_R,
>> +                     (mci_readw(host, HOST_CTRL2_R)
>> +                             & SDMMC_UHS_MODE_SEL_MASK) | SDMMC_HS400);
>> +             break;
>> +     case MMC_TIMING_MMC_HS200:
>> +             mci_writew(host, HOST_CTRL2_R,
>> +                     (mci_readw(host, HOST_CTRL2_R)
>> +                             & SDMMC_UHS_MODE_SEL_MASK) | SDMMC_HS200);
>> +             break;
>> +     case MMC_TIMING_MMC_HS:
>> +             mci_writew(host, HOST_CTRL2_R,
>> +                     (mci_readw(host, HOST_CTRL2_R)
>> +                             & SDMMC_UHS_MODE_SEL_MASK) | SDMMC_SDR);
>> +             break;
>> +     default:
>> +             /*MMC_TIMING_LEGACY case*/
>> +             mci_writew(host, HOST_CTRL2_R,
>> +                     (mci_readw(host, HOST_CTRL2_R)
>> +                             & SDMMC_UHS_MODE_SEL_MASK) | SDMMC_LEGACY);
>> +     }
>> +
>> +     slot->clock = ios->clock;
>> +
>> +     if (drv_data && drv_data->set_ios)
>> +             drv_data->set_ios(slot, ios);
>> +
>> +     switch (ios->bus_width) {
>> +     case MMC_BUS_WIDTH_4:
>> +             mci_writeb(host, HOST_CTRL1_R,
>> +                     (mci_readb(host, HOST_CTRL1_R) &
>> +                     (SDMMC_EXT_DAT_XFER_MASK & SDMMC_DAT_XFER_WIDTH_MASK))
>> +                             |SDMMC_BUS_WIDTH_4);
>> +             break;
>> +     case MMC_BUS_WIDTH_8:
>> +             mci_writeb(host, HOST_CTRL1_R,
>> +                     (mci_readb(host, HOST_CTRL1_R) &
>> +                             SDMMC_EXT_DAT_XFER_MASK) | SDMMC_BUS_WIDTH_8);
>> +             break;
>> +     default:
>> +             /* set default 1 bit mode */
>> +             mci_writeb(host, HOST_CTRL1_R,
>> +                     (mci_readb(host, HOST_CTRL1_R) &
>> +                             (SDMMC_EXT_DAT_XFER_MASK &
>> +                             SDMMC_DAT_XFER_WIDTH_MASK)) | SDMMC_BUS_WIDTH_1);
>> +     }
>> +}
>> +
>> +static int dw_mci_cqe_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
>> +{
>> +     struct dw_mci_slot *slot = mmc_priv(mmc);
>> +     struct dw_mci *host = slot->host;
>> +     const struct dw_mci_drv_data *drv_data = host->drv_data;
>> +
>> +     if (drv_data && drv_data->switch_voltage)
>> +             return drv_data->switch_voltage(mmc, ios);
>> +
>> +     return 0;
>> +}
>> +
>> +static int dw_mci_cqe_get_ro(struct mmc_host *mmc)
>> +{
>> +     int read_only;
>> +     int gpio_ro = mmc_gpio_get_ro(mmc);
>> +
>> +     /* Use platform get_ro function, else try on board write protect */
>> +     if (gpio_ro >= 0)
>> +             read_only = gpio_ro;
>> +     else
>> +             /*Need to read the IP register to judge if ro*/
>> +             dev_err(&mmc->class_dev, "IP get_ro feature is not implemented currently.\n");
>> +
>> +     dev_dbg(&mmc->class_dev, "card is %s\n",
>> +             read_only ? "read-only" : "read-write");
>> +
>> +     return read_only;
>> +}
>> +
>> +static int dw_mci_cqe_execute_tuning(struct mmc_host *mmc, u32 opcode)
>> +{
>> +     struct dw_mci_slot *slot = mmc_priv(mmc);
>> +     struct dw_mci *host = slot->host;
>> +     const struct dw_mci_drv_data *drv_data = host->drv_data;
>> +     int err = -EINVAL;
>> +
>> +     if (drv_data && drv_data->execute_tuning)
>> +             err = drv_data->execute_tuning(slot, opcode);
>> +     return err;
>> +
>> +}
>> +
>> +static int dw_mci_cqe_prepare_hs400_tuning(struct mmc_host *mmc,
>> +                                    struct mmc_ios *ios)
>> +{
>> +     struct dw_mci_slot *slot = mmc_priv(mmc);
>> +     struct dw_mci *host = slot->host;
>> +     const struct dw_mci_drv_data *drv_data = host->drv_data;
>> +
>> +     if (drv_data && drv_data->prepare_hs400_tuning)
>> +             return drv_data->prepare_hs400_tuning(host, ios);
>> +
>> +     return 0;
>> +}
>> +
>> +static void dw_mci_cqe_hs400_complete(struct mmc_host *mmc)
>> +{
>> +     struct dw_mci_slot *slot = mmc_priv(mmc);
>> +     struct dw_mci *host = slot->host;
>> +     const struct dw_mci_drv_data *drv_data = host->drv_data;
>> +
>> +     if (drv_data && drv_data->hs400_complete)
>> +             drv_data->hs400_complete(mmc);
>> +}
>> +
>> +static void dw_mci_cqe_init_card(struct mmc_host *mmc, struct mmc_card *card)
>> +{
>> +     struct dw_mci_slot *slot = mmc_priv(mmc);
>> +     struct dw_mci *host = slot->host;
>> +     const struct dw_mci_drv_data *drv_data = host->drv_data;
>> +
>> +     /*
>> +      * Add any quirks for this synopsys IP here or
>> +      * deal with something special for some specific
>> +      * vendors' SOC platform by calling drv_data->init_card().
>> +      */
>> +     if (drv_data && drv_data->init_card)
>> +             drv_data->init_card(mmc, card);
>> +}
>> +
>> +static const struct mmc_host_ops dw_mci_ops = {
>> +     .request                = dw_mci_cqe_request,
>> +     .pre_req                = dw_mci_cqe_pre_req,
>> +     .post_req               = dw_mci_cqe_post_req,
>> +     .set_ios                = dw_mci_cqe_set_ios,
>> +     .get_ro                 = dw_mci_cqe_get_ro,
>> +     .get_cd                 = dw_mci_cqe_get_cd,
>> +     .execute_tuning         = dw_mci_cqe_execute_tuning,
>> +     .start_signal_voltage_switch = dw_mci_cqe_switch_voltage,
>> +     .init_card              = dw_mci_cqe_init_card,
>> +     .prepare_hs400_tuning   = dw_mci_cqe_prepare_hs400_tuning,
>> +     .hs400_complete         = dw_mci_cqe_hs400_complete,
>> +};
>> +
>> +static void dw_mci_cqe_tasklet_func(unsigned long priv)
>> +{
>> +     struct dw_mci *host = (struct dw_mci *)priv;
>> +     struct mmc_host *prev_mmc = host->slot->mmc;
>> +     struct mmc_request *mrq;
>> +     unsigned long flags;
>> +
>> +     spin_lock_irqsave(&host->irq_lock, flags);
>> +
>> +     host->cmd = NULL;
>> +     host->data = NULL;
>> +     mrq = host->mrq;
>> +     host->slot->mrq = NULL;
>> +     host->mrq = NULL;
>> +
>> +     spin_unlock_irqrestore(&host->irq_lock, flags);
>> +
>> +     mmc_request_done(prev_mmc, mrq);
>> +}
>> +
>> +static irqreturn_t dw_mci_cqe_interrupt(int irq, void *dev_id)
>> +{
>> +     struct dw_mci *host = dev_id;
>> +     struct mmc_host *mmc = host->slot->mmc;
>> +     struct cqhci_host *cq_host = NULL;
>> +     int cmd_error = 0, data_error = 0;
>> +
>> +     if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE))
>> +             cq_host = mmc->cqe_private;
>> +
>> +     dw_mci_get_int(host);
>> +
>> +     if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE)) {
>> +             if (mmc->cqe_on == false && cq_host->activated == false)
>> +                     dw_mci_clr_signal_int(host);
>> +     } else {
>> +             dw_mci_clr_signal_int(host);
>> +     }
>> +     /*if run the cmdq mode*/
>> +     if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE) &&
>> +             mmc->cqe_on == true && cq_host->activated == true) {
>> +             if (host->normal_interrupt & SDMMC_ERR_INTERRUPT) {
>> +                     dev_err(host->dev, "cmdq error: interrupt status=%08x, error interrupt=0x%08x, CQIS=0x%x, CQTCN=0x%x\n",
>> +                             host->normal_interrupt, host->error_interrupt,
>> +                             readl(host->cqe->mmio + CQHCI_IS),
>> +                             readl(host->cqe->mmio + CQHCI_TCN));
>> +
>> +                     dw_mci_cqe_command_complete(host, host->error_interrupt, &cmd_error);
>> +                     dw_mci_cqe_data_complete(host, host->error_interrupt, &data_error);
>> +             }
>> +             cqhci_irq(mmc, (u32)(host->normal_interrupt), cmd_error, data_error);
>> +             dw_mci_clr_int(host);
>> +
>> +             return IRQ_HANDLED;
>> +     }
>> +
>> +     if (host->int_waiting) {
>> +             del_timer(&host->timer);
>> +             complete(host->int_waiting);
>> +     }
>> +
>> +     return IRQ_HANDLED;
>> +
>> +}
>> +
>> +static void dw_mci_cqe_setup(struct dw_mci *host)
>> +{
>> +     mci_writeb(host, SW_RST_R, (SDMMC_RST_ALL|SDMMC_RST_CMD|SDMMC_RST_DAT));
>> +     mci_writeb(host, TOUT_CTRL_R, 0xe);
>> +     mci_writew(host, HOST_CTRL2_R, SDMMC_HOST_VER4_ENABLE|SDMMC_SIGNALING_EN);
>> +     mci_writew(host, NORMAL_INT_STAT_EN_R, 0xffff);
>> +     mci_writew(host, ERROR_INT_STAT_EN_R, SDMMC_ALL_ERR_STAT_EN);
>> +     /*Card detect will be enabled in the last*/
>> +     mci_writew(host, NORMAL_INT_SIGNAL_EN_R,
>> +             (~(SDMMC_CARD_INSERTION_SIGNAL_EN | SDMMC_CARD_REMOVAL_SIGNAL_EN |
>> +                     SDMMC_CARD_INTERRUPT_SIGNAL_EN) & 0xffff));
>> +     mci_writew(host, ERROR_INT_SIGNAL_EN_R, SDMMC_ALL_ERR_SIGNAL_EN);
>> +     mci_writeb(host, CTRL_R, SDMMC_RST_N_OE|SDMMC_RST_N|SDMMC_CARD_IS_EMMC);
>> +     mci_writeb(host, HOST_CTRL1_R,
>> +             (mci_readb(host, HOST_CTRL1_R)&0xe7) | (SDMMC_ADMA2_32 << SDMMC_DMA_SEL));
>> +     mci_writeb(host, MSHC_CTRL_R, mci_readb(host, MSHC_CTRL_R) & (~SDMMC_CMD_CONFLICT_CHECK));
>> +     mci_writew(host, CLK_CTRL_R, mci_readw(host, CLK_CTRL_R)|SDMMC_INTERNAL_CLK_EN);
>> +}
>> +
>> +static int dw_mci_cqe_init_slot_caps(struct dw_mci_slot *slot)
>> +{
>> +     struct dw_mci *host = slot->host;
>> +     const struct dw_mci_drv_data *drv_data = host->drv_data;
>> +     struct mmc_host *mmc = slot->mmc;
>> +     int ctrl_id;
>> +
>> +     if (host->pdata->caps)
>> +             mmc->caps = host->pdata->caps;
>> +
>> +     if (host->pdata->pm_caps)
>> +             mmc->pm_caps = host->pdata->pm_caps;
>> +
>> +     if (host->dev->of_node) {
>> +             ctrl_id = of_alias_get_id(host->dev->of_node, "mshc");
>> +             if (ctrl_id < 0)
>> +                     ctrl_id = 0;
>> +     } else {
>> +             ctrl_id = to_platform_device(host->dev)->id;
>> +     }
>> +
>> +     if (drv_data && drv_data->caps) {
>> +             if (ctrl_id >= drv_data->num_caps) {
>> +                     dev_err(host->dev, "invalid controller id %d\n",
>> +                             ctrl_id);
>> +                     return -EINVAL;
>> +             }
>> +             mmc->caps |= drv_data->caps[ctrl_id];
>> +     }
>> +
>> +     if (drv_data && drv_data->shift_rsp)
>> +             host->shift = 1;
>> +     else
>> +             host->shift = 0;
>> +
>> +     if (host->pdata->caps2)
>> +             mmc->caps2 = host->pdata->caps2;
>> +
>> +     mmc->f_min = DW_MCI_FREQ_MIN;
>> +     if (!mmc->f_max)
>> +             mmc->f_max = DW_MCI_FREQ_MAX;
>> +
>> +     /* Process SDIO IRQs through the sdio_irq_work. */
>> +     if (mmc->caps & MMC_CAP_SDIO_IRQ)
>> +             mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
>> +
>> +     return 0;
>> +}
>> +
>> +static int dw_mci_cqe_init_slot(struct dw_mci *host)
>> +{
>> +     struct mmc_host *mmc;
>> +     struct dw_mci_slot *slot;
>> +     int ret;
>> +
>> +     mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
>> +     if (!mmc)
>> +             return -ENOMEM;
>> +
>> +     slot = mmc_priv(mmc);
>> +     slot->id = 0;
>> +     slot->sdio_id = host->sdio_id0 + slot->id;
>> +     slot->mmc = mmc;
>> +     slot->switch_partition = 0;
>> +     slot->host = host;
>> +     host->slot = slot;
>> +
>> +     mmc->ops = &dw_mci_ops;
>> +
>> +     /*if there are external regulators, get them*/
>> +     ret = mmc_regulator_get_supply(mmc);
>> +     if (ret)
>> +             goto err_host_allocated;
>> +
>> +     if (!mmc->ocr_avail)
>> +             mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
>> +
>> +     dev_info(host->dev, "regulator support volage ocr_avail=0x%x\n",
>> +                     mmc->ocr_avail);
>> +
>> +     ret = mmc_of_parse(mmc);
>> +     if (ret)
>> +             goto err_host_allocated;
>> +
>> +     ret = dw_mci_cqe_init_slot_caps(slot);
>> +     if (ret)
>> +             goto err_host_allocated;
>> +
>> +     /* Useful defaults if platform data is unset. */
>> +     if (host->use_dma == TRANS_MODE_DMA) {
>> +             mmc->max_segs = 256;
>> +             mmc->max_blk_size = 512;
>> +             mmc->max_seg_size = 0x1000;
>> +             mmc->max_req_size = mmc->max_seg_size * mmc->max_segs;
>> +             mmc->max_blk_count = mmc->max_req_size / 512;
>> +     } else {
>> +             dev_info(host->dev, "dw-mmc-cqe pio mode is ToDo.\n");
>> +             /* To DO, TRANS_MODE_PIO */
>> +     }
>> +
>> +     dw_mci_cqe_get_cd(mmc);
>> +
>> +     ret = mmc_add_host(mmc);
>> +     if (ret)
>> +             goto err_host_allocated;
>> +
>> +     return 0;
>> +
>> +err_host_allocated:
>> +     mmc_free_host(mmc);
>> +     return ret;
>> +}
>> +
>> +static void dw_mci_cqe_cleanup_slot(struct dw_mci_slot *slot)
>> +{
>> +     /* Debugfs stuff is cleaned up by mmc core */
>> +     mmc_remove_host(slot->mmc);
>> +     slot->host->slot = NULL;
>> +     mmc_free_host(slot->mmc);
>> +}
>> +
>> +static void dw_mci_cqe_init_dma(struct dw_mci *host)
>> +{
>> +     host->use_dma = TRANS_MODE_DMA;
>> +
>> +     /* Determine which DMA interface to use */
>> +     /* using 32bit DMA by default,
>> +      * user can modify this setting by drv_data->init()
>> +      */
>> +     if (host->use_dma == TRANS_MODE_DMA) {
>> +             host->dma_64bit_address = 0;
>> +             dev_info(host->dev, "IDMAC supports 32-bit address mode.\n");
>> +     }
>> +
>> +     /* Alloc memory for sg translation */
>> +     host->sg_cpu = dma_alloc_coherent(host->dev,
>> +                                             DW_MCI_DESC_LEN,
>> +                                             &host->sg_dma, GFP_KERNEL);
>> +     if (!host->sg_cpu) {
>> +             dev_err(host->dev,
>> +                     "%s: could not alloc DMA memory\n",
>> +                     __func__);
>> +             goto no_dma;
>> +     }
>> +
>> +     return;
>> +
>> +no_dma:
>> +     dev_info(host->dev, "Using PIO mode.\n");
>> +     host->use_dma = TRANS_MODE_PIO;
>> +}
>> +
>> +#ifdef CONFIG_OF
>> +static struct dw_mci_board *dw_mci_cqe_parse_dt(struct dw_mci *host)
>> +{
>> +     struct dw_mci_board *pdata;
>> +     struct device *dev = host->dev;
>> +     const struct dw_mci_drv_data *drv_data = host->drv_data;
>> +     int ret;
>> +     u32 clock_frequency;
>> +
>> +     pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
>> +     if (!pdata)
>> +             return ERR_PTR(-ENOMEM);
>> +
>> +     /* find reset controller when exist */
>> +     pdata->rstc = devm_reset_control_get_optional(dev, "reset");
>> +     if (IS_ERR(pdata->rstc)) {
>> +             if (PTR_ERR(pdata->rstc) == -EPROBE_DEFER)
>> +                     return ERR_PTR(-EPROBE_DEFER);
>> +     }
>> +
>> +     device_property_read_u32(dev, "card-detect-delay",
>> +             &pdata->detect_delay_ms);
>> +
>> +     if (!device_property_read_u32(dev, "clock-frequency", &clock_frequency))
>> +             pdata->bus_hz = clock_frequency;
>> +
>> +     if (drv_data && drv_data->parse_dt) {
>> +             ret = drv_data->parse_dt(host);
>> +             if (ret)
>> +                     return ERR_PTR(ret);
>> +     }
>> +
>> +     return pdata;
>> +}
>> +
>> +#else /* CONFIG_OF */
>> +static struct dw_mci_board *dw_mci_cqe_parse_dt(struct dw_mci *host)
>> +{
>> +     return ERR_PTR(-EINVAL);
>> +}
>> +#endif /* CONFIG_OF */
>> +
>> +static void dw_mci_cqe_cto_timer(struct timer_list *t)
>> +{
>> +     struct dw_mci *host = from_timer(host, t, timer);
>> +
>> +     if (host->int_waiting) {
>> +             dev_err(host->dev, "fired, opcode=%d, arg=0x%x, irq status=0x%x, err irq=0x%x, auto err irq=0x%x\n",
>> +                             host->opcode, host->arg,
>> +                     host->normal_interrupt, host->error_interrupt,
>> +                     host->auto_error_interrupt);
>> +
>> +             dw_mci_clr_signal_int(host);
>> +             dw_mci_get_int(host);
>> +
>> +             complete(host->int_waiting);
>> +     }
>> +}
>> +
>> +
>> +static void dw_mci_cqe_enable_cd(struct dw_mci *host)
>> +{
>> +     /*
>> +      * No need for CD if all slots have a non-error GPIO
>> +      * as well as broken card detection is found.
>> +      */
>> +     if (host->slot->mmc->caps & MMC_CAP_NEEDS_POLL
>> +             || !mmc_card_is_removable(host->slot->mmc))
>> +             return;
>> +}
>> +
>> +static void dw_mci_cqhci_init(struct dw_mci *host)
>> +{
>> +     if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE)) {
>> +             host->cqe = cqhci_pltfm_init(host->pdev);
>> +             if (PTR_ERR(host->cqe) == -EINVAL ||
>> +                PTR_ERR(host->cqe) == -ENOMEM ||
>> +                PTR_ERR(host->cqe) == -EBUSY) {
>> +                     dev_err(host->dev,
>> +                             "Unable to get the cmdq related attribute,err = %ld\n",
>> +                             PTR_ERR(host->cqe));
>> +                     host->cqe = 0;
>> +                     host->pdata->caps2 &= ~(MMC_CAP2_CQE|MMC_CAP2_CQE_DCMD);
>> +             } else {
>> +                     host->cqe->ops = &dw_mci_cqhci_host_ops;
>> +                     cqhci_init(host->cqe, host->slot->mmc, 0);
>> +             }
>> +     }
>> +}
>> +
>> +int dw_mci_cqe_probe(struct dw_mci *host)
>> +{
>> +     const struct dw_mci_drv_data *drv_data = host->drv_data;
>> +     int ret = 0;
>> +
>> +     if (!host->pdata) {
>> +             host->pdata = dw_mci_cqe_parse_dt(host);
>> +             if (PTR_ERR(host->pdata) == -EPROBE_DEFER) {
>> +                     return -EPROBE_DEFER;
>> +             } else if (IS_ERR(host->pdata)) {
>> +                     dev_err(host->dev, "platform data not available\n");
>> +                     return -EINVAL;
>> +             }
>> +     }
>> +
>> +     host->biu_clk = devm_clk_get(host->dev, "biu");
>> +     if (IS_ERR(host->biu_clk)) {
>> +             dev_dbg(host->dev, "biu clock not available\n");
>> +     } else {
>> +             ret = clk_prepare_enable(host->biu_clk);
>> +             if (ret) {
>> +                     dev_err(host->dev, "failed to enable biu clock\n");
>> +                     return ret;
>> +             }
>> +     }
>> +
>> +     host->ciu_clk = devm_clk_get(host->dev, "ciu");
>> +     if (IS_ERR(host->ciu_clk)) {
>> +             dev_dbg(host->dev, "ciu clock not available\n");
>> +             host->bus_hz = host->pdata->bus_hz;
>> +     } else {
>> +             ret = clk_prepare_enable(host->ciu_clk);
>> +             if (ret) {
>> +                     dev_err(host->dev, "failed to enable ciu clock\n");
>> +                     goto err_clk_biu;
>> +             }
>> +
>> +             if (host->pdata->bus_hz) {
>> +                     ret = clk_set_rate(host->ciu_clk, host->pdata->bus_hz);
>> +                     if (ret)
>> +                             dev_warn(host->dev,
>> +                                     "Unable to set bus rate to %uHz\n",
>> +                                      host->pdata->bus_hz);
>> +             }
>> +             host->bus_hz = clk_get_rate(host->ciu_clk);
>> +     }
>> +
>> +     if (!host->bus_hz) {
>> +             dev_err(host->dev,
>> +                     "Platform data must supply bus speed\n");
>> +             ret = -ENODEV;
>> +             goto err_clk_ciu;
>> +     }
>> +
>> +     if (!IS_ERR(host->pdata->rstc)) {
>> +             reset_control_assert(host->pdata->rstc);
>> +             usleep_range(10, 50);
>> +             reset_control_deassert(host->pdata->rstc);
>> +     }
>> +
>> +     timer_setup(&host->timer, dw_mci_cqe_cto_timer, 0);
>> +
>> +     spin_lock_init(&host->lock);
>> +     spin_lock_init(&host->irq_lock);
>> +     init_rwsem(&host->cr_rw_sem);
>> +     tasklet_init(&host->tasklet, dw_mci_cqe_tasklet_func, (unsigned long)host);
>> +
>> +     host->cqe_reenable = 0;
>> +     host->cmd_atomic = false;
>> +
>> +     /*pio mode's parameters should be initialized here*/
>> +
>> +     /*Initialize the eMMC IP related attribute*/
>> +     dw_mci_cqe_setup(host);
>> +
>> +     dw_mci_cqe_init_dma(host);
>> +
>> +     /* This flag will be set 1 when doing tuning,
>> +      * we add this flag because
>> +      * some vendors might use other cmd instead of 21
>> +      * to tune phase under high speed interface.
>> +      * we use this flag to recognize if the system is under tuning stage.
>> +      */
>> +     host->tuning = 0;
>> +
>> +     /*Timing_setting is to avoid sending command
>> +      *before setting phase in hs200, hs400
>> +      */
>> +     host->current_speed = 0;
>> +
>> +     /*Do the rest of init for specific*/
>> +     if (drv_data && drv_data->init) {
>> +             ret = drv_data->init(host);
>> +             if (ret) {
>> +                     dev_err(host->dev,
>> +                             "implementation specific init failed\n");
>> +                     goto err_dmaunmap;
>> +             }
>> +     }
>> +
>> +     ret = dw_mci_cqe_init_slot(host);
>> +     if (ret) {
>> +             dev_err(host->dev, "slot 0 init failed\n");
>> +             goto err_dmaunmap;
>> +     }
>> +
>> +     ret = devm_request_irq(host->dev, host->irq, dw_mci_cqe_interrupt,
>> +                             host->irq_flags, "dw-mci-cqe", host);
>> +     if (ret)
>> +             goto err_dmaunmap;
>> +
>> +     /*After the slot initialization,
>> +      *now we have mmc data and can initialize cmdq if user enabled
>> +      */
>> +     dw_mci_cqhci_init(host);
>> +
>> +     /* Now that slots are all setup, we can enable card detect */
>> +     dw_mci_cqe_enable_cd(host);
>> +
>> +     return 0;
>> +
>> +err_dmaunmap:
>> +     if (!IS_ERR(host->pdata->rstc))
>> +             reset_control_assert(host->pdata->rstc);
>> +err_clk_ciu:
>> +     clk_disable_unprepare(host->ciu_clk);
>> +
>> +err_clk_biu:
>> +     clk_disable_unprepare(host->biu_clk);
>> +
>> +     return ret;
>> +}
>> +EXPORT_SYMBOL(dw_mci_cqe_probe);
>> +
>> +void dw_mci_cqe_remove(struct dw_mci *host)
>> +{
>> +     dev_dbg(host->dev, "remove slot\n");
>> +     if (host->slot)
>> +             dw_mci_cqe_cleanup_slot(host->slot);
>> +
>> +     if (!IS_ERR(host->pdata->rstc))
>> +             reset_control_assert(host->pdata->rstc);
>> +
>> +     clk_disable_unprepare(host->ciu_clk);
>> +     clk_disable_unprepare(host->biu_clk);
>> +
>> +}
>> +EXPORT_SYMBOL(dw_mci_cqe_remove);
>> +
>> +#ifdef CONFIG_PM
>> +int dw_mci_cqe_runtime_suspend(struct device *dev)
>> +{
>> +     struct dw_mci *host = dev_get_drvdata(dev);
>> +     int ret = 0;
>> +
>> +     if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE)) {
>> +             if (host->slot) {
>> +                     dev_info(host->dev, "cqe suspend\n");
>> +                     ret = cqhci_suspend(host->slot->mmc);
>> +                     if (ret) {
>> +                             dev_err(host->dev, "cqe suspend failed\n");
>> +                             return ret;
>> +                     }
>> +             }
>> +     }
>> +
>> +     ret = pm_runtime_force_suspend(dev);
>> +
>> +     return ret;
>> +}
>> +EXPORT_SYMBOL(dw_mci_cqe_runtime_suspend);
>> +
>> +int dw_mci_cqe_runtime_resume(struct device *dev)
>> +{
>> +     struct dw_mci *host = dev_get_drvdata(dev);
>> +     const struct dw_mci_drv_data *drv_data = host->drv_data;
>> +     int ret = 0;
>> +
>> +     ret = pm_runtime_force_resume(dev);
>> +     if (ret) {
>> +             dev_err(host->dev, "pm_runtime_force_resume failed\n");
>> +             return ret;
>> +     }
>> +
>> +     dw_mci_cqe_setup(host);
>> +     if (drv_data && drv_data->init) {
>> +             ret = drv_data->init(host);
>> +             if (ret)
>> +                     dev_err(host->dev, "implementation specific init failed\n");
>> +     }
>> +
>> +     init_completion(host->int_waiting);
>> +
>> +     if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE)) {
>> +             if (host->slot) {
>> +                     dev_info(host->dev, "cqe resume\n");
>> +                     ret = cqhci_resume(host->slot->mmc);
>> +                     if (ret)
>> +                             dev_err(host->dev, "cqe resume failed\n");
>> +             }
>> +     }
>> +
>> +     dw_mci_cqe_setup_bus(host->slot, true);
>> +     dw_mci_cqe_enable_cd(host);
>> +
>> +     return ret;
>> +}
>> +EXPORT_SYMBOL(dw_mci_cqe_runtime_resume);
>> +#endif /* CONFIG_PM */
>> +
>> +static int __init dw_mci_cqe_init(void)
>> +{
>> +     pr_info("Synopsys Designware Multimedia Card Interface Driver\n");
>> +     return 0;
>> +}
>> +
>> +static void __exit dw_mci_cqe_exit(void)
>> +{
>> +}
>> +
>> +module_init(dw_mci_cqe_init);
>> +module_exit(dw_mci_cqe_exit);
>> +
>> +MODULE_DESCRIPTION("DW Multimedia Card CMDQ Interface driver");
>> +MODULE_AUTHOR("<jyanchou@realtek.com>");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/mmc/host/dw_mmc_cqe.h b/drivers/mmc/host/dw_mmc_cqe.h
>> new file mode 100644
>> index 000000000000..ef52b67aceb6
>> --- /dev/null
>> +++ b/drivers/mmc/host/dw_mmc_cqe.h
>> @@ -0,0 +1,444 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> +/*
>> + *  Copyright (C) 2023 Realtek Semiconductors, All Rights Reserved.
>> + *
>> + */
>> +
>> +#ifndef __DW_MMC_CQE_H
>> +#define __DW_MMC_CQE_H
>> +
>> +#include <linux/dmaengine.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/mmc/core.h>
>> +#include <linux/reset.h>
>> +#include <linux/scatterlist.h>
>> +
>> +struct dw_mci {
>> +     spinlock_t              lock;
>> +     spinlock_t              irq_lock;
>> +     struct tasklet_struct   tasklet;
>> +     struct rw_semaphore     cr_rw_sem;
>> +
>> +     void __iomem            *regs;
>> +     resource_size_t         phy_regs;
>> +
>> +     struct mmc_request      *mrq;
>> +     struct mmc_command      *cmd;
>> +     struct mmc_command      stop_abort;
>> +     struct mmc_data         *data;
>> +
>> +     struct clk              *biu_clk;
>> +     struct clk              *ciu_clk;
>> +     struct dw_mci_slot      *slot;
>> +     struct timer_list       timer;
>> +     struct completion       *int_waiting;
>> +
>> +     unsigned int            *desc_vaddr;
>> +     unsigned int            *sg_cpu;
>> +     dma_addr_t              sg_dma;
>> +     int                     use_dma;
>> +
>> +     struct platform_device  *pdev;
>> +     struct device           *dev;
>> +     struct dw_mci_board     *pdata;
>> +     const struct dw_mci_drv_data    *drv_data;
>> +     void                    *priv;
>> +
>> +     u32                     opcode;
>> +     u32                     arg;
>> +     u16                     normal_interrupt;
>> +     u16                     error_interrupt;
>> +     u16                     auto_error_interrupt;
>> +
>> +     u32                     bus_hz;
>> +     u32                     current_speed;
>> +     u32                     stop_cmdr;
>> +     bool                    is_sbc;
>> +     int                     dma_64bit_address;
>> +     int                     using_dma;
>> +
>> +     unsigned long           irq_flags; /* IRQ flags */
>> +     int                     irq;
>> +     int                     sdio_id0;
>> +
>> +     struct scatterlist      *sg;
>> +     u32                     dma_nents;
>> +
>> +     u8                      tuning;
>> +     u8                      cqe_reenable;
>> +     bool                    cmd_atomic;
>> +     bool                    shift;
>> +     struct cqhci_host       *cqe;
>> +};
>> +
>> +enum {
>> +     TRANS_MODE_PIO = 0,
>> +     TRANS_MODE_DMA,
>> +};
>> +
>> +enum dw_mci_cookie {
>> +     COOKIE_UNMAPPED,
>> +     COOKIE_PRE_MAPPED,      /* mapped by pre_req() of dwmmc */
>> +     COOKIE_MAPPED,          /* mapped by prepare_data() of dwmmc */
>> +};
>> +/* eMMC control register definition */
>> +#define SDMMC_SDMASA_R                               0x000
>> +#define SDMMC_BLOCKSIZE_R                    0x004
>> +#define SDMMC_BLOCKCOUNT_R                   0x006
>> +#define SDMMC_ARGUMENT_R                     0x008
>> +#define SDMMC_XFER_MODE_R                    0x00c
>> +#define SDMMC_CMD_R                          0x00e
>> +#define SDMMC_RESP01_R                               0x010
>> +#define SDMMC_RESP23_R                               0x014
>> +#define SDMMC_RESP45_R                               0x018
>> +#define SDMMC_RESP67_R                               0x01c
>> +#define SDMMC_BUF_DATA_R                     0x020
>> +#define SDMMC_PSTATE_REG                     0x024
>> +#define SDMMC_HOST_CTRL1_R                   0x028
>> +#define SDMMC_PWR_CTRL_R                     0x029
>> +#define SDMMC_BGAP_CTRL_R                    0x02a
>> +#define SDMMC_CLK_CTRL_R                     0x02c
>> +#define SDMMC_TOUT_CTRL_R                    0x02e
>> +#define SDMMC_SW_RST_R                               0x02f
>> +#define SDMMC_NORMAL_INT_STAT_R                      0x030
>> +#define SDMMC_ERROR_INT_STAT_R                       0x032
>> +#define SDMMC_NORMAL_INT_STAT_EN_R           0x034
>> +#define SDMMC_ERROR_INT_STAT_EN_R            0x036
>> +#define SDMMC_NORMAL_INT_SIGNAL_EN_R         0x038
>> +#define SDMMC_ERROR_INT_SIGNAL_EN_R          0x03a
>> +#define SDMMC_AUTO_CMD_STAT_R                        0x03c
>> +#define SDMMC_HOST_CTRL2_R                   0x03e
>> +#define SDMMC_ADMA_ERR_STAT_R                        0x054
>> +#define SDMMC_ADMA_SA_LOW_R                  0x058
>> +
>> +#define SDMMC_MSHC_CTRL_R                    0x208
>> +#define SDMMC_CTRL_R                         0x22c
>> +
>> +#define SDMMC_CMD_CONFLICT_CHECK             BIT(0)
>> +#define CMD_IDX_MASK(x)                              ((x >> 8)&0x3f)
>> +
>> +/*0xc*/
>> +#define SDMMC_MULTI_BLK_SEL                  5
>> +#define SDMMC_DATA_XFER_DIR                  4
>> +#define SDMMC_BLOCK_COUNT_ENABLE             BIT(1)
>> +#define SDMMC_DMA_ENABLE                     BIT(0)
>> +#define SDMMC_AUTO_CMD_ENABLE                        2
>> +#define SDMMC_AUTO_CMD_DISABLED                      0x0
>> +#define SDMMC_AUTO_CMD12_ENABLED             0x1
>> +#define SDMMC_AUTO_CMD23_ENABLED             0x2
>> +#define SDMMC_AUTO_CMD_SEL                   0x3
>> +
>> +/*0xe*/
>> +#define SDMMC_RESP_TYPE_SELECT                       0
>> +#define SDMMC_CMD_TYPE                               6
>> +#define SDMMC_NO_RESP                                0x0
>> +#define SDMMC_RESP_LEN_136                   0x1
>> +#define SDMMC_RESP_LEN_48                    0x2
>> +#define SDMMC_RESP_LEN_48B                   0x3
>> +#define SDMMC_CMD_CHK_RESP_CRC                       BIT(3)
>> +#define SDMMC_CMD_IDX_CHK_ENABLE             BIT(4)
>> +#define SDMMC_DATA                           BIT(5)
>> +#define SDMMC_ABORT_CMD                              0x3
>> +#define SDMMC_RESUME_CMD                     0x2
>> +#define SDMMC_SUSPEND_CMD                    0x1
>> +#define SDMMC_NORMAL_CMD                     0x0
>> +
>> +/*0x24 PSTATE*/
>> +#define SDMMC_CMD_INHIBIT                    BIT(0)
>> +#define SDMMC_CMD_INHIBIT_DAT                        BIT(1)
>> +#define SDMMC_DAT_3_0                                (0xf << 20)
>> +#define SDMMC_DAT_7_4                                (0xf << 4)
>> +
>> +/*0x28*/
>> +#define SDMMC_DMA_SEL                                3
>> +#define SDMMC_SDMA                           (0x0)
>> +#define SDMMC_ADMA2_32                               (0x2)
>> +#define SDMMC_ADMA2_64                               (0x3)
>> +#define SDMMC_EXT_DAT_XFER                   BIT(5)
>> +#define SDMMC_EXT_DAT_XFER_MASK                      (~SDMMC_EXT_DAT_XFER & 0xff)
>> +#define SDMMC_HIGH_SPEED_EN                  BIT(2)
>> +#define SDMMC_HIGH_SPEED_MASK                        ((~BIT(2)) & 0xff)
>> +#define SDMMC_UHS_MODE_SEL_MASK                      ((~(BIT(0)|BIT(1)|BIT(2))) & 0xffff)
>> +#define SDMMC_DAT_XFER_WIDTH                 BIT(1)
>> +#define SDMMC_DAT_XFER_WIDTH_MASK            (~SDMMC_DAT_XFER_WIDTH & 0xff)
>> +#define SDMMC_BUS_WIDTH_8                    SDMMC_EXT_DAT_XFER
>> +#define SDMMC_BUS_WIDTH_4                    SDMMC_DAT_XFER_WIDTH
>> +#define SDMMC_BUS_WIDTH_1                    0
>> +#define SDMMC_DMA_SEL_CLR                    (0xff & (~(0x3<<SDMMC_DMA_SEL)))
>> +#define SDMMC_DATA_XFER_CLR                  ((0xff & (~SDMMC_EXT_DAT_XFER)) \
>> +                                             & (~SDMMC_DAT_XFER_WIDTH))
>> +
>> +/*0x2a*/
>> +#define SDMMC_STOP_BG_REQ                    BIT(0)
>> +
>> +/*0x2f SW_RST_R*/
>> +#define SDMMC_RST_DAT                        BIT(2)
>> +#define SDMMC_RST_CMD                        BIT(1)
>> +#define SDMMC_RST_ALL                        BIT(0)
>> +
>> +/*0x30 status bitmap*/
>> +#define SDMMC_STATUS_ALL                     0xffff
>> +#define SDMMC_ERR_INTERRUPT                  BIT(15)
>> +#define SDMMC_CQE_EVENT                              BIT(14)
>> +#define SDMMC_FX_EVENT                               BIT(13)
>> +#define SDMMC_RE_TUNE_EVENT                  BIT(12)
>> +#define SDMMC_INT_C                          BIT(11)
>> +#define SDMMC_INT_B                          BIT(10)
>> +#define SDMMC_INT_A                          BIT(9)
>> +#define SDMMC_CARD_INTERRUPT                 BIT(8)
>> +#define SDMMC_CARD_REMOVAL                   BIT(7)
>> +#define SDMMC_CARD_INSERTION                 BIT(6)
>> +#define SDMMC_BUF_RD_READY                   BIT(5)
>> +#define SDMMC_BUF_WR_READY                   BIT(4)
>> +#define SDMMC_DMA_INTERRPT                   BIT(3)
>> +#define SDMMC_BGAP_EVENT                     BIT(2)
>> +#define SDMMC_XFER_COMPLETE                  BIT(1)
>> +#define SDMMC_CMD_COMPLETE                   BIT(0)
>> +
>> +/*0x32 error bitmap*/
>> +#define SDMMC_VENDOR_ERR3                    BIT(15)
>> +#define SDMMC_VENDOR_ERR2                    BIT(14)
>> +#define SDMMC_VENDOR_ERR1                    BIT(13)
>> +#define SDMMC_BOOT_ACK_ERR                   BIT(12)
>> +#define SDMMC_RESP_ERR                               BIT(11)
>> +#define SDMMC_TUNING_ERR                     BIT(10)
>> +#define SDMMC_ADMA_ERR                               BIT(9)
>> +#define SDMMC_AUTO_CMD_ERR                   BIT(8)
>> +#define SDMMC_CUR_LMT_ERR                    BIT(7)
>> +#define SDMMC_DATA_END_BIT_ERR                       BIT(6)
>> +#define SDMMC_DATA_CRC_ERR                   BIT(5)
>> +#define SDMMC_DATA_TOUT_ERR                  BIT(4)
>> +#define SDMMC_CMD_IDX_ERR                    BIT(3)
>> +#define SDMMC_CMD_END_BIT_ERR                        BIT(2)
>> +#define SDMMC_CMD_CRC_ERR                    BIT(1)
>> +#define SDMMC_CMD_TOUT_ERR                   BIT(0)
>> +#define SDMMC_CMD_ERR                           (SDMMC_AUTO_CMD_ERR| \
>> +                                             SDMMC_CMD_IDX_ERR|SDMMC_CMD_END_BIT_ERR| \
>> +                                             SDMMC_CMD_CRC_ERR|SDMMC_CMD_TOUT_ERR)
>> +#define SDMMC_DATA_ERR                               (SDMMC_ADMA_ERR| \
>> +                                             SDMMC_DATA_END_BIT_ERR| \
>> +                                             SDMMC_DATA_CRC_ERR|SDMMC_DATA_TOUT_ERR)
>> +
>> +/*0x34 status enable bitmap*/
>> +#define SDMMC_CQE_EVENT_STAT_EN                      BIT(14)
>> +#define SDMMC_FX_EVENT_STAT_EN                       BIT(13)
>> +#define SDMMC_RE_TUNE_EVENT_STAT_EN          BIT(12)
>> +#define SDMMC_INT_C_STAT_EN                  BIT(11)
>> +#define SDMMC_INT_B_STAT_EN                  BIT(10)
>> +#define SDMMC_INT_A_STAT_EN                  BIT(9)
>> +#define SDMMC_CARD_INTERRUPT_STAT_EN         BIT(8)
>> +#define SDMMC_CARD_REMOVAL_STAT_EN           BIT(7)
>> +#define SDMMC_CARD_INSERTION_STAT_EN         BIT(6)
>> +#define SDMMC_BUF_RD_READY_STAT_EN           BIT(5)
>> +#define SDMMC_BUF_WR_READY_STAT_EN           BIT(4)
>> +#define SDMMC_DMA_INTERRPT_STAT_EN           BIT(3)
>> +#define SDMMC_BGAP_EVENT_STAT_EN             BIT(2)
>> +#define SDMMC_XFER_COMPLETE_STAT_EN          BIT(1)
>> +#define SDMMC_CMD_COMPLETE_STAT_EN           BIT(0)
>> +
>> +/*0x36 error status enable bitmap*/
>> +#define SDMMC_VENDOR_ERR_STAT_EN3            BIT(15)
>> +#define SDMMC_VENDOR_ERR_STAT_EN2            BIT(14)
>> +#define SDMMC_VENDOR_ERR_STAT_EN1            BIT(13)
>> +#define SDMMC_BOOT_ACK_ERR_STAT_EN           BIT(12)
>> +#define SDMMC_RESP_ERR_STAT_EN                       BIT(11)
>> +#define SDMMC_TUNING_ERR_STAT_EN             BIT(10)
>> +#define SDMMC_ADMA_ERR_STAT_EN                       BIT(9)
>> +#define SDMMC_AUTO_CMD_ERR_STAT_EN           BIT(8)
>> +#define SDMMC_CUR_LMT_ERR_STAT_EN            BIT(7)
>> +#define SDMMC_DATA_END_BIT_ERR_STAT_EN               BIT(6)
>> +#define SDMMC_DATA_CRC_ERR_STAT_EN           BIT(5)
>> +#define SDMMC_DATA_TOUT_ERR_STAT_EN          BIT(4)
>> +#define SDMMC_CMD_IDX_ERR_STAT_EN            BIT(3)
>> +#define SDMMC_CMD_END_BIT_ERR_STAT_EN                BIT(2)
>> +#define SDMMC_CMD_CRC_ERR_STAT_EN            BIT(1)
>> +#define SDMMC_CMD_TOUT_ERR_STAT_EN           BIT(0)
>> +
>> +/*0x38 signal interrupt enable*/
>> +#define SDMMC_CQE_EVENT_SIGNAL_EN            BIT(14)
>> +#define SDMMC_FX_EVENT_SIGNAL_EN             BIT(13)
>> +#define SDMMC_RE_TUNE_EVENT_SIGNAL_EN                BIT(12)
>> +#define SDMMC_INT_C_SIGNAL_EN                        BIT(11)
>> +#define SDMMC_INT_B_SIGNAL_EN                        BIT(10)
>> +#define SDMMC_INT_A_SIGNAL_EN                        BIT(9)
>> +#define SDMMC_CARD_INTERRUPT_SIGNAL_EN               BIT(8)
>> +#define SDMMC_CARD_REMOVAL_SIGNAL_EN         BIT(7)
>> +#define SDMMC_CARD_INSERTION_SIGNAL_EN               BIT(6)
>> +#define SDMMC_BUF_RD_READY_SIGNAL_EN         BIT(5)
>> +#define SDMMC_BUF_WR_READY_SIGNAL_EN         BIT(4)
>> +#define SDMMC_DMA_INTERRPT_SIGNAL_EN         BIT(3)
>> +#define SDMMC_BGAP_EVENT_SIGNAL_EN           BIT(2)
>> +#define SDMMC_XFER_COMPLETE_SIGNAL_EN                BIT(1)
>> +#define SDMMC_CMD_COMPLETE_SIGNAL_EN         BIT(0)
>> +#define SDMMC_NORMAL_INT_SIGNAL_CMD_EN_R     (~(BIT(6) | BIT(7) | BIT(8) | BIT(1)) & 0xffff)
>> +#define SDMMC_NORMAL_INT_SIGNAL_DAT_EN_R     (~(BIT(6) | BIT(7) | BIT(8) | BIT(0)) & 0xffff)
>> +#define SDMMC_NORMAL_INT_SIGNAL_CQE_EN_R     (~(BIT(6) | BIT(7) | BIT(8) | \
>> +                                             BIT(1) | BIT(0)) & 0xffff)
>> +
>> +/*0x3a error ssignal enable bitmap*/
>> +#define SDMMC_VENDOR_ERR_SIGNAL_EN3          BIT(15)
>> +#define SDMMC_VENDOR_ERR_SIGNAL_EN2          BIT(14)
>> +#define SDMMC_VENDOR_ERR_SIGNAL_EN1          BIT(13)
>> +#define SDMMC_BOOT_ACK_ERR_SIGNAL_EN         BIT(12)
>> +#define SDMMC_RESP_ERR_SIGNAL_EN             BIT(11)
>> +#define SDMMC_TUNING_ERR_SIGNAL_EN           BIT(10)
>> +#define SDMMC_ADMA_ERR_SIGNAL_EN             BIT(9)
>> +#define SDMMC_AUTO_CMD_ERR_SIGNAL_EN         BIT(8)
>> +#define SDMMC_CUR_LMT_ERR_SIGNAL_EN          BIT(7)
>> +#define SDMMC_DATA_END_BIT_ERR_SIGNAL_EN     BIT(6)
>> +#define SDMMC_DATA_CRC_ERR_SIGNAL_EN         BIT(5)
>> +#define SDMMC_DATA_TOUT_ERR_SIGNAL_EN                BIT(4)
>> +#define SDMMC_CMD_IDX_ERR_SIGNAL_EN          BIT(3)
>> +#define SDMMC_CMD_END_BIT_ERR_SIGNAL_EN              BIT(2)
>> +#define SDMMC_CMD_CRC_ERR_SIGNAL_EN          BIT(1)
>> +#define SDMMC_CMD_TOUT_ERR_STAT_EN           BIT(0)
>> +
>> +#define SDMMC_ALL_NORMAL_STAT_EN             (0xfeff)
>> +#define SDMMC_ALL_ERR_STAT_EN                        (0xffff)
>> +                                     /*enablle all error initerrupt in 0x36*/
>> +#define SDMMC_ALL_SIGNAL_STAT_EN                (0xfeff)
>> +#define SDMMC_ALL_ERR_SIGNAL_EN                      (0xffff)
>> +                                     /*enable all signal error interrupt in 0x3a*/
>> +
>> +/*0x3e*/
>> +#define SDMMC_LEGACY                         0x0
>> +#define SDMMC_SDR                            0x1
>> +#define SDMMC_HS200                          0x3
>> +#define SDMMC_DDR                            0x4
>> +#define SDMMC_HS400                          0x7
>> +#define SDMMC_HOST_VER4_ENABLE                       BIT(12)
>> +#define SDMMC_SIGNALING_EN                   BIT(3)
>> +
>> +/*0x22c*/
>> +#define SDMMC_RST_N_OE                               BIT(3)
>> +#define SDMMC_RST_N                          BIT(2)
>> +#define SDMMC_CARD_IS_EMMC                   BIT(0)
>> +
>> +#define SDMMC_INTERNAL_CLK_EN                        BIT(0)
>> +#define SDMMC_PLL_USABLE                     BIT(0)
>> +
>> +#define VALID(x)                     ((x & 1) << 0)
>> +#define END(x)                               ((x & 1) << 1)
>> +#define INT(x)                               ((x & 1) << 2)
>> +#define ACT(x)                               ((x & 0x7) << 3)
>> +#define DAT_LENGTH(x)                        ((x & 0xFFFF) << 16)
>> +
>> +
>> +/* Register access macros */
>> +#define mci_readl(dev, reg)                     \
>> +     readl_relaxed((dev)->regs + SDMMC_##reg)
>> +#define mci_writel(dev, reg, value)                     \
>> +     writel_relaxed((value), (dev)->regs + SDMMC_##reg)
>> +
>> +#define mci_readw(dev, reg)                     \
>> +     readw_relaxed((dev)->regs + SDMMC_##reg)
>> +#define mci_writew(dev, reg, value)                     \
>> +     writew_relaxed((value), (dev)->regs + SDMMC_##reg)
>> +
>> +#define mci_readb(dev, reg)                     \
>> +     readb_relaxed((dev)->regs + SDMMC_##reg)
>> +#define mci_writeb(dev, reg, value)                     \
>> +     writeb_relaxed((value), (dev)->regs + SDMMC_##reg)
>> +
>> +#define dw_mci_get_int(dev)    \
>> +     do {    \
>> +             dev->normal_interrupt = mci_readw(dev, NORMAL_INT_STAT_R);   \
>> +             dev->error_interrupt = mci_readw(dev, ERROR_INT_STAT_R);   \
>> +             dev->auto_error_interrupt = mci_readw(dev, AUTO_CMD_STAT_R);     \
>> +     } while (0)
>> +
>> +/*clear status register, we always keep the card interrupt*/
>> +#define dw_mci_clr_int(dev)                                             \
>> +     do {                                                            \
>> +             mci_writew(dev, ERROR_INT_STAT_R, mci_readw(dev, ERROR_INT_STAT_R) & 0xffff); \
>> +             mci_writew(dev, NORMAL_INT_STAT_R, mci_readw(dev, NORMAL_INT_STAT_R) & 0xffff); \
>> +     } while (0)
>> +
>> +/*mask all emmc interrupts*/
>> +#define dw_mci_clr_signal_int(dev)    \
>> +     do {      \
>> +             mci_writew(dev, NORMAL_INT_SIGNAL_EN_R, \
>> +                     mci_readw(dev, NORMAL_INT_SIGNAL_EN_R) & (BIT(6)|BIT(7))); \
>> +             mci_writew(dev, ERROR_INT_SIGNAL_EN_R, 0); \
>> +     } while (0)
>> +
>> +/*for cmdq, we do not need cmd and xfer done, only cqe event*/
>> +#define dw_mci_en_cqe_int(dev)  \
>> +     do { \
>> +             mci_writew(dev, NORMAL_INT_SIGNAL_EN_R, \
>> +                     mci_readw(dev, NORMAL_INT_SIGNAL_EN_R)|SDMMC_NORMAL_INT_SIGNAL_CQE_EN_R); \
>> +             mci_writew(dev, ERROR_INT_SIGNAL_EN_R, SDMMC_ALL_ERR_SIGNAL_EN); \
>> +     } while (0)
>> +
>> +/*used for data, r1b case, we mask cmd done interrupt*/
>> +#define dw_mci_en_xfer_int(dev)  \
>> +     do {  \
>> +             mci_writew(dev, NORMAL_INT_SIGNAL_EN_R, \
>> +                     mci_readw(dev, NORMAL_INT_SIGNAL_EN_R)|SDMMC_NORMAL_INT_SIGNAL_DAT_EN_R); \
>> +             mci_writew(dev, ERROR_INT_SIGNAL_EN_R, SDMMC_ALL_ERR_SIGNAL_EN); \
>> +     } while (0)
>> +
>> +/*used for none-stream case (cmd w/wo/ resp)*/
>> +#define dw_mci_en_cd_int(dev)  \
>> +     do {    \
>> +             mci_writew(dev, NORMAL_INT_SIGNAL_EN_R, \
>> +                     mci_readw(dev, NORMAL_INT_SIGNAL_EN_R)|SDMMC_NORMAL_INT_SIGNAL_CMD_EN_R); \
>> +             mci_writew(dev, ERROR_INT_SIGNAL_EN_R, SDMMC_ALL_ERR_SIGNAL_EN); \
>> +     } while (0)
>> +
>> +extern int dw_mci_cqe_probe(struct dw_mci *host);
>> +extern void dw_mci_cqe_remove(struct dw_mci *host);
>> +#ifdef CONFIG_PM
>> +extern int dw_mci_cqe_runtime_suspend(struct device *device);
>> +extern int dw_mci_cqe_runtime_resume(struct device *device);
>> +#endif
>> +irqreturn_t cqhci_irq(struct mmc_host *mmc, u32 intmask, int cmd_error, int data_error);
>> +
>> +/* Board platform data */
>> +struct dw_mci_board {
>> +     unsigned int bus_hz; /* Clock speed at the cclk_in pad */
>> +     u32 caps;       /* Capabilities */
>> +     u32 caps2;      /* More capabilities */
>> +     u32 pm_caps;    /* PM capabilities */
>> +
>> +     /* delay in mS before detecting cards after interrupt */
>> +     u32 detect_delay_ms;
>> +
>> +     struct reset_control *rstc;
>> +};
>> +
>> +struct dw_mci_slot {
>> +     struct mmc_host         *mmc;
>> +     struct dw_mci           *host;
>> +
>> +     struct mmc_request      *mrq;
>> +
>> +     unsigned int            clock;
>> +     unsigned int            __clk_old;
>> +
>> +     unsigned long           flags;
>> +#define DW_MMC_CARD_PRESENT     1
>> +     int                     id;
>> +     int                     sdio_id;
>> +     u8                      switch_partition;
>> +};
>> +
>> +struct dw_mci_drv_data {
>> +     unsigned long   *caps;
>> +     u32             num_caps;
>> +     int             (*init)(struct dw_mci *host);
>> +     void            (*set_ios)(struct dw_mci_slot *slot, struct mmc_ios *ios);
>> +     int             (*parse_dt)(struct dw_mci *host);
>> +     int             (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode);
>> +     int             (*prepare_hs400_tuning)(struct dw_mci *host,
>> +                                     struct mmc_ios *ios);
>> +     int             (*switch_voltage)(struct mmc_host *mmc,
>> +                                     struct mmc_ios *ios);
>> +     void            (*hs400_complete)(struct mmc_host *mmc);
>> +     void            (*init_card)(struct mmc_host *host,
>> +                                     struct mmc_card *card);
>> +     void            (*shift_rsp)(struct dw_mci *host, struct mmc_command *cmd, u32 *rsp);
>> +};
>> +
>> +void dw_mci_cqe_wait_done(struct dw_mci *host, u32 *addr, u32 mask, u32 value);
>> +#endif
> 
> 
> ------Please consider the environment before printing this e-mail.
diff mbox series

Patch

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 9f793892123c..b8c7727b1897 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -762,6 +762,17 @@  config MMC_DW_PLTFM
 
 	  If unsure, say Y.
 
+config MMC_DW_CQE
+	tristate "Synopsys DesignWare Memory Card with CQE Interface"
+	depends on ARC || ARM || ARM64 || MIPS || COMPILE_TEST
+	select MMC_CQHCI
+	help
+	  This selects support for the Synopsys DesignWare Mobile Storage IP
+	  block. It provides host support for SD and MMC interfaces, and adds
+	  the support of cmdq.
+
+	  If unsure, say N.
+
 config MMC_DW_BLUEFIELD
 	tristate "BlueField specific extensions for Synopsys DW Memory Card Interface"
 	depends on MMC_DW
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index a693fa3d3f1c..7fa1411692e8 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -55,6 +55,7 @@  obj-$(CONFIG_MMC_DW_K3)		+= dw_mmc-k3.o
 obj-$(CONFIG_MMC_DW_PCI)	+= dw_mmc-pci.o
 obj-$(CONFIG_MMC_DW_ROCKCHIP)	+= dw_mmc-rockchip.o
 obj-$(CONFIG_MMC_DW_STARFIVE)	+= dw_mmc-starfive.o
+obj-$(CONFIG_MMC_DW_CQE)                += dw_mmc_cqe.o
 obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
 obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
 obj-$(CONFIG_MMC_VUB300)	+= vub300.o
diff --git a/drivers/mmc/host/dw_mmc_cqe.c b/drivers/mmc/host/dw_mmc_cqe.c
new file mode 100644
index 000000000000..c50a6c71a362
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc_cqe.c
@@ -0,0 +1,1823 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver with CMDQ support
+ *  (Based on Synopsys DesignWare Multimedia Card Interface driver)
+ *
+ * Copyright (c) 2023 Realtek Semiconductor Corp
+ */
+
+#include <linux/bitops.h>
+#include <linux/blkdev.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/slot-gpio.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/seq_file.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+
+#include "dw_mmc_cqe.h"
+#include "cqhci.h"
+
+#define DW_MCI_FREQ_MAX	200000000	/* unit: HZ */
+#define DW_MCI_FREQ_MIN	100000		/* unit: HZ */
+#define DW_MCI_CMDQ_DISABLED	0x30f0001
+#define DW_MCI_CMDQ_ENABLED	0x30f0101
+#define DW_MCI_POWEROFF		0x3220301
+#define DW_MCI_DESC_LEN		0x100000
+#define DW_MCI_MAX_SCRIPT_BLK	128
+#define DW_MCI_TIMEOUT_MS	3000
+#define DW_MCI_TIMEOUT_us	3000000
+#define TUNING_ERR		531
+#define DW_MCI_NOT_READY	9999
+
+DECLARE_COMPLETION(dw_mci_wait);
+
+
+#if defined(CONFIG_DEBUG_FS)
+static int dw_mci_cqe_req_show(struct seq_file *s, void *v)
+{
+	struct dw_mci_slot *slot = s->private;
+	struct mmc_request *mrq;
+	struct mmc_command *cmd;
+	struct mmc_command *stop;
+	struct mmc_data	*data;
+
+	/* Make sure we get a consistent snapshot */
+	spin_lock_bh(&slot->host->lock);
+	mrq = slot->mrq;
+
+	if (mrq) {
+		cmd = mrq->cmd;
+		data = mrq->data;
+		stop = mrq->stop;
+
+		if (cmd)
+			seq_printf(s,
+				   "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				   cmd->opcode, cmd->arg, cmd->flags,
+				   cmd->resp[0], cmd->resp[1], cmd->resp[2],
+				   cmd->resp[2], cmd->error);
+		if (data)
+			seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
+				   data->bytes_xfered, data->blocks,
+				   data->blksz, data->flags, data->error);
+		if (stop)
+			seq_printf(s,
+				   "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				   stop->opcode, stop->arg, stop->flags,
+				   stop->resp[0], stop->resp[1], stop->resp[2],
+				   stop->resp[2], stop->error);
+	}
+
+	spin_unlock_bh(&slot->host->lock);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(dw_mci_cqe_req);
+#endif /* defined(CONFIG_DEBUG_FS) */
+
+static int dw_mci_cqe_regs_show(struct dw_mci *host,
+				struct mmc_command *cmd, u32 cmd_flags)
+{
+	dev_err(host->dev, "opcode = %d, arg = 0x%x, cmdflags = 0x%x\n",
+				cmd->opcode, cmd->arg, cmd_flags);
+	dev_err(host->dev, "status_int = 0x%x\n", host->normal_interrupt);
+	dev_err(host->dev, "error_int = 0x%x\n", host->error_interrupt);
+	dev_err(host->dev, "auto_error_int = 0x%x\n", host->auto_error_interrupt);
+	dev_err(host->dev, "pstate_reg = 0x%x\n", mci_readl(host, PSTATE_REG));
+	dev_err(host->dev, "host_ctrl_1 = 0x%x\n", mci_readb(host, HOST_CTRL1_R));
+	dev_err(host->dev, "xfer_mode_r = 0x%x\n", mci_readw(host, XFER_MODE_R));
+
+	return 0;
+}
+
+static void dw_mci_cqe_dumpregs(struct mmc_host *mmc)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+
+	dev_info(host->dev, "%s: cmd idx 0x%08x\n", __func__, mci_readw(host, CMD_R));
+}
+
+static void dw_mci_cqe_set_tran_desc(u8 *desc,
+					dma_addr_t addr,
+					int len,
+					bool end,
+					bool dma64)
+{
+	__le32 *attr = (__le32 __force *)desc;
+
+	*attr = (CQHCI_VALID(1) |
+		 CQHCI_END(end ? 1 : 0) |
+		 CQHCI_INT(0) |
+		 CQHCI_ACT(0x4) |
+		 CQHCI_DAT_LENGTH(len));
+
+	if (dma64) {
+		__le64 *dataddr = (__le64 __force *)(desc + 4);
+
+		dataddr[0] = cpu_to_le64(addr);
+	} else {
+		__le32 *dataddr = (__le32 __force *)(desc + 4);
+
+		dataddr[0] = cpu_to_le32(addr);
+	}
+}
+
+static void dw_mci_cqe_setup_tran_desc(struct mmc_data *data,
+				      struct cqhci_host *cq_host,
+				      u8 *desc,
+				      int sg_count)
+{
+	struct scatterlist *sg;
+	u32 cur_blk_cnt, remain_blk_cnt;
+	unsigned int begin, end;
+	int i, len;
+	bool last = false;
+	bool dma64 = cq_host->dma64;
+	dma_addr_t addr;
+
+	for_each_sg(data->sg, sg, sg_count, i) {
+		addr = sg_dma_address(sg);
+		len = sg_dma_len(sg);
+		remain_blk_cnt  = len >> 9;
+
+		while (remain_blk_cnt) {
+			/*DW_MCI_MAX_SCRIPT_BLK is tha max for each descriptor record*/
+			if (remain_blk_cnt > DW_MCI_MAX_SCRIPT_BLK)
+				cur_blk_cnt = DW_MCI_MAX_SCRIPT_BLK;
+			else
+				cur_blk_cnt = remain_blk_cnt;
+
+			/* In Synopsys DesignWare Databook Page 84,
+			 * They mentioned the DMA 128MB restriction
+			 */
+			begin = addr / SZ_128M;
+			end = (addr + cur_blk_cnt * SZ_512) / SZ_128M;
+
+			if (begin != end)
+				cur_blk_cnt = (end * SZ_128M - addr) / SZ_512;
+
+			if ((i+1) == sg_count && (remain_blk_cnt == cur_blk_cnt))
+				last = true;
+
+			dw_mci_cqe_set_tran_desc(desc, addr,
+					(cur_blk_cnt << 9), last, dma64);
+
+			addr = addr + (cur_blk_cnt << 9);
+			remain_blk_cnt -= cur_blk_cnt;
+			desc += cq_host->trans_desc_len;
+		}
+	}
+}
+
+static void dw_mci_cqe_enable(struct mmc_host *mmc)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+
+	/*clear data path SW_RST_R.SW_RST_DAT = 1*/
+	mci_writeb(host, SW_RST_R, SDMMC_RST_DAT);
+	/*0x9801200c*/
+	mci_writew(host, XFER_MODE_R,
+		((1 << SDMMC_MULTI_BLK_SEL) | SDMMC_BLOCK_COUNT_ENABLE | SDMMC_DMA_ENABLE));
+
+	/*Set DMA_SEL to ADMA2 only mode in the HOST_CTRL1_R*/
+	mci_writeb(host, HOST_CTRL1_R,
+		(mci_readb(host, HOST_CTRL1_R) & 0xe7) | (SDMMC_ADMA2_32 << SDMMC_DMA_SEL));
+	mci_writew(host, BLOCKSIZE_R, 0x200);
+	mci_writew(host, BLOCKCOUNT_R, 0);
+
+	/*Set SDMASA_R (while using 32 bits) to 0*/
+	mci_writel(host, SDMASA_R, 0);
+	/*we set this register additionally to enhance the IO perofrmance*/
+
+	cqhci_writel(host->cqe, 0x10, CQHCI_SSC1);
+	cqhci_writel(host->cqe, 0, CQHCI_CTL);
+
+	if (cqhci_readl(host->cqe, CQHCI_CTL) && CQHCI_HALT) {
+		dev_err(host->dev, "%s: cqhci: CQE failed to exit halt state\n",
+			mmc_hostname(mmc));
+	}
+
+	/*cmdq interrupt mode*/
+	dw_mci_clr_signal_int(host);
+	dw_mci_en_cqe_int(host);
+}
+
+static const struct cqhci_host_ops dw_mci_cqhci_host_ops = {
+	.enable = dw_mci_cqe_enable,
+	.dumpregs = dw_mci_cqe_dumpregs,
+	.setup_tran_desc = dw_mci_cqe_setup_tran_desc,
+};
+
+void dw_mci_cqe_wait_done(struct dw_mci *host, u32 *addr,
+		      u32 mask, u32 value)
+{
+	int n = 0;
+
+	while (1) {
+		if (((*addr) & mask) == value)
+			break;
+
+		/*error interrupt detected*/
+		if ((mci_readw(host, NORMAL_INT_STAT_R) & SDMMC_ERR_INTERRUPT) != 0)
+			break;
+
+		if (n++ > DW_MCI_TIMEOUT_us) {
+			dev_err(host->dev, "opcode = %d, *addr = 0x%x, mask = 0x%x, value = 0x%x\n",
+				host->opcode, readl(addr), mask, value);
+			break;
+		}
+		udelay(1);
+	}
+}
+EXPORT_SYMBOL(dw_mci_cqe_wait_done);
+
+static void dw_mci_cqe_reset(struct dw_mci *host)
+{
+	/*check the cmd line*/
+	if (mci_readw(host, ERROR_INT_STAT_R) & SDMMC_CMD_ERR) {
+		/*Perform a software reset*/
+		mci_writeb(host, SW_RST_R, SDMMC_RST_CMD);
+		dw_mci_cqe_wait_done(host, (u32 *)(host->regs + SDMMC_CLK_CTRL_R), BIT(25), 0);
+	}
+	/*check data line*/
+	if (mci_readw(host, ERROR_INT_STAT_R) & SDMMC_DATA_ERR) {
+		mci_writeb(host, SW_RST_R, SDMMC_RST_DAT);
+		dw_mci_cqe_wait_done(host, (u32 *)(host->regs + SDMMC_CLK_CTRL_R), BIT(26), 0);
+	}
+}
+
+static void dw_mci_cqe_read_rsp(struct dw_mci *host, struct mmc_command *cmd, u32 *rsp)
+{
+	const struct dw_mci_drv_data *drv_data = host->drv_data;
+
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		if (cmd->flags & MMC_RSP_136) {
+			if (drv_data && drv_data->shift_rsp) {
+				drv_data->shift_rsp(host, cmd, cmd->resp);
+			} else {
+				/*R2 long response*/
+				u32 rsp_tmp[4];
+
+				rsp_tmp[3] = mci_readl(host, RESP01_R);
+				rsp_tmp[2] = mci_readl(host, RESP23_R);
+				rsp_tmp[1] = mci_readl(host, RESP45_R);
+				rsp_tmp[0] = mci_readl(host, RESP67_R);
+			}
+		} else {
+			/*Short response*/
+			rsp[0] = rsp[1] = rsp[2] = rsp[3] = 0;
+			rsp[0] = mci_readl(host, RESP01_R);
+		}
+	}
+}
+
+static u32 dw_mci_cqe_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+	u32 cmdr;
+
+	cmd->error = -EINPROGRESS;
+
+    /* our ip design puts resp in bit 8-135, so we need to shift 8 bits*/
+	if (host->shift)
+		cmdr = (cmd->opcode << 8);
+	else
+		cmdr = cmd->opcode;
+
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		if (cmd->flags & MMC_RSP_136)
+			cmdr |= SDMMC_RESP_LEN_136;
+		else {
+			if (cmd->flags & MMC_RSP_BUSY)
+				cmdr |= SDMMC_RESP_LEN_48B;
+			else
+				cmdr |= SDMMC_RESP_LEN_48;
+		}
+	}
+
+	cmdr |= SDMMC_CMD_CHK_RESP_CRC;
+	if (cmd->opcode == MMC_GO_IDLE_STATE ||
+	   cmd->opcode == MMC_SEND_OP_COND ||
+	   (cmd->opcode == MMC_SELECT_CARD && cmd->flags == (MMC_RSP_NONE | MMC_CMD_AC)))
+		cmdr &= ~SDMMC_CMD_CHK_RESP_CRC;
+
+	cmdr |= SDMMC_CMD_IDX_CHK_ENABLE;
+	if (cmd->opcode == MMC_GO_IDLE_STATE ||
+	   cmd->opcode == MMC_SEND_OP_COND ||
+	   cmd->opcode == MMC_SEND_CSD ||
+	   cmd->opcode == MMC_SEND_CID ||
+	   cmd->opcode == MMC_ALL_SEND_CID ||
+	   (cmd->opcode == MMC_SELECT_CARD && cmd->flags == (MMC_RSP_NONE | MMC_CMD_AC)))
+		cmdr &= ~SDMMC_CMD_IDX_CHK_ENABLE;
+
+	if (cmd->data)
+		cmdr |= SDMMC_DATA;
+
+	if (cmd->opcode == MMC_STOP_TRANSMISSION)
+		cmdr |= (SDMMC_ABORT_CMD << 6);
+
+	return cmdr;
+}
+
+static int dw_mci_cqe_start_command(struct dw_mci *host,
+				 struct mmc_command *cmd, u32 cmd_flags)
+{
+	int err = 0;
+	unsigned long end = 0;
+	unsigned long flags;
+	bool xfer_flag = false;
+
+	host->cmd = cmd;
+
+	switch (cmd->opcode) {
+	case MMC_READ_SINGLE_BLOCK:
+	case MMC_READ_MULTIPLE_BLOCK:
+	case MMC_WRITE_BLOCK:
+	case MMC_WRITE_MULTIPLE_BLOCK:
+	case MMC_SEND_EXT_CSD:
+	case MMC_GEN_CMD:
+	case MMC_SLEEP_AWAKE:
+	case MMC_SWITCH:
+	case MMC_SET_WRITE_PROT:
+	case MMC_CLR_WRITE_PROT:
+	case MMC_SEND_WRITE_PROT:
+	case MMC_ERASE:
+	case MMC_SEND_TUNING_BLOCK_HS200:
+		xfer_flag = true;
+		break;
+	default:
+		xfer_flag = false;
+	}
+
+	host->int_waiting = &dw_mci_wait;
+	end = jiffies + msecs_to_jiffies(DW_MCI_TIMEOUT_MS);
+	mod_timer(&host->timer, end);
+
+	if (host->int_waiting) {
+		dw_mci_clr_signal_int(host);
+		dw_mci_clr_int(host);
+
+		/*command with data, r1b case*/
+		if (xfer_flag == 1)
+			dw_mci_en_xfer_int(host);
+		else
+			dw_mci_en_cd_int(host);
+
+		/*If we use cmd23, we cannot send auto stop command*/
+		if (cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK ||
+		    cmd->opcode == MMC_READ_MULTIPLE_BLOCK) {
+			if (host->is_sbc) {
+				mci_writew(host, XFER_MODE_R,
+					mci_readw(host, XFER_MODE_R) & ~BIT(SDMMC_AUTO_CMD_ENABLE));
+					host->is_sbc = 0;
+			}
+		}
+
+		host->opcode = cmd->opcode;
+		host->arg = cmd->arg;
+
+		spin_lock_irqsave(&host->irq_lock, flags);
+		mci_writew(host, CMD_R, cmd_flags);
+		spin_unlock_irqrestore(&host->irq_lock, flags);
+
+		wait_for_completion(host->int_waiting);
+
+		if (xfer_flag == 1)
+			dw_mci_cqe_wait_done(host,
+				(u32 *)(host->regs + SDMMC_NORMAL_INT_STAT_R),
+				SDMMC_XFER_COMPLETE, SDMMC_XFER_COMPLETE);
+		else
+			dw_mci_cqe_wait_done(host,
+				(u32 *)(host->regs + SDMMC_NORMAL_INT_STAT_R),
+				SDMMC_CMD_COMPLETE, SDMMC_CMD_COMPLETE);
+
+		if (host->normal_interrupt & SDMMC_ERR_INTERRUPT) {
+			if (host->tuning == 1)
+				dev_info(host->dev, "Tuning error ... keep tuning\n");
+			else
+				dw_mci_cqe_regs_show(host, cmd, cmd_flags);
+			err = -1;
+		}
+	}
+
+	return err;
+}
+
+static void dw_mci_cqe_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd)
+{
+
+	struct mmc_command stop;
+	u32 cmdr;
+	/*Stop command only use after data command*/
+	if (!cmd->data)
+		return;
+
+	memset(&stop, 0, sizeof(struct mmc_command));
+
+	if (cmd->opcode == MMC_READ_SINGLE_BLOCK ||
+	    cmd->opcode == MMC_READ_MULTIPLE_BLOCK ||
+	    cmd->opcode == MMC_WRITE_BLOCK ||
+	    cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK ||
+	    cmd->opcode == MMC_SEND_TUNING_BLOCK ||
+	    cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200) {
+		stop.opcode = MMC_STOP_TRANSMISSION;
+		stop.arg = 0;
+		stop.flags = MMC_RSP_R1 | MMC_CMD_AC;
+	} else if (cmd->opcode == SD_IO_RW_EXTENDED) {
+		stop.opcode = SD_IO_RW_DIRECT;
+		stop.arg |= (1 << 31) | (0 << 28) | (SDIO_CCCR_ABORT << 9) |
+			    ((cmd->arg >> 28) & 0x7);
+		stop.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC;
+	} else {
+		return;
+	}
+
+	cmdr = (stop.opcode << 8) | SDMMC_RESP_LEN_48 |
+		SDMMC_CMD_CHK_RESP_CRC | SDMMC_CMD_IDX_CHK_ENABLE;
+	cmdr |= (SDMMC_ABORT_CMD << 6);
+	mci_writew(host, XFER_MODE_R, 0);
+	mci_writel(host, ARGUMENT_R, stop.arg);
+	dw_mci_cqe_start_command(host, &stop, cmdr);
+}
+
+static int dw_mci_cqe_wait_while_busy(struct dw_mci *host, u32 *status)
+{
+	struct mmc_command cmd;
+	u32 cmdr;
+	u32 cur_state;
+	unsigned long timeend;
+	int err = 0;
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+
+	timeend = jiffies + msecs_to_jiffies(600);
+
+	do {
+		cmd.opcode = MMC_SEND_STATUS;
+		cmd.arg = 1 << 16;
+		cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
+		cmd.data = NULL;
+
+		cmdr = (cmd.opcode << 8) | SDMMC_RESP_LEN_48 |
+			SDMMC_CMD_CHK_RESP_CRC | SDMMC_CMD_IDX_CHK_ENABLE;
+
+		mci_writew(host, XFER_MODE_R, 0);
+		mci_writel(host, ARGUMENT_R, cmd.arg);
+
+		err = dw_mci_cqe_start_command(host, &cmd, cmdr);
+		if (err) {
+			dw_mci_cqe_reset(host);
+			break;
+		}
+		dw_mci_cqe_read_rsp(host, &cmd, cmd.resp);
+
+		*status = cmd.resp[0];
+		cur_state = R1_CURRENT_STATE(cmd.resp[0]);
+		err = -DW_MCI_NOT_READY;
+		if (cur_state == R1_STATE_TRAN) {
+			if (cmd.resp[0] & R1_READY_FOR_DATA) {
+				err = 0;
+				break;
+			}
+		}
+	} while (time_before(jiffies, timeend));
+
+	return err;
+
+}
+
+static void dw_mci_cqe_stop_dma(struct dw_mci *host, struct mmc_data *data)
+{
+	u32 dir = 0;
+
+	if (data->flags & MMC_DATA_READ)
+		dir = DMA_FROM_DEVICE;
+	else
+		dir = DMA_TO_DEVICE;
+
+	dma_unmap_sg(mmc_dev(host->slot->mmc), data->sg, data->sg_len, dir);
+	host->sg = NULL;
+}
+
+static void dw_mci_cqe_prepare_desc64(struct dw_mci *host, struct mmc_data *data,
+					struct scatterlist *sg)
+{
+	dev_info(host->dev, "Currently, the 64bit DMA mode is not implemented yet.\n");
+}
+
+
+static void dw_mci_cqe_prepare_desc32(struct dw_mci *host, struct mmc_data *data,
+					struct scatterlist *sg)
+{
+	u32  blk_cnt, cur_blk_cnt, remain_blk_cnt;
+	u32  tmp_val;
+	u32 *desc_base = host->sg_cpu;
+	u32  dma_len = 0;
+	u32  dma_addr;
+	u32  i;
+	unsigned int begin, end;
+
+	for (i = 0; i < host->dma_nents; i++, sg++) {
+		dma_len = sg_dma_len(sg);
+
+		/*blk_cnt must be the multiple of 512(0x200)*/
+		if (dma_len < SZ_512)
+			blk_cnt = 1;
+		else
+			blk_cnt  = dma_len >> 9;
+
+		remain_blk_cnt  = blk_cnt;
+		dma_addr = sg_dma_address(sg);
+
+		while (remain_blk_cnt) {
+			/*DW_MCI_MAX_SCRIPT_BLK is the max
+			 * for each descriptor record
+			 */
+			if (remain_blk_cnt > DW_MCI_MAX_SCRIPT_BLK)
+				cur_blk_cnt = DW_MCI_MAX_SCRIPT_BLK;
+			else
+				cur_blk_cnt = remain_blk_cnt;
+
+			/* In Synopsys DesignWare Databook Page 84,
+			 * They mentioned the DMA 128MB restriction
+			 */
+			begin = dma_addr / SZ_128M;
+			end = (dma_addr + cur_blk_cnt * SZ_512) / SZ_128M;
+
+			/*If begin and end in the different 128MB memory zone*/
+			if (begin != end)
+				cur_blk_cnt = (end * SZ_128M - dma_addr) / SZ_512;
+
+			if (dma_len < SZ_512)
+				tmp_val = ((dma_len) << 16) | VALID(0x1) | ACT(0x4);
+			else
+				tmp_val = ((cur_blk_cnt & 0x7f) << 25) | VALID(0x1) | ACT(0x4);
+
+			/*Last descriptor*/
+			if (i == host->dma_nents - 1 && remain_blk_cnt == cur_blk_cnt)
+				tmp_val |= END(0x1);
+
+			desc_base[0] =  tmp_val;
+			desc_base[1] =  dma_addr;
+
+			dma_addr = dma_addr + (cur_blk_cnt << 9);
+			remain_blk_cnt -= cur_blk_cnt;
+			desc_base += 2;
+		}
+	}
+}
+
+static void dw_mci_cqe_pre_req(struct mmc_host *mmc,
+			   struct mmc_request *mrq)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+	struct mmc_data *data = mrq->data;
+	unsigned int sg_len;
+
+	if (!slot->host->use_dma || !data)
+		return;
+
+	/* This data might be unmapped at this time */
+	data->host_cookie = COOKIE_UNMAPPED;
+
+	sg_len = dma_map_sg(host->dev,
+			    data->sg,
+			    data->sg_len,
+			    mmc_get_dma_dir(data));
+	if (sg_len < 0)
+		data->host_cookie = COOKIE_UNMAPPED;
+}
+
+static void dw_mci_cqe_post_req(struct mmc_host *mmc,
+			    struct mmc_request *mrq,
+			    int err)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct mmc_data *data = mrq->data;
+
+	if (!slot->host->use_dma || !data)
+		return;
+
+	if (data->host_cookie != COOKIE_UNMAPPED)
+		dma_unmap_sg(slot->host->dev,
+			     data->sg,
+			     data->sg_len,
+			     mmc_get_dma_dir(data));
+	data->host_cookie = COOKIE_UNMAPPED;
+}
+
+static int dw_mci_cqe_get_cd(struct mmc_host *mmc)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+	int gpio_cd = mmc_gpio_get_cd(mmc);
+	int present = -1;
+
+	/* Use platform get_cd function, else try onboard card detect */
+	if (((mmc->caps & MMC_CAP_NEEDS_POLL)
+		|| !mmc_card_is_removable(mmc))) {
+		present = 1;
+
+		if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
+			if (mmc->caps & MMC_CAP_NEEDS_POLL) {
+				dev_info(&mmc->class_dev,
+					"card is polling.\n");
+			} else {
+				dev_info(&mmc->class_dev,
+					"card is non-removable.\n");
+			}
+			set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+		}
+
+		return present;
+	} else if (gpio_cd >= 0) {
+		present = gpio_cd;
+	} else {
+		/*SD card detect using IP regs is todo*/
+		dev_err(&mmc->class_dev, "SD card detect using IP regs is ToDo.\n");
+	}
+
+	spin_lock_bh(&host->lock);
+
+	if (present && !test_and_set_bit(DW_MMC_CARD_PRESENT, &slot->flags))
+		dev_dbg(&mmc->class_dev, "card is present\n");
+	else if (!present &&
+		!test_and_clear_bit(DW_MMC_CARD_PRESENT, &slot->flags))
+		dev_dbg(&mmc->class_dev, "card is not present\n");
+
+	spin_unlock_bh(&host->lock);
+
+	return present;
+}
+
+static void dw_mci_cqe_submit_data_dma(struct dw_mci *host)
+{
+	if (host->dma_64bit_address == 1)
+		dw_mci_cqe_prepare_desc64(host, host->data, host->sg);
+	else
+		dw_mci_cqe_prepare_desc32(host, host->data, host->sg);
+
+}
+
+static void dw_mci_cqe_submit_data(struct dw_mci *host, struct mmc_data *data)
+{
+	u32 dir = 0;
+
+	host->sg = NULL;
+	host->data = data;
+
+	if (data->flags & MMC_DATA_READ)
+		dir = DMA_FROM_DEVICE;
+	else
+		dir = DMA_TO_DEVICE;
+
+	host->dma_nents = dma_map_sg(mmc_dev(host->slot->mmc),
+					data->sg, data->sg_len, dir);
+	host->sg = data->sg;
+
+	host->using_dma = 1;
+
+	dw_mci_cqe_submit_data_dma(host);
+}
+
+static void dw_mci_cqe_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
+{
+	struct dw_mci *host = slot->host;
+	const struct dw_mci_drv_data *drv_data = host->drv_data;
+	unsigned int clock = slot->clock;
+	u32 div = 0;
+
+	slot->mmc->actual_clock = 0;
+
+	if (clock != host->current_speed || force_clkinit) {
+		div = host->bus_hz / clock;
+		if (host->bus_hz % clock)
+			div += 1;
+
+		if (clock != slot->__clk_old) {
+			/* Silent the verbose log if calling from PM context */
+			dev_info(&slot->mmc->class_dev,
+				"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n",
+				slot->id, host->bus_hz, clock, host->bus_hz / div, div);
+		}
+
+		slot->__clk_old = clock;
+		slot->mmc->actual_clock = host->bus_hz / div;
+
+		if (drv_data && drv_data->set_ios)
+			drv_data->set_ios(slot, &slot->mmc->ios);
+	}
+}
+
+
+static void dw_mci_cqe_err_handle(struct dw_mci *host, struct mmc_command *cmd)
+{
+	u32 status = 0;
+	int err = 0;
+	int rty_cnt = 0;
+	int pstat_rty = 0;
+
+	do {
+		mci_writew(host, ERROR_INT_STAT_R,
+			mci_readw(host, ERROR_INT_STAT_R) & 0xffff);
+		/*synchronous abort: stop host dma*/
+		mci_writeb(host, BGAP_CTRL_R, SDMMC_STOP_BG_REQ);
+		dw_mci_cqe_wait_done(host,
+			(u32 *)(host->regs + SDMMC_NORMAL_INT_STAT_R),
+				SDMMC_XFER_COMPLETE, SDMMC_XFER_COMPLETE);
+
+		mci_writew(host, NORMAL_INT_STAT_R, SDMMC_XFER_COMPLETE);
+
+		do {
+			if (cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200) {
+				dw_mci_cqe_prep_stop_abort(host, cmd);
+				mdelay(1);
+
+				err = dw_mci_cqe_wait_while_busy(host, &status);
+
+				rty_cnt++;
+				if (rty_cnt > 100) {
+					if (err == -DW_MCI_NOT_READY)
+						dev_err(host->dev, "status check failed, err = %d, status = 0x%x\n",
+							err, status);
+						break;
+				}
+			} else {
+				break;
+			}
+		} while (err);
+
+		mci_writeb(host, SW_RST_R, SDMMC_RST_CMD | SDMMC_RST_DAT);
+		dw_mci_cqe_wait_done(host, (u32 *)(host->regs + SDMMC_CLK_CTRL_R),
+				(BIT(25)|BIT(26)), 0);
+		dw_mci_cqe_wait_done(host, (u32 *)(host->regs + SDMMC_PSTATE_REG),
+				(SDMMC_CMD_INHIBIT | SDMMC_CMD_INHIBIT_DAT), 0);
+		udelay(40);
+
+		pstat_rty++;
+		if (pstat_rty > 5000) {
+			dev_err(host->dev, "wait pstate register data line ready timeout\n");
+			break;
+		}
+	} while ((mci_readl(host, PSTATE_REG) & 0xf00000) != 0xf00000 ||
+		(mci_readl(host, PSTATE_REG) & 0xf0) != 0xf0);
+}
+
+static void dw_mci_cqe_send_stop_abort(struct dw_mci *host,
+			      struct dw_mci_slot *slot,
+			      struct mmc_command *cmd)
+{
+	dw_mci_cqe_reset(host);
+
+	if (cmd->data)
+		dw_mci_cqe_err_handle(host, cmd);
+	else
+		return;
+}
+
+static u32 dw_mci_cqe_prepare_data_flags(struct mmc_command *cmd)
+{
+	u32 dataflags;
+	int read_flag = 1;
+	int mul_blk_flag = 0;
+	int auto_stop_flag = 0;
+
+	if (cmd->opcode == MMC_WRITE_BLOCK ||
+	   cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK ||
+	   cmd->opcode == MMC_LOCK_UNLOCK ||
+	   (cmd->opcode == MMC_GEN_CMD && cmd->arg == 0))
+		read_flag = 0;
+
+	if (cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK ||
+	   cmd->opcode == MMC_READ_MULTIPLE_BLOCK) {
+		mul_blk_flag = 1;
+		auto_stop_flag = 1;
+	}
+
+	dataflags = (mul_blk_flag << SDMMC_MULTI_BLK_SEL) |
+		    (read_flag << SDMMC_DATA_XFER_DIR) |
+		    (auto_stop_flag << SDMMC_AUTO_CMD_ENABLE) |
+		    (SDMMC_BLOCK_COUNT_ENABLE) |
+		    (SDMMC_DMA_ENABLE);
+
+	return dataflags;
+}
+
+static int dw_mci_cqe_command_complete(struct dw_mci *host, u16 interrupt,
+					int *cmd_error)
+{
+	if (interrupt & (SDMMC_CMD_IDX_ERR | SDMMC_CMD_END_BIT_ERR
+		| SDMMC_CMD_CRC_ERR)) {
+		if (host->tuning)
+			*cmd_error = -TUNING_ERR;
+		else
+			*cmd_error = -EILSEQ;
+	} else if (interrupt & SDMMC_CMD_TOUT_ERR) {
+		if (host->tuning)
+			*cmd_error = -TUNING_ERR;
+		else
+			*cmd_error = -ETIMEDOUT;
+	} else {
+		*cmd_error = 0;
+	}
+
+	return *cmd_error;
+}
+
+static int dw_mci_cqe_data_complete(struct dw_mci *host, u16 interrupt,
+					int *data_error)
+{
+	if (interrupt & (SDMMC_DATA_END_BIT_ERR | SDMMC_DATA_CRC_ERR)) {
+		if (host->tuning)
+			*data_error = -TUNING_ERR;
+		else
+			*data_error = -EILSEQ;
+	} else if (interrupt & SDMMC_DATA_TOUT_ERR) {
+		if (host->tuning)
+			*data_error = -TUNING_ERR;
+		else
+			*data_error = -ETIMEDOUT;
+	} else if (interrupt & SDMMC_ADMA_ERR) {
+		*data_error = -EIO;
+	} else {
+		*data_error = 0;
+	}
+
+	return *data_error;
+}
+
+static void __dw_mci_cqe_start_request(struct dw_mci *host,
+				   struct dw_mci_slot *slot,
+				   struct mmc_command *cmd)
+{
+	struct mmc_data *data;
+	u32 cmdflags;
+	u32 dataflags;
+	int ret = 0;
+
+	data = cmd->data;
+
+	if (data) {
+		mci_writew(host, BLOCKCOUNT_R, data->blocks);
+		mci_writel(host, BLOCKSIZE_R, data->blksz);
+		mci_writel(host, ADMA_SA_LOW_R, host->sg_dma);
+
+		dataflags = dw_mci_cqe_prepare_data_flags(cmd);
+
+		mci_writew(host, XFER_MODE_R, dataflags);
+	} else {
+		if (cmd->opcode == MMC_SET_BLOCK_COUNT)
+			host->is_sbc = 1;
+		else
+			host->is_sbc = 0;
+
+		mci_writew(host, XFER_MODE_R, 0);
+	}
+
+	mci_writel(host, ARGUMENT_R, cmd->arg);
+
+	cmdflags = dw_mci_cqe_prepare_command(slot->mmc, cmd);
+
+	if (data) {
+		data->bytes_xfered = 0;
+		if (host->use_dma == TRANS_MODE_DMA) {
+			dw_mci_cqe_submit_data(host, data);
+			wmb(); /* drain writebuffer */
+		} else {
+			/*Using PIO mode*/
+			dev_err(host->dev, "pio mode is not supported currently\n");
+		}
+	}
+
+	ret = dw_mci_cqe_start_command(host, cmd, cmdflags);
+
+	if (ret == 0) {
+		dw_mci_cqe_read_rsp(host, cmd, cmd->resp);
+
+		if (data)
+			data->bytes_xfered += (data->blocks * data->blksz);
+	}
+
+	dw_mci_cqe_command_complete(host, host->error_interrupt, &cmd->error);
+	if (data) {
+		dw_mci_cqe_data_complete(host, host->error_interrupt, &data->error);
+		if (host->use_dma == TRANS_MODE_DMA)
+			dw_mci_cqe_stop_dma(host, data);
+		else {
+			/*Using PIO mode*/
+			dev_err(host->dev, "pio mode is not supported currently\n");
+		}
+	}
+
+	if (ret != 0)
+		dw_mci_cqe_send_stop_abort(host, slot, cmd);
+
+	if (cmd->opcode == SD_SWITCH_VOLTAGE) {
+		/*
+		 * If cmd11 needs to be dealt with specially, put in here.
+		 */
+	}
+}
+
+static void dw_mci_cqe_start_request(struct dw_mci *host,
+				 struct dw_mci_slot *slot)
+{
+	struct mmc_request *mrq = slot->mrq;
+
+	if (mrq->sbc)
+		__dw_mci_cqe_start_request(host, slot, mrq->sbc);
+
+	if (mrq->cmd)
+		__dw_mci_cqe_start_request(host, slot, mrq->cmd);
+}
+
+static int dw_mci_switch(struct mmc_host *mmc,
+			 u8 set,
+			 u8 index,
+			 u8 value,
+			 unsigned int timeout_ms)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+	struct mmc_command cmd;
+	int err = 0;
+	u32 cmdr;
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+
+	cmd.opcode		= MMC_SWITCH;
+	cmd.arg			= (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
+				(index << 16) |
+				(value << 8) |
+				set;
+	cmd.flags		= MMC_CMD_AC|MMC_RSP_SPI_R1B | MMC_RSP_R1B;
+	cmd.data		= NULL;
+
+	cmdr = (cmd.opcode << 8) | SDMMC_RESP_LEN_48B |
+		SDMMC_CMD_CHK_RESP_CRC | SDMMC_CMD_IDX_CHK_ENABLE;
+
+	mci_writew(host, XFER_MODE_R, 0);
+	mci_writel(host, ARGUMENT_R, cmd.arg);
+
+	err = dw_mci_cqe_start_command(host, &cmd, cmdr);
+
+	if (err) {
+		dev_err(host->dev, "interrupt status reg :0x%x, error reg : 0x%x\n",
+			host->normal_interrupt, host->error_interrupt);
+	}
+
+	return err;
+}
+
+static int dw_mci_cqe_switch(struct mmc_host *mmc, bool enable)
+{
+	struct mmc_card *card = mmc->card;
+	int ret = 0;
+	u8 val = enable ? EXT_CSD_CMDQ_MODE_ENABLED : 0;
+
+	if (!card->ext_csd.cmdq_support) {
+		dev_err(&mmc->class_dev, "The device card does not support cqe mode\n");
+		return 0;
+	}
+
+	ret = dw_mci_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+			    EXT_CSD_CMDQ_MODE_EN, val,
+			    card->ext_csd.generic_cmd6_time);
+	if (ret) {
+		dev_err(&mmc->class_dev, "cmdq mode %sable failed %d\n",
+			enable ? "en" : "dis", ret);
+		goto out;
+	} else {
+		card->ext_csd.cmdq_en = enable;
+	}
+
+out:
+	return ret;
+}
+
+static void dw_mci_cqe_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+	int ret;
+	u32 status = 0;
+
+	WARN_ON(slot->mrq);
+
+	/*
+	 * The check for card presence and queueing of the request must be
+	 * atomic, otherwise the card could be removed in between and the
+	 * request wouldn't fail until another card was inserted.
+	 */
+
+	if (!dw_mci_cqe_get_cd(mmc)) {
+		mrq->cmd->error = -ENOMEDIUM;
+		mmc_request_done(mmc, mrq);
+		return;
+	}
+
+	down_write(&host->cr_rw_sem);
+
+	/*cmdq case needs extra check*/
+	if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE)) {
+		if ((host->cqe) == NULL) {
+			dev_err(host->dev, "dw_mci_request_cqe not done yet\n");
+			mdelay(2);
+		}
+
+		if (mmc->cqe_on == false && host->cqe->activated == true
+			&& slot->switch_partition == 0)
+			cqhci_deactivate(mmc);
+
+		if (mrq->cmd->opcode == MMC_SWITCH && mrq->cmd->arg == DW_MCI_CMDQ_DISABLED)
+			slot->switch_partition = 1;
+
+		/* we do not need to disable cmdq if it is rpmb request
+		 * because rpmb has been changed to rpmb partition in block.c
+		 * Also, we do not need to disable cmdq if this command is disable/enable cmdq
+		 */
+		if (mmc->card && mmc->card->ext_csd.cmdq_en == 1
+			&& slot->switch_partition == 0
+			&& host->cmd_atomic == false) {
+			ret = dw_mci_cqe_switch(mmc, false);
+
+			if (mrq->cmd->opcode == MMC_SLEEP_AWAKE ||
+				(mrq->cmd->opcode == MMC_SELECT_CARD
+					&& mrq->cmd->arg == 0) || (mrq->cmd->opcode == MMC_SWITCH
+					&& mrq->cmd->arg == DW_MCI_POWEROFF))
+				host->cqe_reenable = 0;
+			else
+				host->cqe_reenable = 1;
+
+			if (ret)
+				dev_err(host->dev, "disable cmdq failed !\n");
+
+			dw_mci_cqe_wait_while_busy(host, &status);
+		}
+
+		if (mrq->cmd->opcode == MMC_SWITCH
+			&& mrq->cmd->arg == DW_MCI_CMDQ_ENABLED)
+			slot->switch_partition = 0;
+
+		if (mrq->cmd->opcode == MMC_ERASE_GROUP_START) {
+			host->cmd_atomic = true;
+			host->cqe_reenable = 0;
+		}
+
+		if (host->cmd_atomic == true
+			&& mrq->cmd->opcode == MMC_SEND_STATUS) {
+			host->cmd_atomic = false;
+			host->cqe_reenable = 1;
+		}
+	}
+
+	slot->mrq = mrq;
+	host->mrq = mrq;
+
+	dw_mci_cqe_start_request(host, slot);
+
+	tasklet_schedule(&host->tasklet);
+
+	/*cmdq case needs extra check*/
+	if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE) &&
+		host->cqe_reenable == 1) {
+		if (mmc->card && mmc->card->ext_csd.cmdq_en == 0) {
+			ret = dw_mci_cqe_switch(mmc, true);
+			host->cqe_reenable = 0;
+			if (ret)
+				dev_err(host->dev, "switch cmdq failed !\n");
+			dw_mci_cqe_wait_while_busy(host, &status);
+		}
+	}
+
+	up_write(&host->cr_rw_sem);
+}
+
+static void dw_mci_cqe_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+	const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
+
+	switch (ios->timing) {
+	case MMC_TIMING_MMC_HS400:
+		mci_writew(host, HOST_CTRL2_R,
+			(mci_readw(host, HOST_CTRL2_R)
+				& SDMMC_UHS_MODE_SEL_MASK) | SDMMC_HS400);
+		break;
+	case MMC_TIMING_MMC_HS200:
+		mci_writew(host, HOST_CTRL2_R,
+			(mci_readw(host, HOST_CTRL2_R)
+				& SDMMC_UHS_MODE_SEL_MASK) | SDMMC_HS200);
+		break;
+	case MMC_TIMING_MMC_HS:
+		mci_writew(host, HOST_CTRL2_R,
+			(mci_readw(host, HOST_CTRL2_R)
+				& SDMMC_UHS_MODE_SEL_MASK) | SDMMC_SDR);
+		break;
+	default:
+		/*MMC_TIMING_LEGACY case*/
+		mci_writew(host, HOST_CTRL2_R,
+			(mci_readw(host, HOST_CTRL2_R)
+				& SDMMC_UHS_MODE_SEL_MASK) | SDMMC_LEGACY);
+	}
+
+	slot->clock = ios->clock;
+
+	if (drv_data && drv_data->set_ios)
+		drv_data->set_ios(slot, ios);
+
+	switch (ios->bus_width) {
+	case MMC_BUS_WIDTH_4:
+		mci_writeb(host, HOST_CTRL1_R,
+			(mci_readb(host, HOST_CTRL1_R) &
+			(SDMMC_EXT_DAT_XFER_MASK & SDMMC_DAT_XFER_WIDTH_MASK))
+				|SDMMC_BUS_WIDTH_4);
+		break;
+	case MMC_BUS_WIDTH_8:
+		mci_writeb(host, HOST_CTRL1_R,
+			(mci_readb(host, HOST_CTRL1_R) &
+				SDMMC_EXT_DAT_XFER_MASK) | SDMMC_BUS_WIDTH_8);
+		break;
+	default:
+		/* set default 1 bit mode */
+		mci_writeb(host, HOST_CTRL1_R,
+			(mci_readb(host, HOST_CTRL1_R) &
+				(SDMMC_EXT_DAT_XFER_MASK &
+				SDMMC_DAT_XFER_WIDTH_MASK)) | SDMMC_BUS_WIDTH_1);
+	}
+}
+
+static int dw_mci_cqe_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+	const struct dw_mci_drv_data *drv_data = host->drv_data;
+
+	if (drv_data && drv_data->switch_voltage)
+		return drv_data->switch_voltage(mmc, ios);
+
+	return 0;
+}
+
+static int dw_mci_cqe_get_ro(struct mmc_host *mmc)
+{
+	int read_only;
+	int gpio_ro = mmc_gpio_get_ro(mmc);
+
+	/* Use platform get_ro function, else try on board write protect */
+	if (gpio_ro >= 0)
+		read_only = gpio_ro;
+	else
+		/*Need to read the IP register to judge if ro*/
+		dev_err(&mmc->class_dev, "IP get_ro feature is not implemented currently.\n");
+
+	dev_dbg(&mmc->class_dev, "card is %s\n",
+		read_only ? "read-only" : "read-write");
+
+	return read_only;
+}
+
+static int dw_mci_cqe_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+	const struct dw_mci_drv_data *drv_data = host->drv_data;
+	int err = -EINVAL;
+
+	if (drv_data && drv_data->execute_tuning)
+		err = drv_data->execute_tuning(slot, opcode);
+	return err;
+
+}
+
+static int dw_mci_cqe_prepare_hs400_tuning(struct mmc_host *mmc,
+				       struct mmc_ios *ios)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+	const struct dw_mci_drv_data *drv_data = host->drv_data;
+
+	if (drv_data && drv_data->prepare_hs400_tuning)
+		return drv_data->prepare_hs400_tuning(host, ios);
+
+	return 0;
+}
+
+static void dw_mci_cqe_hs400_complete(struct mmc_host *mmc)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+	const struct dw_mci_drv_data *drv_data = host->drv_data;
+
+	if (drv_data && drv_data->hs400_complete)
+		drv_data->hs400_complete(mmc);
+}
+
+static void dw_mci_cqe_init_card(struct mmc_host *mmc, struct mmc_card *card)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+	const struct dw_mci_drv_data *drv_data = host->drv_data;
+
+	/*
+	 * Add any quirks for this synopsys IP here or
+	 * deal with something special for some specific
+	 * vendors' SOC platform by calling drv_data->init_card().
+	 */
+	if (drv_data && drv_data->init_card)
+		drv_data->init_card(mmc, card);
+}
+
+static const struct mmc_host_ops dw_mci_ops = {
+	.request		= dw_mci_cqe_request,
+	.pre_req		= dw_mci_cqe_pre_req,
+	.post_req		= dw_mci_cqe_post_req,
+	.set_ios		= dw_mci_cqe_set_ios,
+	.get_ro			= dw_mci_cqe_get_ro,
+	.get_cd			= dw_mci_cqe_get_cd,
+	.execute_tuning		= dw_mci_cqe_execute_tuning,
+	.start_signal_voltage_switch = dw_mci_cqe_switch_voltage,
+	.init_card		= dw_mci_cqe_init_card,
+	.prepare_hs400_tuning	= dw_mci_cqe_prepare_hs400_tuning,
+	.hs400_complete         = dw_mci_cqe_hs400_complete,
+};
+
+static void dw_mci_cqe_tasklet_func(unsigned long priv)
+{
+	struct dw_mci *host = (struct dw_mci *)priv;
+	struct mmc_host *prev_mmc = host->slot->mmc;
+	struct mmc_request *mrq;
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->irq_lock, flags);
+
+	host->cmd = NULL;
+	host->data = NULL;
+	mrq = host->mrq;
+	host->slot->mrq = NULL;
+	host->mrq = NULL;
+
+	spin_unlock_irqrestore(&host->irq_lock, flags);
+
+	mmc_request_done(prev_mmc, mrq);
+}
+
+static irqreturn_t dw_mci_cqe_interrupt(int irq, void *dev_id)
+{
+	struct dw_mci *host = dev_id;
+	struct mmc_host *mmc = host->slot->mmc;
+	struct cqhci_host *cq_host = NULL;
+	int cmd_error = 0, data_error = 0;
+
+	if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE))
+		cq_host = mmc->cqe_private;
+
+	dw_mci_get_int(host);
+
+	if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE)) {
+		if (mmc->cqe_on == false && cq_host->activated == false)
+			dw_mci_clr_signal_int(host);
+	} else {
+		dw_mci_clr_signal_int(host);
+	}
+	/*if run the cmdq mode*/
+	if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE) &&
+		mmc->cqe_on == true && cq_host->activated == true) {
+		if (host->normal_interrupt & SDMMC_ERR_INTERRUPT) {
+			dev_err(host->dev, "cmdq error: interrupt status=%08x, error interrupt=0x%08x, CQIS=0x%x, CQTCN=0x%x\n",
+				host->normal_interrupt, host->error_interrupt,
+				readl(host->cqe->mmio + CQHCI_IS),
+				readl(host->cqe->mmio + CQHCI_TCN));
+
+			dw_mci_cqe_command_complete(host, host->error_interrupt, &cmd_error);
+			dw_mci_cqe_data_complete(host, host->error_interrupt, &data_error);
+		}
+		cqhci_irq(mmc, (u32)(host->normal_interrupt), cmd_error, data_error);
+		dw_mci_clr_int(host);
+
+		return IRQ_HANDLED;
+	}
+
+	if (host->int_waiting) {
+		del_timer(&host->timer);
+		complete(host->int_waiting);
+	}
+
+	return IRQ_HANDLED;
+
+}
+
+static void dw_mci_cqe_setup(struct dw_mci *host)
+{
+	mci_writeb(host, SW_RST_R, (SDMMC_RST_ALL|SDMMC_RST_CMD|SDMMC_RST_DAT));
+	mci_writeb(host, TOUT_CTRL_R, 0xe);
+	mci_writew(host, HOST_CTRL2_R, SDMMC_HOST_VER4_ENABLE|SDMMC_SIGNALING_EN);
+	mci_writew(host, NORMAL_INT_STAT_EN_R, 0xffff);
+	mci_writew(host, ERROR_INT_STAT_EN_R, SDMMC_ALL_ERR_STAT_EN);
+	/*Card detect will be enabled in the last*/
+	mci_writew(host, NORMAL_INT_SIGNAL_EN_R,
+		(~(SDMMC_CARD_INSERTION_SIGNAL_EN | SDMMC_CARD_REMOVAL_SIGNAL_EN |
+			SDMMC_CARD_INTERRUPT_SIGNAL_EN) & 0xffff));
+	mci_writew(host, ERROR_INT_SIGNAL_EN_R, SDMMC_ALL_ERR_SIGNAL_EN);
+	mci_writeb(host, CTRL_R, SDMMC_RST_N_OE|SDMMC_RST_N|SDMMC_CARD_IS_EMMC);
+	mci_writeb(host, HOST_CTRL1_R,
+		(mci_readb(host, HOST_CTRL1_R)&0xe7) | (SDMMC_ADMA2_32 << SDMMC_DMA_SEL));
+	mci_writeb(host, MSHC_CTRL_R, mci_readb(host, MSHC_CTRL_R) & (~SDMMC_CMD_CONFLICT_CHECK));
+	mci_writew(host, CLK_CTRL_R, mci_readw(host, CLK_CTRL_R)|SDMMC_INTERNAL_CLK_EN);
+}
+
+static int dw_mci_cqe_init_slot_caps(struct dw_mci_slot *slot)
+{
+	struct dw_mci *host = slot->host;
+	const struct dw_mci_drv_data *drv_data = host->drv_data;
+	struct mmc_host *mmc = slot->mmc;
+	int ctrl_id;
+
+	if (host->pdata->caps)
+		mmc->caps = host->pdata->caps;
+
+	if (host->pdata->pm_caps)
+		mmc->pm_caps = host->pdata->pm_caps;
+
+	if (host->dev->of_node) {
+		ctrl_id = of_alias_get_id(host->dev->of_node, "mshc");
+		if (ctrl_id < 0)
+			ctrl_id = 0;
+	} else {
+		ctrl_id = to_platform_device(host->dev)->id;
+	}
+
+	if (drv_data && drv_data->caps) {
+		if (ctrl_id >= drv_data->num_caps) {
+			dev_err(host->dev, "invalid controller id %d\n",
+				ctrl_id);
+			return -EINVAL;
+		}
+		mmc->caps |= drv_data->caps[ctrl_id];
+	}
+
+	if (drv_data && drv_data->shift_rsp)
+		host->shift = 1;
+	else
+		host->shift = 0;
+
+	if (host->pdata->caps2)
+		mmc->caps2 = host->pdata->caps2;
+
+	mmc->f_min = DW_MCI_FREQ_MIN;
+	if (!mmc->f_max)
+		mmc->f_max = DW_MCI_FREQ_MAX;
+
+	/* Process SDIO IRQs through the sdio_irq_work. */
+	if (mmc->caps & MMC_CAP_SDIO_IRQ)
+		mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
+
+	return 0;
+}
+
+static int dw_mci_cqe_init_slot(struct dw_mci *host)
+{
+	struct mmc_host *mmc;
+	struct dw_mci_slot *slot;
+	int ret;
+
+	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	slot = mmc_priv(mmc);
+	slot->id = 0;
+	slot->sdio_id = host->sdio_id0 + slot->id;
+	slot->mmc = mmc;
+	slot->switch_partition = 0;
+	slot->host = host;
+	host->slot = slot;
+
+	mmc->ops = &dw_mci_ops;
+
+	/*if there are external regulators, get them*/
+	ret = mmc_regulator_get_supply(mmc);
+	if (ret)
+		goto err_host_allocated;
+
+	if (!mmc->ocr_avail)
+		mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+	dev_info(host->dev, "regulator support volage ocr_avail=0x%x\n",
+			mmc->ocr_avail);
+
+	ret = mmc_of_parse(mmc);
+	if (ret)
+		goto err_host_allocated;
+
+	ret = dw_mci_cqe_init_slot_caps(slot);
+	if (ret)
+		goto err_host_allocated;
+
+	/* Useful defaults if platform data is unset. */
+	if (host->use_dma == TRANS_MODE_DMA) {
+		mmc->max_segs = 256;
+		mmc->max_blk_size = 512;
+		mmc->max_seg_size = 0x1000;
+		mmc->max_req_size = mmc->max_seg_size * mmc->max_segs;
+		mmc->max_blk_count = mmc->max_req_size / 512;
+	} else {
+		dev_info(host->dev, "dw-mmc-cqe pio mode is ToDo.\n");
+		/* To DO, TRANS_MODE_PIO */
+	}
+
+	dw_mci_cqe_get_cd(mmc);
+
+	ret = mmc_add_host(mmc);
+	if (ret)
+		goto err_host_allocated;
+
+	return 0;
+
+err_host_allocated:
+	mmc_free_host(mmc);
+	return ret;
+}
+
+static void dw_mci_cqe_cleanup_slot(struct dw_mci_slot *slot)
+{
+	/* Debugfs stuff is cleaned up by mmc core */
+	mmc_remove_host(slot->mmc);
+	slot->host->slot = NULL;
+	mmc_free_host(slot->mmc);
+}
+
+static void dw_mci_cqe_init_dma(struct dw_mci *host)
+{
+	host->use_dma = TRANS_MODE_DMA;
+
+	/* Determine which DMA interface to use */
+	/* using 32bit DMA by default,
+	 * user can modify this setting by drv_data->init()
+	 */
+	if (host->use_dma == TRANS_MODE_DMA) {
+		host->dma_64bit_address = 0;
+		dev_info(host->dev, "IDMAC supports 32-bit address mode.\n");
+	}
+
+	/* Alloc memory for sg translation */
+	host->sg_cpu = dma_alloc_coherent(host->dev,
+						DW_MCI_DESC_LEN,
+						&host->sg_dma, GFP_KERNEL);
+	if (!host->sg_cpu) {
+		dev_err(host->dev,
+			"%s: could not alloc DMA memory\n",
+			__func__);
+		goto no_dma;
+	}
+
+	return;
+
+no_dma:
+	dev_info(host->dev, "Using PIO mode.\n");
+	host->use_dma = TRANS_MODE_PIO;
+}
+
+#ifdef CONFIG_OF
+static struct dw_mci_board *dw_mci_cqe_parse_dt(struct dw_mci *host)
+{
+	struct dw_mci_board *pdata;
+	struct device *dev = host->dev;
+	const struct dw_mci_drv_data *drv_data = host->drv_data;
+	int ret;
+	u32 clock_frequency;
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return ERR_PTR(-ENOMEM);
+
+	/* find reset controller when exist */
+	pdata->rstc = devm_reset_control_get_optional(dev, "reset");
+	if (IS_ERR(pdata->rstc)) {
+		if (PTR_ERR(pdata->rstc) == -EPROBE_DEFER)
+			return ERR_PTR(-EPROBE_DEFER);
+	}
+
+	device_property_read_u32(dev, "card-detect-delay",
+		&pdata->detect_delay_ms);
+
+	if (!device_property_read_u32(dev, "clock-frequency", &clock_frequency))
+		pdata->bus_hz = clock_frequency;
+
+	if (drv_data && drv_data->parse_dt) {
+		ret = drv_data->parse_dt(host);
+		if (ret)
+			return ERR_PTR(ret);
+	}
+
+	return pdata;
+}
+
+#else /* CONFIG_OF */
+static struct dw_mci_board *dw_mci_cqe_parse_dt(struct dw_mci *host)
+{
+	return ERR_PTR(-EINVAL);
+}
+#endif /* CONFIG_OF */
+
+static void dw_mci_cqe_cto_timer(struct timer_list *t)
+{
+	struct dw_mci *host = from_timer(host, t, timer);
+
+	if (host->int_waiting) {
+		dev_err(host->dev, "fired, opcode=%d, arg=0x%x, irq status=0x%x, err irq=0x%x, auto err irq=0x%x\n",
+				host->opcode, host->arg,
+			host->normal_interrupt, host->error_interrupt,
+			host->auto_error_interrupt);
+
+		dw_mci_clr_signal_int(host);
+		dw_mci_get_int(host);
+
+		complete(host->int_waiting);
+	}
+}
+
+
+static void dw_mci_cqe_enable_cd(struct dw_mci *host)
+{
+	/*
+	 * No need for CD if all slots have a non-error GPIO
+	 * as well as broken card detection is found.
+	 */
+	if (host->slot->mmc->caps & MMC_CAP_NEEDS_POLL
+		|| !mmc_card_is_removable(host->slot->mmc))
+		return;
+}
+
+static void dw_mci_cqhci_init(struct dw_mci *host)
+{
+	if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE)) {
+		host->cqe = cqhci_pltfm_init(host->pdev);
+		if (PTR_ERR(host->cqe) == -EINVAL ||
+		   PTR_ERR(host->cqe) == -ENOMEM ||
+		   PTR_ERR(host->cqe) == -EBUSY) {
+			dev_err(host->dev,
+				"Unable to get the cmdq related attribute,err = %ld\n",
+				PTR_ERR(host->cqe));
+			host->cqe = 0;
+			host->pdata->caps2 &= ~(MMC_CAP2_CQE|MMC_CAP2_CQE_DCMD);
+		} else {
+			host->cqe->ops = &dw_mci_cqhci_host_ops;
+			cqhci_init(host->cqe, host->slot->mmc, 0);
+		}
+	}
+}
+
+int dw_mci_cqe_probe(struct dw_mci *host)
+{
+	const struct dw_mci_drv_data *drv_data = host->drv_data;
+	int ret = 0;
+
+	if (!host->pdata) {
+		host->pdata = dw_mci_cqe_parse_dt(host);
+		if (PTR_ERR(host->pdata) == -EPROBE_DEFER) {
+			return -EPROBE_DEFER;
+		} else if (IS_ERR(host->pdata)) {
+			dev_err(host->dev, "platform data not available\n");
+			return -EINVAL;
+		}
+	}
+
+	host->biu_clk = devm_clk_get(host->dev, "biu");
+	if (IS_ERR(host->biu_clk)) {
+		dev_dbg(host->dev, "biu clock not available\n");
+	} else {
+		ret = clk_prepare_enable(host->biu_clk);
+		if (ret) {
+			dev_err(host->dev, "failed to enable biu clock\n");
+			return ret;
+		}
+	}
+
+	host->ciu_clk = devm_clk_get(host->dev, "ciu");
+	if (IS_ERR(host->ciu_clk)) {
+		dev_dbg(host->dev, "ciu clock not available\n");
+		host->bus_hz = host->pdata->bus_hz;
+	} else {
+		ret = clk_prepare_enable(host->ciu_clk);
+		if (ret) {
+			dev_err(host->dev, "failed to enable ciu clock\n");
+			goto err_clk_biu;
+		}
+
+		if (host->pdata->bus_hz) {
+			ret = clk_set_rate(host->ciu_clk, host->pdata->bus_hz);
+			if (ret)
+				dev_warn(host->dev,
+					"Unable to set bus rate to %uHz\n",
+					 host->pdata->bus_hz);
+		}
+		host->bus_hz = clk_get_rate(host->ciu_clk);
+	}
+
+	if (!host->bus_hz) {
+		dev_err(host->dev,
+			"Platform data must supply bus speed\n");
+		ret = -ENODEV;
+		goto err_clk_ciu;
+	}
+
+	if (!IS_ERR(host->pdata->rstc)) {
+		reset_control_assert(host->pdata->rstc);
+		usleep_range(10, 50);
+		reset_control_deassert(host->pdata->rstc);
+	}
+
+	timer_setup(&host->timer, dw_mci_cqe_cto_timer, 0);
+
+	spin_lock_init(&host->lock);
+	spin_lock_init(&host->irq_lock);
+	init_rwsem(&host->cr_rw_sem);
+	tasklet_init(&host->tasklet, dw_mci_cqe_tasklet_func, (unsigned long)host);
+
+	host->cqe_reenable = 0;
+	host->cmd_atomic = false;
+
+	/*pio mode's parameters should be initialized here*/
+
+	/*Initialize the eMMC IP related attribute*/
+	dw_mci_cqe_setup(host);
+
+	dw_mci_cqe_init_dma(host);
+
+	/* This flag will be set 1 when doing tuning,
+	 * we add this flag because
+	 * some vendors might use other cmd instead of 21
+	 * to tune phase under high speed interface.
+	 * we use this flag to recognize if the system is under tuning stage.
+	 */
+	host->tuning = 0;
+
+	/*Timing_setting is to avoid sending command
+	 *before setting phase in hs200, hs400
+	 */
+	host->current_speed = 0;
+
+	/*Do the rest of init for specific*/
+	if (drv_data && drv_data->init) {
+		ret = drv_data->init(host);
+		if (ret) {
+			dev_err(host->dev,
+				"implementation specific init failed\n");
+			goto err_dmaunmap;
+		}
+	}
+
+	ret = dw_mci_cqe_init_slot(host);
+	if (ret) {
+		dev_err(host->dev, "slot 0 init failed\n");
+		goto err_dmaunmap;
+	}
+
+	ret = devm_request_irq(host->dev, host->irq, dw_mci_cqe_interrupt,
+				host->irq_flags, "dw-mci-cqe", host);
+	if (ret)
+		goto err_dmaunmap;
+
+	/*After the slot initialization,
+	 *now we have mmc data and can initialize cmdq if user enabled
+	 */
+	dw_mci_cqhci_init(host);
+
+	/* Now that slots are all setup, we can enable card detect */
+	dw_mci_cqe_enable_cd(host);
+
+	return 0;
+
+err_dmaunmap:
+	if (!IS_ERR(host->pdata->rstc))
+		reset_control_assert(host->pdata->rstc);
+err_clk_ciu:
+	clk_disable_unprepare(host->ciu_clk);
+
+err_clk_biu:
+	clk_disable_unprepare(host->biu_clk);
+
+	return ret;
+}
+EXPORT_SYMBOL(dw_mci_cqe_probe);
+
+void dw_mci_cqe_remove(struct dw_mci *host)
+{
+	dev_dbg(host->dev, "remove slot\n");
+	if (host->slot)
+		dw_mci_cqe_cleanup_slot(host->slot);
+
+	if (!IS_ERR(host->pdata->rstc))
+		reset_control_assert(host->pdata->rstc);
+
+	clk_disable_unprepare(host->ciu_clk);
+	clk_disable_unprepare(host->biu_clk);
+
+}
+EXPORT_SYMBOL(dw_mci_cqe_remove);
+
+#ifdef CONFIG_PM
+int dw_mci_cqe_runtime_suspend(struct device *dev)
+{
+	struct dw_mci *host = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE)) {
+		if (host->slot) {
+			dev_info(host->dev, "cqe suspend\n");
+			ret = cqhci_suspend(host->slot->mmc);
+			if (ret) {
+				dev_err(host->dev, "cqe suspend failed\n");
+				return ret;
+			}
+		}
+	}
+
+	ret = pm_runtime_force_suspend(dev);
+
+	return ret;
+}
+EXPORT_SYMBOL(dw_mci_cqe_runtime_suspend);
+
+int dw_mci_cqe_runtime_resume(struct device *dev)
+{
+	struct dw_mci *host = dev_get_drvdata(dev);
+	const struct dw_mci_drv_data *drv_data = host->drv_data;
+	int ret = 0;
+
+	ret = pm_runtime_force_resume(dev);
+	if (ret) {
+		dev_err(host->dev, "pm_runtime_force_resume failed\n");
+		return ret;
+	}
+
+	dw_mci_cqe_setup(host);
+	if (drv_data && drv_data->init) {
+		ret = drv_data->init(host);
+		if (ret)
+			dev_err(host->dev, "implementation specific init failed\n");
+	}
+
+	init_completion(host->int_waiting);
+
+	if (host->pdata && (host->pdata->caps2 & MMC_CAP2_CQE)) {
+		if (host->slot) {
+			dev_info(host->dev, "cqe resume\n");
+			ret = cqhci_resume(host->slot->mmc);
+			if (ret)
+				dev_err(host->dev, "cqe resume failed\n");
+		}
+	}
+
+	dw_mci_cqe_setup_bus(host->slot, true);
+	dw_mci_cqe_enable_cd(host);
+
+	return ret;
+}
+EXPORT_SYMBOL(dw_mci_cqe_runtime_resume);
+#endif /* CONFIG_PM */
+
+static int __init dw_mci_cqe_init(void)
+{
+	pr_info("Synopsys Designware Multimedia Card Interface Driver\n");
+	return 0;
+}
+
+static void __exit dw_mci_cqe_exit(void)
+{
+}
+
+module_init(dw_mci_cqe_init);
+module_exit(dw_mci_cqe_exit);
+
+MODULE_DESCRIPTION("DW Multimedia Card CMDQ Interface driver");
+MODULE_AUTHOR("<jyanchou@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/dw_mmc_cqe.h b/drivers/mmc/host/dw_mmc_cqe.h
new file mode 100644
index 000000000000..ef52b67aceb6
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc_cqe.h
@@ -0,0 +1,444 @@ 
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *  Copyright (C) 2023 Realtek Semiconductors, All Rights Reserved.
+ *
+ */
+
+#ifndef __DW_MMC_CQE_H
+#define __DW_MMC_CQE_H
+
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/mmc/core.h>
+#include <linux/reset.h>
+#include <linux/scatterlist.h>
+
+struct dw_mci {
+	spinlock_t              lock;
+	spinlock_t              irq_lock;
+	struct tasklet_struct   tasklet;
+	struct rw_semaphore     cr_rw_sem;
+
+	void __iomem            *regs;
+	resource_size_t         phy_regs;
+
+	struct mmc_request      *mrq;
+	struct mmc_command      *cmd;
+	struct mmc_command	stop_abort;
+	struct mmc_data         *data;
+
+	struct clk              *biu_clk;
+	struct clk              *ciu_clk;
+	struct dw_mci_slot      *slot;
+	struct timer_list       timer;
+	struct completion	*int_waiting;
+
+	unsigned int		*desc_vaddr;
+	unsigned int            *sg_cpu;
+	dma_addr_t              sg_dma;
+	int                     use_dma;
+
+	struct platform_device  *pdev;
+	struct device           *dev;
+	struct dw_mci_board     *pdata;
+	const struct dw_mci_drv_data    *drv_data;
+	void                    *priv;
+
+	u32			opcode;
+	u32			arg;
+	u16                     normal_interrupt;
+	u16                     error_interrupt;
+	u16                     auto_error_interrupt;
+
+	u32                     bus_hz;
+	u32			current_speed;
+	u32			stop_cmdr;
+	bool			is_sbc;
+	int			dma_64bit_address;
+	int			using_dma;
+
+	unsigned long           irq_flags; /* IRQ flags */
+	int                     irq;
+	int                     sdio_id0;
+
+	struct scatterlist	*sg;
+	u32			dma_nents;
+
+	u8			tuning;
+	u8			cqe_reenable;
+	bool			cmd_atomic;
+	bool			shift;
+	struct cqhci_host       *cqe;
+};
+
+enum {
+	TRANS_MODE_PIO = 0,
+	TRANS_MODE_DMA,
+};
+
+enum dw_mci_cookie {
+	COOKIE_UNMAPPED,
+	COOKIE_PRE_MAPPED,	/* mapped by pre_req() of dwmmc */
+	COOKIE_MAPPED,		/* mapped by prepare_data() of dwmmc */
+};
+/* eMMC control register definition */
+#define SDMMC_SDMASA_R				0x000
+#define SDMMC_BLOCKSIZE_R			0x004
+#define SDMMC_BLOCKCOUNT_R			0x006
+#define SDMMC_ARGUMENT_R			0x008
+#define SDMMC_XFER_MODE_R			0x00c
+#define SDMMC_CMD_R				0x00e
+#define SDMMC_RESP01_R				0x010
+#define SDMMC_RESP23_R				0x014
+#define SDMMC_RESP45_R				0x018
+#define SDMMC_RESP67_R				0x01c
+#define SDMMC_BUF_DATA_R			0x020
+#define SDMMC_PSTATE_REG			0x024
+#define SDMMC_HOST_CTRL1_R			0x028
+#define SDMMC_PWR_CTRL_R			0x029
+#define SDMMC_BGAP_CTRL_R			0x02a
+#define SDMMC_CLK_CTRL_R			0x02c
+#define SDMMC_TOUT_CTRL_R			0x02e
+#define SDMMC_SW_RST_R				0x02f
+#define SDMMC_NORMAL_INT_STAT_R			0x030
+#define SDMMC_ERROR_INT_STAT_R			0x032
+#define SDMMC_NORMAL_INT_STAT_EN_R		0x034
+#define SDMMC_ERROR_INT_STAT_EN_R		0x036
+#define SDMMC_NORMAL_INT_SIGNAL_EN_R		0x038
+#define SDMMC_ERROR_INT_SIGNAL_EN_R		0x03a
+#define SDMMC_AUTO_CMD_STAT_R			0x03c
+#define SDMMC_HOST_CTRL2_R			0x03e
+#define SDMMC_ADMA_ERR_STAT_R			0x054
+#define SDMMC_ADMA_SA_LOW_R			0x058
+
+#define SDMMC_MSHC_CTRL_R			0x208
+#define SDMMC_CTRL_R				0x22c
+
+#define SDMMC_CMD_CONFLICT_CHECK		BIT(0)
+#define CMD_IDX_MASK(x)				((x >> 8)&0x3f)
+
+/*0xc*/
+#define SDMMC_MULTI_BLK_SEL			5
+#define SDMMC_DATA_XFER_DIR			4
+#define SDMMC_BLOCK_COUNT_ENABLE		BIT(1)
+#define SDMMC_DMA_ENABLE			BIT(0)
+#define SDMMC_AUTO_CMD_ENABLE			2
+#define SDMMC_AUTO_CMD_DISABLED			0x0
+#define SDMMC_AUTO_CMD12_ENABLED		0x1
+#define SDMMC_AUTO_CMD23_ENABLED		0x2
+#define SDMMC_AUTO_CMD_SEL			0x3
+
+/*0xe*/
+#define SDMMC_RESP_TYPE_SELECT			0
+#define SDMMC_CMD_TYPE				6
+#define SDMMC_NO_RESP				0x0
+#define SDMMC_RESP_LEN_136			0x1
+#define SDMMC_RESP_LEN_48			0x2
+#define SDMMC_RESP_LEN_48B			0x3
+#define SDMMC_CMD_CHK_RESP_CRC			BIT(3)
+#define SDMMC_CMD_IDX_CHK_ENABLE		BIT(4)
+#define SDMMC_DATA				BIT(5)
+#define SDMMC_ABORT_CMD				0x3
+#define SDMMC_RESUME_CMD			0x2
+#define SDMMC_SUSPEND_CMD			0x1
+#define SDMMC_NORMAL_CMD			0x0
+
+/*0x24 PSTATE*/
+#define SDMMC_CMD_INHIBIT			BIT(0)
+#define SDMMC_CMD_INHIBIT_DAT			BIT(1)
+#define SDMMC_DAT_3_0				(0xf << 20)
+#define SDMMC_DAT_7_4				(0xf << 4)
+
+/*0x28*/
+#define SDMMC_DMA_SEL				3
+#define SDMMC_SDMA				(0x0)
+#define SDMMC_ADMA2_32				(0x2)
+#define SDMMC_ADMA2_64				(0x3)
+#define SDMMC_EXT_DAT_XFER			BIT(5)
+#define SDMMC_EXT_DAT_XFER_MASK			(~SDMMC_EXT_DAT_XFER & 0xff)
+#define SDMMC_HIGH_SPEED_EN			BIT(2)
+#define SDMMC_HIGH_SPEED_MASK			((~BIT(2)) & 0xff)
+#define SDMMC_UHS_MODE_SEL_MASK			((~(BIT(0)|BIT(1)|BIT(2))) & 0xffff)
+#define SDMMC_DAT_XFER_WIDTH			BIT(1)
+#define SDMMC_DAT_XFER_WIDTH_MASK		(~SDMMC_DAT_XFER_WIDTH & 0xff)
+#define SDMMC_BUS_WIDTH_8			SDMMC_EXT_DAT_XFER
+#define SDMMC_BUS_WIDTH_4			SDMMC_DAT_XFER_WIDTH
+#define SDMMC_BUS_WIDTH_1			0
+#define SDMMC_DMA_SEL_CLR			(0xff & (~(0x3<<SDMMC_DMA_SEL)))
+#define SDMMC_DATA_XFER_CLR			((0xff & (~SDMMC_EXT_DAT_XFER)) \
+						& (~SDMMC_DAT_XFER_WIDTH))
+
+/*0x2a*/
+#define SDMMC_STOP_BG_REQ			BIT(0)
+
+/*0x2f SW_RST_R*/
+#define SDMMC_RST_DAT			BIT(2)
+#define SDMMC_RST_CMD			BIT(1)
+#define SDMMC_RST_ALL			BIT(0)
+
+/*0x30 status bitmap*/
+#define SDMMC_STATUS_ALL			0xffff
+#define SDMMC_ERR_INTERRUPT			BIT(15)
+#define SDMMC_CQE_EVENT				BIT(14)
+#define SDMMC_FX_EVENT				BIT(13)
+#define SDMMC_RE_TUNE_EVENT			BIT(12)
+#define SDMMC_INT_C				BIT(11)
+#define SDMMC_INT_B				BIT(10)
+#define SDMMC_INT_A				BIT(9)
+#define SDMMC_CARD_INTERRUPT			BIT(8)
+#define SDMMC_CARD_REMOVAL			BIT(7)
+#define SDMMC_CARD_INSERTION			BIT(6)
+#define SDMMC_BUF_RD_READY			BIT(5)
+#define SDMMC_BUF_WR_READY			BIT(4)
+#define SDMMC_DMA_INTERRPT			BIT(3)
+#define SDMMC_BGAP_EVENT			BIT(2)
+#define SDMMC_XFER_COMPLETE			BIT(1)
+#define SDMMC_CMD_COMPLETE			BIT(0)
+
+/*0x32 error bitmap*/
+#define SDMMC_VENDOR_ERR3			BIT(15)
+#define SDMMC_VENDOR_ERR2			BIT(14)
+#define SDMMC_VENDOR_ERR1			BIT(13)
+#define SDMMC_BOOT_ACK_ERR			BIT(12)
+#define SDMMC_RESP_ERR				BIT(11)
+#define SDMMC_TUNING_ERR			BIT(10)
+#define SDMMC_ADMA_ERR				BIT(9)
+#define SDMMC_AUTO_CMD_ERR			BIT(8)
+#define SDMMC_CUR_LMT_ERR			BIT(7)
+#define SDMMC_DATA_END_BIT_ERR			BIT(6)
+#define SDMMC_DATA_CRC_ERR			BIT(5)
+#define SDMMC_DATA_TOUT_ERR			BIT(4)
+#define SDMMC_CMD_IDX_ERR			BIT(3)
+#define SDMMC_CMD_END_BIT_ERR			BIT(2)
+#define SDMMC_CMD_CRC_ERR			BIT(1)
+#define SDMMC_CMD_TOUT_ERR			BIT(0)
+#define SDMMC_CMD_ERR                           (SDMMC_AUTO_CMD_ERR| \
+						SDMMC_CMD_IDX_ERR|SDMMC_CMD_END_BIT_ERR| \
+						SDMMC_CMD_CRC_ERR|SDMMC_CMD_TOUT_ERR)
+#define SDMMC_DATA_ERR				(SDMMC_ADMA_ERR| \
+						SDMMC_DATA_END_BIT_ERR| \
+						SDMMC_DATA_CRC_ERR|SDMMC_DATA_TOUT_ERR)
+
+/*0x34 status enable bitmap*/
+#define SDMMC_CQE_EVENT_STAT_EN			BIT(14)
+#define SDMMC_FX_EVENT_STAT_EN			BIT(13)
+#define SDMMC_RE_TUNE_EVENT_STAT_EN		BIT(12)
+#define SDMMC_INT_C_STAT_EN			BIT(11)
+#define SDMMC_INT_B_STAT_EN			BIT(10)
+#define SDMMC_INT_A_STAT_EN			BIT(9)
+#define SDMMC_CARD_INTERRUPT_STAT_EN		BIT(8)
+#define SDMMC_CARD_REMOVAL_STAT_EN		BIT(7)
+#define SDMMC_CARD_INSERTION_STAT_EN		BIT(6)
+#define SDMMC_BUF_RD_READY_STAT_EN		BIT(5)
+#define SDMMC_BUF_WR_READY_STAT_EN		BIT(4)
+#define SDMMC_DMA_INTERRPT_STAT_EN		BIT(3)
+#define SDMMC_BGAP_EVENT_STAT_EN		BIT(2)
+#define SDMMC_XFER_COMPLETE_STAT_EN		BIT(1)
+#define SDMMC_CMD_COMPLETE_STAT_EN		BIT(0)
+
+/*0x36 error status enable bitmap*/
+#define SDMMC_VENDOR_ERR_STAT_EN3		BIT(15)
+#define SDMMC_VENDOR_ERR_STAT_EN2		BIT(14)
+#define SDMMC_VENDOR_ERR_STAT_EN1		BIT(13)
+#define SDMMC_BOOT_ACK_ERR_STAT_EN		BIT(12)
+#define SDMMC_RESP_ERR_STAT_EN			BIT(11)
+#define SDMMC_TUNING_ERR_STAT_EN		BIT(10)
+#define SDMMC_ADMA_ERR_STAT_EN			BIT(9)
+#define SDMMC_AUTO_CMD_ERR_STAT_EN		BIT(8)
+#define SDMMC_CUR_LMT_ERR_STAT_EN		BIT(7)
+#define SDMMC_DATA_END_BIT_ERR_STAT_EN		BIT(6)
+#define SDMMC_DATA_CRC_ERR_STAT_EN		BIT(5)
+#define SDMMC_DATA_TOUT_ERR_STAT_EN		BIT(4)
+#define SDMMC_CMD_IDX_ERR_STAT_EN		BIT(3)
+#define SDMMC_CMD_END_BIT_ERR_STAT_EN		BIT(2)
+#define SDMMC_CMD_CRC_ERR_STAT_EN		BIT(1)
+#define SDMMC_CMD_TOUT_ERR_STAT_EN		BIT(0)
+
+/*0x38 signal interrupt enable*/
+#define SDMMC_CQE_EVENT_SIGNAL_EN		BIT(14)
+#define SDMMC_FX_EVENT_SIGNAL_EN		BIT(13)
+#define SDMMC_RE_TUNE_EVENT_SIGNAL_EN		BIT(12)
+#define SDMMC_INT_C_SIGNAL_EN			BIT(11)
+#define SDMMC_INT_B_SIGNAL_EN			BIT(10)
+#define SDMMC_INT_A_SIGNAL_EN			BIT(9)
+#define SDMMC_CARD_INTERRUPT_SIGNAL_EN		BIT(8)
+#define SDMMC_CARD_REMOVAL_SIGNAL_EN		BIT(7)
+#define SDMMC_CARD_INSERTION_SIGNAL_EN		BIT(6)
+#define SDMMC_BUF_RD_READY_SIGNAL_EN		BIT(5)
+#define SDMMC_BUF_WR_READY_SIGNAL_EN		BIT(4)
+#define SDMMC_DMA_INTERRPT_SIGNAL_EN		BIT(3)
+#define SDMMC_BGAP_EVENT_SIGNAL_EN		BIT(2)
+#define SDMMC_XFER_COMPLETE_SIGNAL_EN		BIT(1)
+#define SDMMC_CMD_COMPLETE_SIGNAL_EN		BIT(0)
+#define SDMMC_NORMAL_INT_SIGNAL_CMD_EN_R	(~(BIT(6) | BIT(7) | BIT(8) | BIT(1)) & 0xffff)
+#define SDMMC_NORMAL_INT_SIGNAL_DAT_EN_R	(~(BIT(6) | BIT(7) | BIT(8) | BIT(0)) & 0xffff)
+#define SDMMC_NORMAL_INT_SIGNAL_CQE_EN_R	(~(BIT(6) | BIT(7) | BIT(8) | \
+						BIT(1) | BIT(0)) & 0xffff)
+
+/*0x3a error ssignal enable bitmap*/
+#define SDMMC_VENDOR_ERR_SIGNAL_EN3		BIT(15)
+#define SDMMC_VENDOR_ERR_SIGNAL_EN2		BIT(14)
+#define SDMMC_VENDOR_ERR_SIGNAL_EN1		BIT(13)
+#define SDMMC_BOOT_ACK_ERR_SIGNAL_EN		BIT(12)
+#define SDMMC_RESP_ERR_SIGNAL_EN		BIT(11)
+#define SDMMC_TUNING_ERR_SIGNAL_EN		BIT(10)
+#define SDMMC_ADMA_ERR_SIGNAL_EN		BIT(9)
+#define SDMMC_AUTO_CMD_ERR_SIGNAL_EN		BIT(8)
+#define SDMMC_CUR_LMT_ERR_SIGNAL_EN		BIT(7)
+#define SDMMC_DATA_END_BIT_ERR_SIGNAL_EN	BIT(6)
+#define SDMMC_DATA_CRC_ERR_SIGNAL_EN		BIT(5)
+#define SDMMC_DATA_TOUT_ERR_SIGNAL_EN		BIT(4)
+#define SDMMC_CMD_IDX_ERR_SIGNAL_EN		BIT(3)
+#define SDMMC_CMD_END_BIT_ERR_SIGNAL_EN		BIT(2)
+#define SDMMC_CMD_CRC_ERR_SIGNAL_EN		BIT(1)
+#define SDMMC_CMD_TOUT_ERR_STAT_EN		BIT(0)
+
+#define SDMMC_ALL_NORMAL_STAT_EN		(0xfeff)
+#define SDMMC_ALL_ERR_STAT_EN			(0xffff)
+					/*enablle all error initerrupt in 0x36*/
+#define SDMMC_ALL_SIGNAL_STAT_EN                (0xfeff)
+#define SDMMC_ALL_ERR_SIGNAL_EN			(0xffff)
+					/*enable all signal error interrupt in 0x3a*/
+
+/*0x3e*/
+#define SDMMC_LEGACY				0x0
+#define SDMMC_SDR				0x1
+#define SDMMC_HS200				0x3
+#define SDMMC_DDR				0x4
+#define SDMMC_HS400				0x7
+#define SDMMC_HOST_VER4_ENABLE			BIT(12)
+#define SDMMC_SIGNALING_EN			BIT(3)
+
+/*0x22c*/
+#define SDMMC_RST_N_OE				BIT(3)
+#define SDMMC_RST_N				BIT(2)
+#define SDMMC_CARD_IS_EMMC			BIT(0)
+
+#define SDMMC_INTERNAL_CLK_EN			BIT(0)
+#define SDMMC_PLL_USABLE			BIT(0)
+
+#define VALID(x)			((x & 1) << 0)
+#define END(x)				((x & 1) << 1)
+#define INT(x)				((x & 1) << 2)
+#define ACT(x)				((x & 0x7) << 3)
+#define DAT_LENGTH(x)			((x & 0xFFFF) << 16)
+
+
+/* Register access macros */
+#define mci_readl(dev, reg)                     \
+	readl_relaxed((dev)->regs + SDMMC_##reg)
+#define mci_writel(dev, reg, value)                     \
+	writel_relaxed((value), (dev)->regs + SDMMC_##reg)
+
+#define mci_readw(dev, reg)                     \
+	readw_relaxed((dev)->regs + SDMMC_##reg)
+#define mci_writew(dev, reg, value)                     \
+	writew_relaxed((value), (dev)->regs + SDMMC_##reg)
+
+#define mci_readb(dev, reg)                     \
+	readb_relaxed((dev)->regs + SDMMC_##reg)
+#define mci_writeb(dev, reg, value)                     \
+	writeb_relaxed((value), (dev)->regs + SDMMC_##reg)
+
+#define dw_mci_get_int(dev)    \
+	do {    \
+		dev->normal_interrupt = mci_readw(dev, NORMAL_INT_STAT_R);   \
+		dev->error_interrupt = mci_readw(dev, ERROR_INT_STAT_R);   \
+		dev->auto_error_interrupt = mci_readw(dev, AUTO_CMD_STAT_R);     \
+	} while (0)
+
+/*clear status register, we always keep the card interrupt*/
+#define dw_mci_clr_int(dev)                                             \
+	do {                                                            \
+		mci_writew(dev, ERROR_INT_STAT_R, mci_readw(dev, ERROR_INT_STAT_R) & 0xffff); \
+		mci_writew(dev, NORMAL_INT_STAT_R, mci_readw(dev, NORMAL_INT_STAT_R) & 0xffff); \
+	} while (0)
+
+/*mask all emmc interrupts*/
+#define dw_mci_clr_signal_int(dev)    \
+	do {      \
+		mci_writew(dev, NORMAL_INT_SIGNAL_EN_R, \
+			mci_readw(dev, NORMAL_INT_SIGNAL_EN_R) & (BIT(6)|BIT(7))); \
+		mci_writew(dev, ERROR_INT_SIGNAL_EN_R, 0); \
+	} while (0)
+
+/*for cmdq, we do not need cmd and xfer done, only cqe event*/
+#define dw_mci_en_cqe_int(dev)  \
+	do { \
+		mci_writew(dev, NORMAL_INT_SIGNAL_EN_R, \
+			mci_readw(dev, NORMAL_INT_SIGNAL_EN_R)|SDMMC_NORMAL_INT_SIGNAL_CQE_EN_R); \
+		mci_writew(dev, ERROR_INT_SIGNAL_EN_R, SDMMC_ALL_ERR_SIGNAL_EN); \
+	} while (0)
+
+/*used for data, r1b case, we mask cmd done interrupt*/
+#define dw_mci_en_xfer_int(dev)  \
+	do {  \
+		mci_writew(dev, NORMAL_INT_SIGNAL_EN_R, \
+			mci_readw(dev, NORMAL_INT_SIGNAL_EN_R)|SDMMC_NORMAL_INT_SIGNAL_DAT_EN_R); \
+		mci_writew(dev, ERROR_INT_SIGNAL_EN_R, SDMMC_ALL_ERR_SIGNAL_EN); \
+	} while (0)
+
+/*used for none-stream case (cmd w/wo/ resp)*/
+#define dw_mci_en_cd_int(dev)  \
+	do {    \
+		mci_writew(dev, NORMAL_INT_SIGNAL_EN_R, \
+			mci_readw(dev, NORMAL_INT_SIGNAL_EN_R)|SDMMC_NORMAL_INT_SIGNAL_CMD_EN_R); \
+		mci_writew(dev, ERROR_INT_SIGNAL_EN_R, SDMMC_ALL_ERR_SIGNAL_EN); \
+	} while (0)
+
+extern int dw_mci_cqe_probe(struct dw_mci *host);
+extern void dw_mci_cqe_remove(struct dw_mci *host);
+#ifdef CONFIG_PM
+extern int dw_mci_cqe_runtime_suspend(struct device *device);
+extern int dw_mci_cqe_runtime_resume(struct device *device);
+#endif
+irqreturn_t cqhci_irq(struct mmc_host *mmc, u32 intmask, int cmd_error, int data_error);
+
+/* Board platform data */
+struct dw_mci_board {
+	unsigned int bus_hz; /* Clock speed at the cclk_in pad */
+	u32 caps;       /* Capabilities */
+	u32 caps2;      /* More capabilities */
+	u32 pm_caps;    /* PM capabilities */
+
+	/* delay in mS before detecting cards after interrupt */
+	u32 detect_delay_ms;
+
+	struct reset_control *rstc;
+};
+
+struct dw_mci_slot {
+	struct mmc_host         *mmc;
+	struct dw_mci           *host;
+
+	struct mmc_request      *mrq;
+
+	unsigned int            clock;
+	unsigned int            __clk_old;
+
+	unsigned long           flags;
+#define DW_MMC_CARD_PRESENT     1
+	int                     id;
+	int                     sdio_id;
+	u8                      switch_partition;
+};
+
+struct dw_mci_drv_data {
+	unsigned long   *caps;
+	u32             num_caps;
+	int             (*init)(struct dw_mci *host);
+	void            (*set_ios)(struct dw_mci_slot *slot, struct mmc_ios *ios);
+	int             (*parse_dt)(struct dw_mci *host);
+	int             (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode);
+	int             (*prepare_hs400_tuning)(struct dw_mci *host,
+					struct mmc_ios *ios);
+	int             (*switch_voltage)(struct mmc_host *mmc,
+					struct mmc_ios *ios);
+	void            (*hs400_complete)(struct mmc_host *mmc);
+	void		(*init_card)(struct mmc_host *host,
+					struct mmc_card *card);
+	void            (*shift_rsp)(struct dw_mci *host, struct mmc_command *cmd, u32 *rsp);
+};
+
+void dw_mci_cqe_wait_done(struct dw_mci *host, u32 *addr, u32 mask, u32 value);
+#endif