lkml.org 
[lkml]   [2023]   [Oct]   [30]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH V4][3/4] mmc: Add dw mobile mmc cmdq rtk driver
    Date
    Add Realtek mmc driver to make good use Synopsys
    DesignWare mmc cmdq host driver.

    Signed-off-by: Jyan Chou <jyanchou@realtek.com>

    ---
    v3 -> v4:
    - Modify dma setting's code to fix linux coding style.
    - Drop useless function messages.
    - Remove MODULE_ALIAS().

    v0 -> v1:
    - Seperate different support into single patch.
    - Fix the compiler complains.
    ---
    ---
    drivers/mmc/host/Kconfig | 9 +
    drivers/mmc/host/Makefile | 1 +
    drivers/mmc/host/dw_mmc_cqe-rtk.c | 979 ++++++++++++++++++++++++++++++
    drivers/mmc/host/dw_mmc_cqe-rtk.h | 160 +++++
    4 files changed, 1149 insertions(+)
    create mode 100644 drivers/mmc/host/dw_mmc_cqe-rtk.c
    create mode 100644 drivers/mmc/host/dw_mmc_cqe-rtk.h

    diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
    index 894b89a396bb..23544b226b8e 100644
    --- a/drivers/mmc/host/Kconfig
    +++ b/drivers/mmc/host/Kconfig
    @@ -775,6 +775,15 @@ config MMC_DW_CQE

    If unsure, say Y.

    +config MMC_DW_CQE_RTK
    + tristate "Realtek Soc's specific extensions for Synopsys DW Memory Card Interface"
    + depends on MMC_DW_CQE
    + select REGULATOR_FIXED_VOLTAGE
    + help
    + This selects support for Realtek SoC specific extensions to the
    + Synopsys DesignWare Memory Card Interface driver. Select this option
    + for platforms after RTD16xxb SoC's.
    +
    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 23546b7b692e..fe4676202b95 100644
    --- a/drivers/mmc/host/Makefile
    +++ b/drivers/mmc/host/Makefile
    @@ -56,6 +56,7 @@ 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_DW_CQE_RTK) += dw_mmc_cqe-rtk.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-rtk.c b/drivers/mmc/host/dw_mmc_cqe-rtk.c
    new file mode 100644
    index 000000000000..8394e7850591
    --- /dev/null
    +++ b/drivers/mmc/host/dw_mmc_cqe-rtk.c
    @@ -0,0 +1,979 @@
    +// SPDX-License-Identifier: GPL-2.0-or-later
    +/*
    + * Copyright (c) 2023 Realtek Semiconductor Corp.
    + *
    + */
    +
    +#include <linux/clk.h>
    +#include <linux/delay.h>
    +#include <linux/mfd/syscon.h>
    +#include <linux/mmc/core.h>
    +#include <linux/mmc/host.h>
    +#include <linux/mmc/mmc.h>
    +#include <linux/module.h>
    +#include <linux/nvmem-consumer.h>
    +#include <linux/of_address.h>
    +#include <linux/pinctrl/consumer.h>
    +#include <linux/platform_device.h>
    +#include <linux/regmap.h>
    +#include <linux/slab.h>
    +
    +#include "dw_mmc_cqe.h"
    +#include "dw_mmc_cqe-rtk.h"
    +
    +#define DW_MCI_TIMEOUT 200000
    +#define HS400_WINDOW_ALL_PASS 0xffff
    +#define HS200_WINDOW_ALL_PASS 0xffffffff
    +#define hs400_dqs_delay_val 0x88
    +
    +static void dw_mci_rtk_hs400_complete(struct mmc_host *mmc);
    +static int dw_mci_rtk_execute_tuning(struct dw_mci_slot *slot, u32 opcode);
    +
    +static void reset_fifo(struct dw_mci *host)
    +{
    + mcq_writel(host, OTHER1, mcq_readl(host, OTHER1) & (~SDMMC_TOP_RST_N_FIFO));
    + udelay(1);
    + mcq_writel(host, OTHER1, mcq_readl(host, OTHER1) | SDMMC_TOP_RST_N_FIFO);
    +}
    +
    +static int dw_mci_rtk_set_pinstates(struct dw_mci_rtkemmc_host *priv,
    + unsigned char timing)
    +{
    + switch (timing) {
    + case MMC_TIMING_UHS_SDR50:
    + return pinctrl_select_state(priv->pinctrl,
    + priv->pins_sdr50);
    +
    + case MMC_TIMING_UHS_DDR50:
    + return pinctrl_select_state(priv->pinctrl,
    + priv->pins_ddr50);
    + case MMC_TIMING_MMC_HS200:
    + return pinctrl_select_state(priv->pinctrl,
    + priv->pins_hs200);
    +
    + case MMC_TIMING_MMC_HS400:
    + return pinctrl_select_state(priv->pinctrl,
    + priv->pins_hs400);
    + default:
    + return pinctrl_select_state(priv->pinctrl,
    + priv->pins_default);
    + }
    +}
    +
    +static void dqs_delay_tap_setting(struct dw_mci *host,
    + u32 dqs_dly)
    +{
    + u32 regs;
    +
    + regs = mcq_readl(host, DQS_CTRL1) & ~SDMMC_FW_EN;
    + mcq_writel(host, DQS_CTRL1, regs);
    + mcq_writel(host, DQS_CTRL1, dqs_dly);
    +
    + regs = dqs_dly | SDMMC_FW_EN;
    + mcq_writel(host, DQS_CTRL1, regs);
    +}
    +
    +static void cmd_delay_tap_setting(struct dw_mci *host,
    + u32 cmd_dly_tape)
    +{
    + u32 regs;
    +
    + regs = mcq_readl(host, WCMD_CTRL) & ~SDMMC_FW_SET_EN;
    + mcq_writel(host, WCMD_CTRL, regs);
    +
    + if (cmd_dly_tape == 0)
    + return;
    +
    + mcq_writel(host, WCMD_CTRL, cmd_dly_tape);
    +
    + regs = cmd_dly_tape | SDMMC_FW_SET_EN;
    + mcq_writel(host, WCMD_CTRL, regs);
    +}
    +
    +static void data_delay_tap_setting(struct dw_mci *host)
    +{
    + u32 regs;
    + struct dw_mci_rtkemmc_host *priv = host->priv;
    +
    + regs = mcq_readl(host, RDQ_CTRL0) & (~SDMMC_FW_SET);
    + mcq_writel(host, RDQ_CTRL0, regs);
    +
    + if (priv->rdq_ctrl == 0)
    + return;
    +
    + mcq_writel(host, RDQ_CTRL0, priv->rdq_ctrl);
    + mcq_writel(host, RDQ_CTRL1, priv->rdq_ctrl);
    + mcq_writel(host, RDQ_CTRL2, priv->rdq_ctrl);
    + mcq_writel(host, RDQ_CTRL3, priv->rdq_ctrl);
    + mcq_writel(host, RDQ_CTRL4, priv->rdq_ctrl);
    + mcq_writel(host, RDQ_CTRL5, priv->rdq_ctrl);
    + mcq_writel(host, RDQ_CTRL6, priv->rdq_ctrl);
    + mcq_writel(host, RDQ_CTRL7, priv->rdq_ctrl);
    +
    + regs = mcq_readl(host, RDQ_CTRL0) | SDMMC_FW_SET;
    + mcq_writel(host, RDQ_CTRL0, regs);
    +}
    +
    +static void setup_clk_div(struct dw_mci_slot *slot)
    +{
    + struct dw_mci *host = slot->host;
    + unsigned int clock = slot->clock;
    + u32 div = 0;
    +
    + slot->mmc->actual_clock = 0;
    +
    + if (clock != host->current_speed) {
    + 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;
    +
    + /*In RealTek Stark platform, they only support div 1,4, and 512*/
    + if (div > 4)
    + div = 512;
    +
    + switch (div) {
    + case SDMMC_CLK_DIV1:
    + mcq_writel(host, CKGEN_CTL,
    + mcq_readl(host, CKGEN_CTL) & (~SDMMC_CRC_CLK_DIV_EN));
    + break;
    + case SDMMC_CLK_DIV4:
    + mcq_writel(host, CKGEN_CTL,
    + mcq_readl(host, CKGEN_CTL) | SDMMC_CLK_INV_DIV_SEL);
    + mcq_writel(host, CKGEN_CTL,
    + mcq_readl(host, CKGEN_CTL) | SDMMC_CRC_CLK_DIV_EN);
    + break;
    + case SDMMC_CLK_DIV512:
    + mcq_writel(host, CKGEN_CTL,
    + mcq_readl(host, CKGEN_CTL) & (~SDMMC_CLK_INV_DIV_SEL));
    + mcq_writel(host, CKGEN_CTL,
    + mcq_readl(host, CKGEN_CTL) | SDMMC_CRC_CLK_DIV_EN);
    + break;
    + default:
    + dev_info(&slot->mmc->class_dev, "default case div = %d\n", div);
    + if (div != 0)
    + mcq_writel(host, CKGEN_CTL,
    + mcq_readl(host, CKGEN_CTL) & (~SDMMC_CRC_CLK_DIV_EN));
    + break;
    + }
    + }
    +
    + host->current_speed = clock;
    +}
    +
    +static void dw_mci_rtk_phase_tuning(struct dw_mci *host,
    + u32 tx_phase, u32 rx_phase)
    +{
    + struct dw_mci_rtkemmc_host *priv = host->priv;
    + u32 t1 = 10;
    + u32 t2 = 3;
    + int ret;
    + u32 status;
    +
    + clk_disable(host->ciu_clk);
    + udelay(t1);
    +
    + mcq_writel(host, OTHER1, mcq_readl(host, OTHER1) | SDMMC_CLK_DIV_RST);
    +
    + if (!IS_ERR(priv->vp0) && !IS_ERR(priv->vp1)) {
    + if (tx_phase != 0xff)
    + clk_set_phase(priv->vp0, (tx_phase * 360) / 32);
    + if (rx_phase != 0xff)
    + clk_set_phase(priv->vp1, (rx_phase * 360) / 32);
    + }
    +
    + udelay(t2);
    +
    + mcq_writel(host, OTHER1, mcq_readl(host, OTHER1) & ~SDMMC_CLK_DIV_RST);
    +
    + clk_enable(host->ciu_clk);
    +
    + ret = readl_poll_timeout(host->regs + SDMMC_CLK_CTRL_R, status,
    + (status & SDMMC_INTERNAL_CLK_MSK) == 0x3, 10, DW_MCI_TIMEOUT);
    + if (ret)
    + dev_err(host->dev, "Timeout waiting emmc phase tuning.\n");
    +
    + mcq_writeb(host, SW_RST_R, SDMMC_RST_CMD | SDMMC_RST_DAT);
    +
    + ret = readl_poll_timeout(host->regs + SDMMC_CLK_CTRL_R, status,
    + (status & SW_RST_BOTH_DONE) == 0x0, 10, DW_MCI_TIMEOUT);
    + if (ret)
    + dev_err(host->dev, "Timeout waiting phase tuning sw rest\n");
    +
    + reset_fifo(host);
    +}
    +
    +static void adjust_window(struct dw_mci_rtkemmc_host *priv,
    + const char *w_type, unsigned int loop_cnt, unsigned int window)
    +{
    + switch (loop_cnt) {
    + case 10:
    + pr_err("try pad driving 3: %s = 0x%08x\n", w_type, window);
    + pinctrl_select_state(priv->pinctrl, priv->pins_tune3);
    + break;
    + case 20:
    + pr_err("try pad driving 2: %s = 0x%08x\n", w_type, window);
    + pinctrl_select_state(priv->pinctrl, priv->pins_tune2);
    + break;
    + case 30:
    + pr_err("try pad driving 1: %s = 0x%08x\n", w_type, window);
    + pinctrl_select_state(priv->pinctrl, priv->pins_tune1);
    + break;
    + case 40:
    + pr_err("try pad driving 0: %s = 0x%08x\n", w_type, window);
    + pinctrl_select_state(priv->pinctrl, priv->pins_tune0);
    + break;
    + case 60:
    + pr_err("loop cnt %d: %s = 0x%08x, cannot find a proper phase\n",
    + loop_cnt, w_type, window);
    + default:
    + break;
    + }
    +}
    +
    +static int search_best(u32 window, u32 range)
    +{
    + int i = 0, j = 1, k = 0, max = 0;
    + int window_temp[32];
    + int window_start[32];
    + int window_end[32];
    + int window_max = 0;
    + int window_best = 0;
    + int parse_end = 1;
    +
    + for (i = 0; i < 0x20; i++) {
    + window_temp[i] = 0;
    + window_start[i] = 0;
    + window_end[i] = -1;
    + }
    +
    + i = 0;
    +
    + while ((i < (range - 1)) && (k < (range - 1))) {
    + parse_end = 0;
    + for (i = window_end[j - 1] + 1; i < range; i++) {
    + if (((window >> i) & 1) == 1) {
    + window_start[j] = i;
    + break;
    + }
    + }
    + if (i == range)
    + break;
    +
    + for (k = window_start[j] + 1; k < range; k++) {
    + if (((window >> k) & 1) == 0) {
    + window_end[j] = k - 1;
    + parse_end = 1;
    + break;
    + }
    + }
    +
    + if (parse_end == 0)
    + window_end[j] = range - 1;
    + j++;
    + }
    +
    + for (i = 1; i < j; i++)
    + window_temp[i] = window_end[i] - window_start[i] + 1;
    +
    + if ((((window) & 1) == 1) && (((window >> (range - 1)) & 1) == 1)) {
    + window_temp[1] = window_temp[1] + window_temp[j - 1];
    + window_start[1] = window_start[j - 1];
    + }
    +
    + for (i = 1; i < j; i++) {
    + if (window_temp[i] > window_max) {
    + window_max = window_temp[i];
    + max = i;
    + }
    + }
    +
    + if (window == ~0UL)
    + window_best = 0x10;
    + else if ((window == HS400_WINDOW_ALL_PASS) && (range == 0x10))
    + window_best = 0x8;
    + else if ((((window & 1) == 1) && (((window >> (range - 1)) & 1) == 1)) && (max == 1))
    + window_best = (((window_start[max] + window_end[max] +
    + range) / 2) & (range - 1));
    + else
    + window_best = ((window_start[max] + window_end[max]) / 2) & 0x1f;
    +
    + /*We set a criteria for 6 continuous points*/
    + if (window_max > 6)
    + return window_best;
    + else
    + return 0xff;
    +}
    +
    +static int dw_mci_rtk_send_tuning(struct mmc_host *mmc, u32 opcode, u32 arg, int size)
    +{
    + struct mmc_request mrq = {};
    + struct mmc_command cmd = {};
    + struct mmc_data data = {};
    + struct scatterlist sg;
    + int i, err = 0;
    + u8 *data_buf = NULL;
    +
    + cmd.opcode = opcode;
    + cmd.arg = arg;
    +
    + if (opcode == MMC_SEND_STATUS)
    + cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
    + else
    + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
    +
    + mrq.cmd = &cmd;
    + mrq.data = NULL;
    +
    + if (opcode == MMC_WRITE_MULTIPLE_BLOCK || opcode == MMC_READ_MULTIPLE_BLOCK) {
    + data_buf = kzalloc(size, GFP_KERNEL);
    + if (!data_buf)
    + return -ENOMEM;
    +
    + mrq.data = &data;
    +
    + if (opcode == MMC_WRITE_MULTIPLE_BLOCK) {
    + for (i = 0; i < size / 4; i++)
    + *(u32 *)(data_buf + i * 4) = i;
    + data.flags = MMC_DATA_WRITE;
    + } else {
    + data.flags = MMC_DATA_READ;
    + }
    +
    + data.blksz = 512;
    + data.blocks = size / data.blksz;
    +
    + data.timeout_ns = 150 * NSEC_PER_MSEC;
    +
    + data.sg = &sg;
    + data.sg_len = 1;
    + sg_init_one(&sg, data_buf, size);
    + }
    +
    + mmc_wait_for_req(mmc, &mrq);
    +
    + if (cmd.error) {
    + err = cmd.error;
    + goto out;
    + }
    +
    + if (data.error) {
    + err = data.error;
    + goto out;
    + }
    +
    +out:
    + return err;
    +}
    +
    +static int dw_mci_rtk_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
    +{
    + struct dw_mci *host = slot->host;
    + struct dw_mci_rtkemmc_host *priv = host->priv;
    + struct mmc_host *mmc = slot->mmc;
    + u32 tx_window = 0, rx_window = 0;
    + u32 tx_best = 0, rx_best = 0;
    + u32 tx_range, rx_range = 0x20;
    + unsigned int bitmap = 0;
    + unsigned int max = 0;
    + unsigned int loop_cnt = 0;
    + unsigned int reg;
    + int i, j, ret = 0, rsp = 0;
    + bool fail = false;
    + bool dqs_retry = false;
    +
    + if (mmc->doing_retune == 1) {
    + dev_err(mmc_dev(mmc), "Retune case !!!\n");
    + goto out;
    + }
    +
    + ret = dw_mci_rtk_set_pinstates(priv, mmc->ios.timing);
    + if (ret) {
    + dev_err(mmc_dev(mmc),
    + "Failed to set pinstate err=%d\n", ret);
    + ret = -ENODEV;
    + goto out;
    + }
    +
    + host->tuning = 1;
    +
    + do {
    + if (mmc->ios.timing == MMC_TIMING_MMC_HS400)
    + loop_cnt = 0;
    + else if (mmc->ios.timing == MMC_TIMING_MMC_HS200)
    + loop_cnt = 20;
    + else
    + loop_cnt = 30;
    +
    + /* rx tuning */
    + do {
    + for (i = 0; i < rx_range; i++) {
    + dw_mci_rtk_phase_tuning(host, 0xff, i);
    +
    + if (mmc->ios.timing == MMC_TIMING_MMC_HS200)
    + rsp = mmc_send_tuning(mmc,
    + MMC_SEND_TUNING_BLOCK_HS200, NULL);
    + else
    + rsp = dw_mci_rtk_send_tuning(mmc,
    + MMC_SEND_STATUS, 0x10000, 0);
    + if (rsp)
    + rx_window = rx_window & ~BIT(i);
    + else
    + rx_window = rx_window | BIT(i);
    + }
    +
    + if (rx_window == 0) {
    + dev_err(mmc_dev(mmc), "rx_window = 0, cannot find a proper rx phase\n");
    + ret = -EFAULT;
    + goto out;
    + } else if (rx_window == HS200_WINDOW_ALL_PASS) {
    + loop_cnt++;
    + adjust_window(priv, "rx_window", loop_cnt, rx_window);
    + }
    + } while (rx_window == HS400_WINDOW_ALL_PASS && loop_cnt < 60);
    +
    + rx_best = search_best(rx_window, rx_range);
    + if (rx_best == 0xff) {
    + dev_err(mmc_dev(mmc), "no 6 continuous points for rx phase\n");
    + ret = -EFAULT;
    + goto out;
    + }
    +
    + dw_mci_rtk_phase_tuning(host, 0xff, rx_best);
    + dev_info(mmc_dev(mmc), "rx_window = 0x%x, rx_best = 0x%x\n", rx_window, rx_best);
    +
    + /*dqs tuning in HS400 mode*/
    + if (mmc->ios.timing == MMC_TIMING_MMC_HS400) {
    + if (fail)
    + pinctrl_select_state(priv->pinctrl, priv->pins_tune4);
    + else
    + pinctrl_select_state(priv->pinctrl, priv->pins_hs400);
    +
    + /*reset the bitmap, max, and j for each wcmd_rty*/
    + bitmap = 0;
    + max = 0;
    + j = 0;
    +
    + for (i = 0; i < 0x20; i++) {
    + /*criteria is more than 5 continuous tap sample point*/
    + if (j >= 5)
    + max = j;
    + /*find the max tap length*/
    + if (j == 0 && max != 0)
    + break;
    +
    + dqs_delay_tap_setting(host, (i << 1));
    +
    + if (dw_mci_rtk_send_tuning(mmc, MMC_READ_MULTIPLE_BLOCK,
    + 0x100, 1024) != 0) {
    + j = 0;
    + } else {
    + j++;
    + bitmap |= BIT(i);
    + }
    + }
    +
    + if (max == 0) {
    + if (!fail) {
    + dev_err(mmc_dev(mmc), "DQS_RETRY:bitmap= 0x%x\n", bitmap);
    + fail = true;
    + dqs_retry = true;
    + dev_err(mmc_dev(mmc), "try pad driving 7\n");
    + pinctrl_select_state(priv->pinctrl, priv->pins_tune4);
    + } else {
    + ret = -EFAULT;
    + dev_err(mmc_dev(mmc),
    + "No proper dqs window, dqs bitmap=0x%x\n", bitmap);
    + goto out;
    + }
    + } else {
    + reg = mcq_readl(host, DQS_CTRL1) - 2 - ((max / 2) * 2);
    + /*set the dqs dly tape*/
    + dqs_delay_tap_setting(host, reg);
    + dqs_retry = false;
    + dev_info(mmc_dev(mmc), "max sample point = %d, bitmap = 0x%x, DQS = 0x%x\n",
    + max, bitmap, mcq_readl(host, DQS_CTRL1));
    + }
    + }
    + } while (dqs_retry);
    +
    + /*tx tuning */
    + if (mmc->ios.timing == MMC_TIMING_MMC_HS400) {
    + tx_range = 0x10;
    + loop_cnt = 0;
    + if (fail)
    + pinctrl_select_state(priv->pinctrl, priv->pins_tune4);
    + else
    + pinctrl_select_state(priv->pinctrl, priv->pins_hs400);
    + } else if (mmc->ios.timing == MMC_TIMING_MMC_HS200) {
    + tx_range = 0x20;
    + loop_cnt = 20;
    + if (fail)
    + pinctrl_select_state(priv->pinctrl, priv->pins_tune4);
    + else
    + pinctrl_select_state(priv->pinctrl, priv->pins_hs200);
    + } else {
    + tx_range = 0x10;
    + loop_cnt = 30;
    + }
    +
    + do {
    + for (i = 0; i < tx_range; i++) {
    + dw_mci_rtk_phase_tuning(host, i, 0xff);
    + if (dw_mci_rtk_send_tuning(mmc, MMC_WRITE_MULTIPLE_BLOCK, 0xfe, 1024))
    + tx_window = tx_window & ~BIT(i);
    + else
    + tx_window = tx_window | BIT(i);
    + }
    +
    + if (tx_window == 0) {
    + dev_err(mmc_dev(mmc), "tx_window = 0, cannot find a proper tx phase\n");
    + ret = -EFAULT;
    + goto out;
    + } else if ((tx_window == HS400_WINDOW_ALL_PASS &&
    + mmc->ios.timing == MMC_TIMING_MMC_HS400) ||
    + (tx_window == HS200_WINDOW_ALL_PASS &&
    + mmc->ios.timing == MMC_TIMING_MMC_HS200)) {
    + loop_cnt++;
    + adjust_window(priv, "tx_window", loop_cnt, tx_window);
    + }
    + } while (((tx_window == HS400_WINDOW_ALL_PASS && mmc->ios.timing == MMC_TIMING_MMC_HS400) ||
    + (tx_window == HS200_WINDOW_ALL_PASS && mmc->ios.timing == MMC_TIMING_MMC_HS200)) &&
    + loop_cnt < 60);
    +
    + if (fail)
    + pinctrl_select_state(priv->pinctrl, priv->pins_tune4);
    + else if (mmc->ios.timing == MMC_TIMING_MMC_HS400)
    + pinctrl_select_state(priv->pinctrl, priv->pins_hs400);
    + else if (mmc->ios.timing == MMC_TIMING_MMC_HS200)
    + pinctrl_select_state(priv->pinctrl, priv->pins_hs200);
    +
    + tx_best = search_best(tx_window, tx_range);
    + if (tx_best == 0xff) {
    + dev_err(mmc_dev(mmc), "no 6 continuous points for tx phase\n");
    + ret = -EFAULT;
    + goto out;
    + }
    +
    + dw_mci_rtk_phase_tuning(host, tx_best, 0xff);
    + dev_info(mmc_dev(mmc), "tx_window = 0x%x, tx_best = 0x%x\n", tx_window, tx_best);
    +
    +out:
    + /* We send cmd 13 again because the eMMC handling might send command 12 more than twice.
    + * After kernel 5.4, system might send cmd13 first before issuing any command,
    + * user will see the illegal command status because the emmc tuning error handling
    + */
    +
    + dw_mci_rtk_send_tuning(mmc, MMC_SEND_STATUS, 0x10000, 0);
    + host->tuning = 0;
    +
    + return ret;
    +}
    +
    +static void dw_mci_rtk_set_ios(struct dw_mci_slot *slot, struct mmc_ios *ios)
    +{
    + struct dw_mci *host = slot->host;
    + int ret;
    + u32 status;
    +
    + clk_disable(host->ciu_clk);
    + udelay(10);
    +
    + ret = clk_set_rate(host->ciu_clk, ios->clock);
    +
    + if (ret)
    + dev_warn(host->dev, "failed to set rate %uHz\n", ios->clock);
    +
    + host->bus_hz = clk_get_rate(host->ciu_clk);
    +
    + udelay(10);
    + clk_enable(host->ciu_clk);
    +
    + ret = readl_poll_timeout(host->regs + SDMMC_CLK_CTRL_R, status,
    + (status & SDMMC_INTERNAL_CLK_MSK) == 0x3, 10, DW_MCI_TIMEOUT);
    + if (ret)
    + dev_err(host->dev, "Waiting emmc set_ios timeout\n");
    +
    + mcq_writeb(host, SW_RST_R, SDMMC_RST_CMD | SDMMC_RST_DAT);
    +
    + ret = readl_poll_timeout(host->regs + SDMMC_CLK_CTRL_R, status,
    + (status & SW_RST_BOTH_DONE) == 0x0, 10, DW_MCI_TIMEOUT);
    + if (ret)
    + dev_err(host->dev, "Waiting sw reset timeout\n");
    +
    + clk_disable(host->ciu_clk);
    + udelay(6);
    +
    + setup_clk_div(slot);
    +
    + mcq_writel(host, CKGEN_CTL, mcq_readl(host, CKGEN_CTL) & ~SDMMC_CLK_DIV_SEL);
    + udelay(6);
    +
    + clk_enable(host->ciu_clk);
    + udelay(6);
    +
    + ret = readl_poll_timeout(host->regs + SDMMC_CLK_CTRL_R, status,
    + (status & SDMMC_INTERNAL_CLK_MSK) == 0x3, 10, DW_MCI_TIMEOUT);
    + if (ret)
    + dev_err(host->dev, "Waiting emmc set_ios timeout\n");
    +
    + mcq_writeb(host, SW_RST_R, SDMMC_RST_CMD | SDMMC_RST_DAT);
    +
    + ret = readl_poll_timeout(host->regs + SDMMC_CLK_CTRL_R, status,
    + (status & SW_RST_BOTH_DONE) == 0x0, 10, DW_MCI_TIMEOUT);
    + if (ret)
    + dev_err(host->dev, "Waiting sw reset timeout\n");
    +
    + mcq_writel(host, OTHER1, mcq_readl(host, OTHER1) & (~SDMMC_TOP_RST_N_FIFO));
    + udelay(10);
    + mcq_writel(host, OTHER1, mcq_readl(host, OTHER1) | SDMMC_TOP_RST_N_FIFO);
    + udelay(10);
    +}
    +
    +static int dw_mci_rtk_prepare_hs400_tuning(struct dw_mci *host, struct mmc_ios *ios)
    +{
    + dqs_delay_tap_setting(host, hs400_dqs_delay_val);
    +
    + data_delay_tap_setting(host);
    +
    + cmd_delay_tap_setting(host, 0);
    +
    + return 0;
    +}
    +
    +static void dw_mci_rtk_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;
    + struct dw_mci_rtkemmc_host *priv = host->priv;
    + int err = -EINVAL;
    +
    + if (drv_data && drv_data->execute_tuning)
    + err = drv_data->execute_tuning(slot,
    + MMC_SEND_TUNING_BLOCK_HS200);
    +
    + if (err != 0) {
    + dev_err(mmc_dev(mmc), "HS400 tuning failed and down speed to hs200, err=%d\n", err);
    + host->pdata->caps &= ~MMC_CAP_1_8V_DDR;
    + host->pdata->caps2 &= ~MMC_CAP2_HS400_1_8V;
    + mmc->card->mmc_avail_type &= ~EXT_CSD_CARD_TYPE_HS400_1_8V;
    + priv->emmc_mode = 2;
    + mmc_hw_reset(mmc->card);
    + }
    +}
    +
    +static void dw_mci_rtk_init_card(struct mmc_host *host, struct mmc_card *card)
    +{
    + /* In Realtek Platform, we need to attach eMMC card onto mmc host
    + * during eMMC initialization because of the following reason:
    + * When system cannot run the hs400, we need to down speed to hs200
    + * and call mmc_hw_reset and modify the mmc card attribute through mmc host.
    + * At this moment, system will show errors if host->card = NULL.
    + */
    + host->card = card;
    +}
    +
    +static int dw_mci_rtk_parse_dt(struct dw_mci *host)
    +{
    + struct dw_mci_rtkemmc_host *priv;
    + const u32 *prop;
    + int size;
    +
    + priv = devm_kzalloc(host->dev, sizeof(struct dw_mci_rtkemmc_host), GFP_KERNEL);
    + if (!priv)
    + return -ENOMEM;
    +
    + priv->pinctrl = devm_pinctrl_get(host->dev);
    + if (IS_ERR(priv->pinctrl))
    + dev_dbg(host->dev, "no pinctrl\n");
    +
    + priv->pins_default = pinctrl_lookup_state(priv->pinctrl,
    + PINCTRL_STATE_DEFAULT);
    + if (IS_ERR(priv->pins_default))
    + dev_warn(host->dev, "could not get default state\n");
    +
    + priv->pins_sdr50 = pinctrl_lookup_state(priv->pinctrl,
    + "sdr50");
    + if (IS_ERR(priv->pins_sdr50))
    + dev_warn(host->dev, "could not get sdr50 state\n");
    +
    + priv->pins_hs200 = pinctrl_lookup_state(priv->pinctrl,
    + "hs200");
    + if (IS_ERR(priv->pins_hs200))
    + dev_warn(host->dev, "could not get hs200 state\n");
    +
    + priv->pins_hs400 = pinctrl_lookup_state(priv->pinctrl,
    + "hs400");
    + if (IS_ERR(priv->pins_hs400))
    + dev_warn(host->dev, "could not get hs400 state\n");
    +
    + priv->pins_tune0 = pinctrl_lookup_state(priv->pinctrl,
    + "tune0");
    + if (IS_ERR(priv->pins_tune0))
    + dev_warn(host->dev, "could not get tune0 state\n");
    +
    + priv->pins_tune1 = pinctrl_lookup_state(priv->pinctrl,
    + "tune1");
    + if (IS_ERR(priv->pins_tune1))
    + dev_warn(host->dev, "could not get tune1 state\n");
    +
    + priv->pins_tune2 = pinctrl_lookup_state(priv->pinctrl,
    + "tune2");
    + if (IS_ERR(priv->pins_tune2))
    + dev_warn(host->dev, "could not get tune2 state\n");
    +
    + priv->pins_tune3 = pinctrl_lookup_state(priv->pinctrl,
    + "tune3");
    + if (IS_ERR(priv->pins_tune3))
    + dev_warn(host->dev, "could not get tune3 state\n");
    +
    + priv->pins_tune4 = pinctrl_lookup_state(priv->pinctrl,
    + "tune4");
    +
    + if (IS_ERR(priv->pins_tune4))
    + dev_warn(host->dev, "could not get tune4 state\n");
    +
    + priv->vp0 = devm_clk_get(host->dev, "vp0");
    + if (IS_ERR(priv->vp0))
    + dev_err(host->dev, "could not get vp0 clk\n");
    +
    + priv->vp1 = devm_clk_get(host->dev, "vp1");
    + if (IS_ERR(priv->vp1))
    + dev_err(host->dev, "could not get vp1 clk\n");
    +
    + priv->emmc_mode = 0;
    + prop = of_get_property(host->dev->of_node, "speed-step", &size);
    + if (prop) {
    + priv->emmc_mode = of_read_number(prop, 1);
    + dev_info(host->dev, "emmc mode : %d\n", priv->emmc_mode);
    + } else {
    + dev_info(host->dev, "use default emmc sdr50 mode !\n");
    + }
    +
    + priv->is_cqe = 0;
    + prop = of_get_property(host->dev->of_node, "cqe", &size);
    + if (prop) {
    + priv->is_cqe = of_read_number(prop, 1);
    + dev_info(host->dev, "cmdq mode : %d\n", priv->is_cqe);
    + } else {
    + dev_info(host->dev, "use default eMMC legacy mode !\n");
    + }
    +
    + prop = of_get_property(host->dev->of_node, "rdq-ctrl", &size);
    + if (prop) {
    + priv->rdq_ctrl = of_read_number(prop, 1);
    + dev_info(host->dev, "get rdq-ctrl : %u\n", priv->rdq_ctrl);
    + } else {
    + priv->rdq_ctrl = 0;
    + dev_info(host->dev, "no dqs_dly_tape switch node, use default 0x0 !!\n");
    + }
    +
    + priv->m2tmx = syscon_regmap_lookup_by_phandle(host->dev->of_node, "realtek,m2tmx");
    + if (IS_ERR_OR_NULL(priv->m2tmx))
    + dev_err(host->dev, "can not get m2mtx node.\n");
    +
    + host->priv = priv;
    +
    + return 0;
    +}
    +
    +static int dw_mci_rtk_init(struct dw_mci *host)
    +{
    + struct dw_mci_rtkemmc_host *priv = host->priv;
    +
    + host->pdata->caps2 = MMC_CAP2_NO_SDIO | MMC_CAP2_NO_SD;
    +
    + if (priv->emmc_mode >= 2)
    + host->pdata->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
    + if (priv->emmc_mode >= 3) {
    + host->pdata->caps |= MMC_CAP_1_8V_DDR;
    + host->pdata->caps2 |= MMC_CAP2_HS400_1_8V;
    + }
    +
    + if (priv->is_cqe > 0)
    + host->pdata->caps2 |= (MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD);
    +
    + host->irq_flags = IRQF_SHARED;
    +
    + mcq_writel(host, CP, 0x0);
    +
    + /*Enable L4 gated*/
    + mcq_writel(host, OTHER1, mcq_readl(host, OTHER1) &
    + ~(SDMMC_L4_GATED_DIS | SDMMC_L4_GATED_DIS1));
    +
    + mcq_writel(host, OTHER1, mcq_readl(host, OTHER1) &
    + (~(SDMMC_DQS_CTRL_GATE_DIS | SDMMC_DBUS_MAS_GATING_DIS)));
    +
    + /*Set the eMMC wrapper little Endian*/
    + mcq_writel(host, AHB, mcq_readl(host, AHB) | SDMMC_AHB_BIG);
    +
    + mcq_writel(host, OTHER1,
    + mcq_readl(host, OTHER1) | SDMMC_STARK_CARD_STOP_ENABLE);
    +
    + /*set eMMC instead of nand*/
    + regmap_update_bits_base(priv->m2tmx, SDMMC_NAND_DMA_SEL,
    + SDMMC_SRAM_DMA_SEL, SDMMC_SRAM_DMA_SEL, NULL, false, true);
    +
    + /*Set the clk initial phase*/
    + dw_mci_rtk_phase_tuning(host, 0, 0);
    +
    + return 0;
    +}
    +
    +#ifdef CONFIG_PM
    +static int dw_mci_rtk_suspend(struct device *dev)
    +{
    + struct dw_mci *host = dev_get_drvdata(dev);
    + int ret = 0;
    +
    + ret = dw_mci_cqe_runtime_suspend(dev);
    + mcq_writel(host, AHB, 0);
    +
    + return ret;
    +}
    +
    +static int dw_mci_rtk_resume(struct device *dev)
    +{
    + struct dw_mci *host = dev_get_drvdata(dev);
    + int ret = 0;
    +
    + mcq_writel(host, AHB, mcq_readl(host, AHB) | SDMMC_AHB_BIG);
    + ret = dw_mci_cqe_runtime_resume(dev);
    +
    + return ret;
    +}
    +#else
    +static int dw_mci_rtk_suspend(struct device *dev)
    +{
    + dev_info(dev, "User should enable CONFIG_PM kernel config\n");
    +
    + return 0;
    +}
    +
    +static int dw_mci_rtk_resume(struct device *dev)
    +{
    + dev_info(dev, "User should enable CONFIG_PM kernel config\n");
    +
    + return 0;
    +}
    +#endif /*CONFIG_PM*/
    +static const struct dev_pm_ops rtk_dev_pm_ops = {
    + SET_SYSTEM_SLEEP_PM_OPS(dw_mci_rtk_suspend,
    + dw_mci_rtk_resume)
    + SET_RUNTIME_PM_OPS(dw_mci_cqe_runtime_suspend,
    + dw_mci_cqe_runtime_resume,
    + NULL)
    +};
    +
    +static void dw_mci_rtk_shutdown(struct platform_device *pdev)
    +{
    + dev_info(&pdev->dev, "[eMMC] Shutdown\n");
    + dw_mci_cqe_runtime_resume(&pdev->dev);
    +}
    +
    +static unsigned long dw_mci_rtk_dwmmc_caps[1] = {
    + MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA |
    + MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
    + MMC_CAP_NONREMOVABLE | MMC_CAP_CMD23,
    +};
    +
    +static const struct dw_mci_drv_data rtk_drv_data = {
    + .caps = dw_mci_rtk_dwmmc_caps,
    + .num_caps = ARRAY_SIZE(dw_mci_rtk_dwmmc_caps),
    + .set_ios = dw_mci_rtk_set_ios,
    + .execute_tuning = dw_mci_rtk_execute_tuning,
    + .parse_dt = dw_mci_rtk_parse_dt,
    + .init = dw_mci_rtk_init,
    + .prepare_hs400_tuning = dw_mci_rtk_prepare_hs400_tuning,
    + .hs400_complete = dw_mci_rtk_hs400_complete,
    + .init_card = dw_mci_rtk_init_card,
    +};
    +
    +static const struct of_device_id dw_mci_rtk_match[] = {
    + { .compatible = "realtek,rtd1325-dw-cqe-emmc",
    + .data = &rtk_drv_data },
    + {},
    +};
    +MODULE_DEVICE_TABLE(of, dw_mci_rtk_match);
    +
    +int dw_mci_cqe_pltfm_register(struct platform_device *pdev,
    + const struct dw_mci_drv_data *drv_data)
    +{
    + struct dw_mci *host;
    + struct resource *regs;
    +
    + host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
    + if (!host)
    + return -ENOMEM;
    +
    + host->irq = platform_get_irq(pdev, 0);
    + if (host->irq < 0)
    + return host->irq;
    +
    + host->drv_data = drv_data;
    + host->pdev = pdev;
    + host->dev = &pdev->dev;
    + host->irq_flags = 0;
    + host->pdata = pdev->dev.platform_data;
    +
    + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    + host->regs = devm_ioremap_resource(&pdev->dev, regs);
    + if (IS_ERR(host->regs))
    + return PTR_ERR(host->regs);
    +
    + /* Get registers' physical base address */
    + host->phy_regs = regs->start;
    +
    + platform_set_drvdata(pdev, host);
    +
    + return dw_mci_cqe_probe(host);
    +}
    +
    +static int dw_mci_rtk_probe(struct platform_device *pdev)
    +{
    + const struct dw_mci_drv_data *drv_data;
    + const struct of_device_id *match;
    +
    + if (!pdev->dev.of_node)
    + return -ENODEV;
    +
    + match = of_match_node(dw_mci_rtk_match, pdev->dev.of_node);
    + drv_data = match->data;
    +
    + return dw_mci_cqe_pltfm_register(pdev, drv_data);
    +}
    +
    +int dw_mci_rtk_remove(struct platform_device *pdev)
    +{
    + struct dw_mci *host = platform_get_drvdata(pdev);
    +
    + dw_mci_cqe_remove(host);
    + return 0;
    +}
    +
    +static struct platform_driver dw_mci_rtk_pltfm_driver = {
    + .probe = dw_mci_rtk_probe,
    + .remove = dw_mci_rtk_remove,
    + .driver = {
    + .name = "dwmmc_cqe_rtk",
    + .of_match_table = dw_mci_rtk_match,
    + .pm = &rtk_dev_pm_ops,
    + },
    + .shutdown = dw_mci_rtk_shutdown,
    +};
    +
    +module_platform_driver(dw_mci_rtk_pltfm_driver);
    +
    +MODULE_AUTHOR("<jyanchou@realtek.com>");
    +MODULE_DESCRIPTION("Specific Driver Extension");
    +MODULE_LICENSE("GPL");
    diff --git a/drivers/mmc/host/dw_mmc_cqe-rtk.h b/drivers/mmc/host/dw_mmc_cqe-rtk.h
    new file mode 100644
    index 000000000000..6776f9af05a3
    --- /dev/null
    +++ b/drivers/mmc/host/dw_mmc_cqe-rtk.h
    @@ -0,0 +1,160 @@
    +/* SPDX-License-Identifier: GPL-2.0-or-later */
    +/*
    + * Copyright (C) 2013 Realtek Semiconductors, All Rights Reserved.
    + *
    + */
    +
    +#ifndef __DW_RTK_MMC_CQE_H
    +#define __DW_RTK_MMC_CQE_H
    +
    +#define SDMMC_CP 0x41c
    +#define SDMMC_OTHER1 0x420
    +#define SDMMC_DUMMY_SYS 0x42c
    +#define SDMMC_AHB 0x430
    +#define SDMMC_CKGEN_CTL 0x478
    +#define SDMMC_DQS_CTRL1 0x498
    +#define SDMMC_IP_DESC0 0x4a0
    +#define SDMMC_IP_DESC1 0x4a4
    +#define SDMMC_IP_DESC2 0x4a8
    +#define SDMMC_IP_DESC3 0x4ac
    +#define SDMMC_DQ_CTRL_SET 0x50c
    +#define SDMMC_WDQ_CTRL0 0x510
    +#define SDMMC_WDQ_CTRL1 0x514
    +#define SDMMC_WDQ_CTRL2 0x518
    +#define SDMMC_WDQ_CTRL3 0x51c
    +#define SDMMC_WDQ_CTRL4 0x520
    +#define SDMMC_WDQ_CTRL5 0x524
    +#define SDMMC_WDQ_CTRL6 0x528
    +#define SDMMC_WDQ_CTRL7 0x52c
    +#define SDMMC_RDQ_CTRL0 0x530
    +#define SDMMC_RDQ_CTRL1 0x534
    +#define SDMMC_RDQ_CTRL2 0x538
    +#define SDMMC_RDQ_CTRL3 0x53c
    +#define SDMMC_RDQ_CTRL4 0x540
    +#define SDMMC_RDQ_CTRL5 0x544
    +#define SDMMC_RDQ_CTRL6 0x548
    +#define SDMMC_RDQ_CTRL7 0x54c
    +#define SDMMC_CMD_CTRL_SET 0x550
    +#define SDMMC_WCMD_CTRL 0x554
    +#define SDMMC_RCMD_CTRL 0x558
    +#define SDMMC_PLL_STATUS 0x55c
    +
    +#define SDMMC_NAND_DMA_SEL 0x54
    +#define SDMMC_SRAM_DMA_SEL BIT(0)
    +
    +#define SDMMC_CLK_O_ICG_EN BIT(3)
    +#define SDMMC_CARD_STOP_ENABLE BIT(23)
    +#define SDMMC_STARK_CARD_STOP_ENABLE BIT(11)
    +#define SDMMC_TOP_RST_N_FIFO BIT(3)
    +
    +#define SDMMC_FW_SET BIT(7)
    +#define SDMMC_FW_SET_CMD_W BIT(0)
    +
    +#define SDMMC_CLK4M ((BIT(0) | BIT(1) | BIT(2)) << EMMC_CRC_CLK_CHANGE_SHIFT)
    +#define SDMMC_CRC_CLK_DIV (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | \
    + BIT(5) | BIT(6) | BIT(7))
    +#define SDMMC_CRC_CLK_DIV_MASK (~SDMMC_CRC_CLK_DIV)
    +#define SDMMC_CRC_CLK_DIV_EN BIT(8)
    +#define SDMMC_CLK_INV_DIV_SEL BIT(9)
    +#define SDMMC_SSC_CLK_DIV_SEL (BIT(19) | BIT(20))
    +
    +#define SDMMC_CLK_DIV512 (0x200)
    +#define SDMMC_CLK_DIV4 (0x4)
    +#define SDMMC_CLK_DIV1 (0x1)
    +#define SDMMC_INTERNAL_CLK_MSK (0x3)
    +
    +/*0x2C CLK_CTRL_R*/
    +#define SDMMC_INTERNAL_CLK_STABLE BIT(1)
    +#define SDMMC_INTERNAL_CLK_EN BIT(0)
    +
    +/*0x2f SW_RST_R*/
    +#define SDMMC_RST_DAT BIT(2)
    +#define SDMMC_RST_CMD BIT(1)
    +
    +/*0x420 OTHER1*/
    +#define SDMMC_DBUS_MAS_GATING_DIS BIT(13)
    +#define SDMMC_DQS_CTRL_GATE_DIS BIT(12)
    +#define SDMMC_CLK_DIV_RST BIT(10)
    +#define SDMMC_L4_GATED_DIS1 BIT(2)
    +#define SDMMC_L4_GATED_DIS BIT(0)
    +
    +/*0x430 */
    +#define SDMMC_AHB_BIG BIT(2)
    +
    +/*0x478 */
    +#define SDMMC_CLK_DIV_SEL BIT(20)
    +
    +/*0x498 */
    +#define SDMMC_FW_EN BIT(8)
    +
    +/*0x554 */
    +#define SDMMC_FW_SET_EN BIT(7)
    +
    +struct backupRegs {
    + u32 sdmasa_r; /*0x000*/
    + u16 blocksize_r; /*0x004*/
    + u16 blockcount_r; /*0x006*/
    + u16 xfer_mode_r; /*0x00c*/
    + u8 host_ctrl1_r; /*0x028*/
    + u8 pwr_ctrl_r; /*0x029*/
    + u8 bgap_ctrl_r; /*0x02a*/
    + u16 clk_ctrl_r; /*0x02c*/
    + u8 tout_ctrl_r; /*0x02e*/
    +
    + u16 normal_int_stat_en_r; /*0x034*/
    + u16 error_int_stat_en_r; /*0x036*/
    + u16 normal_int_signal_en_r; /*0x038*/
    + u16 error_int_signal_en_r; /*0x03a*/
    + u16 auto_cmd_stat_r; /*0x03c*/
    + u16 host_ctrl2_r; /*0x03e*/
    + u32 adma_sa_low_r; /*0x058*/
    + u8 mshc_ctrl_r; /*0x208*/
    + u8 ctrl_r; /*0x22c*/
    + u32 other1; /*0x420*/
    + u32 dummy_sys; /*0x42c*/
    + u32 dqs_ctrl1; /*0x498*/
    + u32 wcmd_ctrl; /*0x554*/
    +
    + u32 rdq_ctrl0; /*0x530*/
    + u32 rdq_ctrl1; /*0x534*/
    + u32 rdq_ctrl2; /*0x538*/
    + u32 rdq_ctrl3; /*0x53c*/
    + u32 rdq_ctrl4; /*0x540*/
    + u32 rdq_ctrl5; /*0x544*/
    + u32 rdq_ctrl6; /*0x548*/
    + u32 rdq_ctrl7; /*0x54c*/
    + u32 dq_ctrl_set; /*0x50c*/
    + u32 ahb;
    + u32 ckgen_ctl;
    +};
    +
    +struct dw_mci_rtkemmc_host {
    + struct pinctrl *pinctrl;
    + struct pinctrl_state *pins_default;
    + struct pinctrl_state *pins_sdr50;
    + struct pinctrl_state *pins_ddr50;
    + struct pinctrl_state *pins_hs200;
    + struct pinctrl_state *pins_hs400;
    + struct pinctrl_state *pins_tune0;
    + struct pinctrl_state *pins_tune1;
    + struct pinctrl_state *pins_tune2;
    + struct pinctrl_state *pins_tune3;
    + struct pinctrl_state *pins_tune4;
    + struct regmap *m2tmx;
    + struct clk *vp0;
    + struct clk *vp1;
    + struct backupRegs gRegTbl;
    + int emmc_mode;
    + unsigned int rdq_ctrl;
    + unsigned int is_cqe;
    +};
    +
    +int mmc_cmdq_enable(struct mmc_card *card);
    +int mmc_cmdq_disable(struct mmc_card *card);
    +int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
    + unsigned int timeout_ms);
    +int __mmc_claim_host(struct mmc_host *host, struct mmc_ctx *ctx,
    + atomic_t *abort);
    +void mmc_release_host(struct mmc_host *host);
    +
    +#endif
    --
    2.42.0
    \
     
     \ /
      Last update: 2023-10-30 07:30    [W:6.859 / U:0.040 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site