public inbox for devicetree@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v1 0/2] Add driver support for ESWIN eic7700 SoC sdhci controller
@ 2025-05-16  9:12 dongxuyang
  2025-05-16  9:16 ` [PATCH v1 1/2] dt-bindings: sdhci: eswin: Documentation for eic7700 SoC dongxuyang
  2025-05-16  9:17 ` [PATCH v1 2/2] sdhci: eswin: Add eic7700 sdhci driver dongxuyang
  0 siblings, 2 replies; 8+ messages in thread
From: dongxuyang @ 2025-05-16  9:12 UTC (permalink / raw)
  To: ulf.hansson, robh, krzk+dt, conor+dt, linux-mmc, devicetree,
	linux-kernel, adrian.hunter, p.zabel, shanchun1218
  Cc: ningyu, linmin, xuxiang, Xuyang Dong

From: Xuyang Dong <dongxuyang@eswincomputing.com>

	Add support for the sdhci-emmc and sdhci-sdio functionality in the Linux
	kernel. The driver provides basic functionality of emmc and sdio for the eic7700
	series chips, which are part of the	Eswin SoC family.

	Features:
	 Implement support for the ESWIN eic7700 SoC sdhci-emmc controller and
	 sdhci-sdio controller. Integrate with the Linux sdhci subsystem for consistency and
	 scalability.

	Supported chips:
	 ESWIN eic7700 series SoC.

	Test:
	 Test this patch on the Sifive HiFive Premier P550 (which uses the EIC7700 SoC),
	 including emmc and sdio peripherals. Perform read, write and erase tests on emmc.
	 Read and write tests after mounting the file system. Verification of kernel support
	 for emmc device. So this verifies that sdhci driver patch is working properly.

Xuyang Dong (2):
  dt-bindings: sdhci: eswin: Documentation for eic7700 SoC
  sdhci: eswin: Add eic7700 sdhci driver

 .../bindings/mmc/eswin,sdhci-eic7700.yaml     |  131 ++
 drivers/mmc/host/Kconfig                      |   47 +
 drivers/mmc/host/Makefile                     |    4 +-
 drivers/mmc/host/sdhci-eic7700.c              |  353 ++++++
 drivers/mmc/host/sdhci-eic7700.h              |  237 ++++
 drivers/mmc/host/sdhci-of-eic7700-sdio.c      |  991 ++++++++++++++++
 drivers/mmc/host/sdhci-of-eic7700.c           | 1053 +++++++++++++++++
 7 files changed, 2816 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/mmc/eswin,sdhci-eic7700.yaml
 create mode 100644 drivers/mmc/host/sdhci-eic7700.c
 create mode 100644 drivers/mmc/host/sdhci-eic7700.h
 create mode 100644 drivers/mmc/host/sdhci-of-eic7700-sdio.c
 create mode 100644 drivers/mmc/host/sdhci-of-eic7700.c

--
2.17.1


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v1 1/2] dt-bindings: sdhci: eswin: Documentation for eic7700 SoC
  2025-05-16  9:12 [PATCH v1 0/2] Add driver support for ESWIN eic7700 SoC sdhci controller dongxuyang
@ 2025-05-16  9:16 ` dongxuyang
  2025-05-16 10:26   ` Rob Herring (Arm)
  2025-05-16 13:12   ` Krzysztof Kozlowski
  2025-05-16  9:17 ` [PATCH v1 2/2] sdhci: eswin: Add eic7700 sdhci driver dongxuyang
  1 sibling, 2 replies; 8+ messages in thread
From: dongxuyang @ 2025-05-16  9:16 UTC (permalink / raw)
  To: ulf.hansson, robh, krzk+dt, conor+dt, linux-mmc, devicetree,
	linux-kernel, adrian.hunter, p.zabel, shanchun1218
  Cc: ningyu, linmin, xuxiang, Xuyang Dong

From: Xuyang Dong <dongxuyang@eswincomputing.com>

Add device tree binding documentation for the ESWIN
eic7700 sdhci controller module.

Signed-off-by: Xiang Xu <xuxiang@eswincomputing.com>
Signed-off-by: Xuyang Dong <dongxuyang@eswincomputing.com>
---
 .../bindings/mmc/eswin,sdhci-eic7700.yaml     | 131 ++++++++++++++++++
 1 file changed, 131 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mmc/eswin,sdhci-eic7700.yaml

diff --git a/Documentation/devicetree/bindings/mmc/eswin,sdhci-eic7700.yaml b/Documentation/devicetree/bindings/mmc/eswin,sdhci-eic7700.yaml
new file mode 100644
index 000000000000..d4826f2aa619
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/eswin,sdhci-eic7700.yaml
@@ -0,0 +1,131 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mmc/eswin,sdhci-eic7700.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ESWIN EIC7700 SoC SDHCI controller
+
+maintainers:
+  - Shuang Liang <liangshuang@eswincomputing.com>
+  - Xuyang Dong <dongxuyang@eswincomputing.com>
+
+allOf:
+  - $ref: /schemas/mmc/mmc-controller.yaml#
+
+properties:
+  compatible:
+    const: eswin,eic7700-emmc-sdhci
+
+  reg:
+    maxItems: 1
+    description: Common configuration registers
+  "#address-cells":
+    const: 1
+  "#size-cells":
+    const: 1
+
+  interrupts:
+    maxItems: 1
+
+  assigned-clocks:
+    maxItems: 1
+    description: should contain "core_clk" phandle + clock-specifier pairs.
+
+  assigned-clock-rates:
+    maxItems: 1
+
+  clocks:
+    minItems: 2
+    description: handles to clock for the sdhci controller.
+
+  clock-names:
+    minItems: 2
+    description: the name of each clock.
+
+  clock-output-names:
+    maxItems: 1
+
+  '#clock-cells':
+    enum: [0]
+    description:
+      With this property in place we will export one clock
+      representing the Card Clock. These clocks are expected to be
+      consumed by our PHY.
+
+  resets:
+    description: resets to be used by the controller.
+
+  reset-names:
+    description: names of the resets listed in resets property in the same order.
+
+  bus-width:
+    enum: [4, 8]
+    description: for emmc bus-width is 8, for sdio bus-width is 4.
+
+  eswin,hsp_sp_csr:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    description: hsp_sp_csr regs to be used by the controller.
+    items:
+      - description: phandle to HSP_SP_CSR register block
+      - description: status register offset
+      - description: control register offset
+      - description: configuration register offset
+
+  eswin,syscrg_csr:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    description: syscrg_csr regs to be used by the controller.
+    items:
+      - description: phandle to SYS_CRG_CSR register block
+      - description: status register offset
+      - description: control register offset
+      - description: configuration register offset
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - assigned-clocks
+  - assigned-clock-rates
+  - clocks
+  - clock-names
+  - clock-output-names
+  - resets
+  - reset-names
+  - bus-width
+  - eswin,hsp_sp_csr
+  - eswin,syscrg_csr
+
+unevaluatedProperties: false
+
+examples:
+  - |
+
+    sdhci_emmc: mmc@50450000 {
+      compatible = "eswin,eic7700-emmc-sdhci";
+      reg = <0x50450000 0x10000>;
+      interrupt-parent = <&plic>;
+      interrupts = <79>;
+      clocks = <&clock 554>, <&clock 546>;
+      clock-names = "clk_xin", "clk_ahb";
+      assigned-clocks = <&clock 554>;
+      assigned-clock-rates = <200000000>;
+      clock-output-names = "emmc_cardclock";
+      #clock-cells = <0>;
+
+      resets = <&reset 7 (1 << 6)>,
+        <&reset 7 (1 << 3)>,
+        <&reset 7 (1 << 19)>,
+        <&reset 7 (1 << 23)>;
+      reset-names = "txrx_rst", "phy_rst", "prstn", "arstn";
+
+      core-clk-reg = <0x51828160>;
+      disable-cqe-dcmd;
+      bus-width = <8>;
+      non-removable;
+      mmc-hs400-1_8v;
+      max-frequency = <200000000>;
+      eswin,hsp_sp_csr = <&hsp_sp_csr 0x1038 0x508 0x50c>;
+      eswin,syscrg_csr = <&sys_crg 0x160 0x148 0x14c>;
+      status = "disabled";
+    };
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v1 2/2] sdhci: eswin: Add eic7700 sdhci driver
  2025-05-16  9:12 [PATCH v1 0/2] Add driver support for ESWIN eic7700 SoC sdhci controller dongxuyang
  2025-05-16  9:16 ` [PATCH v1 1/2] dt-bindings: sdhci: eswin: Documentation for eic7700 SoC dongxuyang
@ 2025-05-16  9:17 ` dongxuyang
  2025-05-16 13:17   ` Krzysztof Kozlowski
                     ` (2 more replies)
  1 sibling, 3 replies; 8+ messages in thread
From: dongxuyang @ 2025-05-16  9:17 UTC (permalink / raw)
  To: ulf.hansson, robh, krzk+dt, conor+dt, linux-mmc, devicetree,
	linux-kernel, adrian.hunter, p.zabel, shanchun1218
  Cc: ningyu, linmin, xuxiang, Xuyang Dong

From: Xuyang Dong <dongxuyang@eswincomputing.com>

Add support for sdhci controller in eic7700 series chips.
Provide functionality of sdhci-emmc and sdhci-sdio on the chip.

Signed-off-by: Xiang Xu <xuxiang@eswincomputing.com>
Signed-off-by: Xuyang Dong <dongxuyang@eswincomputing.com>
---
 drivers/mmc/host/Kconfig                 |   47 +
 drivers/mmc/host/Makefile                |    4 +-
 drivers/mmc/host/sdhci-eic7700.c         |  353 ++++++++
 drivers/mmc/host/sdhci-eic7700.h         |  237 +++++
 drivers/mmc/host/sdhci-of-eic7700-sdio.c |  991 ++++++++++++++++++++
 drivers/mmc/host/sdhci-of-eic7700.c      | 1053 ++++++++++++++++++++++
 6 files changed, 2685 insertions(+), 1 deletion(-)
 create mode 100644 drivers/mmc/host/sdhci-eic7700.c
 create mode 100644 drivers/mmc/host/sdhci-eic7700.h
 create mode 100644 drivers/mmc/host/sdhci-of-eic7700-sdio.c
 create mode 100644 drivers/mmc/host/sdhci-of-eic7700.c

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 264e11fa58ea..c1be4b6e1411 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -160,6 +160,53 @@ config MMC_SDHCI_PLTFM
 
 	  If unsure, say N.
 
+config MMC_EIC7700
+	tristate "support for the EIC7700 controllers"
+	depends on MMC_SDHCI_PLTFM
+	depends on OF
+	depends on COMMON_CLK
+	depends on ARCH_ESWIN || COMPILE_TEST
+	select MMC_CQHCI
+	help
+	  This selects the EIC7700 Secure Digital Host Controller Interface
+	  (SDHCI).
+
+	  If you have a controller with this interface, say Y or M here.
+
+	  If unsure, say N.
+
+config MMC_SDHCI_OF_EIC7700
+	tristate "SDHCI OF support for the EIC7700 SDHCI controllers"
+	depends on MMC_SDHCI_PLTFM
+	depends on OF
+	depends on COMMON_CLK
+	depends on ARCH_ESWIN || COMPILE_TEST
+	depends on MMC_EIC7700
+	select MMC_CQHCI
+	help
+	  This selects the EIC7700 Secure Digital Host Controller Interface
+	  (SDHCI).
+
+	  If you have a controller with this interface, say Y or M here.
+
+	  If unsure, say N.
+
+config MMC_SDHCI_OF_SDIO_EIC7700
+	tristate "SDHCI OF SDIO support for the EIC7700 SDHCI controllers"
+	depends on MMC_SDHCI_PLTFM
+	depends on OF
+	depends on COMMON_CLK
+	depends on ARCH_ESWIN || COMPILE_TEST
+	depends on MMC_EIC7700
+	select MMC_CQHCI
+	help
+	 This selects the EIC7700 Secure Digital Host Controller Interface
+	 (SDHCI-SDIO).
+
+	 If you have a controller with this interface, say Y or M here.
+
+	 If unsure, say N.
+
 config MMC_SDHCI_OF_ARASAN
 	tristate "SDHCI OF support for the Arasan SDHCI controllers"
 	depends on MMC_SDHCI_PLTFM
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 5147467ec825..4ba3962f90b8 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -105,7 +105,9 @@ cqhci-y					+= cqhci-core.o
 cqhci-$(CONFIG_MMC_CRYPTO)		+= cqhci-crypto.o
 obj-$(CONFIG_MMC_HSQ)			+= mmc_hsq.o
 obj-$(CONFIG_MMC_LITEX)			+= litex_mmc.o
-
+obj-$(CONFIG_MMC_EIC7700)                 += sdhci-eic7700.o
+obj-$(CONFIG_MMC_SDHCI_OF_EIC7700)        += sdhci-of-eic7700.o
+obj-$(CONFIG_MMC_SDHCI_OF_SDIO_EIC7700)   += sdhci-of-eic7700-sdio.o
 ifeq ($(CONFIG_CB710_DEBUG),y)
 	CFLAGS-cb710-mmc	+= -DDEBUG
 endif
diff --git a/drivers/mmc/host/sdhci-eic7700.c b/drivers/mmc/host/sdhci-eic7700.c
new file mode 100644
index 000000000000..84a7a1186b90
--- /dev/null
+++ b/drivers/mmc/host/sdhci-eic7700.c
@@ -0,0 +1,353 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ESWIN Emmc Driver
+ *
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
+ *
+ * Authors:
+ *	Shuang Liang <liangshuang@eswincomputing.com>
+ *	Xuyang Dong <dongxuyang@eswincomputing.com>
+ */
+#include "sdhci-eic7700.h"
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+static void eswin_mshc_coreclk_config(struct sdhci_host *host, uint16_t divisor,
+				      unsigned int flag_sel)
+{
+	struct sdhci_pltfm_host *pltfm_host;
+	struct eswin_sdhci_data *eswin_sdhci;
+	u32 val = 0;
+	u32 delay = 0xfffff;
+
+	pltfm_host = sdhci_priv(host);
+	eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
+
+	regmap_read(eswin_sdhci->crg_regmap, eswin_sdhci->crg_core_clk, &val);
+	val &= ~MSHC_CORE_CLK_ENABLE;
+	regmap_write(eswin_sdhci->crg_regmap, eswin_sdhci->crg_core_clk, val);
+	while (delay--)
+		;
+	val &= ~(MSHC_CORE_CLK_FREQ_BIT_MASK << MSHC_CORE_CLK_FREQ_BIT_SHIFT);
+	val |= (divisor & MSHC_CORE_CLK_FREQ_BIT_MASK)
+	       << MSHC_CORE_CLK_FREQ_BIT_SHIFT;
+	val &= ~(MSHC_CORE_CLK_SEL_BIT);
+	val |= flag_sel;
+	regmap_write(eswin_sdhci->crg_regmap, eswin_sdhci->crg_core_clk, val);
+
+	udelay(100);
+	val |= MSHC_CORE_CLK_ENABLE;
+	regmap_write(eswin_sdhci->crg_regmap, eswin_sdhci->crg_core_clk, val);
+	mdelay(1);
+}
+
+static void eswin_mshc_coreclk_disable(struct sdhci_host *host)
+{
+	struct sdhci_pltfm_host *pltfm_host;
+	struct eswin_sdhci_data *eswin_sdhci;
+	u32 val = 0;
+
+	pltfm_host = sdhci_priv(host);
+	eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
+
+	regmap_read(eswin_sdhci->crg_regmap, eswin_sdhci->crg_core_clk, &val);
+	val &= ~MSHC_CORE_CLK_ENABLE;
+	regmap_write(eswin_sdhci->crg_regmap, eswin_sdhci->crg_core_clk, val);
+}
+
+void eswin_sdhci_disable_card_clk(struct sdhci_host *host)
+{
+	unsigned int clk;
+
+	/* Reset SD Clock Enable */
+	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+	clk &= ~SDHCI_CLOCK_CARD_EN;
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+}
+
+void eswin_sdhci_enable_card_clk(struct sdhci_host *host)
+{
+	ktime_t timeout;
+	unsigned int clk;
+
+	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+
+	clk |= SDHCI_CLOCK_INT_EN;
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+	/* Wait max 150 ms */
+	timeout = ktime_add_ms(ktime_get(), 150);
+	while (1) {
+		bool timedout = ktime_after(ktime_get(), timeout);
+
+		clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+		if (clk & SDHCI_CLOCK_INT_STABLE)
+			break;
+		if (timedout) {
+			pr_err("%s: Internal clock never stabilised.\n",
+			       mmc_hostname(host->mmc));
+			return;
+		}
+		udelay(10);
+	}
+
+	clk |= SDHCI_CLOCK_CARD_EN;
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+	mdelay(1);
+}
+
+void eswin_sdhci_set_core_clock(struct sdhci_host *host, unsigned int clock)
+{
+	unsigned int div, divide;
+	unsigned int flag_sel, max_clk;
+
+	host->mmc->actual_clock = clock;
+
+	if (clock == 0) {
+		eswin_mshc_coreclk_disable(host);
+		return;
+	}
+
+	if (SDHCI_CLK_208M % clock == 0) {
+		flag_sel = 1;
+		max_clk = SDHCI_CLK_208M;
+	} else {
+		flag_sel = 0;
+		max_clk = SDHCI_CLK_200M;
+	}
+
+	for (div = 1; div <= MAX_CORE_CLK_DIV; div++) {
+		if ((max_clk / div) <= clock)
+			break;
+	}
+	div--;
+
+	if (div == 0 || div == 1)
+		divide = 2;
+	else
+		divide = (div + 1) * 2;
+
+	pr_debug("%s: clock:%d timing:%d\n", mmc_hostname(host->mmc), clock,
+		 host->timing);
+
+	eswin_sdhci_disable_card_clk(host);
+	eswin_mshc_coreclk_config(host, divide, flag_sel);
+	eswin_sdhci_enable_card_clk(host);
+	mdelay(2);
+}
+
+static void eswin_sdhci_set_clk_delays(struct sdhci_host *host)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
+	struct eswin_sdhci_clk_data *clk_data = &eswin_sdhci->clk_data;
+
+	clk_set_phase(clk_data->sampleclk,
+		      clk_data->clk_phase_in[host->timing]);
+	clk_set_phase(clk_data->sdcardclk,
+		      clk_data->clk_phase_out[host->timing]);
+}
+
+static void eswin_sdhci_dt_read_clk_phase(struct device *dev,
+					  struct eswin_sdhci_clk_data *clk_data,
+					  unsigned int timing, const char *prop)
+{
+	struct device_node *np = dev->of_node;
+
+	int clk_phase[2] = {0};
+
+	/*
+	 * Read Tap Delay values from DT, if the DT does not contain the
+	 * Tap Values then use the pre-defined values.
+	 */
+	if (of_property_read_variable_u32_array(np, prop, &clk_phase[0], 2,
+						0)) {
+		dev_dbg(dev, "Using predefined clock phase for %s = %d %d\n",
+			prop, clk_data->clk_phase_in[timing],
+			clk_data->clk_phase_out[timing]);
+		return;
+	}
+
+	/* The values read are Input and Output Clock Delays in order */
+	clk_data->clk_phase_in[timing] = clk_phase[0];
+	clk_data->clk_phase_out[timing] = clk_phase[1];
+}
+
+/**
+ * eswin_sdhci_dt_parse_clk_phases - Read Clock Delay values from DT
+ *
+ * @dev:        Pointer to our struct device.
+ * @clk_data:       Pointer to the Clock Data structure
+ *
+ * Called at initialization to parse the values of Clock Delays.
+ */
+void eswin_sdhci_dt_parse_clk_phases(struct device *dev,
+				     struct eswin_sdhci_clk_data *clk_data)
+{
+	clk_data->set_clk_delays = eswin_sdhci_set_clk_delays;
+
+	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY,
+				      "clk-phase-legacy");
+	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS,
+				      "clk-phase-mmc-hs");
+	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_SD_HS,
+				      "clk-phase-sd-hs");
+	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR12,
+				      "clk-phase-uhs-sdr12");
+	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR25,
+				      "clk-phase-uhs-sdr25");
+	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR50,
+				      "clk-phase-uhs-sdr50");
+	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR104,
+				      "clk-phase-uhs-sdr104");
+	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_DDR50,
+				      "clk-phase-uhs-ddr50");
+	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_DDR52,
+				      "clk-phase-mmc-ddr52");
+	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS200,
+				      "clk-phase-mmc-hs200");
+	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS400,
+				      "clk-phase-mmc-hs400");
+}
+
+unsigned int eswin_convert_drive_impedance_ohm(struct platform_device *pdev,
+					       unsigned int dr_ohm)
+{
+	switch (dr_ohm) {
+	case 100:
+		return PHYCTRL_DR_100OHM;
+	case 66:
+		return PHYCTRL_DR_66OHM;
+	case 50:
+		return PHYCTRL_DR_50OHM;
+	case 40:
+		return PHYCTRL_DR_40OHM;
+	case 33:
+		return PHYCTRL_DR_33OHM;
+	}
+
+	dev_warn(&pdev->dev, "Invalid value %u for drive-impedance-ohm.\n",
+		 dr_ohm);
+	return PHYCTRL_DR_50OHM;
+}
+
+static void eswin_sdhci_do_reset(struct eswin_sdhci_data *eswin_sdhci)
+{
+	int ret;
+
+	ret = reset_control_assert(eswin_sdhci->txrx_rst);
+	WARN_ON(ret != 0);
+	ret = reset_control_assert(eswin_sdhci->phy_rst);
+	WARN_ON(ret != 0);
+	ret = reset_control_assert(eswin_sdhci->prstn);
+	WARN_ON(ret != 0);
+	ret = reset_control_assert(eswin_sdhci->arstn);
+	WARN_ON(ret != 0);
+
+	mdelay(2);
+
+	ret = reset_control_deassert(eswin_sdhci->txrx_rst);
+	WARN_ON(ret != 0);
+	ret = reset_control_deassert(eswin_sdhci->phy_rst);
+	WARN_ON(ret != 0);
+	ret = reset_control_deassert(eswin_sdhci->prstn);
+	WARN_ON(ret != 0);
+	ret = reset_control_deassert(eswin_sdhci->arstn);
+	WARN_ON(ret != 0);
+}
+
+int eswin_sdhci_reset_init(struct device *dev,
+			   struct eswin_sdhci_data *eswin_sdhci)
+{
+	int ret = 0;
+
+	eswin_sdhci->txrx_rst =
+		devm_reset_control_get_optional(dev, "txrx_rst");
+	if (IS_ERR_OR_NULL(eswin_sdhci->txrx_rst)) {
+		dev_err_probe(dev, PTR_ERR(eswin_sdhci->txrx_rst),
+			      "txrx_rst reset not found.\n");
+		return -EFAULT;
+	}
+
+	eswin_sdhci->phy_rst = devm_reset_control_get_optional(dev, "phy_rst");
+	if (IS_ERR_OR_NULL(eswin_sdhci->phy_rst)) {
+		dev_err_probe(dev, PTR_ERR(eswin_sdhci->phy_rst),
+			      "phy_rst reset not found.\n");
+		return -EFAULT;
+	}
+
+	eswin_sdhci->prstn = devm_reset_control_get_optional(dev, "prstn");
+	if (IS_ERR_OR_NULL(eswin_sdhci->prstn)) {
+		dev_err_probe(dev, PTR_ERR(eswin_sdhci->prstn),
+			      "prstn reset not found.\n");
+		return -EFAULT;
+	}
+
+	eswin_sdhci->arstn = devm_reset_control_get_optional(dev, "arstn");
+	if (IS_ERR_OR_NULL(eswin_sdhci->arstn)) {
+		dev_err_probe(dev, PTR_ERR(eswin_sdhci->arstn),
+			      "arstn reset not found.\n");
+		return -EFAULT;
+	}
+	eswin_sdhci_do_reset(eswin_sdhci);
+
+	return ret;
+}
+
+#define DRIVER_NAME "sdhci_esw"
+#define SDHCI_ESW_DUMP(f, x...)                                                \
+	pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ##x)
+
+void eswin_sdhci_dump_vendor_regs(struct sdhci_host *host)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
+	int ret;
+	u32 val = 0, val1 = 0;
+
+	SDHCI_ESW_DUMP("----------- VENDOR REGISTER DUMP -----------\n");
+
+	ret = regmap_read(eswin_sdhci->crg_regmap, eswin_sdhci->crg_core_clk,
+			  &val);
+	if (ret) {
+		pr_err("%s: read crg_core_clk failed, ret:%d\n",
+		       mmc_hostname(host->mmc), ret);
+		return;
+	}
+
+	SDHCI_ESW_DUMP("CORE CLK: 0x%08x | IRQ FLAG:  0x%016lx\n", val,
+		       arch_local_save_flags());
+
+	ret = regmap_read(eswin_sdhci->crg_regmap, eswin_sdhci->crg_aclk_ctrl,
+			  &val);
+	if (ret) {
+		pr_err("%s: read crg_aclk_ctrl failed, ret:%d\n",
+		       mmc_hostname(host->mmc), ret);
+		return;
+	}
+	ret = regmap_read(eswin_sdhci->crg_regmap, eswin_sdhci->crg_cfg_ctrl,
+			  &val1);
+	if (ret) {
+		pr_err("%s: read crg_cfg_ctrl failed, ret:%d\n",
+		       mmc_hostname(host->mmc), ret);
+		return;
+	}
+	SDHCI_ESW_DUMP("HSP ACLK: 0x%08x | HSP CFG:  0x%08x\n", val, val1);
+
+	ret = regmap_read(eswin_sdhci->hsp_regmap, eswin_sdhci->hsp_int_status,
+			  &val);
+	if (ret) {
+		pr_err("%s: read hsp_int_status failed, ret:%d\n",
+		       mmc_hostname(host->mmc), ret);
+		return;
+	}
+	ret = regmap_read(eswin_sdhci->hsp_regmap, eswin_sdhci->hsp_pwr_ctrl,
+			  &val1);
+	if (ret) {
+		pr_err("%s: read hsp_pwr_ctrl failed, ret:%d\n",
+		       mmc_hostname(host->mmc), ret);
+		return;
+	}
+	SDHCI_ESW_DUMP("HSP STA: 0x%08x | PWR CTRL:  0x%08x\n", val, val1);
+}
diff --git a/drivers/mmc/host/sdhci-eic7700.h b/drivers/mmc/host/sdhci-eic7700.h
new file mode 100644
index 000000000000..4e6f4ac8e7ff
--- /dev/null
+++ b/drivers/mmc/host/sdhci-eic7700.h
@@ -0,0 +1,237 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ESWIN SDHCI Driver
+ *
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
+ *
+ * Authors:
+ *	Shuang Liang <liangshuang@eswincomputing.com>
+ *	Xuyang Dong <dongxuyang@eswincomputing.com>
+ */
+#ifndef _DRIVERS_MMC_SDHCI_ESWIN_H
+#define _DRIVERS_MMC_SDHCI_ESWIN_H
+
+#include "sdhci-pltfm.h"
+#include <linux/clk-provider.h>
+#include <linux/reset.h>
+
+#define MSHC_CARD_CLK_STABLE BIT(28)
+#define MSHC_INT_BCLK_STABLE BIT(16)
+#define MSHC_INT_ACLK_STABLE BIT(8)
+#define MSHC_INT_TMCLK_STABLE BIT(0)
+#define MSHC_INT_CLK_STABLE                                                    \
+	(MSHC_CARD_CLK_STABLE | MSHC_INT_ACLK_STABLE | MSHC_INT_BCLK_STABLE |  \
+	 MSHC_INT_TMCLK_STABLE)
+#define MSHC_HOST_VAL_STABLE BIT(0)
+#define EMMC0_CARD_DETECT BIT(9)
+#define EMMC0_CARD_WRITE_PROT BIT(8)
+
+#define MSHC_CORE_CLK_ENABLE BIT(16)
+#define MSHC_CORE_CLK_FREQ_BIT_SHIFT 4
+#define MSHC_CORE_CLK_FREQ_BIT_MASK 0xfffu
+#define MSHC_CORE_CLK_SEL_BIT BIT(0)
+
+/* Controller does not have CD wired and will not function normally without */
+#define SDHCI_ESWIN_QUIRK_FORCE_CDTEST BIT(0)
+/* Controller immediately reports SDHCI_CLOCK_INT_STABLE after enabling the
+ * internal clock even when the clock isn't stable
+ */
+#define SDHCI_ESWIN_QUIRK_CLOCK_UNSTABLE BIT(1)
+
+#define ESWIN_SDHCI_CTRL_HS400                                                 \
+	0x0007 // Non-standard, for eswin,these bits are 0x7
+
+#define SDHCI_CLK_208M 208000000
+#define SDHCI_CLK_200M 200000000
+
+#define AWSMMUSID GENMASK(31, 24)  // The sid of write operation
+#define AWSMMUSSID GENMASK(23, 16) // The ssid of write operation
+#define ARSMMUSID GENMASK(15, 8)   // The sid of read operation
+#define ARSMMUSSID GENMASK(7, 0)   // The ssid of read operation
+
+/* DWC_mshc_map/DWC_mshc_phy_block register */
+#define DWC_MSHC_PTR_PHY_R 0x300
+#define PHY_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x00)
+#define PHY_CMDPAD_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x04)
+#define PHY_DATAPAD_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x06)
+#define PHY_CLKPAD_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x08)
+#define PHY_STBPAD_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x0a)
+#define PHY_RSTNPAD_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x0c)
+#define PHY_PADTEST_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x0e)
+#define PHY_PADTEST_OUT_R (DWC_MSHC_PTR_PHY_R + 0x10)
+#define PHY_PADTEST_IN_R (DWC_MSHC_PTR_PHY_R + 0x12)
+#define PHY_PRBS_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x18)
+#define PHY_PHYLBK_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x1a)
+#define PHY_COMMDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x1c)
+#define PHY_SDCLKDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x1d)
+#define PHY_SDCLKDL_DC_R (DWC_MSHC_PTR_PHY_R + 0x1e)
+#define PHY_SMPLDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x20)
+#define PHY_ATDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x21)
+#define PHY_DLL_CTRL_R (DWC_MSHC_PTR_PHY_R + 0x24)
+#define PHY_DLL_CNFG1_R (DWC_MSHC_PTR_PHY_R + 0x25)
+#define PHY_DLL_CNFG2_R (DWC_MSHC_PTR_PHY_R + 0x26)
+#define PHY_DLLDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x28)
+#define PHY_DLL_OFFST_R (DWC_MSHC_PTR_PHY_R + 0x29)
+#define PHY_DLLMST_TSTDC_R (DWC_MSHC_PTR_PHY_R + 0x2a)
+#define PHY_DLLBT_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x2c)
+#define PHY_DLL_STATUS_R (DWC_MSHC_PTR_PHY_R + 0x2e)
+#define PHY_DLLDBG_MLKDC_R (DWC_MSHC_PTR_PHY_R + 0x30)
+#define PHY_DLLDBG_SLKDC_R (DWC_MSHC_PTR_PHY_R + 0x32)
+
+#define ENABLE 1
+#define DISABLE 0
+/* strength definition */
+#define PHYCTRL_DR_33OHM 0xee
+#define PHYCTRL_DR_40OHM 0xcc
+#define PHYCTRL_DR_50OHM 0x88
+#define PHYCTRL_DR_66OHM 0x44
+#define PHYCTRL_DR_100OHM 0x00
+
+#define PHY_PAD_MAX_DRIVE_STRENGTH 0xf
+#define PHY_CLK_MAX_DELAY_MASK 0x7f
+#define PHY_PAD_SP_DRIVE_SHIF 16
+#define PHY_PAD_SN_DRIVE_SHIF 20
+
+#define PHY_RSTN BIT(0)
+#define PHY_UPDATE_DELAY_CODE BIT(4)
+
+#define VENDOR_EMMC_CTRL_R 0x52c
+#define EMMC_CRAD_PRESENT BIT(0)
+#define EMMC_RST_N_OE BIT(3)
+#define EMMC_RST_N BIT(2)
+
+#define PHY_SLEW_0 0x0
+#define PHY_SLEW_1 0x1
+#define PHY_SLEW_2 0x2
+#define PHY_SLEW_3 0x3
+#define PHY_TX_SLEW_CTRL_P_BIT_SHIFT 5
+#define PHY_TX_SLEW_CTRL_N_BIT_SHIFT 9
+
+#define PHY_PULL_BIT_SHIF 0x3
+#define PHY_PULL_DISABLED 0x0
+#define PHY_PULL_UP 0x1
+#define PHY_PULL_DOWN 0x2
+#define PHY_PULL_MASK 0x3
+
+#define PHY_PAD_RXSEL_0 0x0
+#define PHY_PAD_RXSEL_1 0x1
+
+#define VENDOR_AT_CTRL_R 0x540
+#define LATENCY_LT_BIT_OFFSET 19
+#define LATENCY_LT_MASK 0x3
+
+#define LATENCY_LT_1 0x0
+#define LATENCY_LT_2 0x1
+#define LATENCY_LT_3 0x2
+#define LATENCY_LT_4 0x3
+#define SW_TUNE_ENABLE BIT(4)
+
+#define VENDOR_AT_SATA_R 0x544
+#define MAX_PHASE_CODE 0xff
+
+#define DLL_ENABEL BIT(0)
+#define DLL_LOCK_STS BIT(0)
+#define DLL_ERROR_STS BIT(1)
+#define PHY_DELAY_CODE_MASK 0x7f
+#define PHY_DELAY_CODE_MAX 0x7f
+
+#define MAX_CORE_CLK_DIV 0xfff
+
+/**
+ * struct eswin_sdhci_clk_ops - Clock Operations for eswin SD controller
+ *
+ * @sdcardclk_ops:  The output clock related operations
+ * @sampleclk_ops:  The sample clock related operations
+ */
+struct eswin_sdhci_clk_ops {
+	const struct clk_ops *sdcardclk_ops;
+	const struct clk_ops *sampleclk_ops;
+};
+
+/**
+ * struct eswin_sdhci_clk_data - ESWIN Controller Clock Data.
+ *
+ * @sdcardclk_hw:   Struct for the clock we might provide to a PHY.
+ * @sdcardclk:      Pointer to normal 'struct clock' for sdcardclk_hw.
+ * @sampleclk_hw:   Struct for the clock we might provide to a PHY.
+ * @sampleclk:      Pointer to normal 'struct clock' for sampleclk_hw.
+ * @clk_phase_in:   Array of Input Clock Phase Delays for all speed modes
+ * @clk_phase_out:  Array of Output Clock Phase Delays for all speed modes
+ * @set_clk_delays: Function pointer for setting Clock Delays
+ * @clk_of_data:    Platform specific runtime clock data storage pointer
+ */
+struct eswin_sdhci_clk_data {
+	struct clk_hw sdcardclk_hw;
+	struct clk *sdcardclk;
+	struct clk_hw sampleclk_hw;
+	struct clk *sampleclk;
+	int clk_phase_in[MMC_TIMING_MMC_HS400 + 1];
+	int clk_phase_out[MMC_TIMING_MMC_HS400 + 1];
+	void (*set_clk_delays)(struct sdhci_host *host);
+	void *clk_of_data;
+};
+
+struct eswin_sdhci_phy_data {
+	unsigned int drive_impedance;
+	unsigned int enable_strobe_pulldown;
+	unsigned int enable_data_pullup;
+	unsigned int enable_cmd_pullup;
+	unsigned int delay_code;
+};
+
+/**
+ * struct eswin_sdhci_data - ESWIN Controller Data
+ *
+ * @host:       Pointer to the main SDHCI host structure.
+ * @clk_ahb:        Pointer to the AHB clock
+ * @has_cqe:        True if controller has command queuing engine.
+ * @clk_data:       Struct for the ESWIN Controller Clock Data.
+ * @clk_ops:        Struct for the ESWIN Controller Clock Operations.
+ * @soc_ctl_base:   Pointer to regmap for syscon for soc_ctl registers.
+ * @soc_ctl_map:    Map to get offsets into soc_ctl registers.
+ * @quirks:     ESWIN deviations from spec.
+ * @phy:        ESWIN sdhci phy configs.
+ * @private:    private for spec driver.
+ */
+struct eswin_sdhci_data {
+	struct sdhci_host *host;
+	struct clk *clk_ahb;
+	bool has_cqe;
+	struct eswin_sdhci_clk_data clk_data;
+	const struct eswin_sdhci_clk_ops *clk_ops;
+	unsigned int quirks;
+
+	struct regmap *crg_regmap;
+	unsigned int crg_core_clk;
+	unsigned int crg_aclk_ctrl;
+	unsigned int crg_cfg_ctrl;
+
+	struct regmap *hsp_regmap;
+	unsigned int hsp_int_status;
+	unsigned int hsp_pwr_ctrl;
+
+	struct reset_control *txrx_rst;
+	struct reset_control *phy_rst;
+	struct reset_control *prstn;
+	struct reset_control *arstn;
+	struct eswin_sdhci_phy_data phy;
+	unsigned long private[] ____cacheline_aligned;
+};
+
+struct eswin_sdhci_of_data {
+	const struct sdhci_pltfm_data *pdata;
+	const struct eswin_sdhci_clk_ops *clk_ops;
+};
+
+void eswin_sdhci_set_core_clock(struct sdhci_host *host, unsigned int clock);
+void eswin_sdhci_disable_card_clk(struct sdhci_host *host);
+void eswin_sdhci_enable_card_clk(struct sdhci_host *host);
+void eswin_sdhci_dt_parse_clk_phases(struct device *dev,
+				     struct eswin_sdhci_clk_data *clk_data);
+unsigned int eswin_convert_drive_impedance_ohm(struct platform_device *pdev,
+					       unsigned int dr_ohm);
+int eswin_sdhci_reset_init(struct device *dev,
+			   struct eswin_sdhci_data *eswin_sdhci);
+void eswin_sdhci_dump_vendor_regs(struct sdhci_host *host);
+
+#endif /* _DRIVERS_MMC_SDHCI_ESWIN_H */
diff --git a/drivers/mmc/host/sdhci-of-eic7700-sdio.c b/drivers/mmc/host/sdhci-of-eic7700-sdio.c
new file mode 100644
index 000000000000..68f9995ab3b6
--- /dev/null
+++ b/drivers/mmc/host/sdhci-of-eic7700-sdio.c
@@ -0,0 +1,991 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ESWIN sdio Driver
+ *
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
+ *
+ * Authors:
+ *	Shuang Liang <liangshuang@eswincomputing.com>
+ *	Xuyang Dong <dongxuyang@eswincomputing.com>
+ */
+
+#include "cqhci.h"
+#include "sdhci-eic7700.h"
+#include "sdhci-pltfm.h"
+#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
+#include <linux/iommu.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#define ESWIN_SDHCI_SD_CQE_BASE_ADDR 0x180
+#define TUNING_RANGE_THRESHOLD 40
+
+static inline void *sdhci_sdio_priv(struct eswin_sdhci_data *sdio)
+{
+	return sdio->private;
+}
+
+static void eswin_sdhci_sdio_set_clock(struct sdhci_host *host,
+				       unsigned int clock)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct eswin_sdhci_data *eswin_sdhci_sdio =
+		sdhci_pltfm_priv(pltfm_host);
+	struct eswin_sdhci_clk_data *clk_data = &eswin_sdhci_sdio->clk_data;
+
+	/* Set the Input and Output Clock Phase Delays */
+	if (clk_data->set_clk_delays)
+		clk_data->set_clk_delays(host);
+
+	eswin_sdhci_set_core_clock(host, clock);
+
+	if (eswin_sdhci_sdio->quirks & SDHCI_ESWIN_QUIRK_CLOCK_UNSTABLE)
+		/*
+		 * Some controllers immediately report SDHCI_CLOCK_INT_STABLE
+		 * after enabling the clock even though the clock is not
+		 * stable. Trying to use a clock without waiting here results
+		 * in EILSEQ while detecting some older/slower cards. The
+		 * chosen delay is the maximum delay from sdhci_set_clock.
+		 */
+		msleep(20);
+}
+
+static void eswin_sdhci_sdio_config_phy_delay(struct sdhci_host *host,
+					      int delay)
+{
+	delay &= PHY_CLK_MAX_DELAY_MASK;
+
+	/*phy clk delay line config*/
+	sdhci_writeb(host, PHY_UPDATE_DELAY_CODE, PHY_SDCLKDL_CNFG_R);
+	sdhci_writeb(host, delay, PHY_SDCLKDL_DC_R);
+	sdhci_writeb(host, 0x0, PHY_SDCLKDL_CNFG_R);
+}
+
+static void eswin_sdhci_sdio_config_phy(struct sdhci_host *host)
+{
+	unsigned int val = 0;
+	unsigned int drv = 0;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
+	struct eswin_sdhci_phy_data *phy = &eswin_sdhci->phy;
+
+	drv = phy->drive_impedance << PHY_PAD_SP_DRIVE_SHIF;
+	pr_debug("%s: phy drv=0x%x\n", mmc_hostname(host->mmc), drv);
+
+	eswin_sdhci_disable_card_clk(host);
+
+	/* reset phy,config phy's pad */
+	sdhci_writel(host, drv | (~PHY_RSTN), PHY_CNFG_R);
+	/*CMDPAD_CNFS*/
+	val = (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_P_BIT_SHIFT) |
+	      (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_N_BIT_SHIFT) |
+	      (phy->enable_cmd_pullup << PHY_PULL_BIT_SHIF) | PHY_PAD_RXSEL_1;
+	sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
+	pr_debug("%s: phy cmd=0x%x\n", mmc_hostname(host->mmc), val);
+
+	/*DATA PAD CNFG*/
+	val = (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_P_BIT_SHIFT) |
+	      (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_N_BIT_SHIFT) |
+	      (phy->enable_data_pullup << PHY_PULL_BIT_SHIF) | PHY_PAD_RXSEL_1;
+	sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
+	pr_debug("%s: phy data=0x%x\n", mmc_hostname(host->mmc), val);
+
+	/*Clock PAD Setting*/
+	val = (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_P_BIT_SHIFT) |
+	      (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_N_BIT_SHIFT) | PHY_PAD_RXSEL_0;
+	sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
+	pr_debug("%s: phy clk=0x%x\n", mmc_hostname(host->mmc), val);
+	mdelay(2);
+
+	/*PHY RSTN PAD setting*/
+	val = (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_P_BIT_SHIFT) |
+	      (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_N_BIT_SHIFT) |
+	      (PHY_PULL_UP << PHY_PULL_BIT_SHIF) | PHY_PAD_RXSEL_1;
+	sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
+
+	sdhci_writel(host, drv | PHY_RSTN, PHY_CNFG_R);
+
+	eswin_sdhci_sdio_config_phy_delay(host, phy->delay_code);
+
+	eswin_sdhci_enable_card_clk(host);
+}
+
+static void eswin_sdhci_sdio_reset(struct sdhci_host *host, u8 mask)
+{
+	u8 ctrl;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct eswin_sdhci_data *eswin_sdhci_sdio =
+		sdhci_pltfm_priv(pltfm_host);
+
+	sdhci_writel(host, 0, SDHCI_INT_ENABLE);
+	sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
+
+	if (mask & SDHCI_RESET_ALL)
+		sdhci_reset(host, SDHCI_RESET_ALL);
+
+	if (mask & SDHCI_RESET_DATA)
+		sdhci_reset(host, SDHCI_RESET_DATA);
+
+	if (mask & SDHCI_RESET_CMD)
+		sdhci_reset(host, SDHCI_RESET_CMD);
+
+	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+
+	if (eswin_sdhci_sdio->quirks & SDHCI_ESWIN_QUIRK_FORCE_CDTEST) {
+		ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+		ctrl |= SDHCI_CTRL_CDTEST_INS | SDHCI_CTRL_CDTEST_EN;
+		sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+	}
+	if (mask == SDHCI_RESET_ALL) { // after reset all,the phy`s config will
+				       // be clear.
+		eswin_sdhci_sdio_config_phy(host);
+	}
+}
+
+static int eswin_sdhci_sdio_phase_code_tuning(struct sdhci_host *host,
+					      u32 opcode)
+{
+	int cmd_error = 0;
+	int ret = 0;
+	int phase_code = -1;
+	int code_min = -1;
+	int code_max = -1;
+	int code_range = -1;
+	int i = 0;
+
+	for (i = 0; i <= MAX_PHASE_CODE; i++) {
+		eswin_sdhci_disable_card_clk(host);
+		sdhci_writew(host, i, VENDOR_AT_SATA_R);
+		eswin_sdhci_enable_card_clk(host);
+
+		ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
+		host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+		if (ret) {
+			udelay(200);
+			pr_debug("%s: bad phase_code:0x%x!\n",
+				 mmc_hostname(host->mmc), i);
+			if (code_min != -1 && code_max != -1) {
+				if (code_max - code_min > code_range) {
+					code_range = code_max - code_min;
+					phase_code = (code_min + code_max) / 2;
+					if (code_range > TUNING_RANGE_THRESHOLD)
+						break;
+				}
+				code_min = -1;
+				code_max = -1;
+			}
+		} else {
+			pr_debug("%s: ok phase_code:0x%x\n",
+				 mmc_hostname(host->mmc), i);
+			if (code_min == -1)
+				code_min = i;
+
+			code_max = i;
+			if (i == MAX_PHASE_CODE) {
+				if (code_max - code_min > code_range) {
+					code_range = code_max - code_min;
+					phase_code = (code_min + code_max) / 2;
+				}
+			}
+			continue;
+		}
+	}
+
+	if (phase_code == -1) {
+		pr_err("%s: phase code tuning failed!\n",
+		       mmc_hostname(host->mmc));
+		eswin_sdhci_disable_card_clk(host);
+		sdhci_writew(host, 0, VENDOR_AT_SATA_R);
+		eswin_sdhci_enable_card_clk(host);
+		return -EIO;
+	}
+
+	pr_debug("%s: set phase_code:0x%x\n", mmc_hostname(host->mmc),
+		 phase_code);
+
+	eswin_sdhci_disable_card_clk(host);
+	sdhci_writew(host, phase_code, VENDOR_AT_SATA_R);
+	eswin_sdhci_enable_card_clk(host);
+
+	ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
+	host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+	if (ret) {
+		pr_err("%s: phase_code code(0x%x) not work, tuning failed!\n",
+		       mmc_hostname(host->mmc), phase_code);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int eswin_sdhci_sdio_executing_tuning(struct sdhci_host *host,
+					     u32 opcode)
+{
+	u32 ctrl;
+	u32 val;
+	int ret = 0;
+	struct sdhci_pltfm_host *pltfm_host;
+
+	pltfm_host = sdhci_priv(host);
+	sdhci_pltfm_priv(pltfm_host);
+
+	eswin_sdhci_disable_card_clk(host);
+
+	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+	ctrl &= ~SDHCI_CTRL_TUNED_CLK;
+	sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+	val = sdhci_readl(host, VENDOR_AT_CTRL_R);
+	val |= SW_TUNE_ENABLE;
+	sdhci_writew(host, val, VENDOR_AT_CTRL_R);
+	sdhci_writew(host, 0, VENDOR_AT_SATA_R);
+
+	eswin_sdhci_enable_card_clk(host);
+
+	sdhci_writew(host, 0x0, SDHCI_CMD_DATA);
+
+	ret = eswin_sdhci_sdio_phase_code_tuning(host, opcode);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static u32 eswin_sdhci_sdio_cqhci_irq(struct sdhci_host *host, u32 intmask)
+{
+	int cmd_error = 0;
+	int data_error = 0;
+
+	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
+		return intmask;
+
+	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
+
+	return 0;
+}
+
+static void eswin_sdhci_sdio_dumpregs(struct mmc_host *mmc)
+{
+	sdhci_dumpregs(mmc_priv(mmc));
+}
+
+static void eswin_sdhci_sdio_cqe_enable(struct mmc_host *mmc)
+{
+	struct sdhci_host *host = mmc_priv(mmc);
+	u32 reg;
+
+	reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
+	while (reg & SDHCI_DATA_AVAILABLE) {
+		sdhci_readl(host, SDHCI_BUFFER);
+		reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
+	}
+
+	sdhci_cqe_enable(mmc);
+}
+
+static const struct cqhci_host_ops eswin_sdhci_sdio_cqhci_ops = {
+	.enable = eswin_sdhci_sdio_cqe_enable,
+	.disable = sdhci_cqe_disable,
+	.dumpregs = eswin_sdhci_sdio_dumpregs,
+};
+
+static const struct sdhci_ops eswin_sdhci_sdio_cqe_ops = {
+	.set_clock = eswin_sdhci_sdio_set_clock,
+	.get_max_clock = sdhci_pltfm_clk_get_max_clock,
+	.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
+	.set_bus_width = sdhci_set_bus_width,
+	.reset = eswin_sdhci_sdio_reset,
+	.set_uhs_signaling = sdhci_set_uhs_signaling,
+	.set_power = sdhci_set_power_and_bus_voltage,
+	.irq = eswin_sdhci_sdio_cqhci_irq,
+	.platform_execute_tuning = eswin_sdhci_sdio_executing_tuning,
+	.dump_vendor_regs = eswin_sdhci_dump_vendor_regs,
+};
+
+static const struct sdhci_pltfm_data eswin_sdhci_sdio_cqe_pdata = {
+	.ops = &eswin_sdhci_sdio_cqe_ops,
+	.quirks = SDHCI_QUIRK_BROKEN_CQE | SDHCI_QUIRK_SINGLE_POWER_WRITE |
+		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+		  SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
+	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+		   SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
+};
+
+#ifdef CONFIG_PM_SLEEP
+/**
+ * eswin_sdhci_sdio_suspend- Suspend method for the driver
+ * @dev:        Address of the device structure
+ *
+ * Put the device in a low power state.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int eswin_sdhci_sdio_suspend(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct eswin_sdhci_data *eswin_sdhci_sdio =
+		sdhci_pltfm_priv(pltfm_host);
+	int ret;
+
+	pm_runtime_get_sync(dev);
+
+	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
+		mmc_retune_needed(host->mmc);
+
+	ret = sdhci_suspend_host(host);
+	if (ret)
+		return ret;
+
+	clk_disable_unprepare(pltfm_host->clk);
+	clk_disable_unprepare(eswin_sdhci_sdio->clk_ahb);
+
+	return 0;
+}
+
+/**
+ * eswin_sdhci_sdio_resume- Resume method for the driver
+ * @dev:        Address of the device structure
+ *
+ * Resume operation after suspend
+ *
+ * Return: 0 on success and error value on error
+ */
+static int eswin_sdhci_sdio_resume(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct eswin_sdhci_data *eswin_sdhci_sdio =
+		sdhci_pltfm_priv(pltfm_host);
+	int ret;
+
+	pm_runtime_put_sync(dev);
+	ret = clk_prepare_enable(eswin_sdhci_sdio->clk_ahb);
+	if (ret) {
+		dev_err(dev, "can't enable clk_ahb.\n");
+		return ret;
+	}
+	ret = clk_prepare_enable(pltfm_host->clk);
+	if (ret) {
+		dev_err(dev, "can't enable mainck.\n");
+		goto clk_ahb_disable;
+	}
+
+	ret = sdhci_resume_host(host);
+	if (ret) {
+		dev_err(dev, "runtime resume failed!\n");
+		goto clk_disable;
+	}
+
+	return 0;
+clk_disable:
+	clk_disable_unprepare(pltfm_host->clk);
+clk_ahb_disable:
+	clk_disable_unprepare(eswin_sdhci_sdio->clk_ahb);
+
+	return ret;
+}
+
+static int eswin_sdhci_sdio_runtime_suspend(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct eswin_sdhci_data *eswin_sdhci_sdio =
+		sdhci_pltfm_priv(pltfm_host);
+	int ret;
+
+	ret = sdhci_runtime_suspend_host(host);
+	if (ret)
+		return ret;
+
+	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
+		mmc_retune_needed(host->mmc);
+
+	clk_disable_unprepare(pltfm_host->clk);
+	clk_disable_unprepare(eswin_sdhci_sdio->clk_ahb);
+
+	return 0;
+}
+
+static int eswin_sdhci_sdio_runtime_resume(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct eswin_sdhci_data *eswin_sdhci_sdio =
+		sdhci_pltfm_priv(pltfm_host);
+	int ret;
+
+	ret = clk_prepare_enable(eswin_sdhci_sdio->clk_ahb);
+	if (ret) {
+		dev_err(dev, "can't enable clk_ahb\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(pltfm_host->clk);
+	if (ret) {
+		dev_err(dev, "can't enable mainck\n");
+		goto clk_ahb_disable;
+	}
+
+	ret = sdhci_runtime_resume_host(host, 1);
+	if (ret) {
+		dev_err(dev, "runtime resume failed!\n");
+		goto clk_disable;
+	}
+
+	return 0;
+clk_disable:
+	clk_disable_unprepare(pltfm_host->clk);
+clk_ahb_disable:
+	clk_disable_unprepare(eswin_sdhci_sdio->clk_ahb);
+
+	return ret;
+}
+
+#endif /* ! CONFIG_PM_SLEEP */
+
+/**
+ * eswin_sdhci_sdio_sdcardclk_recalc_rate- Return the card clock rate
+ *
+ * @hw:                 Pointer to the hardware clock structure.
+ * @parent_rate:                The parent rate (should be rate of clk_xin).
+ *
+ * Return the current actual rate of the SD card clock.  This can be used
+ * to communicate with out PHY.
+ *
+ * Return: The card clock rate.
+ */
+static unsigned long
+eswin_sdhci_sdio_sdcardclk_recalc_rate(struct clk_hw *hw,
+				       unsigned long parent_rate)
+{
+	struct eswin_sdhci_clk_data *clk_data =
+		container_of(hw, struct eswin_sdhci_clk_data, sdcardclk_hw);
+	struct eswin_sdhci_data *eswin_sdhci_sdio =
+		container_of(clk_data, struct eswin_sdhci_data, clk_data);
+	struct sdhci_host *host = eswin_sdhci_sdio->host;
+
+	return host->mmc->actual_clock;
+}
+
+static const struct clk_ops eswin_sdio_sdcardclk_ops = {
+	.recalc_rate = eswin_sdhci_sdio_sdcardclk_recalc_rate,
+};
+
+/**
+ * eswin_sdhci_sdio_sampleclk_recalc_rate- Return the sampling clock rate
+ *
+ * @hw:                 Pointer to the hardware clock structure.
+ * @parent_rate:                The parent rate (should be rate of clk_xin).
+ *
+ * Return the current actual rate of the sampling clock.  This can be used
+ * to communicate with out PHY.
+ *
+ * Return: The sample clock rate.
+ */
+static unsigned long
+eswin_sdhci_sdio_sampleclk_recalc_rate(struct clk_hw *hw,
+				       unsigned long parent_rate)
+{
+	struct eswin_sdhci_clk_data *clk_data =
+		container_of(hw, struct eswin_sdhci_clk_data, sampleclk_hw);
+	struct eswin_sdhci_data *eswin_sdhci_sdio =
+		container_of(clk_data, struct eswin_sdhci_data, clk_data);
+	struct sdhci_host *host = eswin_sdhci_sdio->host;
+
+	return host->mmc->actual_clock;
+}
+
+static const struct clk_ops eswin_sdio_sampleclk_ops = {
+	.recalc_rate = eswin_sdhci_sdio_sampleclk_recalc_rate,
+};
+
+static const struct eswin_sdhci_clk_ops eswin_sdio_clk_ops = {
+	.sdcardclk_ops = &eswin_sdio_sdcardclk_ops,
+	.sampleclk_ops = &eswin_sdio_sampleclk_ops,
+};
+
+static struct eswin_sdhci_of_data eswin_sdhci_fu800_sdio_data = {
+	.pdata = &eswin_sdhci_sdio_cqe_pdata,
+	.clk_ops = &eswin_sdio_clk_ops,
+};
+
+static const struct of_device_id eswin_sdhci_sdio_of_match[] = {
+	/* SoC-specific compatible strings*/
+	{
+		.compatible = "eswin,sdhci-sdio",
+		.data = &eswin_sdhci_fu800_sdio_data,
+	},
+	{/* sentinel */}};
+MODULE_DEVICE_TABLE(of, eswin_sdhci_sdio_of_match);
+
+/**
+ * eswin_sdhci_sdio_register_sdcardclk- Register the sdcardclk for a PHY to use
+ *
+ * @eswin_sdhci_sdio:       Our private data structure.
+ * @clk_xin:            Pointer to the functional clock
+ * @dev:                Pointer to our struct device.
+ *
+ * Some PHY devices need to know what the actual card clock is.  In order for
+ * them to find out, we'll provide a clock through the common clock framework
+ * for them to query.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int
+eswin_sdhci_sdio_register_sdcardclk(struct eswin_sdhci_data *eswin_sdhci_sdio,
+				    struct clk *clk_xin, struct device *dev)
+{
+	struct eswin_sdhci_clk_data *clk_data = &eswin_sdhci_sdio->clk_data;
+	struct device_node *np = dev->of_node;
+	struct clk_init_data sdcardclk_init;
+	const char *parent_clk_name;
+	int ret;
+
+	ret = of_property_read_string_index(np, "clock-output-names", 0,
+					    &sdcardclk_init.name);
+	if (ret) {
+		dev_err(dev, "DT has #clock-cells but no clock-output-names\n");
+		return ret;
+	}
+
+	parent_clk_name = __clk_get_name(clk_xin);
+	sdcardclk_init.parent_names = &parent_clk_name;
+	sdcardclk_init.num_parents = 1;
+	sdcardclk_init.flags = CLK_GET_RATE_NOCACHE;
+	sdcardclk_init.ops = eswin_sdhci_sdio->clk_ops->sdcardclk_ops;
+
+	clk_data->sdcardclk_hw.init = &sdcardclk_init;
+	clk_data->sdcardclk = devm_clk_register(dev, &clk_data->sdcardclk_hw);
+	if (IS_ERR(clk_data->sdcardclk))
+		return PTR_ERR(clk_data->sdcardclk);
+
+	clk_data->sdcardclk_hw.init = NULL;
+
+	ret = of_clk_add_provider(np, of_clk_src_simple_get,
+				  clk_data->sdcardclk);
+	if (ret)
+		dev_err(dev, "Failed to add sdcard clock provider\n");
+
+	return ret;
+}
+
+/**
+ * eswin_sdhci_sdio_register_sampleclk - Register the sampleclk for a PHY to use
+ *
+ * @eswin_sdhci_sdio:       Our private data structure.
+ * @clk_xin:            Pointer to the functional clock
+ * @dev:                Pointer to our struct device.
+ *
+ * Some PHY devices need to know what the actual card clock is.  In order for
+ * them to find out, we'll provide a clock through the common clock framework
+ * for them to query.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int
+eswin_sdhci_sdio_register_sampleclk(struct eswin_sdhci_data *eswin_sdhci_sdio,
+				    struct clk *clk_xin, struct device *dev)
+{
+	struct eswin_sdhci_clk_data *clk_data = &eswin_sdhci_sdio->clk_data;
+	struct device_node *np = dev->of_node;
+	struct clk_init_data sampleclk_init;
+	const char *parent_clk_name;
+	int ret;
+
+	ret = of_property_read_string_index(np, "clock-output-names", 1,
+					    &sampleclk_init.name);
+	if (ret) {
+		dev_err(dev, "DT has #clock-cells but no clock-output-names\n");
+		return ret;
+	}
+
+	parent_clk_name = __clk_get_name(clk_xin);
+	sampleclk_init.parent_names = &parent_clk_name;
+	sampleclk_init.num_parents = 1;
+	sampleclk_init.flags = CLK_GET_RATE_NOCACHE;
+	sampleclk_init.ops = eswin_sdhci_sdio->clk_ops->sampleclk_ops;
+
+	clk_data->sampleclk_hw.init = &sampleclk_init;
+	clk_data->sampleclk = devm_clk_register(dev, &clk_data->sampleclk_hw);
+	if (IS_ERR(clk_data->sampleclk))
+		return PTR_ERR(clk_data->sampleclk);
+	clk_data->sampleclk_hw.init = NULL;
+
+	ret = of_clk_add_provider(np, of_clk_src_simple_get,
+				  clk_data->sampleclk);
+	if (ret)
+		dev_err(dev, "Failed to add sample clock provider\n");
+
+	return ret;
+}
+
+/**
+ * eswin_sdhci_sdio_unregister_sdclk- Undoes sdhci_arasan_register_sdclk()
+ *
+ * @dev:                Pointer to our struct device.
+ *
+ * Should be called any time we're exiting and sdhci_arasan_register_sdclk()
+ * returned success.
+ */
+static void eswin_sdhci_sdio_unregister_sdclk(struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+
+	if (!of_find_property(np, "#clock-cells", NULL))
+		return;
+
+	of_clk_del_provider(dev->of_node);
+}
+
+/**
+ * eswin_sdhci_sdio_register_sdclk- Register the sdcardclk for a PHY to use
+ *
+ * @eswin_sdhci_sdio:   Our private data structure.
+ * @clk_xin:            Pointer to the functional clock
+ * @dev:                Pointer to our struct device.
+ *
+ * Some PHY devices need to know what the actual card clock is.  In order for
+ * them to find out, we'll provide a clock through the common clock framework
+ * for them to query.
+ *
+ * Note: without seriously re-architecting SDHCI's clock code and testing on
+ * all platforms, there's no way to create a totally beautiful clock here
+ * with all clock ops implemented.      Instead, we'll just create a clock that
+ * can be queried and set the CLK_GET_RATE_NOCACHE attribute to tell common
+ * clock framework that we're doing things behind its back.  This should be
+ * sufficient to create nice clean device tree bindings and later (if needed) we
+ * can try re-architecting SDHCI if we see some benefit to it.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int
+eswin_sdhci_sdio_register_sdclk(struct eswin_sdhci_data *eswin_sdhci_sdio,
+				struct clk *clk_xin, struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+	u32 num_clks = 0;
+	int ret;
+
+	/* Providing a clock to the PHY is optional; no error if missing */
+	if (of_property_read_u32(np, "#clock-cells", &num_clks) < 0)
+		return 0;
+
+	ret = eswin_sdhci_sdio_register_sdcardclk(eswin_sdhci_sdio, clk_xin,
+						  dev);
+	if (ret)
+		return ret;
+
+	if (num_clks) {
+		ret = eswin_sdhci_sdio_register_sampleclk(eswin_sdhci_sdio,
+							  clk_xin, dev);
+		if (ret) {
+			eswin_sdhci_sdio_unregister_sdclk(dev);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int eswin_sdhci_sdio_add_host(struct eswin_sdhci_data *eswin_sdhci_sdio)
+{
+	struct sdhci_host *host = eswin_sdhci_sdio->host;
+	struct cqhci_host *cq_host;
+	bool dma64;
+	int ret;
+
+	if (!eswin_sdhci_sdio->has_cqe)
+		return sdhci_add_host(host);
+
+	ret = sdhci_setup_host(host);
+	if (ret)
+		return ret;
+
+	cq_host = devm_kzalloc(host->mmc->parent, sizeof(*cq_host), GFP_KERNEL);
+	if (!cq_host) {
+		ret = -ENOMEM;
+		goto cleanup;
+	}
+
+	cq_host->mmio = host->ioaddr + ESWIN_SDHCI_SD_CQE_BASE_ADDR;
+	cq_host->ops = &eswin_sdhci_sdio_cqhci_ops;
+
+	dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
+	if (dma64)
+		cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
+
+	ret = cqhci_init(cq_host, host->mmc, dma64);
+	if (ret)
+		goto cleanup;
+
+	ret = __sdhci_add_host(host);
+	if (ret)
+		goto cleanup;
+
+	return 0;
+
+cleanup:
+	sdhci_cleanup_host(host);
+	return ret;
+}
+
+static int eswin_sdhci_sdio_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct clk *clk_xin;
+	struct clk *clk_spll2_fout3;
+	struct clk *clk_mux;
+	struct sdhci_host *host;
+	struct sdhci_pltfm_host *pltfm_host;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct eswin_sdhci_data *eswin_sdhci_sdio;
+	const struct eswin_sdhci_of_data *data;
+	unsigned int val = 0;
+
+	data = of_device_get_match_data(dev);
+	host = sdhci_pltfm_init(pdev, data->pdata, sizeof(*eswin_sdhci_sdio));
+
+	if (IS_ERR(host))
+		return PTR_ERR(host);
+
+	pltfm_host = sdhci_priv(host);
+	eswin_sdhci_sdio = sdhci_pltfm_priv(pltfm_host);
+	eswin_sdhci_sdio->host = host;
+	eswin_sdhci_sdio->has_cqe = false;
+
+	sdhci_get_of_property(pdev);
+
+	eswin_sdhci_sdio->clk_ops = data->clk_ops;
+	eswin_sdhci_sdio->clk_ahb = devm_clk_get(dev, "clk_ahb");
+	if (IS_ERR(eswin_sdhci_sdio->clk_ahb)) {
+		ret = dev_err_probe(dev, PTR_ERR(eswin_sdhci_sdio->clk_ahb),
+				    "clk_ahb clock not found.\n");
+		goto err_pltfm_free;
+	}
+
+	clk_xin = devm_clk_get(dev, "clk_xin");
+	if (IS_ERR(clk_xin)) {
+		ret = dev_err_probe(dev, PTR_ERR(clk_xin),
+				    "clk_xin clock not found.\n");
+		goto err_pltfm_free;
+	}
+
+	clk_spll2_fout3 = devm_clk_get(dev, "clk_spll2_fout3");
+
+	if (IS_ERR(clk_spll2_fout3)) {
+		ret = dev_err_probe(dev, PTR_ERR(clk_spll2_fout3),
+				    "clk_spll2_fout3 clock not found.\n");
+		goto err_pltfm_free;
+	}
+
+	if (of_device_is_compatible(np, "eswin,sdhci-sdio")) {
+		clk_mux = devm_clk_get(dev, "clk_mux1_1");
+		if (IS_ERR(clk_mux)) {
+			ret = dev_err_probe(dev, PTR_ERR(clk_mux),
+					    "clk_mux1_1 clock not found.\n");
+			goto err_pltfm_free;
+		}
+		/*switch the core clk source*/
+		clk_set_parent(clk_mux, clk_spll2_fout3);
+	}
+
+	ret = clk_prepare_enable(eswin_sdhci_sdio->clk_ahb);
+	if (ret) {
+		dev_err(dev, "Unable to enable AHB clock.\n");
+		goto err_pltfm_free;
+	}
+	/* If clock-frequency property is set, use the provided value */
+	if (pltfm_host->clock && pltfm_host->clock != clk_get_rate(clk_xin)) {
+		ret = clk_set_rate(clk_xin, pltfm_host->clock);
+		if (ret) {
+			dev_err(&pdev->dev, "Failed to set SD clock rate\n");
+			goto clk_dis_ahb;
+		}
+	}
+
+	ret = clk_prepare_enable(clk_xin);
+	if (ret) {
+		dev_err(dev, "Unable to enable SD clock.\n");
+		goto clk_dis_ahb;
+	}
+
+	pltfm_host->clk = clk_xin;
+	ret = eswin_sdhci_sdio_register_sdclk(eswin_sdhci_sdio, clk_xin, dev);
+	if (ret)
+		goto clk_disable_all;
+
+	ret = eswin_sdhci_reset_init(dev, eswin_sdhci_sdio);
+	if (ret < 0) {
+		dev_err(dev, "failed to reset\n");
+		goto clk_disable_all;
+	}
+
+	eswin_sdhci_sdio->crg_regmap = syscon_regmap_lookup_by_phandle(
+		pdev->dev.of_node, "eswin,syscrg_csr");
+	if (IS_ERR(eswin_sdhci_sdio->crg_regmap)) {
+		dev_dbg(&pdev->dev, "No syscrg_csr phandle specified\n");
+		goto clk_disable_all;
+	}
+
+	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,syscrg_csr",
+					 1, &eswin_sdhci_sdio->crg_core_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "can't get crg_core_clk (%d)\n", ret);
+		goto clk_disable_all;
+	}
+	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,syscrg_csr",
+					 2, &eswin_sdhci_sdio->crg_aclk_ctrl);
+	if (ret) {
+		dev_err(&pdev->dev, "can't get crg_aclk_ctrl (%d)\n", ret);
+		goto clk_disable_all;
+	}
+	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,syscrg_csr",
+					 3, &eswin_sdhci_sdio->crg_cfg_ctrl);
+	if (ret) {
+		dev_err(&pdev->dev, "can't get crg_cfg_ctrl (%d)\n", ret);
+		goto clk_disable_all;
+	}
+
+	eswin_sdhci_sdio->hsp_regmap = syscon_regmap_lookup_by_phandle(
+		dev->of_node, "eswin,hsp_sp_csr");
+	if (IS_ERR(eswin_sdhci_sdio->hsp_regmap)) {
+		dev_dbg(dev, "No hsp_sp_csr phandle specified\n");
+		goto clk_disable_all;
+	}
+
+	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,hsp_sp_csr",
+					 2, &eswin_sdhci_sdio->hsp_int_status);
+	if (ret) {
+		dev_err(&pdev->dev, "can't get hsp_int_status (%d)\n", ret);
+		goto clk_disable_all;
+	}
+	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,hsp_sp_csr",
+					 3, &eswin_sdhci_sdio->hsp_pwr_ctrl);
+	if (ret) {
+		dev_err(&pdev->dev, "can't get hsp_pwr_ctrl (%d)\n", ret);
+		goto clk_disable_all;
+	}
+
+	regmap_write(eswin_sdhci_sdio->hsp_regmap,
+		     eswin_sdhci_sdio->hsp_int_status, MSHC_INT_CLK_STABLE);
+	regmap_write(eswin_sdhci_sdio->hsp_regmap,
+		     eswin_sdhci_sdio->hsp_pwr_ctrl, MSHC_HOST_VAL_STABLE);
+
+	if (!of_property_read_u32(dev->of_node, "delay_code", &val))
+		eswin_sdhci_sdio->phy.delay_code = val;
+
+	if (!of_property_read_u32(dev->of_node, "drive-impedance-ohm", &val))
+		eswin_sdhci_sdio->phy.drive_impedance =
+			eswin_convert_drive_impedance_ohm(pdev, val);
+
+	if (of_property_read_bool(dev->of_node, "enable-cmd-pullup"))
+		eswin_sdhci_sdio->phy.enable_cmd_pullup = ENABLE;
+	else
+		eswin_sdhci_sdio->phy.enable_cmd_pullup = DISABLE;
+
+	if (of_property_read_bool(dev->of_node, "enable-data-pullup"))
+		eswin_sdhci_sdio->phy.enable_data_pullup = ENABLE;
+	else
+		eswin_sdhci_sdio->phy.enable_data_pullup = DISABLE;
+
+	eswin_sdhci_dt_parse_clk_phases(dev, &eswin_sdhci_sdio->clk_data);
+	ret = mmc_of_parse(host->mmc);
+	if (ret) {
+		ret = dev_err_probe(dev, ret, "parsing dt failed.\n");
+		goto unreg_clk;
+	}
+
+	ret = eswin_sdhci_sdio_add_host(eswin_sdhci_sdio);
+	if (ret)
+		goto unreg_clk;
+
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_suspend_ignore_children(&pdev->dev, 1);
+	pm_runtime_enable(&pdev->dev);
+
+	return 0;
+
+unreg_clk:
+	eswin_sdhci_sdio_unregister_sdclk(dev);
+clk_disable_all:
+	clk_disable_unprepare(clk_xin);
+clk_dis_ahb:
+	clk_disable_unprepare(eswin_sdhci_sdio->clk_ahb);
+err_pltfm_free:
+	sdhci_pltfm_free(pdev);
+	return ret;
+}
+
+static void eswin_sdhci_sdio_remove(struct platform_device *pdev)
+{
+	int ret;
+	struct sdhci_host *host = platform_get_drvdata(pdev);
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct eswin_sdhci_data *eswin_sdhci_sdio =
+		sdhci_pltfm_priv(pltfm_host);
+	struct clk *clk_ahb = eswin_sdhci_sdio->clk_ahb;
+
+	pm_runtime_get_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_put_noidle(&pdev->dev);
+
+	sdhci_pltfm_remove(pdev);
+
+	if (eswin_sdhci_sdio->txrx_rst) {
+		ret = reset_control_assert(eswin_sdhci_sdio->txrx_rst);
+		WARN_ON(ret != 0);
+	}
+
+	if (eswin_sdhci_sdio->phy_rst) {
+		ret = reset_control_assert(eswin_sdhci_sdio->phy_rst);
+		WARN_ON(ret != 0);
+	}
+
+	if (eswin_sdhci_sdio->prstn) {
+		ret = reset_control_assert(eswin_sdhci_sdio->prstn);
+		WARN_ON(ret != 0);
+	}
+
+	if (eswin_sdhci_sdio->arstn) {
+		ret = reset_control_assert(eswin_sdhci_sdio->arstn);
+		WARN_ON(ret != 0);
+	}
+
+	eswin_sdhci_sdio_unregister_sdclk(&pdev->dev);
+	clk_disable_unprepare(clk_ahb);
+}
+
+static const struct dev_pm_ops eswin_sdhci_sdio_pmops = {
+	SET_SYSTEM_SLEEP_PM_OPS(eswin_sdhci_sdio_suspend,
+				eswin_sdhci_sdio_resume)
+		SET_RUNTIME_PM_OPS(eswin_sdhci_sdio_runtime_suspend,
+				   eswin_sdhci_sdio_runtime_resume, NULL)};
+
+static struct platform_driver eswin_sdhci_sdio_driver = {
+	.driver = {
+		.name = "eswin-sdhci-sdio",
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+		.of_match_table = eswin_sdhci_sdio_of_match,
+		.pm = &eswin_sdhci_sdio_pmops,
+	},
+	.probe = eswin_sdhci_sdio_probe,
+	.remove = eswin_sdhci_sdio_remove,
+};
+
+module_platform_driver(eswin_sdhci_sdio_driver);
+
+MODULE_DESCRIPTION("Driver for the Eswin SDHCI Controller");
+MODULE_AUTHOR("Shuang Liang <liangshuang@eswincomputing.com>");
+MODULE_AUTHOR("Xuyang Dong <dongxuyang@eswincomputing.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/sdhci-of-eic7700.c b/drivers/mmc/host/sdhci-of-eic7700.c
new file mode 100644
index 000000000000..078f15b99d67
--- /dev/null
+++ b/drivers/mmc/host/sdhci-of-eic7700.c
@@ -0,0 +1,1053 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ESWIN Emmc Driver
+ *
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
+ *
+ * Authors:
+ *	Shuang Liang <liangshuang@eswincomputing.com>
+ *	Xuyang Dong <dongxuyang@eswincomputing.com>
+ */
+
+#include "cqhci.h"
+#include "sdhci-eic7700.h"
+#include "sdhci-pltfm.h"
+#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
+#include <linux/iommu.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+// EMMC_DWC_MSHC_CRYPTO_CFG_PTR 8 -- parameter
+#define eswin_sdhci_VENDOR_REGISTER_BASEADDR 0x800
+#define eswin_sdhci_VENDOR_EMMC_CTRL_REGISTER 0x2c
+#define VENDOR_ENHANCED_STROBE BIT(8)
+
+#define eswin_sdhci_CQE_BASE_ADDR eswin_sdhci_VENDOR_REGISTER_BASEADDR
+
+/* Controller does not have CD wired and will not function normally without */
+#define eswin_sdhci_QUIRK_FORCE_CDTEST BIT(0)
+/* Controller immediately reports SDHCI_CLOCK_INT_STABLE after enabling the
+ * internal clock even when the clock isn't stable
+ */
+#define eswin_sdhci_QUIRK_CLOCK_UNSTABLE BIT(1)
+
+/*
+ * On some SoCs the syscon area has a feature where the upper 16-bits of
+ * each 32-bit register act as a write mask for the lower 16-bits.  This allows
+ * atomic updates of the register without locking.  This macro is used on SoCs
+ * that have that feature.
+ */
+#define HIWORD_UPDATE(val, mask, shift)                                        \
+	((val) << (shift) | (mask) << ((shift) + 16))
+
+static void eswin_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
+	struct eswin_sdhci_clk_data *clk_data = &eswin_sdhci->clk_data;
+
+	/* Set the Input and Output Clock Phase Delays */
+	if (clk_data->set_clk_delays)
+		clk_data->set_clk_delays(host);
+
+	eswin_sdhci_set_core_clock(host, clock);
+
+	/*
+	 * Some controllers immediately report SDHCI_CLOCK_INT_STABLE
+	 * after enabling the clock even though the clock is not
+	 * stable. Trying to use a clock without waiting here results
+	 * in EILSEQ while detecting some older/slower cards. The
+	 * chosen delay is the maximum delay from sdhci_set_clock.
+	 */
+	if (eswin_sdhci->quirks & SDHCI_ESWIN_QUIRK_CLOCK_UNSTABLE)
+		msleep(20);
+}
+
+static void eswin_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc,
+					      struct mmc_ios *ios)
+{
+	u32 vendor;
+	struct sdhci_host *host = mmc_priv(mmc);
+
+	vendor = sdhci_readl(host, eswin_sdhci_VENDOR_EMMC_CTRL_REGISTER);
+	if (ios->enhanced_strobe)
+		vendor |= VENDOR_ENHANCED_STROBE;
+	else
+		vendor &= ~VENDOR_ENHANCED_STROBE;
+
+	sdhci_writel(host, vendor, eswin_sdhci_VENDOR_EMMC_CTRL_REGISTER);
+}
+
+static void eswin_sdhci_config_phy_delay(struct sdhci_host *host, int delay)
+{
+	delay &= PHY_CLK_MAX_DELAY_MASK;
+
+	/*phy clk delay line config*/
+	sdhci_writeb(host, PHY_UPDATE_DELAY_CODE, PHY_SDCLKDL_CNFG_R);
+	sdhci_writeb(host, delay, PHY_SDCLKDL_DC_R);
+	sdhci_writeb(host, 0x0, PHY_SDCLKDL_CNFG_R);
+}
+
+static void eswin_sdhci_config_phy(struct sdhci_host *host)
+{
+	unsigned int val = 0;
+	unsigned int drv = 0;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
+	struct eswin_sdhci_phy_data *phy = &eswin_sdhci->phy;
+
+	drv = phy->drive_impedance << PHY_PAD_SP_DRIVE_SHIF;
+
+	pr_debug("%s: phy drv=0x%x\n", mmc_hostname(host->mmc), drv);
+
+	val = sdhci_readw(host, VENDOR_EMMC_CTRL_R);
+	val |= EMMC_CRAD_PRESENT; // emmc card
+	sdhci_writew(host, val, VENDOR_EMMC_CTRL_R);
+
+	eswin_sdhci_disable_card_clk(host);
+
+	/* reset phy,config phy's pad */
+	sdhci_writel(host, drv | (~PHY_RSTN), PHY_CNFG_R);
+	/*CMDPAD_CNFS*/
+	val = (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_P_BIT_SHIFT) |
+	      (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_N_BIT_SHIFT) |
+	      (phy->enable_cmd_pullup << PHY_PULL_BIT_SHIF) | PHY_PAD_RXSEL_1;
+	sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
+	pr_debug("%s: phy cmd=0x%x\n", mmc_hostname(host->mmc), val);
+
+	/*DATA PAD CNFG*/
+	val = (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_P_BIT_SHIFT) |
+	      (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_N_BIT_SHIFT) |
+	      (phy->enable_data_pullup << PHY_PULL_BIT_SHIF) | PHY_PAD_RXSEL_1;
+	sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
+	pr_debug("%s: phy data=0x%x\n", mmc_hostname(host->mmc), val);
+
+	/*Clock PAD Setting*/
+	val = (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_P_BIT_SHIFT) |
+	      (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_N_BIT_SHIFT) | PHY_PAD_RXSEL_0;
+	sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
+	pr_debug("%s: phy clk=0x%x\n", mmc_hostname(host->mmc), val);
+
+	/*PHY strobe PAD setting*/
+	val = (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_P_BIT_SHIFT) |
+	      (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_N_BIT_SHIFT) |
+	      ((phy->enable_strobe_pulldown * PHY_PULL_DOWN)
+	       << PHY_PULL_BIT_SHIF) |
+	      PHY_PAD_RXSEL_1;
+	sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
+	pr_debug("%s: phy strobe=0x%x\n", mmc_hostname(host->mmc), val);
+	mdelay(2);
+
+	/*PHY RSTN PAD setting*/
+	val = (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_P_BIT_SHIFT) |
+	      (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_N_BIT_SHIFT) |
+	      (PHY_PULL_UP << PHY_PULL_BIT_SHIF) | PHY_PAD_RXSEL_1;
+	sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
+	pr_debug("%s: phy rstn=0x%x\n", mmc_hostname(host->mmc), val);
+
+	sdhci_writel(host, drv | PHY_RSTN, PHY_CNFG_R);
+
+	eswin_sdhci_config_phy_delay(host, phy->delay_code);
+
+	eswin_sdhci_enable_card_clk(host);
+}
+
+static void eswin_sdhci_reset(struct sdhci_host *host, u8 mask)
+{
+	u8 ctrl;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
+
+	sdhci_writel(host, 0, SDHCI_INT_ENABLE);
+	sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
+	sdhci_reset(host, mask);
+	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+
+	if (eswin_sdhci->quirks & SDHCI_ESWIN_QUIRK_FORCE_CDTEST) {
+		ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+		ctrl |= SDHCI_CTRL_CDTEST_INS | SDHCI_CTRL_CDTEST_EN;
+		sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+	}
+
+	if (mask == SDHCI_RESET_ALL) { // after reset all,the phy`s config will
+				       // be clear.
+		eswin_sdhci_config_phy(host);
+	}
+}
+
+static u32 eswin_sdhci_cqhci_irq(struct sdhci_host *host, u32 intmask)
+{
+	int cmd_error = 0;
+	int data_error = 0;
+
+	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
+		return intmask;
+
+	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
+
+	return 0;
+}
+
+static void eswin_sdhci_dumpregs(struct mmc_host *mmc)
+{
+	sdhci_dumpregs(mmc_priv(mmc));
+}
+
+static void eswin_sdhci_cqe_enable(struct mmc_host *mmc)
+{
+	struct sdhci_host *host = mmc_priv(mmc);
+	u32 reg;
+
+	reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
+	while (reg & SDHCI_DATA_AVAILABLE) {
+		sdhci_readl(host, SDHCI_BUFFER);
+		reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
+	}
+
+	sdhci_cqe_enable(mmc);
+}
+
+static int eswin_sdhci_delay_tuning(struct sdhci_host *host, u32 opcode)
+{
+	int ret;
+	int delay = 0;
+	int i = 0;
+	int delay_min = -1;
+	int delay_max = -1;
+	int cmd_error = 0;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
+
+	for (i = 0; i <= PHY_DELAY_CODE_MAX; i++) {
+		eswin_sdhci_disable_card_clk(host);
+		eswin_sdhci_config_phy_delay(host, i);
+		eswin_sdhci_enable_card_clk(host);
+		ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
+		if (ret) {
+			host->ops->reset(host,
+					 SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+			udelay(200);
+			if (delay_min != -1 && delay_max != -1)
+				break;
+		} else {
+			if (delay_min == -1) {
+				delay_min = i;
+				continue;
+			} else {
+				delay_max = i;
+				continue;
+			}
+		}
+	}
+	if (delay_min == -1 && delay_max == -1) {
+		pr_err("%s: delay code tuning failed!\n",
+		       mmc_hostname(host->mmc));
+		eswin_sdhci_disable_card_clk(host);
+		eswin_sdhci_config_phy_delay(host, eswin_sdhci->phy.delay_code);
+		eswin_sdhci_enable_card_clk(host);
+
+		return ret;
+	}
+
+	delay = (delay_min + delay_max) / 2;
+	pr_debug("%s: set delay:0x%x\n", mmc_hostname(host->mmc), delay);
+	eswin_sdhci_disable_card_clk(host);
+	eswin_sdhci_config_phy_delay(host, delay);
+	eswin_sdhci_enable_card_clk(host);
+
+	return 0;
+}
+
+static int eswin_sdhci_phase_code_tuning(struct sdhci_host *host, u32 opcode)
+{
+	int cmd_error = 0;
+	int ret = 0;
+	int phase_code = 0;
+	int code_min = -1;
+	int code_max = -1;
+
+	for (phase_code = 0; phase_code <= MAX_PHASE_CODE; phase_code++) {
+		eswin_sdhci_disable_card_clk(host);
+		sdhci_writew(host, phase_code, VENDOR_AT_SATA_R);
+		eswin_sdhci_enable_card_clk(host);
+
+		ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
+		if (ret) {
+			host->ops->reset(host,
+					 SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+			udelay(200);
+			if (code_min != -1 && code_max != -1)
+				break;
+		} else {
+			if (code_min == -1) {
+				code_min = phase_code;
+				continue;
+			} else {
+				code_max = phase_code;
+				continue;
+			}
+		}
+	}
+	if (code_min == -1 && code_max == -1) {
+		pr_err("%s: phase code tuning failed!\n",
+		       mmc_hostname(host->mmc));
+		eswin_sdhci_disable_card_clk(host);
+		sdhci_writew(host, 0, VENDOR_AT_SATA_R);
+		eswin_sdhci_enable_card_clk(host);
+		return -EIO;
+	}
+
+	phase_code = (code_min + code_max) / 2;
+	pr_debug("%s: set phase_code:0x%x\n", mmc_hostname(host->mmc),
+		 phase_code);
+
+	eswin_sdhci_disable_card_clk(host);
+	sdhci_writew(host, phase_code, VENDOR_AT_SATA_R);
+	eswin_sdhci_enable_card_clk(host);
+
+	return 0;
+}
+
+static int eswin_sdhci_executing_tuning(struct sdhci_host *host, u32 opcode)
+{
+	u32 ctrl;
+	u32 val;
+	int ret = 0;
+
+	eswin_sdhci_disable_card_clk(host);
+
+	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+	ctrl &= ~SDHCI_CTRL_TUNED_CLK;
+	sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+	val = sdhci_readl(host, VENDOR_AT_CTRL_R);
+	val |= SW_TUNE_ENABLE;
+	sdhci_writew(host, val, VENDOR_AT_CTRL_R);
+	sdhci_writew(host, 0, VENDOR_AT_SATA_R);
+
+	eswin_sdhci_enable_card_clk(host);
+
+	sdhci_writew(host, 0x0, SDHCI_CMD_DATA);
+
+	ret = eswin_sdhci_delay_tuning(host, opcode);
+	if (ret < 0)
+		return ret;
+
+	ret = eswin_sdhci_phase_code_tuning(host, opcode);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void eswin_sdhci_set_uhs_signaling(struct sdhci_host *host,
+					  unsigned int timing)
+{
+	u32 val;
+	u32 status;
+	u32 timeout = 0;
+	u16 ctrl_2;
+
+	ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+	/* Select Bus Speed Mode for host */
+	ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+	if ((timing == MMC_TIMING_MMC_HS200) ||
+	    (timing == MMC_TIMING_UHS_SDR104))
+		ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
+	else if (timing == MMC_TIMING_UHS_SDR12)
+		ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
+	else if (timing == MMC_TIMING_UHS_SDR25)
+		ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
+	else if (timing == MMC_TIMING_UHS_SDR50)
+		ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
+	else if ((timing == MMC_TIMING_UHS_DDR50) ||
+		 (timing == MMC_TIMING_MMC_DDR52))
+		ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
+	else if (timing == MMC_TIMING_MMC_HS400)
+		ctrl_2 |= ESWIN_SDHCI_CTRL_HS400; /* Non-standard */
+	sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+
+	/*
+	 * here need make dll locked when in hs400 at 200MHz
+	 */
+	if ((timing == MMC_TIMING_MMC_HS400) && (host->clock == 200000000)) {
+		eswin_sdhci_disable_card_clk(host);
+
+		val = sdhci_readl(host, VENDOR_AT_CTRL_R);
+		val &= ~(LATENCY_LT_MASK << LATENCY_LT_BIT_OFFSET);
+		val |= (LATENCY_LT_3 << LATENCY_LT_MASK);
+		sdhci_writel(host, val, VENDOR_AT_CTRL_R);
+
+		sdhci_writeb(host, 0x23, PHY_DLL_CNFG1_R);
+		sdhci_writeb(host, 0x02, PHY_DLL_CNFG2_R);
+		sdhci_writeb(host, 0x60, PHY_DLLDL_CNFG_R);
+		sdhci_writeb(host, 0x00, PHY_DLL_OFFST_R);
+		sdhci_writew(host, 0xffff, PHY_DLLBT_CNFG_R);
+
+		eswin_sdhci_enable_card_clk(host);
+		sdhci_writeb(host, DLL_ENABEL, PHY_DLL_CTRL_R);
+		udelay(100);
+
+		while (1) {
+			status = sdhci_readb(host, PHY_DLL_STATUS_R);
+			if (status & DLL_LOCK_STS) {
+				pr_debug("%s: locked status:0x%x\n",
+					 mmc_hostname(host->mmc), status);
+				break;
+			}
+			timeout++;
+			udelay(100);
+			if (timeout > 10000) {
+				pr_err("%s: DLL lock failed!status:0x%x\n",
+				       mmc_hostname(host->mmc), status);
+				return;
+			}
+		}
+
+		status = sdhci_readb(host, PHY_DLL_STATUS_R);
+		if (status & DLL_ERROR_STS) {
+			pr_err("%s: DLL lock failed!err_status:0x%x\n",
+			       mmc_hostname(host->mmc), status);
+		} else {
+			pr_debug("%s: DLL lock is success\n",
+				 mmc_hostname(host->mmc));
+		}
+	}
+}
+
+static const struct cqhci_host_ops eswin_sdhci_cqhci_ops = {
+	.enable = eswin_sdhci_cqe_enable,
+	.disable = sdhci_cqe_disable,
+	.dumpregs = eswin_sdhci_dumpregs,
+};
+
+static const struct sdhci_ops eswin_sdhci_cqe_ops = {
+	.set_clock = eswin_sdhci_set_clock,
+	.get_max_clock = sdhci_pltfm_clk_get_max_clock,
+	.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
+	.set_bus_width = sdhci_set_bus_width,
+	.reset = eswin_sdhci_reset,
+	.set_uhs_signaling = eswin_sdhci_set_uhs_signaling,
+	.set_power = sdhci_set_power_and_bus_voltage,
+	.irq = eswin_sdhci_cqhci_irq,
+	.platform_execute_tuning = eswin_sdhci_executing_tuning,
+	.dump_vendor_regs = eswin_sdhci_dump_vendor_regs,
+};
+
+static const struct sdhci_pltfm_data eswin_sdhci_cqe_pdata = {
+	.ops = &eswin_sdhci_cqe_ops,
+	.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+		  SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
+	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+		   SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
+};
+
+#ifdef CONFIG_PM_SLEEP
+/**
+ * eswin_sdhci_suspend - Suspend method for the driver
+ * @dev:    Address of the device structure
+ *
+ * Put the device in a low power state.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int eswin_sdhci_suspend(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
+	int ret;
+
+	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
+		mmc_retune_needed(host->mmc);
+
+	ret = sdhci_suspend_host(host);
+	if (ret)
+		return ret;
+
+	clk_disable_unprepare(pltfm_host->clk);
+	clk_disable_unprepare(eswin_sdhci->clk_ahb);
+
+	return 0;
+}
+
+/**
+ * eswin_sdhci_resume - Resume method for the driver
+ * @dev:    Address of the device structure
+ *
+ * Resume operation after suspend
+ *
+ * Return: 0 on success and error value on error
+ */
+static int eswin_sdhci_resume(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
+	int ret;
+
+	ret = clk_prepare_enable(eswin_sdhci->clk_ahb);
+	if (ret) {
+		dev_err(dev, "can't enable clk_ahb\n");
+		return ret;
+	}
+	ret = clk_prepare_enable(pltfm_host->clk);
+	if (ret) {
+		dev_err(dev, "can't enable mainck\n");
+		goto clk_ahb_disable;
+	}
+
+	ret = sdhci_resume_host(host);
+	if (ret) {
+		dev_err(dev, "runtime resume failed!\n");
+		goto clk_disable;
+	}
+
+	return 0;
+clk_disable:
+	clk_disable_unprepare(pltfm_host->clk);
+clk_ahb_disable:
+	clk_disable_unprepare(eswin_sdhci->clk_ahb);
+
+	return ret;
+}
+
+#endif /* ! CONFIG_PM_SLEEP */
+
+/**
+ * eswin_sdhci_sdcardclk_recalc_rate - Return the card clock rate
+ *
+ * @hw:         Pointer to the hardware clock structure.
+ * @parent_rate:        The parent rate (should be rate of clk_xin).
+ *
+ * Return the current actual rate of the SD card clock.  This can be used
+ * to communicate with out PHY.
+ *
+ * Return: The card clock rate.
+ */
+static unsigned long
+eswin_sdhci_sdcardclk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+	struct eswin_sdhci_clk_data *clk_data =
+		container_of(hw, struct eswin_sdhci_clk_data, sdcardclk_hw);
+	struct eswin_sdhci_data *eswin_sdhci =
+		container_of(clk_data, struct eswin_sdhci_data, clk_data);
+	struct sdhci_host *host = eswin_sdhci->host;
+
+	return host->mmc->actual_clock;
+}
+
+static const struct clk_ops eswin_sdcardclk_ops = {
+	.recalc_rate = eswin_sdhci_sdcardclk_recalc_rate,
+};
+
+/**
+ * eswin_sdhci_sampleclk_recalc_rate - Return the sampling clock rate
+ *
+ * @hw:         Pointer to the hardware clock structure.
+ * @parent_rate:        The parent rate (should be rate of clk_xin).
+ *
+ * Return the current actual rate of the sampling clock.  This can be used
+ * to communicate with out PHY.
+ *
+ * Return: The sample clock rate.
+ */
+static unsigned long
+eswin_sdhci_sampleclk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+	struct eswin_sdhci_clk_data *clk_data =
+		container_of(hw, struct eswin_sdhci_clk_data, sampleclk_hw);
+	struct eswin_sdhci_data *eswin_sdhci =
+		container_of(clk_data, struct eswin_sdhci_data, clk_data);
+	struct sdhci_host *host = eswin_sdhci->host;
+
+	return host->mmc->actual_clock;
+}
+
+static const struct clk_ops eswin_sampleclk_ops = {
+	.recalc_rate = eswin_sdhci_sampleclk_recalc_rate,
+};
+
+static const struct eswin_sdhci_clk_ops eswin_clk_ops = {
+	.sdcardclk_ops = &eswin_sdcardclk_ops,
+	.sampleclk_ops = &eswin_sampleclk_ops,
+};
+
+static struct eswin_sdhci_of_data eswin_sdhci_fu800_data = {
+	.pdata = &eswin_sdhci_cqe_pdata,
+	.clk_ops = &eswin_clk_ops,
+};
+
+static const struct of_device_id eswin_sdhci_of_match[] = {
+	{
+		.compatible = "eswin,eic7700-emmc-sdhci",
+		.data = &eswin_sdhci_fu800_data,
+	},
+	{/* sentinel */}};
+MODULE_DEVICE_TABLE(of, eswin_sdhci_of_match);
+
+/**
+ * eswin_sdhci_register_sdcardclk - Register the sdcardclk for a PHY to use
+ *
+ * @eswin_sdhci:    Our private data structure.
+ * @clk_xin:        Pointer to the functional clock
+ * @dev:        Pointer to our struct device.
+ *
+ * Some PHY devices need to know what the actual card clock is.  In order for
+ * them to find out, we'll provide a clock through the common clock framework
+ * for them to query.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int eswin_sdhci_register_sdcardclk(struct eswin_sdhci_data *eswin_sdhci,
+					  struct clk *clk_xin,
+					  struct device *dev)
+{
+	struct eswin_sdhci_clk_data *clk_data = &eswin_sdhci->clk_data;
+	struct device_node *np = dev->of_node;
+	struct clk_init_data sdcardclk_init;
+	const char *parent_clk_name;
+	int ret;
+
+	ret = of_property_read_string_index(np, "clock-output-names", 0,
+					    &sdcardclk_init.name);
+	if (ret) {
+		dev_err(dev, "DT has #clock-cells but no clock-output-names\n");
+		return ret;
+	}
+
+	parent_clk_name = __clk_get_name(clk_xin);
+	sdcardclk_init.parent_names = &parent_clk_name;
+	sdcardclk_init.num_parents = 1;
+	sdcardclk_init.flags = CLK_GET_RATE_NOCACHE;
+	sdcardclk_init.ops = eswin_sdhci->clk_ops->sdcardclk_ops;
+
+	clk_data->sdcardclk_hw.init = &sdcardclk_init;
+	clk_data->sdcardclk = devm_clk_register(dev, &clk_data->sdcardclk_hw);
+	if (IS_ERR(clk_data->sdcardclk))
+		return PTR_ERR(clk_data->sdcardclk);
+	clk_data->sdcardclk_hw.init = NULL;
+
+	ret = of_clk_add_provider(np, of_clk_src_simple_get,
+				  clk_data->sdcardclk);
+	if (ret)
+		dev_err(dev, "Failed to add sdcard clock provider\n");
+
+	return ret;
+}
+
+/**
+ * eswin_sdhci_register_sampleclk - Register the sampleclk for a PHY to use
+ *
+ * @eswin_sdhci:    Our private data structure.
+ * @clk_xin:        Pointer to the functional clock
+ * @dev:        Pointer to our struct device.
+ *
+ * Some PHY devices need to know what the actual card clock is.  In order for
+ * them to find out, we'll provide a clock through the common clock framework
+ * for them to query.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int eswin_sdhci_register_sampleclk(struct eswin_sdhci_data *eswin_sdhci,
+					  struct clk *clk_xin,
+					  struct device *dev)
+{
+	struct eswin_sdhci_clk_data *clk_data = &eswin_sdhci->clk_data;
+	struct device_node *np = dev->of_node;
+	struct clk_init_data sampleclk_init;
+	const char *parent_clk_name;
+	int ret;
+
+	ret = of_property_read_string_index(np, "clock-output-names", 1,
+					    &sampleclk_init.name);
+	if (ret) {
+		dev_err(dev, "DT has #clock-cells but no clock-output-names\n");
+		return ret;
+	}
+
+	parent_clk_name = __clk_get_name(clk_xin);
+	sampleclk_init.parent_names = &parent_clk_name;
+	sampleclk_init.num_parents = 1;
+	sampleclk_init.flags = CLK_GET_RATE_NOCACHE;
+	sampleclk_init.ops = eswin_sdhci->clk_ops->sampleclk_ops;
+
+	clk_data->sampleclk_hw.init = &sampleclk_init;
+	clk_data->sampleclk = devm_clk_register(dev, &clk_data->sampleclk_hw);
+	if (IS_ERR(clk_data->sampleclk))
+		return PTR_ERR(clk_data->sampleclk);
+	clk_data->sampleclk_hw.init = NULL;
+
+	ret = of_clk_add_provider(np, of_clk_src_simple_get,
+				  clk_data->sampleclk);
+	if (ret)
+		dev_err(dev, "Failed to add sample clock provider\n");
+
+	return ret;
+}
+
+/**
+ * eswin_sdhci_unregister_sdclk - Undoes eswin_sdhci_register_sdclk()
+ *
+ * @dev:        Pointer to our struct device.
+ *
+ * Should be called any time we're exiting and eswin_sdhci_register_sdclk()
+ * returned success.
+ */
+static void eswin_sdhci_unregister_sdclk(struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+
+	if (!of_find_property(np, "#clock-cells", NULL))
+		return;
+
+	of_clk_del_provider(dev->of_node);
+}
+
+/**
+ * eswin_sdhci_register_sdclk - Register the sdcardclk for a PHY to use
+ *
+ * @eswin_sdhci:    Our private data structure.
+ * @clk_xin:        Pointer to the functional clock
+ * @dev:        Pointer to our struct device.
+ *
+ * Some PHY devices need to know what the actual card clock is.  In order for
+ * them to find out, we'll provide a clock through the common clock framework
+ * for them to query.
+ *
+ * Note: without seriously re-architecting SDHCI's clock code and testing on
+ * all platforms, there's no way to create a totally beautiful clock here
+ * with all clock ops implemented.  Instead, we'll just create a clock that can
+ * be queried and set the CLK_GET_RATE_NOCACHE attribute to tell common clock
+ * framework that we're doing things behind its back.  This should be sufficient
+ * to create nice clean device tree bindings and later (if needed) we can try
+ * re-architecting SDHCI if we see some benefit to it.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int eswin_sdhci_register_sdclk(struct eswin_sdhci_data *eswin_sdhci,
+				      struct clk *clk_xin, struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+	u32 num_clks = 0;
+	int ret;
+
+	/* Providing a clock to the PHY is optional; no error if missing */
+	if (of_property_read_u32(np, "#clock-cells", &num_clks) < 0)
+		return 0;
+
+	ret = eswin_sdhci_register_sdcardclk(eswin_sdhci, clk_xin, dev);
+	if (ret)
+		return ret;
+
+	if (num_clks) {
+		ret = eswin_sdhci_register_sampleclk(eswin_sdhci, clk_xin, dev);
+		if (ret) {
+			eswin_sdhci_unregister_sdclk(dev);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int eswin_sdhci_add_host(struct eswin_sdhci_data *eswin_sdhci)
+{
+	struct sdhci_host *host = eswin_sdhci->host;
+	struct cqhci_host *cq_host;
+	bool dma64;
+	int ret;
+
+	if (!eswin_sdhci->has_cqe)
+		return sdhci_add_host(host);
+
+	ret = sdhci_setup_host(host);
+	if (ret)
+		return ret;
+
+	cq_host = devm_kzalloc(host->mmc->parent, sizeof(*cq_host), GFP_KERNEL);
+	if (!cq_host) {
+		ret = -ENOMEM;
+		goto cleanup;
+	}
+
+	cq_host->mmio = host->ioaddr + eswin_sdhci_CQE_BASE_ADDR;
+	cq_host->ops = &eswin_sdhci_cqhci_ops;
+
+	dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
+	if (dma64)
+		cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
+
+	ret = cqhci_init(cq_host, host->mmc, dma64);
+	if (ret)
+		goto cleanup;
+
+	ret = __sdhci_add_host(host);
+	if (ret)
+		goto cleanup;
+
+	return 0;
+
+cleanup:
+	sdhci_cleanup_host(host);
+	return ret;
+}
+
+static int eswin_sdhci_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct clk *clk_xin;
+	struct sdhci_host *host;
+	struct sdhci_pltfm_host *pltfm_host;
+	struct device *dev = &pdev->dev;
+	struct eswin_sdhci_data *eswin_sdhci;
+	const struct eswin_sdhci_of_data *data;
+	unsigned int val = 0;
+
+	data = of_device_get_match_data(dev);
+	host = sdhci_pltfm_init(pdev, data->pdata, sizeof(*eswin_sdhci));
+	if (IS_ERR(host))
+		return PTR_ERR(host);
+
+	pltfm_host = sdhci_priv(host);
+	eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
+	eswin_sdhci->host = host;
+	eswin_sdhci->clk_ops = data->clk_ops;
+
+	eswin_sdhci->clk_ahb = devm_clk_get(dev, "clk_ahb");
+	if (IS_ERR(eswin_sdhci->clk_ahb)) {
+		ret = dev_err_probe(dev, PTR_ERR(eswin_sdhci->clk_ahb),
+				    "clk_ahb clock not found.\n");
+		goto err_pltfm_free;
+	}
+
+	clk_xin = devm_clk_get(dev, "clk_xin");
+	if (IS_ERR(clk_xin)) {
+		ret = dev_err_probe(dev, PTR_ERR(clk_xin),
+				    "clk_xin clock not found.\n");
+		goto err_pltfm_free;
+	}
+
+	ret = clk_prepare_enable(eswin_sdhci->clk_ahb);
+	if (ret) {
+		dev_err(dev, "Unable to enable AHB clock.\n");
+		goto err_pltfm_free;
+	}
+
+	ret = clk_prepare_enable(clk_xin);
+	if (ret) {
+		dev_err(dev, "Unable to enable SD clock.\n");
+		goto clk_dis_ahb;
+	}
+
+	ret = eswin_sdhci_reset_init(dev, eswin_sdhci);
+	if (ret < 0) {
+		dev_err(dev, "failed to reset\n");
+		goto clk_disable_all;
+	}
+
+	eswin_sdhci->crg_regmap = syscon_regmap_lookup_by_phandle(
+		pdev->dev.of_node, "eswin,syscrg_csr");
+	if (IS_ERR(eswin_sdhci->crg_regmap)) {
+		dev_dbg(&pdev->dev, "No syscrg_csr phandle specified\n");
+		goto clk_disable_all;
+	}
+
+	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,syscrg_csr",
+					 1, &eswin_sdhci->crg_core_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "can't get crg_core_clk (%d)\n", ret);
+		goto clk_disable_all;
+	}
+	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,syscrg_csr",
+					 2, &eswin_sdhci->crg_aclk_ctrl);
+	if (ret) {
+		dev_err(&pdev->dev, "can't get crg_aclk_ctrl (%d)\n", ret);
+		goto clk_disable_all;
+	}
+	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,syscrg_csr",
+					 3, &eswin_sdhci->crg_cfg_ctrl);
+	if (ret) {
+		dev_err(&pdev->dev, "can't get crg_cfg_ctrl (%d)\n", ret);
+		goto clk_disable_all;
+	}
+
+	eswin_sdhci->hsp_regmap = syscon_regmap_lookup_by_phandle(
+		dev->of_node, "eswin,hsp_sp_csr");
+	if (IS_ERR(eswin_sdhci->hsp_regmap)) {
+		dev_dbg(dev, "No hsp_sp_csr phandle specified\n");
+		goto clk_disable_all;
+	}
+
+	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,hsp_sp_csr",
+					 2, &eswin_sdhci->hsp_int_status);
+	if (ret) {
+		dev_err(&pdev->dev, "can't get hsp_int_status (%d)\n", ret);
+		goto clk_disable_all;
+	}
+	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,hsp_sp_csr",
+					 3, &eswin_sdhci->hsp_pwr_ctrl);
+	if (ret) {
+		dev_err(&pdev->dev, "can't get hsp_pwr_ctrl (%d)\n", ret);
+		goto clk_disable_all;
+	}
+
+	regmap_write(eswin_sdhci->hsp_regmap, eswin_sdhci->hsp_int_status,
+		     MSHC_INT_CLK_STABLE);
+	regmap_write(eswin_sdhci->hsp_regmap, eswin_sdhci->hsp_pwr_ctrl,
+		     MSHC_HOST_VAL_STABLE);
+
+	if (!of_property_read_u32(dev->of_node, "delay_code", &val))
+		eswin_sdhci->phy.delay_code = val;
+
+	if (!of_property_read_u32(dev->of_node, "drive-impedance-ohm", &val))
+		eswin_sdhci->phy.drive_impedance =
+			eswin_convert_drive_impedance_ohm(pdev, val);
+
+	if (of_property_read_bool(dev->of_node, "enable-cmd-pullup"))
+		eswin_sdhci->phy.enable_cmd_pullup = ENABLE;
+	else
+		eswin_sdhci->phy.enable_cmd_pullup = DISABLE;
+
+	if (of_property_read_bool(dev->of_node, "enable-data-pullup"))
+		eswin_sdhci->phy.enable_data_pullup = ENABLE;
+	else
+		eswin_sdhci->phy.enable_data_pullup = DISABLE;
+
+	if (of_property_read_bool(dev->of_node, "enable-strobe-pulldown"))
+		eswin_sdhci->phy.enable_strobe_pulldown = ENABLE;
+	else
+		eswin_sdhci->phy.enable_strobe_pulldown = DISABLE;
+
+	sdhci_get_of_property(pdev);
+
+	pltfm_host->clk = clk_xin;
+
+	ret = eswin_sdhci_register_sdclk(eswin_sdhci, clk_xin, dev);
+	if (ret)
+		goto clk_disable_all;
+
+	eswin_sdhci_dt_parse_clk_phases(dev, &eswin_sdhci->clk_data);
+
+	ret = mmc_of_parse(host->mmc);
+	if (ret) {
+		ret = dev_err_probe(dev, ret, "parsing dt failed.\n");
+		goto unreg_clk;
+	}
+
+	if (of_device_is_compatible(dev->of_node, "eswin,sdhci-5.1")) {
+		host->mmc_host_ops.hs400_enhanced_strobe =
+			eswin_sdhci_hs400_enhanced_strobe;
+		eswin_sdhci->has_cqe = true;
+		host->mmc->caps2 |= MMC_CAP2_CQE;
+
+		if (!of_property_read_bool(dev->of_node, "disable-cqe-dcmd"))
+			host->mmc->caps2 |= MMC_CAP2_CQE_DCMD;
+	}
+
+	sdhci_enable_v4_mode(eswin_sdhci->host);
+
+	ret = eswin_sdhci_add_host(eswin_sdhci);
+	if (ret)
+		goto unreg_clk;
+
+	return 0;
+
+unreg_clk:
+	eswin_sdhci_unregister_sdclk(dev);
+clk_disable_all:
+	clk_disable_unprepare(clk_xin);
+clk_dis_ahb:
+	clk_disable_unprepare(eswin_sdhci->clk_ahb);
+err_pltfm_free:
+	sdhci_pltfm_free(pdev);
+	return ret;
+}
+
+static void eswin_sdhci_remove(struct platform_device *pdev)
+{
+	int ret;
+	struct sdhci_host *host = platform_get_drvdata(pdev);
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
+	struct clk *clk_ahb = eswin_sdhci->clk_ahb;
+
+	sdhci_pltfm_remove(pdev);
+
+	if (eswin_sdhci->txrx_rst) {
+		ret = reset_control_assert(eswin_sdhci->txrx_rst);
+		WARN_ON(ret != 0);
+	}
+
+	if (eswin_sdhci->phy_rst) {
+		ret = reset_control_assert(eswin_sdhci->phy_rst);
+		WARN_ON(ret != 0);
+	}
+
+	if (eswin_sdhci->prstn) {
+		ret = reset_control_assert(eswin_sdhci->prstn);
+		WARN_ON(ret != 0);
+	}
+
+	if (eswin_sdhci->arstn) {
+		ret = reset_control_assert(eswin_sdhci->arstn);
+		WARN_ON(ret != 0);
+	}
+	eswin_sdhci_unregister_sdclk(&pdev->dev);
+	clk_disable_unprepare(clk_ahb);
+}
+
+static void emmc_hard_reset(struct sdhci_host *host)
+{
+	unsigned int val;
+
+	val = sdhci_readw(host, VENDOR_EMMC_CTRL_R);
+	val |= EMMC_RST_N_OE;
+	sdhci_writew(host, val, VENDOR_EMMC_CTRL_R);
+	val &= ~EMMC_RST_N;
+	sdhci_writew(host, val, VENDOR_EMMC_CTRL_R);
+	mdelay(20);
+	val |= EMMC_RST_N;
+	sdhci_writew(host, val, VENDOR_EMMC_CTRL_R);
+}
+
+static void eswin_sdhci_shutdown(struct platform_device *pdev)
+{
+	struct sdhci_host *host = platform_get_drvdata(pdev);
+
+	if (!host)
+		return;
+
+	emmc_hard_reset(host);
+	host->ops->reset(host, SDHCI_RESET_ALL);
+	platform_set_drvdata(pdev, NULL);
+}
+
+static const struct dev_pm_ops eswin_sdhci_pmops = {
+	SET_SYSTEM_SLEEP_PM_OPS(eswin_sdhci_suspend, eswin_sdhci_resume)};
+
+static struct platform_driver eswin_sdhci_driver = {
+	.driver = {
+		.name = "sdhci-eic7700",
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+		.of_match_table = eswin_sdhci_of_match,
+		.pm = &eswin_sdhci_pmops,
+	},
+	.probe = eswin_sdhci_probe,
+	.remove = eswin_sdhci_remove,
+	.shutdown = eswin_sdhci_shutdown,
+};
+
+module_platform_driver(eswin_sdhci_driver);
+
+MODULE_DESCRIPTION("Driver for the ESWIN SDHCI Controller");
+MODULE_AUTHOR("Shuang Liang <liangshuang@eswincomputing.com>");
+MODULE_AUTHOR("Xuyang Dong <dongxuyang@eswincomputing.com>");
+MODULE_LICENSE("GPL");
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH v1 1/2] dt-bindings: sdhci: eswin: Documentation for eic7700 SoC
  2025-05-16  9:16 ` [PATCH v1 1/2] dt-bindings: sdhci: eswin: Documentation for eic7700 SoC dongxuyang
@ 2025-05-16 10:26   ` Rob Herring (Arm)
  2025-05-16 13:12   ` Krzysztof Kozlowski
  1 sibling, 0 replies; 8+ messages in thread
From: Rob Herring (Arm) @ 2025-05-16 10:26 UTC (permalink / raw)
  To: dongxuyang
  Cc: adrian.hunter, ningyu, conor+dt, devicetree, p.zabel,
	linux-kernel, ulf.hansson, shanchun1218, xuxiang, linmin,
	linux-mmc, krzk+dt


On Fri, 16 May 2025 17:16:50 +0800, dongxuyang@eswincomputing.com wrote:
> From: Xuyang Dong <dongxuyang@eswincomputing.com>
> 
> Add device tree binding documentation for the ESWIN
> eic7700 sdhci controller module.
> 
> Signed-off-by: Xiang Xu <xuxiang@eswincomputing.com>
> Signed-off-by: Xuyang Dong <dongxuyang@eswincomputing.com>
> ---
>  .../bindings/mmc/eswin,sdhci-eic7700.yaml     | 131 ++++++++++++++++++
>  1 file changed, 131 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mmc/eswin,sdhci-eic7700.yaml
> 

My bot found errors running 'make dt_binding_check' on your patch:

yamllint warnings/errors:

dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/mmc/eswin,sdhci-eic7700.example.dtb: mmc@50450000 (eswin,eic7700-emmc-sdhci): 'eswin,hsp_sp_csr', 'eswin,syscrg_csr' do not match any of the regexes: '^#.*', '^(at25|bm|devbus|dmacap|dsa|exynos|fsi[ab]|gpio-fan|gpio-key|gpio|gpmc|hdmi|i2c-gpio),.*', '^(keypad|m25p|max8952|max8997|max8998|mpmc),.*', '^(pciclass|pinctrl-single|#pinctrl-single|PowerPC),.*', '^(pl022|pxa-mmc|rcar_sound|rotary-encoder|s5m8767|sdhci),.*', '^(simple-audio-card|st-plgpio|st-spics|ts),.*', '^100ask,.*', '^70mai,.*', '^8dev,.*', '^GEFanuc,.*', '^IBM,.*', '^ORCL,.*', '^SUNW,.*', '^[a-zA-Z0-9#_][a-zA-Z0-9+\\-._@]{0,63}$', '^[a-zA-Z0-9+\\-._]*@[0-9a-zA-Z,]*$', '^abb,.*', '^abilis,.*', '^abracon,.*', '^abt,.*', '^acbel,.*', '^acelink,.*', '^acer,.*', '^acme,.*', '^actions,.*', '^active-semi,.*', '^ad,.*', '^adafruit,.*', '^adapteva,.*', '^adaptrum,.*', '^adh,.*', '^adi,.*', '^adieng,.*', '^admatec,.*', '^advantech,.*', '^aeroflexgaisler,.*', '^aesop,.*', '^airoha,.*', '^al,.*', '^alcatel,.*', '^aldec,.*', '^alfa-network,.*', '^allegro,.*', '^allegromicro,.*', '^alliedvision,.*', '^allo,.*', '^allwinner,.*', '^alphascale,.*', '^alps,.*', '^alt,.*', '^altr,.*', '^amarula,.*', '^amazon,.*', '^amcc,.*', '^amd,.*', '^amediatech,.*', '^amlogic,.*', '^ampere,.*', '^amphenol,.*', '^ampire,.*', '^ams,.*', '^amstaos,.*', '^analogix,.*', '^anbernic,.*', '^andestech,.*', '^anvo,.*', '^aoly,.*', '^aosong,.*', '^apm,.*', '^apple,.*', '^aptina,.*', '^arasan,.*', '^archermind,.*', '^arcom,.*', '^arctic,.*', '^arcx,.*', '^ariaboard,.*', '^aries,.*', '^arm,.*', '^armadeus,.*', '^armsom,.*', '^arrow,.*', '^artesyn,.*', '^asahi-kasei,.*', '^asc,.*', '^asix,.*', '^aspeed,.*', '^asrock,.*', '^asteralabs,.*', '^asus,.*', '^atheros,.*', '^atlas,.*', '^atmel,.*', '^auo,.*', '^auvidea,.*', '^avago,.*', '^avia,.*', '^avic,.*', '^avnet,.*', '^awinic,.*', '^axentia,.*', '^axis,.*', '^azoteq,.*', '^azw,.*', '^baikal,.*', '^bananapi,.*', '^beacon,.*', '^beagle,.*', '^belling,.*', '^bhf,.*', '^bigtreetech,.*', '^bitmain,.*', '^blaize,.*', '^blutek,.*', '^boe,.*', '^bosch,.*', '^boundary,.*', '^brcm,.*', '^broadmobi,.*', '^bsh,.*', '^bticino,.*', '^buffalo,.*', '^bur,.*', '^bytedance,.*', '^calamp,.*', '^calao,.*', '^calaosystems,.*', '^calxeda,.*', '^cameo,.*', '^canaan,.*', '^caninos,.*', '^capella,.*', '^cascoda,.*', '^catalyst,.*', '^cavium,.*', '^cct,.*', '^cdns,.*', '^cdtech,.*', '^cellwise,.*', '^ceva,.*', '^chargebyte,.*', '^checkpoint,.*', '^chefree,.*', '^chipidea,.*', '^chipone,.*', '^chipspark,.*', '^chongzhou,.*', '^chrontel,.*', '^chrp,.*', '^chunghwa,.*', '^chuwi,.*', '^ciaa,.*', '^cirrus,.*', '^cisco,.*', '^clockwork,.*', '^cloos,.*', '^cloudengines,.*', '^cnm,.*', '^cnxt,.*', '^colorfly,.*', '^compulab,.*', '^comvetia,.*', '^congatec,.*', '^coolpi,.*', '^coreriver,.*', '^corpro,.*', '^cortina,.*', '^cosmic,.*', '^crane,.*', '^creative,.*', '^crystalfontz,.*', '^csky,.*', '^csot,.*', '^csq,.*', '^ctera,.*', '^ctu,.*', '^cubietech,.*', '^cudy,.*', '^cui,.*', '^cypress,.*', '^cyx,.*', '^cznic,.*', '^dallas,.*', '^dataimage,.*', '^davicom,.*', '^deepcomputing,.*', '^dell,.*', '^delta,.*', '^densitron,.*', '^denx,.*', '^devantech,.*', '^dfi,.*', '^dfrobot,.*', '^dh,.*', '^difrnce,.*', '^digi,.*', '^digilent,.*', '^dimonoff,.*', '^diodes,.*', '^dioo,.*', '^dlc,.*', '^dlg,.*', '^dlink,.*', '^dmo,.*', '^domintech,.*', '^dongwoon,.*', '^dptechnics,.*', '^dragino,.*', '^dream,.*', '^ds,.*', '^dserve,.*', '^dynaimage,.*', '^ea,.*', '^ebang,.*', '^ebbg,.*', '^ebs-systart,.*', '^ebv,.*', '^eckelmann,.*', '^econet,.*', '^edgeble,.*', '^edimax,.*', '^edt,.*', '^ees,.*', '^eeti,.*', '^einfochips,.*', '^eink,.*', '^elan,.*', '^element14,.*', '^elgin,.*', '^elida,.*', '^elimo,.*', '^elpida,.*', '^embedfire,.*', '^embest,.*', '^emcraft,.*', '^emlid,.*', '^emmicro,.*', '^empire-electronix,.*', '^emtrion,.*', '^enclustra,.*', '^endless,.*', '^ene,.*', '^energymicro,.*', '^engicam,.*', '^engleder,.*', '^epcos,.*', '^epfl,.*', '^epson,.*', '^esp,.*', '^est,.*', '^ettus,.*', '^eukrea,.*', '^everest,.*', '^everspin,.*', '^evervision,.*', '^exar,.*', '^excito,.*', '^exegin,.*', '^ezchip,.*', '^facebook,.*', '^fairchild,.*', '^fairphone,.*', '^faraday,.*', '^fascontek,.*', '^fastrax,.*', '^fcs,.*', '^feixin,.*', '^feiyang,.*', '^fii,.*', '^firefly,.*', '^focaltech,.*', '^forlinx,.*', '^freebox,.*', '^freecom,.*', '^frida,.*', '^friendlyarm,.*', '^fsl,.*', '^fujitsu,.*', '^fxtec,.*', '^galaxycore,.*', '^gameforce,.*', '^gardena,.*', '^gateway,.*', '^gateworks,.*', '^gcw,.*', '^ge,.*', '^geekbuying,.*', '^gef,.*', '^gehc,.*', '^gemei,.*', '^gemtek,.*', '^genesys,.*', '^genexis,.*', '^geniatech,.*', '^giantec,.*', '^giantplus,.*', '^glinet,.*', '^globalscale,.*', '^globaltop,.*', '^gmt,.*', '^gocontroll,.*', '^goldelico,.*', '^goodix,.*', '^google,.*', '^goramo,.*', '^gplus,.*', '^grinn,.*', '^grmn,.*', '^gumstix,.*', '^gw,.*', '^hannstar,.*', '^haochuangyi,.*', '^haoyu,.*', '^hardkernel,.*', '^hechuang,.*', '^hideep,.*', '^himax,.*', '^hirschmann,.*', '^hisi,.*', '^hisilicon,.*', '^hit,.*', '^hitex,.*', '^holt,.*', '^holtek,.*', '^honestar,.*', '^honeywell,.*', '^hoperf,.*', '^hoperun,.*', '^hp,.*', '^hpe,.*', '^hsg,.*', '^htc,.*', '^huawei,.*', '^hugsun,.*', '^hwacom,.*', '^hxt,.*', '^hycon,.*', '^hydis,.*', '^hynitron,.*', '^hynix,.*', '^hyundai,.*', '^i2se,.*', '^ibm,.*', '^icplus,.*', '^idt,.*', '^iei,.*', '^ifi,.*', '^ilitek,.*', '^imagis,.*', '^img,.*', '^imi,.*', '^inanbo,.*', '^incircuit,.*', '^indiedroid,.*', '^inet-tek,.*', '^infineon,.*', '^inforce,.*', '^ingenic,.*', '^ingrasys,.*', '^injoinic,.*', '^innocomm,.*', '^innolux,.*', '^inside-secure,.*', '^insignal,.*', '^inspur,.*', '^intel,.*', '^intercontrol,.*', '^invensense,.*', '^inventec,.*', '^inversepath,.*', '^iom,.*', '^irondevice,.*', '^isee,.*', '^isil,.*', '^issi,.*', '^ite,.*', '^itead,.*', '^itian,.*', '^ivo,.*', '^iwave,.*', '^jadard,.*', '^jasonic,.*', '^jdi,.*', '^jedec,.*', '^jenson,.*', '^jesurun,.*', '^jethome,.*', '^jianda,.*', '^jide,.*', '^joz,.*', '^kam,.*', '^karo,.*', '^keithkoep,.*', '^keymile,.*', '^khadas,.*', '^kiebackpeter,.*', '^kinetic,.*', '^kingdisplay,.*', '^kingnovel,.*', '^kionix,.*', '^kobo,.*', '^kobol,.*', '^koe,.*', '^kontron,.*', '^kosagi,.*', '^kvg,.*', '^kyo,.*', '^lacie,.*', '^laird,.*', '^lamobo,.*', '^lantiq,.*', '^lattice,.*', '^lckfb,.*', '^lctech,.*', '^leadtek,.*', '^leez,.*', '^lego,.*', '^lemaker,.*', '^lenovo,.*', '^lg,.*', '^lgphilips,.*', '^libretech,.*', '^licheepi,.*', '^linaro,.*', '^lincolntech,.*', '^lineartechnology,.*', '^linksprite,.*', '^linksys,.*', '^linutronix,.*', '^linux,.*', '^linx,.*', '^liontron,.*', '^liteon,.*', '^litex,.*', '^lltc,.*', '^logicpd,.*', '^logictechno,.*', '^longcheer,.*', '^lontium,.*', '^loongmasses,.*', '^loongson,.*', '^lsi,.*', '^lunzn,.*', '^luxul,.*', '^lwn,.*', '^lxa,.*', '^m5stack,.*', '^macnica,.*', '^mantix,.*', '^mapleboard,.*', '^marantec,.*', '^marvell,.*', '^maxbotix,.*', '^maxim,.*', '^maxlinear,.*', '^mbvl,.*', '^mcube,.*', '^meas,.*', '^mecer,.*', '^mediatek,.*', '^megachips,.*', '^mele,.*', '^melexis,.*', '^melfas,.*', '^mellanox,.*', '^memsensing,.*', '^memsic,.*', '^menlo,.*', '^mentor,.*', '^meraki,.*', '^merrii,.*', '^methode,.*', '^micrel,.*', '^microchip,.*', '^microcrystal,.*', '^micron,.*', '^microsoft,.*', '^microsys,.*', '^microtips,.*', '^mikroe,.*', '^mikrotik,.*', '^milkv,.*', '^miniand,.*', '^minix,.*', '^mips,.*', '^miramems,.*', '^mitsubishi,.*', '^mitsumi,.*', '^mixel,.*', '^miyoo,.*', '^mntre,.*', '^mobileye,.*', '^modtronix,.*', '^moortec,.*', '^mosaixtech,.*', '^motorcomm,.*', '^motorola,.*', '^moxa,.*', '^mpl,.*', '^mps,.*', '^mqmaker,.*', '^mrvl,.*', '^mscc,.*', '^msi,.*', '^mstar,.*', '^mti,.*', '^multi-inno,.*', '^mundoreader,.*', '^murata,.*', '^mxic,.*', '^mxicy,.*', '^myir,.*', '^national,.*', '^neardi,.*', '^nec,.*', '^neofidelity,.*', '^neonode,.*', '^netcube,.*', '^netgear,.*', '^netlogic,.*', '^netron-dy,.*', '^netronix,.*', '^netxeon,.*', '^neweast,.*', '^newhaven,.*', '^newvision,.*', '^nexbox,.*', '^nextthing,.*', '^ni,.*', '^nintendo,.*', '^nlt,.*', '^nokia,.*', '^nordic,.*', '^nothing,.*', '^novatek,.*', '^novtech,.*', '^numonyx,.*', '^nutsboard,.*', '^nuvoton,.*', '^nvd,.*', '^nvidia,.*', '^nxp,.*', '^oceanic,.*', '^ocs,.*', '^oct,.*', '^okaya,.*', '^oki,.*', '^olimex,.*', '^olpc,.*', '^oneplus,.*', '^onie,.*', '^onion,.*', '^onnn,.*', '^ontat,.*', '^opalkelly,.*', '^openailab,.*', '^opencores,.*', '^openembed,.*', '^openpandora,.*', '^openrisc,.*', '^openwrt,.*', '^option,.*', '^oranth,.*', '^orisetech,.*', '^ortustech,.*', '^osddisplays,.*', '^osmc,.*', '^ouya,.*', '^overkiz,.*', '^ovti,.*', '^oxsemi,.*', '^ozzmaker,.*', '^panasonic,.*', '^parade,.*', '^parallax,.*', '^pda,.*', '^pegatron,.*', '^pericom,.*', '^pervasive,.*', '^phicomm,.*', '^phytec,.*', '^picochip,.*', '^pinctrl-[0-9]+$', '^pine64,.*', '^pineriver,.*', '^pixcir,.*', '^plantower,.*', '^plathome,.*', '^plda,.*', '^plx,.*', '^ply,.*', '^pni,.*', '^pocketbook,.*', '^polaroid,.*', '^polyhex,.*', '^portwell,.*', '^poslab,.*', '^pov,.*', '^powertip,.*', '^powervr,.*', '^powkiddy,.*', '^pri,.*', '^primeview,.*', '^primux,.*', '^probox2,.*', '^prt,.*', '^pulsedlight,.*', '^purism,.*', '^puya,.*', '^qca,.*', '^qcom,.*', '^qemu,.*', '^qi,.*', '^qiaodian,.*', '^qihua,.*', '^qishenglong,.*', '^qnap,.*', '^quanta,.*', '^radxa,.*', '^raidsonic,.*', '^ralink,.*', '^ramtron,.*', '^raspberrypi,.*', '^raydium,.*', '^rda,.*', '^realtek,.*', '^relfor,.*', '^remarkable,.*', '^renesas,.*', '^rervision,.*', '^retronix,.*', '^revotics,.*', '^rex,.*', '^richtek,.*', '^ricoh,.*', '^rikomagic,.*', '^riot,.*', '^riscv,.*', '^rockchip,.*', '^rocktech,.*', '^rohm,.*', '^ronbo,.*', '^roofull,.*', '^roseapplepi,.*', '^rve,.*', '^saef,.*', '^samsung,.*', '^samtec,.*', '^sancloud,.*', '^sandisk,.*', '^satoz,.*', '^sbs,.*', '^schindler,.*', '^schneider,.*', '^sciosense,.*', '^seagate,.*', '^seeed,.*', '^seirobotics,.*', '^semtech,.*', '^senseair,.*', '^sensirion,.*', '^sensortek,.*', '^sercomm,.*', '^sff,.*', '^sgd,.*', '^sgmicro,.*', '^sgx,.*', '^sharp,.*', '^shift,.*', '^shimafuji,.*', '^shineworld,.*', '^shiratech,.*', '^si-en,.*', '^si-linux,.*', '^siemens,.*', '^sifive,.*', '^siflower,.*', '^sigma,.*', '^sii,.*', '^sil,.*', '^silabs,.*', '^silan,.*', '^silead,.*', '^silergy,.*', '^silex-insight,.*', '^siliconfile,.*', '^siliconmitus,.*', '^silvaco,.*', '^simtek,.*', '^sinlinx,.*', '^sinovoip,.*', '^sinowealth,.*', '^sipeed,.*', '^sirf,.*', '^sis,.*', '^sitronix,.*', '^skov,.*', '^skyworks,.*', '^smartlabs,.*', '^smartrg,.*', '^smi,.*', '^smsc,.*', '^snps,.*', '^sochip,.*', '^socionext,.*', '^solidrun,.*', '^solomon,.*', '^sony,.*', '^sophgo,.*', '^sourceparts,.*', '^spacemit,.*', '^spansion,.*', '^sparkfun,.*', '^spinalhdl,.*', '^sprd,.*', '^square,.*', '^ssi,.*', '^sst,.*', '^sstar,.*', '^st,.*', '^st-ericsson,.*', '^starfive,.*', '^starry,.*', '^startek,.*', '^starterkit,.*', '^ste,.*', '^stericsson,.*', '^storlink,.*', '^storm,.*', '^storopack,.*', '^summit,.*', '^sunchip,.*', '^sundance,.*', '^sunplus,.*', '^supermicro,.*', '^swir,.*', '^syna,.*', '^synology,.*', '^synopsys,.*', '^tbs,.*', '^tbs-biometrics,.*', '^tcg,.*', '^tcl,.*', '^tcs,.*', '^tcu,.*', '^tdo,.*', '^team-source-display,.*', '^technexion,.*', '^technologic,.*', '^techstar,.*', '^techwell,.*', '^teejet,.*', '^teltonika,.*', '^tempo,.*', '^terasic,.*', '^tesla,.*', '^test,.*', '^tfc,.*', '^thead,.*', '^thine,.*', '^thingyjp,.*', '^thundercomm,.*', '^thwc,.*', '^ti,.*', '^tianma,.*', '^tlm,.*', '^tmt,.*', '^topeet,.*', '^topic,.*', '^topland,.*', '^toppoly,.*', '^topwise,.*', '^toradex,.*', '^toshiba,.*', '^toumaz,.*', '^tpk,.*', '^tplink,.*', '^tpo,.*', '^tq,.*', '^transpeed,.*', '^traverse,.*', '^tronfy,.*', '^tronsmart,.*', '^truly,.*', '^tsd,.*', '^turing,.*', '^tyan,.*', '^tyhx,.*', '^u-blox,.*', '^u-boot,.*', '^ubnt,.*', '^ucrobotics,.*', '^udoo,.*', '^ufispace,.*', '^ugoos,.*', '^ultratronik,.*', '^uni-t,.*', '^uniwest,.*', '^upisemi,.*', '^urt,.*', '^usi,.*', '^usr,.*', '^utoo,.*', '^v3,.*', '^vaisala,.*', '^vamrs,.*', '^variscite,.*', '^vdl,.*', '^vertexcom,.*', '^via,.*', '^vialab,.*', '^vicor,.*', '^videostrong,.*', '^virtio,.*', '^virtual,.*', '^vishay,.*', '^visionox,.*', '^vitesse,.*', '^vivante,.*', '^vivax,.*', '^vocore,.*', '^voipac,.*', '^voltafield,.*', '^vot,.*', '^vscom,.*', '^vxt,.*', '^wacom,.*', '^wanchanglong,.*', '^wand,.*', '^waveshare,.*', '^wd,.*', '^we,.*', '^welltech,.*', '^wetek,.*', '^wexler,.*', '^whwave,.*', '^wi2wi,.*', '^widora,.*', '^wiligear,.*', '^willsemi,.*', '^winbond,.*', '^wingtech,.*', '^winlink,.*', '^winsen,.*', '^winstar,.*', '^wirelesstag,.*', '^wits,.*', '^wlf,.*', '^wm,.*', '^wobo,.*', '^wolfvision,.*', '^x-powers,.*', '^xen,.*', '^xes,.*', '^xiaomi,.*', '^xillybus,.*', '^xingbangda,.*', '^xinpeng,.*', '^xiphera,.*', '^xlnx,.*', '^xnano,.*', '^xunlong,.*', '^xylon,.*', '^yadro,.*', '^yamaha,.*', '^yes-optoelectronics,.*', '^yic,.*', '^yiming,.*', '^ylm,.*', '^yna,.*', '^yones-toptech,.*', '^ys,.*', '^ysoft,.*', '^yuridenki,.*', '^yuzukihd,.*', '^zarlink,.*', '^zealz,.*', '^zeitec,.*', '^zidoo,.*', '^zii,.*', '^zinitix,.*', '^zkmagic,.*', '^zte,.*', '^zyxel,.*'
	from schema $id: http://devicetree.org/schemas/vendor-prefixes.yaml#

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20250516091650.832-1-dongxuyang@eswincomputing.com

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v1 1/2] dt-bindings: sdhci: eswin: Documentation for eic7700 SoC
  2025-05-16  9:16 ` [PATCH v1 1/2] dt-bindings: sdhci: eswin: Documentation for eic7700 SoC dongxuyang
  2025-05-16 10:26   ` Rob Herring (Arm)
@ 2025-05-16 13:12   ` Krzysztof Kozlowski
  1 sibling, 0 replies; 8+ messages in thread
From: Krzysztof Kozlowski @ 2025-05-16 13:12 UTC (permalink / raw)
  To: dongxuyang, ulf.hansson, robh, krzk+dt, conor+dt, linux-mmc,
	devicetree, linux-kernel, adrian.hunter, p.zabel, shanchun1218
  Cc: ningyu, linmin, xuxiang

On 16/05/2025 11:16, dongxuyang@eswincomputing.com wrote:
> From: Xuyang Dong <dongxuyang@eswincomputing.com>
> 
> Add device tree binding documentation for the ESWIN
> eic7700 sdhci controller module.
> 
> Signed-off-by: Xiang Xu <xuxiang@eswincomputing.com>
> Signed-off-by: Xuyang Dong <dongxuyang@eswincomputing.com>
> ---


Folks,you need to slow down because you are sending patchsets with the
same issues, thus we need to repeat the same review comments. Learn from
the review first.

>  .../bindings/mmc/eswin,sdhci-eic7700.yaml     | 131 ++++++++++++++++++
>  1 file changed, 131 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mmc/eswin,sdhci-eic7700.yaml
> 
> diff --git a/Documentation/devicetree/bindings/mmc/eswin,sdhci-eic7700.yaml b/Documentation/devicetree/bindings/mmc/eswin,sdhci-eic7700.yaml
> new file mode 100644
> index 000000000000..d4826f2aa619
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mmc/eswin,sdhci-eic7700.yaml
> @@ -0,0 +1,131 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mmc/eswin,sdhci-eic7700.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: ESWIN EIC7700 SoC SDHCI controller
> +
> +maintainers:
> +  - Shuang Liang <liangshuang@eswincomputing.com>
> +  - Xuyang Dong <dongxuyang@eswincomputing.com>
> +
> +allOf:
> +  - $ref: /schemas/mmc/mmc-controller.yaml#
> +
> +properties:
> +  compatible:
> +    const: eswin,eic7700-emmc-sdhci
> +
> +  reg:
> +    maxItems: 1
> +    description: Common configuration registers

Blank line, drop description

> +  "#address-cells":
> +    const: 1

Blank line

> +  "#size-cells":
> +    const: 1

This is wrong.

Why do you need these properties?  You are duplicating (and wrongly) MMC
schema.


> +
> +  interrupts:
> +    maxItems: 1
> +
> +  assigned-clocks:
> +    maxItems: 1
> +    description: should contain "core_clk" phandle + clock-specifier pairs.
> +
> +  assigned-clock-rates:
> +    maxItems: 1

Drop both properties. Do you see this anywhere in the bindings?

> +
> +  clocks:
> +    minItems: 2

No list items instead.

> +    description: handles to clock for the sdhci controller.

Drop description

> +
> +  clock-names:
> +    minItems: 2
> +    description: the name of each clock.

No, list items instead. Do you see any bindings written like that?

> +
> +  clock-output-names:
> +    maxItems: 1
> +
> +  '#clock-cells':
> +    enum: [0]
> +    description:
> +      With this property in place we will export one clock
> +      representing the Card Clock. These clocks are expected to be
> +      consumed by our PHY.
> +
> +  resets:
> +    description: resets to be used by the controller.

Missing constraints. Drop description, useless.

> +
> +  reset-names:
> +    description: names of the resets listed in resets property in the same order.

Drop property

> +
> +  bus-width:
> +    enum: [4, 8]
> +    description: for emmc bus-width is 8, for sdio bus-width is 4.

Why do you need it? Look at other bindings.

> +
> +  eswin,hsp_sp_csr:

Look at DTS coding style first. What characters are allowed in properties?

> +    $ref: /schemas/types.yaml#/definitions/phandle-array
> +    description: hsp_sp_csr regs to be used by the controller.

For what purpose?

> +    items:
> +      - description: phandle to HSP_SP_CSR register block
> +      - description: status register offset
> +      - description: control register offset
> +      - description: configuration register offset
> +
> +  eswin,syscrg_csr:
> +    $ref: /schemas/types.yaml#/definitions/phandle-array
> +    description: syscrg_csr regs to be used by the controller.

For what purpose?


> +    items:
> +      - description: phandle to SYS_CRG_CSR register block
> +      - description: status register offset
> +      - description: control register offset
> +      - description: configuration register offset
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - assigned-clocks
> +  - assigned-clock-rates
> +  - clocks
> +  - clock-names
> +  - clock-output-names
> +  - resets
> +  - reset-names
> +  - bus-width
> +  - eswin,hsp_sp_csr
> +  - eswin,syscrg_csr
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +

Drop blank line

> +    sdhci_emmc: mmc@50450000 {

Drop label

> +      compatible = "eswin,eic7700-emmc-sdhci";
> +      reg = <0x50450000 0x10000>;
> +      interrupt-parent = <&plic>;
> +      interrupts = <79>;
> +      clocks = <&clock 554>, <&clock 546>;
> +      clock-names = "clk_xin", "clk_ahb";
> +      assigned-clocks = <&clock 554>;
> +      assigned-clock-rates = <200000000>;
> +      clock-output-names = "emmc_cardclock";
> +      #clock-cells = <0>;
> +
> +      resets = <&reset 7 (1 << 6)>,
> +        <&reset 7 (1 << 3)>,
> +        <&reset 7 (1 << 19)>,
> +        <&reset 7 (1 << 23)>;
> +      reset-names = "txrx_rst", "phy_rst", "prstn", "arstn";
> +
> +      core-clk-reg = <0x51828160>;
> +      disable-cqe-dcmd;
> +      bus-width = <8>;
> +      non-removable;
> +      mmc-hs400-1_8v;
> +      max-frequency = <200000000>;
> +      eswin,hsp_sp_csr = <&hsp_sp_csr 0x1038 0x508 0x50c>;
> +      eswin,syscrg_csr = <&sys_crg 0x160 0x148 0x14c>;
> +      status = "disabled";

No, drop.


Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v1 2/2] sdhci: eswin: Add eic7700 sdhci driver
  2025-05-16  9:17 ` [PATCH v1 2/2] sdhci: eswin: Add eic7700 sdhci driver dongxuyang
@ 2025-05-16 13:17   ` Krzysztof Kozlowski
  2025-05-17  1:57   ` kernel test robot
  2025-05-22  7:48   ` Adrian Hunter
  2 siblings, 0 replies; 8+ messages in thread
From: Krzysztof Kozlowski @ 2025-05-16 13:17 UTC (permalink / raw)
  To: dongxuyang, ulf.hansson, robh, krzk+dt, conor+dt, linux-mmc,
	devicetree, linux-kernel, adrian.hunter, p.zabel, shanchun1218
  Cc: ningyu, linmin, xuxiang

On 16/05/2025 11:17, dongxuyang@eswincomputing.com wrote:
> +	if (of_property_read_u32(np, "#clock-cells", &num_clks) < 0)
> +		return 0;
> +
> +	ret = eswin_sdhci_sdio_register_sdcardclk(eswin_sdhci_sdio, clk_xin,
> +						  dev);
> +	if (ret)
> +		return ret;
> +
> +	if (num_clks) {
> +		ret = eswin_sdhci_sdio_register_sampleclk(eswin_sdhci_sdio,
> +							  clk_xin, dev);
> +		if (ret) {
> +			eswin_sdhci_sdio_unregister_sdclk(dev);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int eswin_sdhci_sdio_add_host(struct eswin_sdhci_data *eswin_sdhci_sdio)
> +{
> +	struct sdhci_host *host = eswin_sdhci_sdio->host;

Why do you have two probes for one driver?

> +	struct cqhci_host *cq_host;
> +	bool dma64;
> +	int ret;
> +
> +	if (!eswin_sdhci_sdio->has_cqe)
> +		return sdhci_add_host(host);
> +
> +	ret = sdhci_setup_host(host);
> +	if (ret)
> +		return ret;
> +
> +	cq_host = devm_kzalloc(host->mmc->parent, sizeof(*cq_host), GFP_KERNEL);
> +	if (!cq_host) {
> +		ret = -ENOMEM;
> +		goto cleanup;
> +	}
> +
> +	cq_host->mmio = host->ioaddr + ESWIN_SDHCI_SD_CQE_BASE_ADDR;
> +	cq_host->ops = &eswin_sdhci_sdio_cqhci_ops;
> +
> +	dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
> +	if (dma64)
> +		cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
> +
> +	ret = cqhci_init(cq_host, host->mmc, dma64);
> +	if (ret)
> +		goto cleanup;
> +
> +	ret = __sdhci_add_host(host);
> +	if (ret)
> +		goto cleanup;
> +
> +	return 0;
> +
> +cleanup:
> +	sdhci_cleanup_host(host);
> +	return ret;
> +}
> +
> +static int eswin_sdhci_sdio_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +	struct clk *clk_xin;
> +	struct clk *clk_spll2_fout3;
> +	struct clk *clk_mux;
> +	struct sdhci_host *host;
> +	struct sdhci_pltfm_host *pltfm_host;
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	struct eswin_sdhci_data *eswin_sdhci_sdio;
> +	const struct eswin_sdhci_of_data *data;
> +	unsigned int val = 0;
> +
> +	data = of_device_get_match_data(dev);
> +	host = sdhci_pltfm_init(pdev, data->pdata, sizeof(*eswin_sdhci_sdio));
> +
> +	if (IS_ERR(host))
> +		return PTR_ERR(host);
> +
> +	pltfm_host = sdhci_priv(host);
> +	eswin_sdhci_sdio = sdhci_pltfm_priv(pltfm_host);
> +	eswin_sdhci_sdio->host = host;
> +	eswin_sdhci_sdio->has_cqe = false;
> +
> +	sdhci_get_of_property(pdev);
> +
> +	eswin_sdhci_sdio->clk_ops = data->clk_ops;
> +	eswin_sdhci_sdio->clk_ahb = devm_clk_get(dev, "clk_ahb");
> +	if (IS_ERR(eswin_sdhci_sdio->clk_ahb)) {
> +		ret = dev_err_probe(dev, PTR_ERR(eswin_sdhci_sdio->clk_ahb),
> +				    "clk_ahb clock not found.\n");
> +		goto err_pltfm_free;
> +	}
> +
> +	clk_xin = devm_clk_get(dev, "clk_xin");
> +	if (IS_ERR(clk_xin)) {
> +		ret = dev_err_probe(dev, PTR_ERR(clk_xin),
> +				    "clk_xin clock not found.\n");
> +		goto err_pltfm_free;
> +	}
> +
> +	clk_spll2_fout3 = devm_clk_get(dev, "clk_spll2_fout3");
> +
> +	if (IS_ERR(clk_spll2_fout3)) {
> +		ret = dev_err_probe(dev, PTR_ERR(clk_spll2_fout3),
> +				    "clk_spll2_fout3 clock not found.\n");
> +		goto err_pltfm_free;
> +	}
> +
> +	if (of_device_is_compatible(np, "eswin,sdhci-sdio")) {
> +		clk_mux = devm_clk_get(dev, "clk_mux1_1");
> +		if (IS_ERR(clk_mux)) {
> +			ret = dev_err_probe(dev, PTR_ERR(clk_mux),
> +					    "clk_mux1_1 clock not found.\n");
> +			goto err_pltfm_free;
> +		}
> +		/*switch the core clk source*/
> +		clk_set_parent(clk_mux, clk_spll2_fout3);
> +	}
> +
> +	ret = clk_prepare_enable(eswin_sdhci_sdio->clk_ahb);
> +	if (ret) {
> +		dev_err(dev, "Unable to enable AHB clock.\n");
> +		goto err_pltfm_free;
> +	}
> +	/* If clock-frequency property is set, use the provided value */
> +	if (pltfm_host->clock && pltfm_host->clock != clk_get_rate(clk_xin)) {
> +		ret = clk_set_rate(clk_xin, pltfm_host->clock);
> +		if (ret) {
> +			dev_err(&pdev->dev, "Failed to set SD clock rate\n");
> +			goto clk_dis_ahb;
> +		}
> +	}
> +
> +	ret = clk_prepare_enable(clk_xin);
> +	if (ret) {
> +		dev_err(dev, "Unable to enable SD clock.\n");
> +		goto clk_dis_ahb;
> +	}
> +
> +	pltfm_host->clk = clk_xin;
> +	ret = eswin_sdhci_sdio_register_sdclk(eswin_sdhci_sdio, clk_xin, dev);
> +	if (ret)
> +		goto clk_disable_all;
> +
> +	ret = eswin_sdhci_reset_init(dev, eswin_sdhci_sdio);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to reset\n");
> +		goto clk_disable_all;
> +	}
> +
> +	eswin_sdhci_sdio->crg_regmap = syscon_regmap_lookup_by_phandle(
> +		pdev->dev.of_node, "eswin,syscrg_csr");
> +	if (IS_ERR(eswin_sdhci_sdio->crg_regmap)) {
> +		dev_dbg(&pdev->dev, "No syscrg_csr phandle specified\n");
> +		goto clk_disable_all;
> +	}
> +
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,syscrg_csr",
> +					 1, &eswin_sdhci_sdio->crg_core_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get crg_core_clk (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,syscrg_csr",
> +					 2, &eswin_sdhci_sdio->crg_aclk_ctrl);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get crg_aclk_ctrl (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,syscrg_csr",
> +					 3, &eswin_sdhci_sdio->crg_cfg_ctrl);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get crg_cfg_ctrl (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +
> +	eswin_sdhci_sdio->hsp_regmap = syscon_regmap_lookup_by_phandle(
> +		dev->of_node, "eswin,hsp_sp_csr");
> +	if (IS_ERR(eswin_sdhci_sdio->hsp_regmap)) {
> +		dev_dbg(dev, "No hsp_sp_csr phandle specified\n");
> +		goto clk_disable_all;
> +	}
> +
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,hsp_sp_csr",
> +					 2, &eswin_sdhci_sdio->hsp_int_status);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get hsp_int_status (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,hsp_sp_csr",
> +					 3, &eswin_sdhci_sdio->hsp_pwr_ctrl);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get hsp_pwr_ctrl (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +
> +	regmap_write(eswin_sdhci_sdio->hsp_regmap,
> +		     eswin_sdhci_sdio->hsp_int_status, MSHC_INT_CLK_STABLE);
> +	regmap_write(eswin_sdhci_sdio->hsp_regmap,
> +		     eswin_sdhci_sdio->hsp_pwr_ctrl, MSHC_HOST_VAL_STABLE);
> +
> +	if (!of_property_read_u32(dev->of_node, "delay_code", &val))
> +		eswin_sdhci_sdio->phy.delay_code = val;
> +
> +	if (!of_property_read_u32(dev->of_node, "drive-impedance-ohm", &val))
> +		eswin_sdhci_sdio->phy.drive_impedance =
> +			eswin_convert_drive_impedance_ohm(pdev, val);
> +
> +	if (of_property_read_bool(dev->of_node, "enable-cmd-pullup"))
> +		eswin_sdhci_sdio->phy.enable_cmd_pullup = ENABLE;
> +	else
> +		eswin_sdhci_sdio->phy.enable_cmd_pullup = DISABLE;
> +
> +	if (of_property_read_bool(dev->of_node, "enable-data-pullup"))
> +		eswin_sdhci_sdio->phy.enable_data_pullup = ENABLE;
> +	else
> +		eswin_sdhci_sdio->phy.enable_data_pullup = DISABLE;
> +
> +	eswin_sdhci_dt_parse_clk_phases(dev, &eswin_sdhci_sdio->clk_data);
> +	ret = mmc_of_parse(host->mmc);
> +	if (ret) {
> +		ret = dev_err_probe(dev, ret, "parsing dt failed.\n");
> +		goto unreg_clk;
> +	}
> +
> +	ret = eswin_sdhci_sdio_add_host(eswin_sdhci_sdio);
> +	if (ret)
> +		goto unreg_clk;
> +
> +	pm_runtime_set_active(&pdev->dev);
> +	pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
> +	pm_runtime_use_autosuspend(&pdev->dev);
> +	pm_suspend_ignore_children(&pdev->dev, 1);
> +	pm_runtime_enable(&pdev->dev);
> +
> +	return 0;
> +
> +unreg_clk:
> +	eswin_sdhci_sdio_unregister_sdclk(dev);
> +clk_disable_all:
> +	clk_disable_unprepare(clk_xin);
> +clk_dis_ahb:
> +	clk_disable_unprepare(eswin_sdhci_sdio->clk_ahb);
> +err_pltfm_free:
> +	sdhci_pltfm_free(pdev);
> +	return ret;
> +}


....

> +
> +static int eswin_sdhci_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +	struct clk *clk_xin;
> +	struct sdhci_host *host;
> +	struct sdhci_pltfm_host *pltfm_host;
> +	struct device *dev = &pdev->dev;
> +	struct eswin_sdhci_data *eswin_sdhci;
> +	const struct eswin_sdhci_of_data *data;
> +	unsigned int val = 0;
> +
> +	data = of_device_get_match_data(dev);
> +	host = sdhci_pltfm_init(pdev, data->pdata, sizeof(*eswin_sdhci));
> +	if (IS_ERR(host))
> +		return PTR_ERR(host);
> +
> +	pltfm_host = sdhci_priv(host);
> +	eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
> +	eswin_sdhci->host = host;
> +	eswin_sdhci->clk_ops = data->clk_ops;
> +
> +	eswin_sdhci->clk_ahb = devm_clk_get(dev, "clk_ahb");

Undocumented ABI

Anyway, drop clk_ in property name.

> +	if (IS_ERR(eswin_sdhci->clk_ahb)) {
> +		ret = dev_err_probe(dev, PTR_ERR(eswin_sdhci->clk_ahb),
> +				    "clk_ahb clock not found.\n");
> +		goto err_pltfm_free;
> +	}
> +
> +	clk_xin = devm_clk_get(dev, "clk_xin");

drop clk_ in property name.

> +	if (IS_ERR(clk_xin)) {
> +		ret = dev_err_probe(dev, PTR_ERR(clk_xin),
> +				    "clk_xin clock not found.\n");
> +		goto err_pltfm_free;
> +	}
> +
> +	ret = clk_prepare_enable(eswin_sdhci->clk_ahb);

So just use devm_clk_get_enabled.

> +	if (ret) {
> +		dev_err(dev, "Unable to enable AHB clock.\n");
> +		goto err_pltfm_free;
> +	}
> +
> +	ret = clk_prepare_enable(clk_xin);
> +	if (ret) {
> +		dev_err(dev, "Unable to enable SD clock.\n");
> +		goto clk_dis_ahb;
> +	}
> +
> +	ret = eswin_sdhci_reset_init(dev, eswin_sdhci);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to reset\n");
> +		goto clk_disable_all;
> +	}
> +
> +	eswin_sdhci->crg_regmap = syscon_regmap_lookup_by_phandle(

Use wrapper for getting the arguments.

> +		pdev->dev.of_node, "eswin,syscrg_csr");
> +	if (IS_ERR(eswin_sdhci->crg_regmap)) {
> +		dev_dbg(&pdev->dev, "No syscrg_csr phandle specified\n");
> +		goto clk_disable_all;
> +	}
> +
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,syscrg_csr",
> +					 1, &eswin_sdhci->crg_core_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get crg_core_clk (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,syscrg_csr",
> +					 2, &eswin_sdhci->crg_aclk_ctrl);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get crg_aclk_ctrl (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,syscrg_csr",
> +					 3, &eswin_sdhci->crg_cfg_ctrl);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get crg_cfg_ctrl (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +
> +	eswin_sdhci->hsp_regmap = syscon_regmap_lookup_by_phandle(
> +		dev->of_node, "eswin,hsp_sp_csr");
> +	if (IS_ERR(eswin_sdhci->hsp_regmap)) {
> +		dev_dbg(dev, "No hsp_sp_csr phandle specified\n");
> +		goto clk_disable_all;
> +	}
> +
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,hsp_sp_csr",
> +					 2, &eswin_sdhci->hsp_int_status);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get hsp_int_status (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,hsp_sp_csr",
> +					 3, &eswin_sdhci->hsp_pwr_ctrl);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get hsp_pwr_ctrl (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +
> +	regmap_write(eswin_sdhci->hsp_regmap, eswin_sdhci->hsp_int_status,
> +		     MSHC_INT_CLK_STABLE);
> +	regmap_write(eswin_sdhci->hsp_regmap, eswin_sdhci->hsp_pwr_ctrl,
> +		     MSHC_HOST_VAL_STABLE);
> +
> +	if (!of_property_read_u32(dev->of_node, "delay_code", &val))

NAK, undocumented ABI.

> +		eswin_sdhci->phy.delay_code = val;
> +
> +	if (!of_property_read_u32(dev->of_node, "drive-impedance-ohm", &val))

NAK

> +		eswin_sdhci->phy.drive_impedance =
> +			eswin_convert_drive_impedance_ohm(pdev, val);
> +
> +	if (of_property_read_bool(dev->of_node, "enable-cmd-pullup"))

NAK

> +		eswin_sdhci->phy.enable_cmd_pullup = ENABLE;
> +	else
> +		eswin_sdhci->phy.enable_cmd_pullup = DISABLE;
> +
> +	if (of_property_read_bool(dev->of_node, "enable-data-pullup"))

NAK


> +		eswin_sdhci->phy.enable_data_pullup = ENABLE;
> +	else
> +		eswin_sdhci->phy.enable_data_pullup = DISABLE;
> +
> +	if (of_property_read_bool(dev->of_node, "enable-strobe-pulldown"))
> +		eswin_sdhci->phy.enable_strobe_pulldown = ENABLE;
> +	else
> +		eswin_sdhci->phy.enable_strobe_pulldown = DISABLE;
> +
> +	sdhci_get_of_property(pdev);
> +
> +	pltfm_host->clk = clk_xin;
> +
> +	ret = eswin_sdhci_register_sdclk(eswin_sdhci, clk_xin, dev);
> +	if (ret)
> +		goto clk_disable_all;
> +
> +	eswin_sdhci_dt_parse_clk_phases(dev, &eswin_sdhci->clk_data);
> +
> +	ret = mmc_of_parse(host->mmc);
> +	if (ret) {
> +		ret = dev_err_probe(dev, ret, "parsing dt failed.\n");
> +		goto unreg_clk;
> +	}
> +
> +	if (of_device_is_compatible(dev->of_node, "eswin,sdhci-5.1")) {

NAK, there is no such compatible. If you tested your DTS, you would spot it.

This driver is in really poor shape.

> +		host->mmc_host_ops.hs400_enhanced_strobe =
> +			eswin_sdhci_hs400_enhanced_strobe;
> +		eswin_sdhci->has_cqe = true;
> +		host->mmc->caps2 |= MMC_CAP2_CQE;
> +
> +		if (!of_property_read_bool(dev->of_node, "disable-cqe-dcmd"))
> +			host->mmc->caps2 |= MMC_CAP2_CQE_DCMD;
> +	}
> +
> +	sdhci_enable_v4_mode(eswin_sdhci->host);
> +
> +	ret = eswin_sdhci_add_host(eswin_sdhci);
> +	if (ret)
> +		goto unreg_clk;
> +
> +	return 0;
> +
> +unreg_clk:
> +	eswin_sdhci_unregister_sdclk(dev);
> +clk_disable_all:
> +	clk_disable_unprepare(clk_xin);
> +clk_dis_ahb:
> +	clk_disable_unprepare(eswin_sdhci->clk_ahb);
> +err_pltfm_free:
> +	sdhci_pltfm_free(pdev);
> +	return ret;
> +}
> +
> +static void eswin_sdhci_remove(struct platform_device *pdev)
> +{
> +	int ret;
> +	struct sdhci_host *host = platform_get_drvdata(pdev);
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
> +	struct clk *clk_ahb = eswin_sdhci->clk_ahb;
> +
> +	sdhci_pltfm_remove(pdev);
> +
> +	if (eswin_sdhci->txrx_rst) {
> +		ret = reset_control_assert(eswin_sdhci->txrx_rst);
> +		WARN_ON(ret != 0);

Drop. You can print some useful error msg, but you cannot have WARNs. It
not useful at all.


Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v1 2/2] sdhci: eswin: Add eic7700 sdhci driver
  2025-05-16  9:17 ` [PATCH v1 2/2] sdhci: eswin: Add eic7700 sdhci driver dongxuyang
  2025-05-16 13:17   ` Krzysztof Kozlowski
@ 2025-05-17  1:57   ` kernel test robot
  2025-05-22  7:48   ` Adrian Hunter
  2 siblings, 0 replies; 8+ messages in thread
From: kernel test robot @ 2025-05-17  1:57 UTC (permalink / raw)
  To: dongxuyang, ulf.hansson, robh, krzk+dt, conor+dt, linux-mmc,
	devicetree, linux-kernel, adrian.hunter, p.zabel, shanchun1218
  Cc: oe-kbuild-all, ningyu, linmin, xuxiang, Xuyang Dong

Hi,

kernel test robot noticed the following build errors:

[auto build test ERROR on robh/for-next]
[also build test ERROR on linus/master ulf-hansson-mmc-mirror/next v6.15-rc6 next-20250516]
[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/dongxuyang-eswincomputing-com/dt-bindings-sdhci-eswin-Documentation-for-eic7700-SoC/20250516-171918
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link:    https://lore.kernel.org/r/20250516091727.887-1-dongxuyang%40eswincomputing.com
patch subject: [PATCH v1 2/2] sdhci: eswin: Add eic7700 sdhci driver
config: arc-allmodconfig (https://download.01.org/0day-ci/archive/20250517/202505170947.NTFfubuO-lkp@intel.com/config)
compiler: arc-linux-gcc (GCC) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250517/202505170947.NTFfubuO-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/202505170947.NTFfubuO-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from include/linux/device.h:25,
                    from include/linux/platform_device.h:13,
                    from drivers/mmc/host/sdhci-pltfm.h:12,
                    from drivers/mmc/host/sdhci-eic7700.h:14,
                    from drivers/mmc/host/sdhci-of-eic7700-sdio.c:13:
>> drivers/mmc/host/sdhci-of-eic7700-sdio.c:972:36: error: 'eswin_sdhci_sdio_runtime_suspend' undeclared here (not in a function); did you mean 'eswin_sdhci_sdio_reset'?
     972 |                 SET_RUNTIME_PM_OPS(eswin_sdhci_sdio_runtime_suspend,
         |                                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/pm.h:337:28: note: in definition of macro 'RUNTIME_PM_OPS'
     337 |         .runtime_suspend = suspend_fn, \
         |                            ^~~~~~~~~~
   drivers/mmc/host/sdhci-of-eic7700-sdio.c:972:17: note: in expansion of macro 'SET_RUNTIME_PM_OPS'
     972 |                 SET_RUNTIME_PM_OPS(eswin_sdhci_sdio_runtime_suspend,
         |                 ^~~~~~~~~~~~~~~~~~
>> drivers/mmc/host/sdhci-of-eic7700-sdio.c:973:36: error: 'eswin_sdhci_sdio_runtime_resume' undeclared here (not in a function); did you mean 'eswin_sdhci_sdio_dumpregs'?
     973 |                                    eswin_sdhci_sdio_runtime_resume, NULL)};
         |                                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/pm.h:338:27: note: in definition of macro 'RUNTIME_PM_OPS'
     338 |         .runtime_resume = resume_fn, \
         |                           ^~~~~~~~~
   drivers/mmc/host/sdhci-of-eic7700-sdio.c:972:17: note: in expansion of macro 'SET_RUNTIME_PM_OPS'
     972 |                 SET_RUNTIME_PM_OPS(eswin_sdhci_sdio_runtime_suspend,
         |                 ^~~~~~~~~~~~~~~~~~


vim +972 drivers/mmc/host/sdhci-of-eic7700-sdio.c

   968	
   969	static const struct dev_pm_ops eswin_sdhci_sdio_pmops = {
   970		SET_SYSTEM_SLEEP_PM_OPS(eswin_sdhci_sdio_suspend,
   971					eswin_sdhci_sdio_resume)
 > 972			SET_RUNTIME_PM_OPS(eswin_sdhci_sdio_runtime_suspend,
 > 973					   eswin_sdhci_sdio_runtime_resume, NULL)};
   974	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v1 2/2] sdhci: eswin: Add eic7700 sdhci driver
  2025-05-16  9:17 ` [PATCH v1 2/2] sdhci: eswin: Add eic7700 sdhci driver dongxuyang
  2025-05-16 13:17   ` Krzysztof Kozlowski
  2025-05-17  1:57   ` kernel test robot
@ 2025-05-22  7:48   ` Adrian Hunter
  2 siblings, 0 replies; 8+ messages in thread
From: Adrian Hunter @ 2025-05-22  7:48 UTC (permalink / raw)
  To: dongxuyang, ulf.hansson, robh, krzk+dt, conor+dt, linux-mmc,
	devicetree, linux-kernel, p.zabel, shanchun1218
  Cc: ningyu, linmin, xuxiang

On 16/05/2025 12:17, dongxuyang@eswincomputing.com wrote:
> From: Xuyang Dong <dongxuyang@eswincomputing.com>
> 
> Add support for sdhci controller in eic7700 series chips.
> Provide functionality of sdhci-emmc and sdhci-sdio on the chip.
> 
> Signed-off-by: Xiang Xu <xuxiang@eswincomputing.com>
> Signed-off-by: Xuyang Dong <dongxuyang@eswincomputing.com>
> ---
>  drivers/mmc/host/Kconfig                 |   47 +
>  drivers/mmc/host/Makefile                |    4 +-
>  drivers/mmc/host/sdhci-eic7700.c         |  353 ++++++++
>  drivers/mmc/host/sdhci-eic7700.h         |  237 +++++
>  drivers/mmc/host/sdhci-of-eic7700-sdio.c |  991 ++++++++++++++++++++
>  drivers/mmc/host/sdhci-of-eic7700.c      | 1053 ++++++++++++++++++++++
>  6 files changed, 2685 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/mmc/host/sdhci-eic7700.c
>  create mode 100644 drivers/mmc/host/sdhci-eic7700.h
>  create mode 100644 drivers/mmc/host/sdhci-of-eic7700-sdio.c
>  create mode 100644 drivers/mmc/host/sdhci-of-eic7700.c

Thanks for sending your driver.  The code is neat but doesn't
follow kernel expectations in a few ways.

Please aim for simplicity.  For example,
why not one C file (1 module) and no .h file?

I haven't looked closely but there are a few comments below,
and plenty to work on.

> 
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 264e11fa58ea..c1be4b6e1411 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -160,6 +160,53 @@ config MMC_SDHCI_PLTFM
>  
>  	  If unsure, say N.
>  
> +config MMC_EIC7700
> +	tristate "support for the EIC7700 controllers"
> +	depends on MMC_SDHCI_PLTFM
> +	depends on OF
> +	depends on COMMON_CLK
> +	depends on ARCH_ESWIN || COMPILE_TEST
> +	select MMC_CQHCI
> +	help
> +	  This selects the EIC7700 Secure Digital Host Controller Interface
> +	  (SDHCI).
> +
> +	  If you have a controller with this interface, say Y or M here.
> +
> +	  If unsure, say N.
> +
> +config MMC_SDHCI_OF_EIC7700
> +	tristate "SDHCI OF support for the EIC7700 SDHCI controllers"
> +	depends on MMC_SDHCI_PLTFM
> +	depends on OF
> +	depends on COMMON_CLK
> +	depends on ARCH_ESWIN || COMPILE_TEST
> +	depends on MMC_EIC7700
> +	select MMC_CQHCI
> +	help
> +	  This selects the EIC7700 Secure Digital Host Controller Interface
> +	  (SDHCI).
> +
> +	  If you have a controller with this interface, say Y or M here.
> +
> +	  If unsure, say N.
> +
> +config MMC_SDHCI_OF_SDIO_EIC7700
> +	tristate "SDHCI OF SDIO support for the EIC7700 SDHCI controllers"
> +	depends on MMC_SDHCI_PLTFM
> +	depends on OF
> +	depends on COMMON_CLK
> +	depends on ARCH_ESWIN || COMPILE_TEST
> +	depends on MMC_EIC7700
> +	select MMC_CQHCI
> +	help
> +	 This selects the EIC7700 Secure Digital Host Controller Interface
> +	 (SDHCI-SDIO).
> +
> +	 If you have a controller with this interface, say Y or M here.
> +
> +	 If unsure, say N.
> +
>  config MMC_SDHCI_OF_ARASAN
>  	tristate "SDHCI OF support for the Arasan SDHCI controllers"
>  	depends on MMC_SDHCI_PLTFM
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index 5147467ec825..4ba3962f90b8 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -105,7 +105,9 @@ cqhci-y					+= cqhci-core.o
>  cqhci-$(CONFIG_MMC_CRYPTO)		+= cqhci-crypto.o
>  obj-$(CONFIG_MMC_HSQ)			+= mmc_hsq.o
>  obj-$(CONFIG_MMC_LITEX)			+= litex_mmc.o
> -
> +obj-$(CONFIG_MMC_EIC7700)                 += sdhci-eic7700.o
> +obj-$(CONFIG_MMC_SDHCI_OF_EIC7700)        += sdhci-of-eic7700.o
> +obj-$(CONFIG_MMC_SDHCI_OF_SDIO_EIC7700)   += sdhci-of-eic7700-sdio.o
>  ifeq ($(CONFIG_CB710_DEBUG),y)
>  	CFLAGS-cb710-mmc	+= -DDEBUG
>  endif
> diff --git a/drivers/mmc/host/sdhci-eic7700.c b/drivers/mmc/host/sdhci-eic7700.c
> new file mode 100644
> index 000000000000..84a7a1186b90
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-eic7700.c
> @@ -0,0 +1,353 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * ESWIN Emmc Driver
> + *
> + * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
> + *
> + * Authors:
> + *	Shuang Liang <liangshuang@eswincomputing.com>
> + *	Xuyang Dong <dongxuyang@eswincomputing.com>
> + */
> +#include "sdhci-eic7700.h"
> +#include <linux/delay.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +
> +static void eswin_mshc_coreclk_config(struct sdhci_host *host, uint16_t divisor,
> +				      unsigned int flag_sel)
> +{
> +	struct sdhci_pltfm_host *pltfm_host;
> +	struct eswin_sdhci_data *eswin_sdhci;
> +	u32 val = 0;
> +	u32 delay = 0xfffff;
> +
> +	pltfm_host = sdhci_priv(host);
> +	eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
> +
> +	regmap_read(eswin_sdhci->crg_regmap, eswin_sdhci->crg_core_clk, &val);
> +	val &= ~MSHC_CORE_CLK_ENABLE;
> +	regmap_write(eswin_sdhci->crg_regmap, eswin_sdhci->crg_core_clk, val);
> +	while (delay--)
> +		;

Please use a kernel delay/sleep function

> +	val &= ~(MSHC_CORE_CLK_FREQ_BIT_MASK << MSHC_CORE_CLK_FREQ_BIT_SHIFT);
> +	val |= (divisor & MSHC_CORE_CLK_FREQ_BIT_MASK)
> +	       << MSHC_CORE_CLK_FREQ_BIT_SHIFT;
> +	val &= ~(MSHC_CORE_CLK_SEL_BIT);
> +	val |= flag_sel;
> +	regmap_write(eswin_sdhci->crg_regmap, eswin_sdhci->crg_core_clk, val);
> +
> +	udelay(100);

100 is generally considered too long for udelay.
usleep_range() is better

> +	val |= MSHC_CORE_CLK_ENABLE;
> +	regmap_write(eswin_sdhci->crg_regmap, eswin_sdhci->crg_core_clk, val);
> +	mdelay(1);

Prefer usleep_range() to mdelay

> +}
> +
> +static void eswin_mshc_coreclk_disable(struct sdhci_host *host)
> +{
> +	struct sdhci_pltfm_host *pltfm_host;
> +	struct eswin_sdhci_data *eswin_sdhci;
> +	u32 val = 0;
> +
> +	pltfm_host = sdhci_priv(host);
> +	eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
> +
> +	regmap_read(eswin_sdhci->crg_regmap, eswin_sdhci->crg_core_clk, &val);
> +	val &= ~MSHC_CORE_CLK_ENABLE;
> +	regmap_write(eswin_sdhci->crg_regmap, eswin_sdhci->crg_core_clk, val);
> +}
> +
> +void eswin_sdhci_disable_card_clk(struct sdhci_host *host)

sdhci drivers tend to do the naming convention the other way around
e.g. sdhci_eswin_disable_card_clk
or SDHCI_ESWIN_...

> +{
> +	unsigned int clk;
> +
> +	/* Reset SD Clock Enable */
> +	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> +	clk &= ~SDHCI_CLOCK_CARD_EN;
> +	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> +}
> +
> +void eswin_sdhci_enable_card_clk(struct sdhci_host *host)
> +{
> +	ktime_t timeout;
> +	unsigned int clk;
> +
> +	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> +
> +	clk |= SDHCI_CLOCK_INT_EN;
> +	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> +
> +	/* Wait max 150 ms */
> +	timeout = ktime_add_ms(ktime_get(), 150);
> +	while (1) {
> +		bool timedout = ktime_after(ktime_get(), timeout);
> +
> +		clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> +		if (clk & SDHCI_CLOCK_INT_STABLE)
> +			break;
> +		if (timedout) {
> +			pr_err("%s: Internal clock never stabilised.\n",
> +			       mmc_hostname(host->mmc));
> +			return;
> +		}
> +		udelay(10);
> +	}
> +
> +	clk |= SDHCI_CLOCK_CARD_EN;
> +	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> +	mdelay(1);
> +}
> +
> +void eswin_sdhci_set_core_clock(struct sdhci_host *host, unsigned int clock)
> +{
> +	unsigned int div, divide;
> +	unsigned int flag_sel, max_clk;
> +
> +	host->mmc->actual_clock = clock;
> +
> +	if (clock == 0) {
> +		eswin_mshc_coreclk_disable(host);
> +		return;
> +	}
> +
> +	if (SDHCI_CLK_208M % clock == 0) {
> +		flag_sel = 1;
> +		max_clk = SDHCI_CLK_208M;
> +	} else {
> +		flag_sel = 0;
> +		max_clk = SDHCI_CLK_200M;
> +	}
> +
> +	for (div = 1; div <= MAX_CORE_CLK_DIV; div++) {
> +		if ((max_clk / div) <= clock)
> +			break;
> +	}
> +	div--;
> +
> +	if (div == 0 || div == 1)
> +		divide = 2;
> +	else
> +		divide = (div + 1) * 2;
> +
> +	pr_debug("%s: clock:%d timing:%d\n", mmc_hostname(host->mmc), clock,
> +		 host->timing);
> +
> +	eswin_sdhci_disable_card_clk(host);
> +	eswin_mshc_coreclk_config(host, divide, flag_sel);
> +	eswin_sdhci_enable_card_clk(host);
> +	mdelay(2);
> +}
> +
> +static void eswin_sdhci_set_clk_delays(struct sdhci_host *host)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
> +	struct eswin_sdhci_clk_data *clk_data = &eswin_sdhci->clk_data;
> +
> +	clk_set_phase(clk_data->sampleclk,
> +		      clk_data->clk_phase_in[host->timing]);
> +	clk_set_phase(clk_data->sdcardclk,
> +		      clk_data->clk_phase_out[host->timing]);
> +}
> +
> +static void eswin_sdhci_dt_read_clk_phase(struct device *dev,
> +					  struct eswin_sdhci_clk_data *clk_data,
> +					  unsigned int timing, const char *prop)
> +{
> +	struct device_node *np = dev->of_node;
> +
> +	int clk_phase[2] = {0};
> +
> +	/*
> +	 * Read Tap Delay values from DT, if the DT does not contain the
> +	 * Tap Values then use the pre-defined values.
> +	 */
> +	if (of_property_read_variable_u32_array(np, prop, &clk_phase[0], 2,
> +						0)) {
> +		dev_dbg(dev, "Using predefined clock phase for %s = %d %d\n",
> +			prop, clk_data->clk_phase_in[timing],
> +			clk_data->clk_phase_out[timing]);
> +		return;
> +	}
> +
> +	/* The values read are Input and Output Clock Delays in order */
> +	clk_data->clk_phase_in[timing] = clk_phase[0];
> +	clk_data->clk_phase_out[timing] = clk_phase[1];
> +}
> +
> +/**
> + * eswin_sdhci_dt_parse_clk_phases - Read Clock Delay values from DT
> + *
> + * @dev:        Pointer to our struct device.
> + * @clk_data:       Pointer to the Clock Data structure
> + *
> + * Called at initialization to parse the values of Clock Delays.
> + */
> +void eswin_sdhci_dt_parse_clk_phases(struct device *dev,
> +				     struct eswin_sdhci_clk_data *clk_data)
> +{
> +	clk_data->set_clk_delays = eswin_sdhci_set_clk_delays;
> +
> +	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY,
> +				      "clk-phase-legacy");
> +	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS,
> +				      "clk-phase-mmc-hs");
> +	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_SD_HS,
> +				      "clk-phase-sd-hs");
> +	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR12,
> +				      "clk-phase-uhs-sdr12");
> +	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR25,
> +				      "clk-phase-uhs-sdr25");
> +	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR50,
> +				      "clk-phase-uhs-sdr50");
> +	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR104,
> +				      "clk-phase-uhs-sdr104");
> +	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_DDR50,
> +				      "clk-phase-uhs-ddr50");
> +	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_DDR52,
> +				      "clk-phase-mmc-ddr52");
> +	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS200,
> +				      "clk-phase-mmc-hs200");
> +	eswin_sdhci_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS400,
> +				      "clk-phase-mmc-hs400");
> +}
> +
> +unsigned int eswin_convert_drive_impedance_ohm(struct platform_device *pdev,
> +					       unsigned int dr_ohm)
> +{
> +	switch (dr_ohm) {
> +	case 100:
> +		return PHYCTRL_DR_100OHM;
> +	case 66:
> +		return PHYCTRL_DR_66OHM;
> +	case 50:
> +		return PHYCTRL_DR_50OHM;
> +	case 40:
> +		return PHYCTRL_DR_40OHM;
> +	case 33:
> +		return PHYCTRL_DR_33OHM;
> +	}
> +
> +	dev_warn(&pdev->dev, "Invalid value %u for drive-impedance-ohm.\n",
> +		 dr_ohm);
> +	return PHYCTRL_DR_50OHM;
> +}
> +
> +static void eswin_sdhci_do_reset(struct eswin_sdhci_data *eswin_sdhci)
> +{
> +	int ret;
> +
> +	ret = reset_control_assert(eswin_sdhci->txrx_rst);
> +	WARN_ON(ret != 0);
> +	ret = reset_control_assert(eswin_sdhci->phy_rst);
> +	WARN_ON(ret != 0);
> +	ret = reset_control_assert(eswin_sdhci->prstn);
> +	WARN_ON(ret != 0);
> +	ret = reset_control_assert(eswin_sdhci->arstn);
> +	WARN_ON(ret != 0);
> +
> +	mdelay(2);
> +
> +	ret = reset_control_deassert(eswin_sdhci->txrx_rst);
> +	WARN_ON(ret != 0);
> +	ret = reset_control_deassert(eswin_sdhci->phy_rst);
> +	WARN_ON(ret != 0);
> +	ret = reset_control_deassert(eswin_sdhci->prstn);
> +	WARN_ON(ret != 0);
> +	ret = reset_control_deassert(eswin_sdhci->arstn);
> +	WARN_ON(ret != 0);

Really need to report only 1 error for this function.  pr_err()
is more appropriate than WARN_ON() for device errors.

> +}
> +
> +int eswin_sdhci_reset_init(struct device *dev,
> +			   struct eswin_sdhci_data *eswin_sdhci)
> +{
> +	int ret = 0;
> +
> +	eswin_sdhci->txrx_rst =
> +		devm_reset_control_get_optional(dev, "txrx_rst");
> +	if (IS_ERR_OR_NULL(eswin_sdhci->txrx_rst)) {
> +		dev_err_probe(dev, PTR_ERR(eswin_sdhci->txrx_rst),
> +			      "txrx_rst reset not found.\n");
> +		return -EFAULT;
> +	}
> +
> +	eswin_sdhci->phy_rst = devm_reset_control_get_optional(dev, "phy_rst");
> +	if (IS_ERR_OR_NULL(eswin_sdhci->phy_rst)) {
> +		dev_err_probe(dev, PTR_ERR(eswin_sdhci->phy_rst),
> +			      "phy_rst reset not found.\n");
> +		return -EFAULT;
> +	}
> +
> +	eswin_sdhci->prstn = devm_reset_control_get_optional(dev, "prstn");
> +	if (IS_ERR_OR_NULL(eswin_sdhci->prstn)) {
> +		dev_err_probe(dev, PTR_ERR(eswin_sdhci->prstn),
> +			      "prstn reset not found.\n");
> +		return -EFAULT;
> +	}
> +
> +	eswin_sdhci->arstn = devm_reset_control_get_optional(dev, "arstn");
> +	if (IS_ERR_OR_NULL(eswin_sdhci->arstn)) {
> +		dev_err_probe(dev, PTR_ERR(eswin_sdhci->arstn),
> +			      "arstn reset not found.\n");
> +		return -EFAULT;
> +	}
> +	eswin_sdhci_do_reset(eswin_sdhci);
> +
> +	return ret;
> +}
> +
> +#define DRIVER_NAME "sdhci_esw"
> +#define SDHCI_ESW_DUMP(f, x...)                                                \
> +	pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ##x)

Older code does this kind of thing, but it can be done with pr_fmt()
instead

> +
> +void eswin_sdhci_dump_vendor_regs(struct sdhci_host *host)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
> +	int ret;
> +	u32 val = 0, val1 = 0;
> +
> +	SDHCI_ESW_DUMP("----------- VENDOR REGISTER DUMP -----------\n");
> +
> +	ret = regmap_read(eswin_sdhci->crg_regmap, eswin_sdhci->crg_core_clk,
> +			  &val);
> +	if (ret) {
> +		pr_err("%s: read crg_core_clk failed, ret:%d\n",
> +		       mmc_hostname(host->mmc), ret);
> +		return;
> +	}
> +
> +	SDHCI_ESW_DUMP("CORE CLK: 0x%08x | IRQ FLAG:  0x%016lx\n", val,
> +		       arch_local_save_flags());
> +
> +	ret = regmap_read(eswin_sdhci->crg_regmap, eswin_sdhci->crg_aclk_ctrl,
> +			  &val);
> +	if (ret) {
> +		pr_err("%s: read crg_aclk_ctrl failed, ret:%d\n",
> +		       mmc_hostname(host->mmc), ret);
> +		return;
> +	}
> +	ret = regmap_read(eswin_sdhci->crg_regmap, eswin_sdhci->crg_cfg_ctrl,
> +			  &val1);
> +	if (ret) {
> +		pr_err("%s: read crg_cfg_ctrl failed, ret:%d\n",
> +		       mmc_hostname(host->mmc), ret);
> +		return;
> +	}
> +	SDHCI_ESW_DUMP("HSP ACLK: 0x%08x | HSP CFG:  0x%08x\n", val, val1);
> +
> +	ret = regmap_read(eswin_sdhci->hsp_regmap, eswin_sdhci->hsp_int_status,
> +			  &val);
> +	if (ret) {
> +		pr_err("%s: read hsp_int_status failed, ret:%d\n",
> +		       mmc_hostname(host->mmc), ret);
> +		return;
> +	}
> +	ret = regmap_read(eswin_sdhci->hsp_regmap, eswin_sdhci->hsp_pwr_ctrl,
> +			  &val1);
> +	if (ret) {
> +		pr_err("%s: read hsp_pwr_ctrl failed, ret:%d\n",
> +		       mmc_hostname(host->mmc), ret);
> +		return;
> +	}
> +	SDHCI_ESW_DUMP("HSP STA: 0x%08x | PWR CTRL:  0x%08x\n", val, val1);
> +}
> diff --git a/drivers/mmc/host/sdhci-eic7700.h b/drivers/mmc/host/sdhci-eic7700.h
> new file mode 100644
> index 000000000000..4e6f4ac8e7ff
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-eic7700.h
> @@ -0,0 +1,237 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * ESWIN SDHCI Driver
> + *
> + * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
> + *
> + * Authors:
> + *	Shuang Liang <liangshuang@eswincomputing.com>
> + *	Xuyang Dong <dongxuyang@eswincomputing.com>
> + */
> +#ifndef _DRIVERS_MMC_SDHCI_ESWIN_H
> +#define _DRIVERS_MMC_SDHCI_ESWIN_H
> +
> +#include "sdhci-pltfm.h"
> +#include <linux/clk-provider.h>
> +#include <linux/reset.h>
> +
> +#define MSHC_CARD_CLK_STABLE BIT(28)
> +#define MSHC_INT_BCLK_STABLE BIT(16)
> +#define MSHC_INT_ACLK_STABLE BIT(8)
> +#define MSHC_INT_TMCLK_STABLE BIT(0)
> +#define MSHC_INT_CLK_STABLE                                                    \
> +	(MSHC_CARD_CLK_STABLE | MSHC_INT_ACLK_STABLE | MSHC_INT_BCLK_STABLE |  \
> +	 MSHC_INT_TMCLK_STABLE)
> +#define MSHC_HOST_VAL_STABLE BIT(0)
> +#define EMMC0_CARD_DETECT BIT(9)
> +#define EMMC0_CARD_WRITE_PROT BIT(8)
> +
> +#define MSHC_CORE_CLK_ENABLE BIT(16)
> +#define MSHC_CORE_CLK_FREQ_BIT_SHIFT 4
> +#define MSHC_CORE_CLK_FREQ_BIT_MASK 0xfffu
> +#define MSHC_CORE_CLK_SEL_BIT BIT(0)
> +
> +/* Controller does not have CD wired and will not function normally without */
> +#define SDHCI_ESWIN_QUIRK_FORCE_CDTEST BIT(0)
> +/* Controller immediately reports SDHCI_CLOCK_INT_STABLE after enabling the
> + * internal clock even when the clock isn't stable
> + */
> +#define SDHCI_ESWIN_QUIRK_CLOCK_UNSTABLE BIT(1)
> +
> +#define ESWIN_SDHCI_CTRL_HS400                                                 \
> +	0x0007 // Non-standard, for eswin,these bits are 0x7

Can wrap at 100 cols, and for sdhci vendor drivers, I prefer /* */ to //

#define ESWIN_SDHCI_CTRL_HS400	0x0007 /* Non-standard, for eswin, these bits are 0x7 */

> +
> +#define SDHCI_CLK_208M 208000000
> +#define SDHCI_CLK_200M 200000000

These names don't describe the meaning.  Might just as
well use constants e.g. 208 * HZ_PER_MHZ

Also, by itself, the SDHCI_ prefix really belongs to sdhci module.
So SDHCI_ESWIN_ is better

> +
> +#define AWSMMUSID GENMASK(31, 24)  // The sid of write operation
> +#define AWSMMUSSID GENMASK(23, 16) // The ssid of write operation
> +#define ARSMMUSID GENMASK(15, 8)   // The sid of read operation
> +#define ARSMMUSSID GENMASK(7, 0)   // The ssid of read operation

Try to line up everything up

#define ARSMMUSSID		GENMASK(7, 0)   /* The ssid of read operation */

#define DWC_MSHC_PTR_PHY_R	0x300
#define PHY_CNFG_R		(DWC_MSHC_PTR_PHY_R + 0x00)
#define PHY_CMDPAD_CNFG_R	(DWC_MSHC_PTR_PHY_R + 0x04)

> +
> +/* DWC_mshc_map/DWC_mshc_phy_block register */

If this is based on DWCMSHC, did you consider using sdhci-of-dwcmshc.c?

> +#define DWC_MSHC_PTR_PHY_R 0x300
> +#define PHY_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x00)
> +#define PHY_CMDPAD_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x04)
> +#define PHY_DATAPAD_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x06)
> +#define PHY_CLKPAD_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x08)
> +#define PHY_STBPAD_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x0a)
> +#define PHY_RSTNPAD_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x0c)
> +#define PHY_PADTEST_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x0e)
> +#define PHY_PADTEST_OUT_R (DWC_MSHC_PTR_PHY_R + 0x10)
> +#define PHY_PADTEST_IN_R (DWC_MSHC_PTR_PHY_R + 0x12)
> +#define PHY_PRBS_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x18)
> +#define PHY_PHYLBK_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x1a)
> +#define PHY_COMMDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x1c)
> +#define PHY_SDCLKDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x1d)
> +#define PHY_SDCLKDL_DC_R (DWC_MSHC_PTR_PHY_R + 0x1e)
> +#define PHY_SMPLDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x20)
> +#define PHY_ATDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x21)
> +#define PHY_DLL_CTRL_R (DWC_MSHC_PTR_PHY_R + 0x24)
> +#define PHY_DLL_CNFG1_R (DWC_MSHC_PTR_PHY_R + 0x25)
> +#define PHY_DLL_CNFG2_R (DWC_MSHC_PTR_PHY_R + 0x26)
> +#define PHY_DLLDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x28)
> +#define PHY_DLL_OFFST_R (DWC_MSHC_PTR_PHY_R + 0x29)
> +#define PHY_DLLMST_TSTDC_R (DWC_MSHC_PTR_PHY_R + 0x2a)
> +#define PHY_DLLBT_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x2c)
> +#define PHY_DLL_STATUS_R (DWC_MSHC_PTR_PHY_R + 0x2e)
> +#define PHY_DLLDBG_MLKDC_R (DWC_MSHC_PTR_PHY_R + 0x30)
> +#define PHY_DLLDBG_SLKDC_R (DWC_MSHC_PTR_PHY_R + 0x32)
> +
> +#define ENABLE 1
> +#define DISABLE 0

Don't use macros for this - just use 1 and 0

> +/* strength definition */
> +#define PHYCTRL_DR_33OHM 0xee
> +#define PHYCTRL_DR_40OHM 0xcc
> +#define PHYCTRL_DR_50OHM 0x88
> +#define PHYCTRL_DR_66OHM 0x44
> +#define PHYCTRL_DR_100OHM 0x00
> +
> +#define PHY_PAD_MAX_DRIVE_STRENGTH 0xf
> +#define PHY_CLK_MAX_DELAY_MASK 0x7f
> +#define PHY_PAD_SP_DRIVE_SHIF 16
> +#define PHY_PAD_SN_DRIVE_SHIF 20
> +
> +#define PHY_RSTN BIT(0)
> +#define PHY_UPDATE_DELAY_CODE BIT(4)
> +
> +#define VENDOR_EMMC_CTRL_R 0x52c
> +#define EMMC_CRAD_PRESENT BIT(0)
> +#define EMMC_RST_N_OE BIT(3)
> +#define EMMC_RST_N BIT(2)

Arguably EMMC_ names belong to mmc core.
Just prefix with ESWIN_

> +
> +#define PHY_SLEW_0 0x0
> +#define PHY_SLEW_1 0x1
> +#define PHY_SLEW_2 0x2
> +#define PHY_SLEW_3 0x3
> +#define PHY_TX_SLEW_CTRL_P_BIT_SHIFT 5
> +#define PHY_TX_SLEW_CTRL_N_BIT_SHIFT 9
> +
> +#define PHY_PULL_BIT_SHIF 0x3
> +#define PHY_PULL_DISABLED 0x0
> +#define PHY_PULL_UP 0x1
> +#define PHY_PULL_DOWN 0x2
> +#define PHY_PULL_MASK 0x3
> +
> +#define PHY_PAD_RXSEL_0 0x0
> +#define PHY_PAD_RXSEL_1 0x1
> +
> +#define VENDOR_AT_CTRL_R 0x540
> +#define LATENCY_LT_BIT_OFFSET 19
> +#define LATENCY_LT_MASK 0x3
> +
> +#define LATENCY_LT_1 0x0
> +#define LATENCY_LT_2 0x1
> +#define LATENCY_LT_3 0x2
> +#define LATENCY_LT_4 0x3
> +#define SW_TUNE_ENABLE BIT(4)
> +
> +#define VENDOR_AT_SATA_R 0x544
> +#define MAX_PHASE_CODE 0xff
> +
> +#define DLL_ENABEL BIT(0)
> +#define DLL_LOCK_STS BIT(0)
> +#define DLL_ERROR_STS BIT(1)
> +#define PHY_DELAY_CODE_MASK 0x7f
> +#define PHY_DELAY_CODE_MAX 0x7f
> +
> +#define MAX_CORE_CLK_DIV 0xfff
> +
> +/**
> + * struct eswin_sdhci_clk_ops - Clock Operations for eswin SD controller
> + *
> + * @sdcardclk_ops:  The output clock related operations
> + * @sampleclk_ops:  The sample clock related operations
> + */
> +struct eswin_sdhci_clk_ops {
> +	const struct clk_ops *sdcardclk_ops;
> +	const struct clk_ops *sampleclk_ops;
> +};

Kernel style is to avoid creating sub-structures.  Instead
just put the members in parent structure e.g. eswin_sdhci_data
and eswin_sdhci_of_data

> +
> +/**
> + * struct eswin_sdhci_clk_data - ESWIN Controller Clock Data.
> + *
> + * @sdcardclk_hw:   Struct for the clock we might provide to a PHY.
> + * @sdcardclk:      Pointer to normal 'struct clock' for sdcardclk_hw.
> + * @sampleclk_hw:   Struct for the clock we might provide to a PHY.
> + * @sampleclk:      Pointer to normal 'struct clock' for sampleclk_hw.
> + * @clk_phase_in:   Array of Input Clock Phase Delays for all speed modes
> + * @clk_phase_out:  Array of Output Clock Phase Delays for all speed modes
> + * @set_clk_delays: Function pointer for setting Clock Delays
> + * @clk_of_data:    Platform specific runtime clock data storage pointer
> + */
> +struct eswin_sdhci_clk_data {
> +	struct clk_hw sdcardclk_hw;
> +	struct clk *sdcardclk;
> +	struct clk_hw sampleclk_hw;
> +	struct clk *sampleclk;
> +	int clk_phase_in[MMC_TIMING_MMC_HS400 + 1];
> +	int clk_phase_out[MMC_TIMING_MMC_HS400 + 1];
> +	void (*set_clk_delays)(struct sdhci_host *host);
> +	void *clk_of_data;
> +};

Kernel style is to avoid creating sub-structures.  Instead
just put the members in parent structure e.g. eswin_sdhci_data

> +
> +struct eswin_sdhci_phy_data {
> +	unsigned int drive_impedance;
> +	unsigned int enable_strobe_pulldown;
> +	unsigned int enable_data_pullup;
> +	unsigned int enable_cmd_pullup;
> +	unsigned int delay_code;
> +};

Kernel style is to avoid creating sub-structures.  Instead
just put the members in parent structure e.g. eswin_sdhci_data

> +
> +/**
> + * struct eswin_sdhci_data - ESWIN Controller Data
> + *
> + * @host:       Pointer to the main SDHCI host structure.
> + * @clk_ahb:        Pointer to the AHB clock
> + * @has_cqe:        True if controller has command queuing engine.
> + * @clk_data:       Struct for the ESWIN Controller Clock Data.
> + * @clk_ops:        Struct for the ESWIN Controller Clock Operations.
> + * @soc_ctl_base:   Pointer to regmap for syscon for soc_ctl registers.
> + * @soc_ctl_map:    Map to get offsets into soc_ctl registers.
> + * @quirks:     ESWIN deviations from spec.
> + * @phy:        ESWIN sdhci phy configs.
> + * @private:    private for spec driver.
> + */
> +struct eswin_sdhci_data {

The main struct name will come up a lot, so it could be shortened to
eswin_host or sdhci_eswin

> +	struct sdhci_host *host;
> +	struct clk *clk_ahb;
> +	bool has_cqe;
> +	struct eswin_sdhci_clk_data clk_data;
> +	const struct eswin_sdhci_clk_ops *clk_ops;
> +	unsigned int quirks;
> +
> +	struct regmap *crg_regmap;
> +	unsigned int crg_core_clk;
> +	unsigned int crg_aclk_ctrl;
> +	unsigned int crg_cfg_ctrl;
> +
> +	struct regmap *hsp_regmap;
> +	unsigned int hsp_int_status;
> +	unsigned int hsp_pwr_ctrl;
> +
> +	struct reset_control *txrx_rst;
> +	struct reset_control *phy_rst;
> +	struct reset_control *prstn;
> +	struct reset_control *arstn;
> +	struct eswin_sdhci_phy_data phy;
> +	unsigned long private[] ____cacheline_aligned;

Don't know why a driver would need anonymous private data,
but it doesn't look it is being used, so it could be removed.

> +};
> +
> +struct eswin_sdhci_of_data {

Might be better to call this eswin_sdhci_of_pdata to avoid
confusion with eswin_sdhci_data

> +	const struct sdhci_pltfm_data *pdata;
> +	const struct eswin_sdhci_clk_ops *clk_ops;
> +};
> +
> +void eswin_sdhci_set_core_clock(struct sdhci_host *host, unsigned int clock);
> +void eswin_sdhci_disable_card_clk(struct sdhci_host *host);
> +void eswin_sdhci_enable_card_clk(struct sdhci_host *host);
> +void eswin_sdhci_dt_parse_clk_phases(struct device *dev,
> +				     struct eswin_sdhci_clk_data *clk_data);
> +unsigned int eswin_convert_drive_impedance_ohm(struct platform_device *pdev,
> +					       unsigned int dr_ohm);
> +int eswin_sdhci_reset_init(struct device *dev,
> +			   struct eswin_sdhci_data *eswin_sdhci);
> +void eswin_sdhci_dump_vendor_regs(struct sdhci_host *host);
> +
> +#endif /* _DRIVERS_MMC_SDHCI_ESWIN_H */
> diff --git a/drivers/mmc/host/sdhci-of-eic7700-sdio.c b/drivers/mmc/host/sdhci-of-eic7700-sdio.c
> new file mode 100644
> index 000000000000..68f9995ab3b6
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-of-eic7700-sdio.c
> @@ -0,0 +1,991 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * ESWIN sdio Driver
> + *
> + * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
> + *
> + * Authors:
> + *	Shuang Liang <liangshuang@eswincomputing.com>
> + *	Xuyang Dong <dongxuyang@eswincomputing.com>
> + */
> +
> +#include "cqhci.h"
> +#include "sdhci-eic7700.h"
> +#include "sdhci-pltfm.h"
> +#include <linux/bitfield.h>
> +#include <linux/clk-provider.h>
> +#include <linux/iommu.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/phy/phy.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +
> +#define ESWIN_SDHCI_SD_CQE_BASE_ADDR 0x180
> +#define TUNING_RANGE_THRESHOLD 40
> +
> +static inline void *sdhci_sdio_priv(struct eswin_sdhci_data *sdio)

Not used?

> +{
> +	return sdio->private;
> +}
> +
> +static void eswin_sdhci_sdio_set_clock(struct sdhci_host *host,
> +				       unsigned int clock)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct eswin_sdhci_data *eswin_sdhci_sdio =
> +		sdhci_pltfm_priv(pltfm_host);
> +	struct eswin_sdhci_clk_data *clk_data = &eswin_sdhci_sdio->clk_data;
> +
> +	/* Set the Input and Output Clock Phase Delays */
> +	if (clk_data->set_clk_delays)
> +		clk_data->set_clk_delays(host);
> +
> +	eswin_sdhci_set_core_clock(host, clock);
> +
> +	if (eswin_sdhci_sdio->quirks & SDHCI_ESWIN_QUIRK_CLOCK_UNSTABLE)
> +		/*
> +		 * Some controllers immediately report SDHCI_CLOCK_INT_STABLE
> +		 * after enabling the clock even though the clock is not
> +		 * stable. Trying to use a clock without waiting here results
> +		 * in EILSEQ while detecting some older/slower cards. The
> +		 * chosen delay is the maximum delay from sdhci_set_clock.
> +		 */
> +		msleep(20);
> +}
> +
> +static void eswin_sdhci_sdio_config_phy_delay(struct sdhci_host *host,
> +					      int delay)
> +{
> +	delay &= PHY_CLK_MAX_DELAY_MASK;
> +
> +	/*phy clk delay line config*/
> +	sdhci_writeb(host, PHY_UPDATE_DELAY_CODE, PHY_SDCLKDL_CNFG_R);
> +	sdhci_writeb(host, delay, PHY_SDCLKDL_DC_R);
> +	sdhci_writeb(host, 0x0, PHY_SDCLKDL_CNFG_R);
> +}
> +
> +static void eswin_sdhci_sdio_config_phy(struct sdhci_host *host)
> +{
> +	unsigned int val = 0;
> +	unsigned int drv = 0;
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
> +	struct eswin_sdhci_phy_data *phy = &eswin_sdhci->phy;
> +
> +	drv = phy->drive_impedance << PHY_PAD_SP_DRIVE_SHIF;
> +	pr_debug("%s: phy drv=0x%x\n", mmc_hostname(host->mmc), drv);
> +
> +	eswin_sdhci_disable_card_clk(host);
> +
> +	/* reset phy,config phy's pad */
> +	sdhci_writel(host, drv | (~PHY_RSTN), PHY_CNFG_R);
> +	/*CMDPAD_CNFS*/
> +	val = (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_P_BIT_SHIFT) |
> +	      (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_N_BIT_SHIFT) |
> +	      (phy->enable_cmd_pullup << PHY_PULL_BIT_SHIF) | PHY_PAD_RXSEL_1;
> +	sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
> +	pr_debug("%s: phy cmd=0x%x\n", mmc_hostname(host->mmc), val);
> +
> +	/*DATA PAD CNFG*/
> +	val = (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_P_BIT_SHIFT) |
> +	      (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_N_BIT_SHIFT) |
> +	      (phy->enable_data_pullup << PHY_PULL_BIT_SHIF) | PHY_PAD_RXSEL_1;
> +	sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
> +	pr_debug("%s: phy data=0x%x\n", mmc_hostname(host->mmc), val);
> +
> +	/*Clock PAD Setting*/
> +	val = (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_P_BIT_SHIFT) |
> +	      (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_N_BIT_SHIFT) | PHY_PAD_RXSEL_0;
> +	sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
> +	pr_debug("%s: phy clk=0x%x\n", mmc_hostname(host->mmc), val);
> +	mdelay(2);
> +
> +	/*PHY RSTN PAD setting*/
> +	val = (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_P_BIT_SHIFT) |
> +	      (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_N_BIT_SHIFT) |
> +	      (PHY_PULL_UP << PHY_PULL_BIT_SHIF) | PHY_PAD_RXSEL_1;
> +	sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
> +
> +	sdhci_writel(host, drv | PHY_RSTN, PHY_CNFG_R);
> +
> +	eswin_sdhci_sdio_config_phy_delay(host, phy->delay_code);
> +
> +	eswin_sdhci_enable_card_clk(host);
> +}
> +
> +static void eswin_sdhci_sdio_reset(struct sdhci_host *host, u8 mask)
> +{
> +	u8 ctrl;
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct eswin_sdhci_data *eswin_sdhci_sdio =
> +		sdhci_pltfm_priv(pltfm_host);
> +
> +	sdhci_writel(host, 0, SDHCI_INT_ENABLE);
> +	sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
> +
> +	if (mask & SDHCI_RESET_ALL)
> +		sdhci_reset(host, SDHCI_RESET_ALL);
> +
> +	if (mask & SDHCI_RESET_DATA)
> +		sdhci_reset(host, SDHCI_RESET_DATA);
> +
> +	if (mask & SDHCI_RESET_CMD)
> +		sdhci_reset(host, SDHCI_RESET_CMD);
> +
> +	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
> +	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
> +
> +	if (eswin_sdhci_sdio->quirks & SDHCI_ESWIN_QUIRK_FORCE_CDTEST) {
> +		ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
> +		ctrl |= SDHCI_CTRL_CDTEST_INS | SDHCI_CTRL_CDTEST_EN;
> +		sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
> +	}
> +	if (mask == SDHCI_RESET_ALL) { // after reset all,the phy`s config will
> +				       // be clear.
> +		eswin_sdhci_sdio_config_phy(host);
> +	}
> +}
> +
> +static int eswin_sdhci_sdio_phase_code_tuning(struct sdhci_host *host,
> +					      u32 opcode)
> +{
> +	int cmd_error = 0;
> +	int ret = 0;
> +	int phase_code = -1;
> +	int code_min = -1;
> +	int code_max = -1;
> +	int code_range = -1;
> +	int i = 0;
> +
> +	for (i = 0; i <= MAX_PHASE_CODE; i++) {
> +		eswin_sdhci_disable_card_clk(host);
> +		sdhci_writew(host, i, VENDOR_AT_SATA_R);
> +		eswin_sdhci_enable_card_clk(host);
> +
> +		ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
> +		host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
> +		if (ret) {
> +			udelay(200);
> +			pr_debug("%s: bad phase_code:0x%x!\n",
> +				 mmc_hostname(host->mmc), i);
> +			if (code_min != -1 && code_max != -1) {
> +				if (code_max - code_min > code_range) {
> +					code_range = code_max - code_min;
> +					phase_code = (code_min + code_max) / 2;
> +					if (code_range > TUNING_RANGE_THRESHOLD)
> +						break;
> +				}
> +				code_min = -1;
> +				code_max = -1;
> +			}
> +		} else {
> +			pr_debug("%s: ok phase_code:0x%x\n",
> +				 mmc_hostname(host->mmc), i);
> +			if (code_min == -1)
> +				code_min = i;
> +
> +			code_max = i;
> +			if (i == MAX_PHASE_CODE) {
> +				if (code_max - code_min > code_range) {
> +					code_range = code_max - code_min;
> +					phase_code = (code_min + code_max) / 2;
> +				}
> +			}
> +			continue;
> +		}
> +	}
> +
> +	if (phase_code == -1) {
> +		pr_err("%s: phase code tuning failed!\n",
> +		       mmc_hostname(host->mmc));
> +		eswin_sdhci_disable_card_clk(host);
> +		sdhci_writew(host, 0, VENDOR_AT_SATA_R);
> +		eswin_sdhci_enable_card_clk(host);
> +		return -EIO;
> +	}
> +
> +	pr_debug("%s: set phase_code:0x%x\n", mmc_hostname(host->mmc),
> +		 phase_code);
> +
> +	eswin_sdhci_disable_card_clk(host);
> +	sdhci_writew(host, phase_code, VENDOR_AT_SATA_R);
> +	eswin_sdhci_enable_card_clk(host);
> +
> +	ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
> +	host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
> +	if (ret) {
> +		pr_err("%s: phase_code code(0x%x) not work, tuning failed!\n",
> +		       mmc_hostname(host->mmc), phase_code);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int eswin_sdhci_sdio_executing_tuning(struct sdhci_host *host,
> +					     u32 opcode)
> +{
> +	u32 ctrl;
> +	u32 val;
> +	int ret = 0;
> +	struct sdhci_pltfm_host *pltfm_host;
> +
> +	pltfm_host = sdhci_priv(host);
> +	sdhci_pltfm_priv(pltfm_host);
> +
> +	eswin_sdhci_disable_card_clk(host);
> +
> +	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +	ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> +	sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> +	val = sdhci_readl(host, VENDOR_AT_CTRL_R);
> +	val |= SW_TUNE_ENABLE;
> +	sdhci_writew(host, val, VENDOR_AT_CTRL_R);
> +	sdhci_writew(host, 0, VENDOR_AT_SATA_R);
> +
> +	eswin_sdhci_enable_card_clk(host);
> +
> +	sdhci_writew(host, 0x0, SDHCI_CMD_DATA);
> +
> +	ret = eswin_sdhci_sdio_phase_code_tuning(host, opcode);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static u32 eswin_sdhci_sdio_cqhci_irq(struct sdhci_host *host, u32 intmask)
> +{
> +	int cmd_error = 0;
> +	int data_error = 0;
> +
> +	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
> +		return intmask;
> +
> +	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
> +
> +	return 0;
> +}
> +
> +static void eswin_sdhci_sdio_dumpregs(struct mmc_host *mmc)
> +{
> +	sdhci_dumpregs(mmc_priv(mmc));
> +}
> +
> +static void eswin_sdhci_sdio_cqe_enable(struct mmc_host *mmc)
> +{
> +	struct sdhci_host *host = mmc_priv(mmc);
> +	u32 reg;
> +
> +	reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
> +	while (reg & SDHCI_DATA_AVAILABLE) {
> +		sdhci_readl(host, SDHCI_BUFFER);
> +		reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
> +	}
> +
> +	sdhci_cqe_enable(mmc);
> +}
> +
> +static const struct cqhci_host_ops eswin_sdhci_sdio_cqhci_ops = {
> +	.enable = eswin_sdhci_sdio_cqe_enable,
> +	.disable = sdhci_cqe_disable,
> +	.dumpregs = eswin_sdhci_sdio_dumpregs,
> +};
> +
> +static const struct sdhci_ops eswin_sdhci_sdio_cqe_ops = {
> +	.set_clock = eswin_sdhci_sdio_set_clock,
> +	.get_max_clock = sdhci_pltfm_clk_get_max_clock,
> +	.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
> +	.set_bus_width = sdhci_set_bus_width,
> +	.reset = eswin_sdhci_sdio_reset,
> +	.set_uhs_signaling = sdhci_set_uhs_signaling,
> +	.set_power = sdhci_set_power_and_bus_voltage,
> +	.irq = eswin_sdhci_sdio_cqhci_irq,
> +	.platform_execute_tuning = eswin_sdhci_sdio_executing_tuning,
> +	.dump_vendor_regs = eswin_sdhci_dump_vendor_regs,
> +};
> +
> +static const struct sdhci_pltfm_data eswin_sdhci_sdio_cqe_pdata = {
> +	.ops = &eswin_sdhci_sdio_cqe_ops,
> +	.quirks = SDHCI_QUIRK_BROKEN_CQE | SDHCI_QUIRK_SINGLE_POWER_WRITE |
> +		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
> +		  SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
> +	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
> +		   SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
> +};
> +
> +#ifdef CONFIG_PM_SLEEP
> +/**
> + * eswin_sdhci_sdio_suspend- Suspend method for the driver
> + * @dev:        Address of the device structure
> + *
> + * Put the device in a low power state.
> + *
> + * Return: 0 on success and error value on error
> + */
> +static int eswin_sdhci_sdio_suspend(struct device *dev)
> +{
> +	struct sdhci_host *host = dev_get_drvdata(dev);
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct eswin_sdhci_data *eswin_sdhci_sdio =
> +		sdhci_pltfm_priv(pltfm_host);
> +	int ret;
> +
> +	pm_runtime_get_sync(dev);
> +
> +	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
> +		mmc_retune_needed(host->mmc);
> +
> +	ret = sdhci_suspend_host(host);
> +	if (ret)
> +		return ret;
> +
> +	clk_disable_unprepare(pltfm_host->clk);
> +	clk_disable_unprepare(eswin_sdhci_sdio->clk_ahb);
> +
> +	return 0;
> +}
> +
> +/**
> + * eswin_sdhci_sdio_resume- Resume method for the driver
> + * @dev:        Address of the device structure
> + *
> + * Resume operation after suspend
> + *
> + * Return: 0 on success and error value on error
> + */
> +static int eswin_sdhci_sdio_resume(struct device *dev)
> +{
> +	struct sdhci_host *host = dev_get_drvdata(dev);
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct eswin_sdhci_data *eswin_sdhci_sdio =
> +		sdhci_pltfm_priv(pltfm_host);
> +	int ret;
> +
> +	pm_runtime_put_sync(dev);
> +	ret = clk_prepare_enable(eswin_sdhci_sdio->clk_ahb);
> +	if (ret) {
> +		dev_err(dev, "can't enable clk_ahb.\n");
> +		return ret;
> +	}
> +	ret = clk_prepare_enable(pltfm_host->clk);
> +	if (ret) {
> +		dev_err(dev, "can't enable mainck.\n");
> +		goto clk_ahb_disable;
> +	}
> +
> +	ret = sdhci_resume_host(host);
> +	if (ret) {
> +		dev_err(dev, "runtime resume failed!\n");
> +		goto clk_disable;
> +	}
> +
> +	return 0;
> +clk_disable:
> +	clk_disable_unprepare(pltfm_host->clk);
> +clk_ahb_disable:
> +	clk_disable_unprepare(eswin_sdhci_sdio->clk_ahb);
> +
> +	return ret;
> +}
> +
> +static int eswin_sdhci_sdio_runtime_suspend(struct device *dev)
> +{
> +	struct sdhci_host *host = dev_get_drvdata(dev);
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct eswin_sdhci_data *eswin_sdhci_sdio =
> +		sdhci_pltfm_priv(pltfm_host);
> +	int ret;
> +
> +	ret = sdhci_runtime_suspend_host(host);
> +	if (ret)
> +		return ret;
> +
> +	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
> +		mmc_retune_needed(host->mmc);
> +
> +	clk_disable_unprepare(pltfm_host->clk);
> +	clk_disable_unprepare(eswin_sdhci_sdio->clk_ahb);
> +
> +	return 0;
> +}
> +
> +static int eswin_sdhci_sdio_runtime_resume(struct device *dev)
> +{
> +	struct sdhci_host *host = dev_get_drvdata(dev);
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct eswin_sdhci_data *eswin_sdhci_sdio =
> +		sdhci_pltfm_priv(pltfm_host);
> +	int ret;
> +
> +	ret = clk_prepare_enable(eswin_sdhci_sdio->clk_ahb);
> +	if (ret) {
> +		dev_err(dev, "can't enable clk_ahb\n");
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(pltfm_host->clk);
> +	if (ret) {
> +		dev_err(dev, "can't enable mainck\n");
> +		goto clk_ahb_disable;
> +	}
> +
> +	ret = sdhci_runtime_resume_host(host, 1);
> +	if (ret) {
> +		dev_err(dev, "runtime resume failed!\n");
> +		goto clk_disable;
> +	}
> +
> +	return 0;
> +clk_disable:
> +	clk_disable_unprepare(pltfm_host->clk);
> +clk_ahb_disable:
> +	clk_disable_unprepare(eswin_sdhci_sdio->clk_ahb);
> +
> +	return ret;
> +}
> +
> +#endif /* ! CONFIG_PM_SLEEP */
> +
> +/**
> + * eswin_sdhci_sdio_sdcardclk_recalc_rate- Return the card clock rate
> + *
> + * @hw:                 Pointer to the hardware clock structure.
> + * @parent_rate:                The parent rate (should be rate of clk_xin).
> + *
> + * Return the current actual rate of the SD card clock.  This can be used
> + * to communicate with out PHY.
> + *
> + * Return: The card clock rate.
> + */
> +static unsigned long
> +eswin_sdhci_sdio_sdcardclk_recalc_rate(struct clk_hw *hw,
> +				       unsigned long parent_rate)
> +{
> +	struct eswin_sdhci_clk_data *clk_data =
> +		container_of(hw, struct eswin_sdhci_clk_data, sdcardclk_hw);
> +	struct eswin_sdhci_data *eswin_sdhci_sdio =
> +		container_of(clk_data, struct eswin_sdhci_data, clk_data);
> +	struct sdhci_host *host = eswin_sdhci_sdio->host;
> +
> +	return host->mmc->actual_clock;
> +}
> +
> +static const struct clk_ops eswin_sdio_sdcardclk_ops = {
> +	.recalc_rate = eswin_sdhci_sdio_sdcardclk_recalc_rate,
> +};
> +
> +/**
> + * eswin_sdhci_sdio_sampleclk_recalc_rate- Return the sampling clock rate
> + *
> + * @hw:                 Pointer to the hardware clock structure.
> + * @parent_rate:                The parent rate (should be rate of clk_xin).
> + *
> + * Return the current actual rate of the sampling clock.  This can be used
> + * to communicate with out PHY.
> + *
> + * Return: The sample clock rate.
> + */
> +static unsigned long
> +eswin_sdhci_sdio_sampleclk_recalc_rate(struct clk_hw *hw,
> +				       unsigned long parent_rate)
> +{
> +	struct eswin_sdhci_clk_data *clk_data =
> +		container_of(hw, struct eswin_sdhci_clk_data, sampleclk_hw);
> +	struct eswin_sdhci_data *eswin_sdhci_sdio =
> +		container_of(clk_data, struct eswin_sdhci_data, clk_data);
> +	struct sdhci_host *host = eswin_sdhci_sdio->host;
> +
> +	return host->mmc->actual_clock;
> +}
> +
> +static const struct clk_ops eswin_sdio_sampleclk_ops = {
> +	.recalc_rate = eswin_sdhci_sdio_sampleclk_recalc_rate,
> +};
> +
> +static const struct eswin_sdhci_clk_ops eswin_sdio_clk_ops = {
> +	.sdcardclk_ops = &eswin_sdio_sdcardclk_ops,
> +	.sampleclk_ops = &eswin_sdio_sampleclk_ops,
> +};
> +
> +static struct eswin_sdhci_of_data eswin_sdhci_fu800_sdio_data = {
> +	.pdata = &eswin_sdhci_sdio_cqe_pdata,
> +	.clk_ops = &eswin_sdio_clk_ops,
> +};
> +
> +static const struct of_device_id eswin_sdhci_sdio_of_match[] = {
> +	/* SoC-specific compatible strings*/
> +	{
> +		.compatible = "eswin,sdhci-sdio",
> +		.data = &eswin_sdhci_fu800_sdio_data,
> +	},
> +	{/* sentinel */}};
> +MODULE_DEVICE_TABLE(of, eswin_sdhci_sdio_of_match);
> +
> +/**
> + * eswin_sdhci_sdio_register_sdcardclk- Register the sdcardclk for a PHY to use
> + *
> + * @eswin_sdhci_sdio:       Our private data structure.
> + * @clk_xin:            Pointer to the functional clock
> + * @dev:                Pointer to our struct device.
> + *
> + * Some PHY devices need to know what the actual card clock is.  In order for
> + * them to find out, we'll provide a clock through the common clock framework
> + * for them to query.
> + *
> + * Return: 0 on success and error value on error
> + */
> +static int
> +eswin_sdhci_sdio_register_sdcardclk(struct eswin_sdhci_data *eswin_sdhci_sdio,
> +				    struct clk *clk_xin, struct device *dev)
> +{
> +	struct eswin_sdhci_clk_data *clk_data = &eswin_sdhci_sdio->clk_data;
> +	struct device_node *np = dev->of_node;
> +	struct clk_init_data sdcardclk_init;
> +	const char *parent_clk_name;
> +	int ret;
> +
> +	ret = of_property_read_string_index(np, "clock-output-names", 0,
> +					    &sdcardclk_init.name);
> +	if (ret) {
> +		dev_err(dev, "DT has #clock-cells but no clock-output-names\n");
> +		return ret;
> +	}
> +
> +	parent_clk_name = __clk_get_name(clk_xin);
> +	sdcardclk_init.parent_names = &parent_clk_name;
> +	sdcardclk_init.num_parents = 1;
> +	sdcardclk_init.flags = CLK_GET_RATE_NOCACHE;
> +	sdcardclk_init.ops = eswin_sdhci_sdio->clk_ops->sdcardclk_ops;
> +
> +	clk_data->sdcardclk_hw.init = &sdcardclk_init;
> +	clk_data->sdcardclk = devm_clk_register(dev, &clk_data->sdcardclk_hw);
> +	if (IS_ERR(clk_data->sdcardclk))
> +		return PTR_ERR(clk_data->sdcardclk);
> +
> +	clk_data->sdcardclk_hw.init = NULL;
> +
> +	ret = of_clk_add_provider(np, of_clk_src_simple_get,
> +				  clk_data->sdcardclk);
> +	if (ret)
> +		dev_err(dev, "Failed to add sdcard clock provider\n");
> +
> +	return ret;
> +}
> +
> +/**
> + * eswin_sdhci_sdio_register_sampleclk - Register the sampleclk for a PHY to use
> + *
> + * @eswin_sdhci_sdio:       Our private data structure.
> + * @clk_xin:            Pointer to the functional clock
> + * @dev:                Pointer to our struct device.
> + *
> + * Some PHY devices need to know what the actual card clock is.  In order for
> + * them to find out, we'll provide a clock through the common clock framework
> + * for them to query.
> + *
> + * Return: 0 on success and error value on error
> + */
> +static int
> +eswin_sdhci_sdio_register_sampleclk(struct eswin_sdhci_data *eswin_sdhci_sdio,
> +				    struct clk *clk_xin, struct device *dev)
> +{
> +	struct eswin_sdhci_clk_data *clk_data = &eswin_sdhci_sdio->clk_data;
> +	struct device_node *np = dev->of_node;
> +	struct clk_init_data sampleclk_init;
> +	const char *parent_clk_name;
> +	int ret;
> +
> +	ret = of_property_read_string_index(np, "clock-output-names", 1,
> +					    &sampleclk_init.name);
> +	if (ret) {
> +		dev_err(dev, "DT has #clock-cells but no clock-output-names\n");
> +		return ret;
> +	}
> +
> +	parent_clk_name = __clk_get_name(clk_xin);
> +	sampleclk_init.parent_names = &parent_clk_name;
> +	sampleclk_init.num_parents = 1;
> +	sampleclk_init.flags = CLK_GET_RATE_NOCACHE;
> +	sampleclk_init.ops = eswin_sdhci_sdio->clk_ops->sampleclk_ops;
> +
> +	clk_data->sampleclk_hw.init = &sampleclk_init;
> +	clk_data->sampleclk = devm_clk_register(dev, &clk_data->sampleclk_hw);
> +	if (IS_ERR(clk_data->sampleclk))
> +		return PTR_ERR(clk_data->sampleclk);
> +	clk_data->sampleclk_hw.init = NULL;
> +
> +	ret = of_clk_add_provider(np, of_clk_src_simple_get,
> +				  clk_data->sampleclk);
> +	if (ret)
> +		dev_err(dev, "Failed to add sample clock provider\n");
> +
> +	return ret;
> +}
> +
> +/**
> + * eswin_sdhci_sdio_unregister_sdclk- Undoes sdhci_arasan_register_sdclk()
> + *
> + * @dev:                Pointer to our struct device.
> + *
> + * Should be called any time we're exiting and sdhci_arasan_register_sdclk()
> + * returned success.
> + */
> +static void eswin_sdhci_sdio_unregister_sdclk(struct device *dev)
> +{
> +	struct device_node *np = dev->of_node;
> +
> +	if (!of_find_property(np, "#clock-cells", NULL))
> +		return;
> +
> +	of_clk_del_provider(dev->of_node);
> +}
> +
> +/**
> + * eswin_sdhci_sdio_register_sdclk- Register the sdcardclk for a PHY to use
> + *
> + * @eswin_sdhci_sdio:   Our private data structure.
> + * @clk_xin:            Pointer to the functional clock
> + * @dev:                Pointer to our struct device.
> + *
> + * Some PHY devices need to know what the actual card clock is.  In order for
> + * them to find out, we'll provide a clock through the common clock framework
> + * for them to query.
> + *
> + * Note: without seriously re-architecting SDHCI's clock code and testing on
> + * all platforms, there's no way to create a totally beautiful clock here
> + * with all clock ops implemented.      Instead, we'll just create a clock that
> + * can be queried and set the CLK_GET_RATE_NOCACHE attribute to tell common
> + * clock framework that we're doing things behind its back.  This should be
> + * sufficient to create nice clean device tree bindings and later (if needed) we
> + * can try re-architecting SDHCI if we see some benefit to it.
> + *
> + * Return: 0 on success and error value on error
> + */
> +static int
> +eswin_sdhci_sdio_register_sdclk(struct eswin_sdhci_data *eswin_sdhci_sdio,
> +				struct clk *clk_xin, struct device *dev)
> +{
> +	struct device_node *np = dev->of_node;
> +	u32 num_clks = 0;
> +	int ret;
> +
> +	/* Providing a clock to the PHY is optional; no error if missing */
> +	if (of_property_read_u32(np, "#clock-cells", &num_clks) < 0)
> +		return 0;
> +
> +	ret = eswin_sdhci_sdio_register_sdcardclk(eswin_sdhci_sdio, clk_xin,
> +						  dev);
> +	if (ret)
> +		return ret;
> +
> +	if (num_clks) {
> +		ret = eswin_sdhci_sdio_register_sampleclk(eswin_sdhci_sdio,
> +							  clk_xin, dev);
> +		if (ret) {
> +			eswin_sdhci_sdio_unregister_sdclk(dev);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int eswin_sdhci_sdio_add_host(struct eswin_sdhci_data *eswin_sdhci_sdio)
> +{
> +	struct sdhci_host *host = eswin_sdhci_sdio->host;
> +	struct cqhci_host *cq_host;
> +	bool dma64;
> +	int ret;
> +
> +	if (!eswin_sdhci_sdio->has_cqe)
> +		return sdhci_add_host(host);
> +
> +	ret = sdhci_setup_host(host);
> +	if (ret)
> +		return ret;
> +
> +	cq_host = devm_kzalloc(host->mmc->parent, sizeof(*cq_host), GFP_KERNEL);
> +	if (!cq_host) {
> +		ret = -ENOMEM;
> +		goto cleanup;
> +	}
> +
> +	cq_host->mmio = host->ioaddr + ESWIN_SDHCI_SD_CQE_BASE_ADDR;
> +	cq_host->ops = &eswin_sdhci_sdio_cqhci_ops;
> +
> +	dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
> +	if (dma64)
> +		cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
> +
> +	ret = cqhci_init(cq_host, host->mmc, dma64);
> +	if (ret)
> +		goto cleanup;
> +
> +	ret = __sdhci_add_host(host);
> +	if (ret)
> +		goto cleanup;
> +
> +	return 0;
> +
> +cleanup:
> +	sdhci_cleanup_host(host);
> +	return ret;
> +}
> +
> +static int eswin_sdhci_sdio_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +	struct clk *clk_xin;
> +	struct clk *clk_spll2_fout3;
> +	struct clk *clk_mux;
> +	struct sdhci_host *host;
> +	struct sdhci_pltfm_host *pltfm_host;
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	struct eswin_sdhci_data *eswin_sdhci_sdio;
> +	const struct eswin_sdhci_of_data *data;
> +	unsigned int val = 0;
> +
> +	data = of_device_get_match_data(dev);
> +	host = sdhci_pltfm_init(pdev, data->pdata, sizeof(*eswin_sdhci_sdio));
> +
> +	if (IS_ERR(host))
> +		return PTR_ERR(host);
> +
> +	pltfm_host = sdhci_priv(host);
> +	eswin_sdhci_sdio = sdhci_pltfm_priv(pltfm_host);
> +	eswin_sdhci_sdio->host = host;
> +	eswin_sdhci_sdio->has_cqe = false;
> +
> +	sdhci_get_of_property(pdev);
> +
> +	eswin_sdhci_sdio->clk_ops = data->clk_ops;
> +	eswin_sdhci_sdio->clk_ahb = devm_clk_get(dev, "clk_ahb");
> +	if (IS_ERR(eswin_sdhci_sdio->clk_ahb)) {
> +		ret = dev_err_probe(dev, PTR_ERR(eswin_sdhci_sdio->clk_ahb),
> +				    "clk_ahb clock not found.\n");
> +		goto err_pltfm_free;
> +	}
> +
> +	clk_xin = devm_clk_get(dev, "clk_xin");
> +	if (IS_ERR(clk_xin)) {
> +		ret = dev_err_probe(dev, PTR_ERR(clk_xin),
> +				    "clk_xin clock not found.\n");
> +		goto err_pltfm_free;
> +	}
> +
> +	clk_spll2_fout3 = devm_clk_get(dev, "clk_spll2_fout3");
> +
> +	if (IS_ERR(clk_spll2_fout3)) {
> +		ret = dev_err_probe(dev, PTR_ERR(clk_spll2_fout3),
> +				    "clk_spll2_fout3 clock not found.\n");
> +		goto err_pltfm_free;
> +	}
> +
> +	if (of_device_is_compatible(np, "eswin,sdhci-sdio")) {
> +		clk_mux = devm_clk_get(dev, "clk_mux1_1");
> +		if (IS_ERR(clk_mux)) {
> +			ret = dev_err_probe(dev, PTR_ERR(clk_mux),
> +					    "clk_mux1_1 clock not found.\n");
> +			goto err_pltfm_free;
> +		}
> +		/*switch the core clk source*/
> +		clk_set_parent(clk_mux, clk_spll2_fout3);
> +	}
> +
> +	ret = clk_prepare_enable(eswin_sdhci_sdio->clk_ahb);
> +	if (ret) {
> +		dev_err(dev, "Unable to enable AHB clock.\n");
> +		goto err_pltfm_free;
> +	}
> +	/* If clock-frequency property is set, use the provided value */
> +	if (pltfm_host->clock && pltfm_host->clock != clk_get_rate(clk_xin)) {
> +		ret = clk_set_rate(clk_xin, pltfm_host->clock);
> +		if (ret) {
> +			dev_err(&pdev->dev, "Failed to set SD clock rate\n");
> +			goto clk_dis_ahb;
> +		}
> +	}
> +
> +	ret = clk_prepare_enable(clk_xin);
> +	if (ret) {
> +		dev_err(dev, "Unable to enable SD clock.\n");
> +		goto clk_dis_ahb;
> +	}
> +
> +	pltfm_host->clk = clk_xin;
> +	ret = eswin_sdhci_sdio_register_sdclk(eswin_sdhci_sdio, clk_xin, dev);
> +	if (ret)
> +		goto clk_disable_all;
> +
> +	ret = eswin_sdhci_reset_init(dev, eswin_sdhci_sdio);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to reset\n");
> +		goto clk_disable_all;
> +	}
> +
> +	eswin_sdhci_sdio->crg_regmap = syscon_regmap_lookup_by_phandle(
> +		pdev->dev.of_node, "eswin,syscrg_csr");
> +	if (IS_ERR(eswin_sdhci_sdio->crg_regmap)) {
> +		dev_dbg(&pdev->dev, "No syscrg_csr phandle specified\n");
> +		goto clk_disable_all;
> +	}
> +
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,syscrg_csr",
> +					 1, &eswin_sdhci_sdio->crg_core_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get crg_core_clk (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,syscrg_csr",
> +					 2, &eswin_sdhci_sdio->crg_aclk_ctrl);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get crg_aclk_ctrl (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,syscrg_csr",
> +					 3, &eswin_sdhci_sdio->crg_cfg_ctrl);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get crg_cfg_ctrl (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +
> +	eswin_sdhci_sdio->hsp_regmap = syscon_regmap_lookup_by_phandle(
> +		dev->of_node, "eswin,hsp_sp_csr");
> +	if (IS_ERR(eswin_sdhci_sdio->hsp_regmap)) {
> +		dev_dbg(dev, "No hsp_sp_csr phandle specified\n");
> +		goto clk_disable_all;
> +	}
> +
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,hsp_sp_csr",
> +					 2, &eswin_sdhci_sdio->hsp_int_status);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get hsp_int_status (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,hsp_sp_csr",
> +					 3, &eswin_sdhci_sdio->hsp_pwr_ctrl);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get hsp_pwr_ctrl (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +
> +	regmap_write(eswin_sdhci_sdio->hsp_regmap,
> +		     eswin_sdhci_sdio->hsp_int_status, MSHC_INT_CLK_STABLE);
> +	regmap_write(eswin_sdhci_sdio->hsp_regmap,
> +		     eswin_sdhci_sdio->hsp_pwr_ctrl, MSHC_HOST_VAL_STABLE);
> +
> +	if (!of_property_read_u32(dev->of_node, "delay_code", &val))
> +		eswin_sdhci_sdio->phy.delay_code = val;
> +
> +	if (!of_property_read_u32(dev->of_node, "drive-impedance-ohm", &val))
> +		eswin_sdhci_sdio->phy.drive_impedance =
> +			eswin_convert_drive_impedance_ohm(pdev, val);
> +
> +	if (of_property_read_bool(dev->of_node, "enable-cmd-pullup"))
> +		eswin_sdhci_sdio->phy.enable_cmd_pullup = ENABLE;
> +	else
> +		eswin_sdhci_sdio->phy.enable_cmd_pullup = DISABLE;
> +
> +	if (of_property_read_bool(dev->of_node, "enable-data-pullup"))
> +		eswin_sdhci_sdio->phy.enable_data_pullup = ENABLE;
> +	else
> +		eswin_sdhci_sdio->phy.enable_data_pullup = DISABLE;
> +
> +	eswin_sdhci_dt_parse_clk_phases(dev, &eswin_sdhci_sdio->clk_data);
> +	ret = mmc_of_parse(host->mmc);
> +	if (ret) {
> +		ret = dev_err_probe(dev, ret, "parsing dt failed.\n");
> +		goto unreg_clk;
> +	}
> +
> +	ret = eswin_sdhci_sdio_add_host(eswin_sdhci_sdio);
> +	if (ret)
> +		goto unreg_clk;
> +
> +	pm_runtime_set_active(&pdev->dev);
> +	pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
> +	pm_runtime_use_autosuspend(&pdev->dev);
> +	pm_suspend_ignore_children(&pdev->dev, 1);
> +	pm_runtime_enable(&pdev->dev);
> +
> +	return 0;
> +
> +unreg_clk:
> +	eswin_sdhci_sdio_unregister_sdclk(dev);
> +clk_disable_all:
> +	clk_disable_unprepare(clk_xin);
> +clk_dis_ahb:
> +	clk_disable_unprepare(eswin_sdhci_sdio->clk_ahb);
> +err_pltfm_free:
> +	sdhci_pltfm_free(pdev);
> +	return ret;
> +}
> +
> +static void eswin_sdhci_sdio_remove(struct platform_device *pdev)
> +{
> +	int ret;
> +	struct sdhci_host *host = platform_get_drvdata(pdev);
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct eswin_sdhci_data *eswin_sdhci_sdio =
> +		sdhci_pltfm_priv(pltfm_host);
> +	struct clk *clk_ahb = eswin_sdhci_sdio->clk_ahb;
> +
> +	pm_runtime_get_sync(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +	pm_runtime_put_noidle(&pdev->dev);
> +
> +	sdhci_pltfm_remove(pdev);
> +
> +	if (eswin_sdhci_sdio->txrx_rst) {
> +		ret = reset_control_assert(eswin_sdhci_sdio->txrx_rst);
> +		WARN_ON(ret != 0);
> +	}
> +
> +	if (eswin_sdhci_sdio->phy_rst) {
> +		ret = reset_control_assert(eswin_sdhci_sdio->phy_rst);
> +		WARN_ON(ret != 0);
> +	}
> +
> +	if (eswin_sdhci_sdio->prstn) {
> +		ret = reset_control_assert(eswin_sdhci_sdio->prstn);
> +		WARN_ON(ret != 0);
> +	}
> +
> +	if (eswin_sdhci_sdio->arstn) {
> +		ret = reset_control_assert(eswin_sdhci_sdio->arstn);
> +		WARN_ON(ret != 0);
> +	}
> +
> +	eswin_sdhci_sdio_unregister_sdclk(&pdev->dev);
> +	clk_disable_unprepare(clk_ahb);
> +}
> +
> +static const struct dev_pm_ops eswin_sdhci_sdio_pmops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(eswin_sdhci_sdio_suspend,
> +				eswin_sdhci_sdio_resume)
> +		SET_RUNTIME_PM_OPS(eswin_sdhci_sdio_runtime_suspend,
> +				   eswin_sdhci_sdio_runtime_resume, NULL)};
> +
> +static struct platform_driver eswin_sdhci_sdio_driver = {
> +	.driver = {
> +		.name = "eswin-sdhci-sdio",
> +		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
> +		.of_match_table = eswin_sdhci_sdio_of_match,
> +		.pm = &eswin_sdhci_sdio_pmops,
> +	},
> +	.probe = eswin_sdhci_sdio_probe,
> +	.remove = eswin_sdhci_sdio_remove,
> +};
> +
> +module_platform_driver(eswin_sdhci_sdio_driver);
> +
> +MODULE_DESCRIPTION("Driver for the Eswin SDHCI Controller");
> +MODULE_AUTHOR("Shuang Liang <liangshuang@eswincomputing.com>");
> +MODULE_AUTHOR("Xuyang Dong <dongxuyang@eswincomputing.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/mmc/host/sdhci-of-eic7700.c b/drivers/mmc/host/sdhci-of-eic7700.c
> new file mode 100644
> index 000000000000..078f15b99d67
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-of-eic7700.c
> @@ -0,0 +1,1053 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * ESWIN Emmc Driver
> + *
> + * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
> + *
> + * Authors:
> + *	Shuang Liang <liangshuang@eswincomputing.com>
> + *	Xuyang Dong <dongxuyang@eswincomputing.com>
> + */
> +
> +#include "cqhci.h"
> +#include "sdhci-eic7700.h"
> +#include "sdhci-pltfm.h"
> +#include <linux/bitfield.h>
> +#include <linux/clk-provider.h>
> +#include <linux/iommu.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/phy/phy.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +
> +// EMMC_DWC_MSHC_CRYPTO_CFG_PTR 8 -- parameter
> +#define eswin_sdhci_VENDOR_REGISTER_BASEADDR 0x800
> +#define eswin_sdhci_VENDOR_EMMC_CTRL_REGISTER 0x2c
> +#define VENDOR_ENHANCED_STROBE BIT(8)
> +
> +#define eswin_sdhci_CQE_BASE_ADDR eswin_sdhci_VENDOR_REGISTER_BASEADDR
> +
> +/* Controller does not have CD wired and will not function normally without */
> +#define eswin_sdhci_QUIRK_FORCE_CDTEST BIT(0)
> +/* Controller immediately reports SDHCI_CLOCK_INT_STABLE after enabling the
> + * internal clock even when the clock isn't stable
> + */
> +#define eswin_sdhci_QUIRK_CLOCK_UNSTABLE BIT(1)
> +
> +/*
> + * On some SoCs the syscon area has a feature where the upper 16-bits of
> + * each 32-bit register act as a write mask for the lower 16-bits.  This allows
> + * atomic updates of the register without locking.  This macro is used on SoCs
> + * that have that feature.
> + */
> +#define HIWORD_UPDATE(val, mask, shift)                                        \
> +	((val) << (shift) | (mask) << ((shift) + 16))
> +
> +static void eswin_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
> +	struct eswin_sdhci_clk_data *clk_data = &eswin_sdhci->clk_data;
> +
> +	/* Set the Input and Output Clock Phase Delays */
> +	if (clk_data->set_clk_delays)
> +		clk_data->set_clk_delays(host);
> +
> +	eswin_sdhci_set_core_clock(host, clock);
> +
> +	/*
> +	 * Some controllers immediately report SDHCI_CLOCK_INT_STABLE
> +	 * after enabling the clock even though the clock is not
> +	 * stable. Trying to use a clock without waiting here results
> +	 * in EILSEQ while detecting some older/slower cards. The
> +	 * chosen delay is the maximum delay from sdhci_set_clock.
> +	 */
> +	if (eswin_sdhci->quirks & SDHCI_ESWIN_QUIRK_CLOCK_UNSTABLE)
> +		msleep(20);
> +}
> +
> +static void eswin_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc,
> +					      struct mmc_ios *ios)
> +{
> +	u32 vendor;
> +	struct sdhci_host *host = mmc_priv(mmc);
> +
> +	vendor = sdhci_readl(host, eswin_sdhci_VENDOR_EMMC_CTRL_REGISTER);
> +	if (ios->enhanced_strobe)
> +		vendor |= VENDOR_ENHANCED_STROBE;
> +	else
> +		vendor &= ~VENDOR_ENHANCED_STROBE;
> +
> +	sdhci_writel(host, vendor, eswin_sdhci_VENDOR_EMMC_CTRL_REGISTER);
> +}
> +
> +static void eswin_sdhci_config_phy_delay(struct sdhci_host *host, int delay)
> +{
> +	delay &= PHY_CLK_MAX_DELAY_MASK;
> +
> +	/*phy clk delay line config*/
> +	sdhci_writeb(host, PHY_UPDATE_DELAY_CODE, PHY_SDCLKDL_CNFG_R);
> +	sdhci_writeb(host, delay, PHY_SDCLKDL_DC_R);
> +	sdhci_writeb(host, 0x0, PHY_SDCLKDL_CNFG_R);
> +}
> +
> +static void eswin_sdhci_config_phy(struct sdhci_host *host)
> +{
> +	unsigned int val = 0;
> +	unsigned int drv = 0;
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
> +	struct eswin_sdhci_phy_data *phy = &eswin_sdhci->phy;
> +
> +	drv = phy->drive_impedance << PHY_PAD_SP_DRIVE_SHIF;
> +
> +	pr_debug("%s: phy drv=0x%x\n", mmc_hostname(host->mmc), drv);
> +
> +	val = sdhci_readw(host, VENDOR_EMMC_CTRL_R);
> +	val |= EMMC_CRAD_PRESENT; // emmc card
> +	sdhci_writew(host, val, VENDOR_EMMC_CTRL_R);
> +
> +	eswin_sdhci_disable_card_clk(host);
> +
> +	/* reset phy,config phy's pad */
> +	sdhci_writel(host, drv | (~PHY_RSTN), PHY_CNFG_R);
> +	/*CMDPAD_CNFS*/
> +	val = (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_P_BIT_SHIFT) |
> +	      (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_N_BIT_SHIFT) |
> +	      (phy->enable_cmd_pullup << PHY_PULL_BIT_SHIF) | PHY_PAD_RXSEL_1;
> +	sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
> +	pr_debug("%s: phy cmd=0x%x\n", mmc_hostname(host->mmc), val);
> +
> +	/*DATA PAD CNFG*/
> +	val = (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_P_BIT_SHIFT) |
> +	      (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_N_BIT_SHIFT) |
> +	      (phy->enable_data_pullup << PHY_PULL_BIT_SHIF) | PHY_PAD_RXSEL_1;
> +	sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
> +	pr_debug("%s: phy data=0x%x\n", mmc_hostname(host->mmc), val);
> +
> +	/*Clock PAD Setting*/
> +	val = (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_P_BIT_SHIFT) |
> +	      (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_N_BIT_SHIFT) | PHY_PAD_RXSEL_0;
> +	sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
> +	pr_debug("%s: phy clk=0x%x\n", mmc_hostname(host->mmc), val);
> +
> +	/*PHY strobe PAD setting*/
> +	val = (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_P_BIT_SHIFT) |
> +	      (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_N_BIT_SHIFT) |
> +	      ((phy->enable_strobe_pulldown * PHY_PULL_DOWN)
> +	       << PHY_PULL_BIT_SHIF) |
> +	      PHY_PAD_RXSEL_1;
> +	sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
> +	pr_debug("%s: phy strobe=0x%x\n", mmc_hostname(host->mmc), val);
> +	mdelay(2);
> +
> +	/*PHY RSTN PAD setting*/
> +	val = (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_P_BIT_SHIFT) |
> +	      (PHY_SLEW_2 << PHY_TX_SLEW_CTRL_N_BIT_SHIFT) |
> +	      (PHY_PULL_UP << PHY_PULL_BIT_SHIF) | PHY_PAD_RXSEL_1;
> +	sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
> +	pr_debug("%s: phy rstn=0x%x\n", mmc_hostname(host->mmc), val);
> +
> +	sdhci_writel(host, drv | PHY_RSTN, PHY_CNFG_R);
> +
> +	eswin_sdhci_config_phy_delay(host, phy->delay_code);
> +
> +	eswin_sdhci_enable_card_clk(host);
> +}
> +
> +static void eswin_sdhci_reset(struct sdhci_host *host, u8 mask)
> +{
> +	u8 ctrl;
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
> +
> +	sdhci_writel(host, 0, SDHCI_INT_ENABLE);
> +	sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
> +	sdhci_reset(host, mask);
> +	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
> +	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
> +
> +	if (eswin_sdhci->quirks & SDHCI_ESWIN_QUIRK_FORCE_CDTEST) {
> +		ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
> +		ctrl |= SDHCI_CTRL_CDTEST_INS | SDHCI_CTRL_CDTEST_EN;
> +		sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
> +	}
> +
> +	if (mask == SDHCI_RESET_ALL) { // after reset all,the phy`s config will
> +				       // be clear.
> +		eswin_sdhci_config_phy(host);
> +	}
> +}
> +
> +static u32 eswin_sdhci_cqhci_irq(struct sdhci_host *host, u32 intmask)
> +{
> +	int cmd_error = 0;
> +	int data_error = 0;
> +
> +	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
> +		return intmask;
> +
> +	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
> +
> +	return 0;
> +}
> +
> +static void eswin_sdhci_dumpregs(struct mmc_host *mmc)
> +{
> +	sdhci_dumpregs(mmc_priv(mmc));
> +}
> +
> +static void eswin_sdhci_cqe_enable(struct mmc_host *mmc)
> +{
> +	struct sdhci_host *host = mmc_priv(mmc);
> +	u32 reg;
> +
> +	reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
> +	while (reg & SDHCI_DATA_AVAILABLE) {
> +		sdhci_readl(host, SDHCI_BUFFER);
> +		reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
> +	}
> +
> +	sdhci_cqe_enable(mmc);
> +}
> +
> +static int eswin_sdhci_delay_tuning(struct sdhci_host *host, u32 opcode)
> +{
> +	int ret;
> +	int delay = 0;
> +	int i = 0;
> +	int delay_min = -1;
> +	int delay_max = -1;
> +	int cmd_error = 0;
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
> +
> +	for (i = 0; i <= PHY_DELAY_CODE_MAX; i++) {
> +		eswin_sdhci_disable_card_clk(host);
> +		eswin_sdhci_config_phy_delay(host, i);
> +		eswin_sdhci_enable_card_clk(host);
> +		ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
> +		if (ret) {
> +			host->ops->reset(host,
> +					 SDHCI_RESET_CMD | SDHCI_RESET_DATA);
> +			udelay(200);
> +			if (delay_min != -1 && delay_max != -1)
> +				break;
> +		} else {
> +			if (delay_min == -1) {
> +				delay_min = i;
> +				continue;
> +			} else {
> +				delay_max = i;
> +				continue;
> +			}
> +		}
> +	}
> +	if (delay_min == -1 && delay_max == -1) {
> +		pr_err("%s: delay code tuning failed!\n",
> +		       mmc_hostname(host->mmc));
> +		eswin_sdhci_disable_card_clk(host);
> +		eswin_sdhci_config_phy_delay(host, eswin_sdhci->phy.delay_code);
> +		eswin_sdhci_enable_card_clk(host);
> +
> +		return ret;
> +	}
> +
> +	delay = (delay_min + delay_max) / 2;
> +	pr_debug("%s: set delay:0x%x\n", mmc_hostname(host->mmc), delay);
> +	eswin_sdhci_disable_card_clk(host);
> +	eswin_sdhci_config_phy_delay(host, delay);
> +	eswin_sdhci_enable_card_clk(host);
> +
> +	return 0;
> +}
> +
> +static int eswin_sdhci_phase_code_tuning(struct sdhci_host *host, u32 opcode)
> +{
> +	int cmd_error = 0;
> +	int ret = 0;
> +	int phase_code = 0;
> +	int code_min = -1;
> +	int code_max = -1;
> +
> +	for (phase_code = 0; phase_code <= MAX_PHASE_CODE; phase_code++) {
> +		eswin_sdhci_disable_card_clk(host);
> +		sdhci_writew(host, phase_code, VENDOR_AT_SATA_R);
> +		eswin_sdhci_enable_card_clk(host);
> +
> +		ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
> +		if (ret) {
> +			host->ops->reset(host,
> +					 SDHCI_RESET_CMD | SDHCI_RESET_DATA);
> +			udelay(200);
> +			if (code_min != -1 && code_max != -1)
> +				break;
> +		} else {
> +			if (code_min == -1) {
> +				code_min = phase_code;
> +				continue;
> +			} else {
> +				code_max = phase_code;
> +				continue;
> +			}
> +		}
> +	}
> +	if (code_min == -1 && code_max == -1) {
> +		pr_err("%s: phase code tuning failed!\n",
> +		       mmc_hostname(host->mmc));
> +		eswin_sdhci_disable_card_clk(host);
> +		sdhci_writew(host, 0, VENDOR_AT_SATA_R);
> +		eswin_sdhci_enable_card_clk(host);
> +		return -EIO;
> +	}
> +
> +	phase_code = (code_min + code_max) / 2;
> +	pr_debug("%s: set phase_code:0x%x\n", mmc_hostname(host->mmc),
> +		 phase_code);
> +
> +	eswin_sdhci_disable_card_clk(host);
> +	sdhci_writew(host, phase_code, VENDOR_AT_SATA_R);
> +	eswin_sdhci_enable_card_clk(host);
> +
> +	return 0;
> +}
> +
> +static int eswin_sdhci_executing_tuning(struct sdhci_host *host, u32 opcode)
> +{
> +	u32 ctrl;
> +	u32 val;
> +	int ret = 0;
> +
> +	eswin_sdhci_disable_card_clk(host);
> +
> +	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +	ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> +	sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> +	val = sdhci_readl(host, VENDOR_AT_CTRL_R);
> +	val |= SW_TUNE_ENABLE;
> +	sdhci_writew(host, val, VENDOR_AT_CTRL_R);
> +	sdhci_writew(host, 0, VENDOR_AT_SATA_R);
> +
> +	eswin_sdhci_enable_card_clk(host);
> +
> +	sdhci_writew(host, 0x0, SDHCI_CMD_DATA);
> +
> +	ret = eswin_sdhci_delay_tuning(host, opcode);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = eswin_sdhci_phase_code_tuning(host, opcode);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static void eswin_sdhci_set_uhs_signaling(struct sdhci_host *host,
> +					  unsigned int timing)
> +{
> +	u32 val;
> +	u32 status;
> +	u32 timeout = 0;
> +	u16 ctrl_2;
> +
> +	ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +	/* Select Bus Speed Mode for host */
> +	ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
> +	if ((timing == MMC_TIMING_MMC_HS200) ||
> +	    (timing == MMC_TIMING_UHS_SDR104))
> +		ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
> +	else if (timing == MMC_TIMING_UHS_SDR12)
> +		ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
> +	else if (timing == MMC_TIMING_UHS_SDR25)
> +		ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
> +	else if (timing == MMC_TIMING_UHS_SDR50)
> +		ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
> +	else if ((timing == MMC_TIMING_UHS_DDR50) ||
> +		 (timing == MMC_TIMING_MMC_DDR52))
> +		ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
> +	else if (timing == MMC_TIMING_MMC_HS400)
> +		ctrl_2 |= ESWIN_SDHCI_CTRL_HS400; /* Non-standard */
> +	sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
> +
> +	/*
> +	 * here need make dll locked when in hs400 at 200MHz
> +	 */
> +	if ((timing == MMC_TIMING_MMC_HS400) && (host->clock == 200000000)) {
> +		eswin_sdhci_disable_card_clk(host);
> +
> +		val = sdhci_readl(host, VENDOR_AT_CTRL_R);
> +		val &= ~(LATENCY_LT_MASK << LATENCY_LT_BIT_OFFSET);
> +		val |= (LATENCY_LT_3 << LATENCY_LT_MASK);
> +		sdhci_writel(host, val, VENDOR_AT_CTRL_R);
> +
> +		sdhci_writeb(host, 0x23, PHY_DLL_CNFG1_R);
> +		sdhci_writeb(host, 0x02, PHY_DLL_CNFG2_R);
> +		sdhci_writeb(host, 0x60, PHY_DLLDL_CNFG_R);
> +		sdhci_writeb(host, 0x00, PHY_DLL_OFFST_R);
> +		sdhci_writew(host, 0xffff, PHY_DLLBT_CNFG_R);
> +
> +		eswin_sdhci_enable_card_clk(host);
> +		sdhci_writeb(host, DLL_ENABEL, PHY_DLL_CTRL_R);
> +		udelay(100);
> +
> +		while (1) {
> +			status = sdhci_readb(host, PHY_DLL_STATUS_R);
> +			if (status & DLL_LOCK_STS) {
> +				pr_debug("%s: locked status:0x%x\n",
> +					 mmc_hostname(host->mmc), status);
> +				break;
> +			}
> +			timeout++;
> +			udelay(100);
> +			if (timeout > 10000) {
> +				pr_err("%s: DLL lock failed!status:0x%x\n",
> +				       mmc_hostname(host->mmc), status);
> +				return;
> +			}
> +		}
> +
> +		status = sdhci_readb(host, PHY_DLL_STATUS_R);
> +		if (status & DLL_ERROR_STS) {
> +			pr_err("%s: DLL lock failed!err_status:0x%x\n",
> +			       mmc_hostname(host->mmc), status);
> +		} else {
> +			pr_debug("%s: DLL lock is success\n",
> +				 mmc_hostname(host->mmc));
> +		}
> +	}
> +}
> +
> +static const struct cqhci_host_ops eswin_sdhci_cqhci_ops = {
> +	.enable = eswin_sdhci_cqe_enable,
> +	.disable = sdhci_cqe_disable,
> +	.dumpregs = eswin_sdhci_dumpregs,
> +};
> +
> +static const struct sdhci_ops eswin_sdhci_cqe_ops = {
> +	.set_clock = eswin_sdhci_set_clock,
> +	.get_max_clock = sdhci_pltfm_clk_get_max_clock,
> +	.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
> +	.set_bus_width = sdhci_set_bus_width,
> +	.reset = eswin_sdhci_reset,
> +	.set_uhs_signaling = eswin_sdhci_set_uhs_signaling,
> +	.set_power = sdhci_set_power_and_bus_voltage,
> +	.irq = eswin_sdhci_cqhci_irq,
> +	.platform_execute_tuning = eswin_sdhci_executing_tuning,
> +	.dump_vendor_regs = eswin_sdhci_dump_vendor_regs,
> +};
> +
> +static const struct sdhci_pltfm_data eswin_sdhci_cqe_pdata = {
> +	.ops = &eswin_sdhci_cqe_ops,
> +	.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
> +		  SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
> +	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
> +		   SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
> +};
> +
> +#ifdef CONFIG_PM_SLEEP
> +/**
> + * eswin_sdhci_suspend - Suspend method for the driver
> + * @dev:    Address of the device structure
> + *
> + * Put the device in a low power state.
> + *
> + * Return: 0 on success and error value on error
> + */
> +static int eswin_sdhci_suspend(struct device *dev)
> +{
> +	struct sdhci_host *host = dev_get_drvdata(dev);
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
> +	int ret;
> +
> +	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
> +		mmc_retune_needed(host->mmc);
> +
> +	ret = sdhci_suspend_host(host);
> +	if (ret)
> +		return ret;
> +
> +	clk_disable_unprepare(pltfm_host->clk);
> +	clk_disable_unprepare(eswin_sdhci->clk_ahb);
> +
> +	return 0;
> +}
> +
> +/**
> + * eswin_sdhci_resume - Resume method for the driver
> + * @dev:    Address of the device structure
> + *
> + * Resume operation after suspend
> + *
> + * Return: 0 on success and error value on error
> + */
> +static int eswin_sdhci_resume(struct device *dev)
> +{
> +	struct sdhci_host *host = dev_get_drvdata(dev);
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
> +	int ret;
> +
> +	ret = clk_prepare_enable(eswin_sdhci->clk_ahb);
> +	if (ret) {
> +		dev_err(dev, "can't enable clk_ahb\n");
> +		return ret;
> +	}
> +	ret = clk_prepare_enable(pltfm_host->clk);
> +	if (ret) {
> +		dev_err(dev, "can't enable mainck\n");
> +		goto clk_ahb_disable;
> +	}
> +
> +	ret = sdhci_resume_host(host);
> +	if (ret) {
> +		dev_err(dev, "runtime resume failed!\n");
> +		goto clk_disable;
> +	}
> +
> +	return 0;
> +clk_disable:
> +	clk_disable_unprepare(pltfm_host->clk);
> +clk_ahb_disable:
> +	clk_disable_unprepare(eswin_sdhci->clk_ahb);
> +
> +	return ret;
> +}
> +
> +#endif /* ! CONFIG_PM_SLEEP */
> +
> +/**
> + * eswin_sdhci_sdcardclk_recalc_rate - Return the card clock rate
> + *
> + * @hw:         Pointer to the hardware clock structure.
> + * @parent_rate:        The parent rate (should be rate of clk_xin).
> + *
> + * Return the current actual rate of the SD card clock.  This can be used
> + * to communicate with out PHY.
> + *
> + * Return: The card clock rate.
> + */
> +static unsigned long
> +eswin_sdhci_sdcardclk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
> +{
> +	struct eswin_sdhci_clk_data *clk_data =
> +		container_of(hw, struct eswin_sdhci_clk_data, sdcardclk_hw);
> +	struct eswin_sdhci_data *eswin_sdhci =
> +		container_of(clk_data, struct eswin_sdhci_data, clk_data);
> +	struct sdhci_host *host = eswin_sdhci->host;
> +
> +	return host->mmc->actual_clock;
> +}
> +
> +static const struct clk_ops eswin_sdcardclk_ops = {
> +	.recalc_rate = eswin_sdhci_sdcardclk_recalc_rate,
> +};
> +
> +/**
> + * eswin_sdhci_sampleclk_recalc_rate - Return the sampling clock rate
> + *
> + * @hw:         Pointer to the hardware clock structure.
> + * @parent_rate:        The parent rate (should be rate of clk_xin).
> + *
> + * Return the current actual rate of the sampling clock.  This can be used
> + * to communicate with out PHY.
> + *
> + * Return: The sample clock rate.
> + */
> +static unsigned long
> +eswin_sdhci_sampleclk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
> +{
> +	struct eswin_sdhci_clk_data *clk_data =
> +		container_of(hw, struct eswin_sdhci_clk_data, sampleclk_hw);
> +	struct eswin_sdhci_data *eswin_sdhci =
> +		container_of(clk_data, struct eswin_sdhci_data, clk_data);
> +	struct sdhci_host *host = eswin_sdhci->host;
> +
> +	return host->mmc->actual_clock;
> +}
> +
> +static const struct clk_ops eswin_sampleclk_ops = {
> +	.recalc_rate = eswin_sdhci_sampleclk_recalc_rate,
> +};
> +
> +static const struct eswin_sdhci_clk_ops eswin_clk_ops = {
> +	.sdcardclk_ops = &eswin_sdcardclk_ops,
> +	.sampleclk_ops = &eswin_sampleclk_ops,
> +};
> +
> +static struct eswin_sdhci_of_data eswin_sdhci_fu800_data = {
> +	.pdata = &eswin_sdhci_cqe_pdata,
> +	.clk_ops = &eswin_clk_ops,
> +};
> +
> +static const struct of_device_id eswin_sdhci_of_match[] = {
> +	{
> +		.compatible = "eswin,eic7700-emmc-sdhci",
> +		.data = &eswin_sdhci_fu800_data,
> +	},
> +	{/* sentinel */}};
> +MODULE_DEVICE_TABLE(of, eswin_sdhci_of_match);
> +
> +/**
> + * eswin_sdhci_register_sdcardclk - Register the sdcardclk for a PHY to use
> + *
> + * @eswin_sdhci:    Our private data structure.
> + * @clk_xin:        Pointer to the functional clock
> + * @dev:        Pointer to our struct device.
> + *
> + * Some PHY devices need to know what the actual card clock is.  In order for
> + * them to find out, we'll provide a clock through the common clock framework
> + * for them to query.
> + *
> + * Return: 0 on success and error value on error
> + */
> +static int eswin_sdhci_register_sdcardclk(struct eswin_sdhci_data *eswin_sdhci,
> +					  struct clk *clk_xin,
> +					  struct device *dev)
> +{
> +	struct eswin_sdhci_clk_data *clk_data = &eswin_sdhci->clk_data;
> +	struct device_node *np = dev->of_node;
> +	struct clk_init_data sdcardclk_init;
> +	const char *parent_clk_name;
> +	int ret;
> +
> +	ret = of_property_read_string_index(np, "clock-output-names", 0,
> +					    &sdcardclk_init.name);
> +	if (ret) {
> +		dev_err(dev, "DT has #clock-cells but no clock-output-names\n");
> +		return ret;
> +	}
> +
> +	parent_clk_name = __clk_get_name(clk_xin);
> +	sdcardclk_init.parent_names = &parent_clk_name;
> +	sdcardclk_init.num_parents = 1;
> +	sdcardclk_init.flags = CLK_GET_RATE_NOCACHE;
> +	sdcardclk_init.ops = eswin_sdhci->clk_ops->sdcardclk_ops;
> +
> +	clk_data->sdcardclk_hw.init = &sdcardclk_init;
> +	clk_data->sdcardclk = devm_clk_register(dev, &clk_data->sdcardclk_hw);
> +	if (IS_ERR(clk_data->sdcardclk))
> +		return PTR_ERR(clk_data->sdcardclk);
> +	clk_data->sdcardclk_hw.init = NULL;
> +
> +	ret = of_clk_add_provider(np, of_clk_src_simple_get,
> +				  clk_data->sdcardclk);
> +	if (ret)
> +		dev_err(dev, "Failed to add sdcard clock provider\n");
> +
> +	return ret;
> +}
> +
> +/**
> + * eswin_sdhci_register_sampleclk - Register the sampleclk for a PHY to use
> + *
> + * @eswin_sdhci:    Our private data structure.
> + * @clk_xin:        Pointer to the functional clock
> + * @dev:        Pointer to our struct device.
> + *
> + * Some PHY devices need to know what the actual card clock is.  In order for
> + * them to find out, we'll provide a clock through the common clock framework
> + * for them to query.
> + *
> + * Return: 0 on success and error value on error
> + */
> +static int eswin_sdhci_register_sampleclk(struct eswin_sdhci_data *eswin_sdhci,
> +					  struct clk *clk_xin,
> +					  struct device *dev)
> +{
> +	struct eswin_sdhci_clk_data *clk_data = &eswin_sdhci->clk_data;
> +	struct device_node *np = dev->of_node;
> +	struct clk_init_data sampleclk_init;
> +	const char *parent_clk_name;
> +	int ret;
> +
> +	ret = of_property_read_string_index(np, "clock-output-names", 1,
> +					    &sampleclk_init.name);
> +	if (ret) {
> +		dev_err(dev, "DT has #clock-cells but no clock-output-names\n");
> +		return ret;
> +	}
> +
> +	parent_clk_name = __clk_get_name(clk_xin);
> +	sampleclk_init.parent_names = &parent_clk_name;
> +	sampleclk_init.num_parents = 1;
> +	sampleclk_init.flags = CLK_GET_RATE_NOCACHE;
> +	sampleclk_init.ops = eswin_sdhci->clk_ops->sampleclk_ops;
> +
> +	clk_data->sampleclk_hw.init = &sampleclk_init;
> +	clk_data->sampleclk = devm_clk_register(dev, &clk_data->sampleclk_hw);
> +	if (IS_ERR(clk_data->sampleclk))
> +		return PTR_ERR(clk_data->sampleclk);
> +	clk_data->sampleclk_hw.init = NULL;
> +
> +	ret = of_clk_add_provider(np, of_clk_src_simple_get,
> +				  clk_data->sampleclk);
> +	if (ret)
> +		dev_err(dev, "Failed to add sample clock provider\n");
> +
> +	return ret;
> +}
> +
> +/**
> + * eswin_sdhci_unregister_sdclk - Undoes eswin_sdhci_register_sdclk()
> + *
> + * @dev:        Pointer to our struct device.
> + *
> + * Should be called any time we're exiting and eswin_sdhci_register_sdclk()
> + * returned success.
> + */
> +static void eswin_sdhci_unregister_sdclk(struct device *dev)
> +{
> +	struct device_node *np = dev->of_node;
> +
> +	if (!of_find_property(np, "#clock-cells", NULL))
> +		return;
> +
> +	of_clk_del_provider(dev->of_node);
> +}
> +
> +/**
> + * eswin_sdhci_register_sdclk - Register the sdcardclk for a PHY to use
> + *
> + * @eswin_sdhci:    Our private data structure.
> + * @clk_xin:        Pointer to the functional clock
> + * @dev:        Pointer to our struct device.
> + *
> + * Some PHY devices need to know what the actual card clock is.  In order for
> + * them to find out, we'll provide a clock through the common clock framework
> + * for them to query.
> + *
> + * Note: without seriously re-architecting SDHCI's clock code and testing on
> + * all platforms, there's no way to create a totally beautiful clock here
> + * with all clock ops implemented.  Instead, we'll just create a clock that can
> + * be queried and set the CLK_GET_RATE_NOCACHE attribute to tell common clock
> + * framework that we're doing things behind its back.  This should be sufficient
> + * to create nice clean device tree bindings and later (if needed) we can try
> + * re-architecting SDHCI if we see some benefit to it.
> + *
> + * Return: 0 on success and error value on error
> + */
> +static int eswin_sdhci_register_sdclk(struct eswin_sdhci_data *eswin_sdhci,
> +				      struct clk *clk_xin, struct device *dev)
> +{
> +	struct device_node *np = dev->of_node;
> +	u32 num_clks = 0;
> +	int ret;
> +
> +	/* Providing a clock to the PHY is optional; no error if missing */
> +	if (of_property_read_u32(np, "#clock-cells", &num_clks) < 0)
> +		return 0;
> +
> +	ret = eswin_sdhci_register_sdcardclk(eswin_sdhci, clk_xin, dev);
> +	if (ret)
> +		return ret;
> +
> +	if (num_clks) {
> +		ret = eswin_sdhci_register_sampleclk(eswin_sdhci, clk_xin, dev);
> +		if (ret) {
> +			eswin_sdhci_unregister_sdclk(dev);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int eswin_sdhci_add_host(struct eswin_sdhci_data *eswin_sdhci)
> +{
> +	struct sdhci_host *host = eswin_sdhci->host;
> +	struct cqhci_host *cq_host;
> +	bool dma64;
> +	int ret;
> +
> +	if (!eswin_sdhci->has_cqe)
> +		return sdhci_add_host(host);
> +
> +	ret = sdhci_setup_host(host);
> +	if (ret)
> +		return ret;
> +
> +	cq_host = devm_kzalloc(host->mmc->parent, sizeof(*cq_host), GFP_KERNEL);
> +	if (!cq_host) {
> +		ret = -ENOMEM;
> +		goto cleanup;
> +	}
> +
> +	cq_host->mmio = host->ioaddr + eswin_sdhci_CQE_BASE_ADDR;
> +	cq_host->ops = &eswin_sdhci_cqhci_ops;
> +
> +	dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
> +	if (dma64)
> +		cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
> +
> +	ret = cqhci_init(cq_host, host->mmc, dma64);
> +	if (ret)
> +		goto cleanup;
> +
> +	ret = __sdhci_add_host(host);
> +	if (ret)
> +		goto cleanup;
> +
> +	return 0;
> +
> +cleanup:
> +	sdhci_cleanup_host(host);
> +	return ret;
> +}
> +
> +static int eswin_sdhci_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +	struct clk *clk_xin;
> +	struct sdhci_host *host;
> +	struct sdhci_pltfm_host *pltfm_host;
> +	struct device *dev = &pdev->dev;
> +	struct eswin_sdhci_data *eswin_sdhci;
> +	const struct eswin_sdhci_of_data *data;
> +	unsigned int val = 0;
> +
> +	data = of_device_get_match_data(dev);
> +	host = sdhci_pltfm_init(pdev, data->pdata, sizeof(*eswin_sdhci));
> +	if (IS_ERR(host))
> +		return PTR_ERR(host);
> +
> +	pltfm_host = sdhci_priv(host);
> +	eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
> +	eswin_sdhci->host = host;
> +	eswin_sdhci->clk_ops = data->clk_ops;
> +
> +	eswin_sdhci->clk_ahb = devm_clk_get(dev, "clk_ahb");
> +	if (IS_ERR(eswin_sdhci->clk_ahb)) {
> +		ret = dev_err_probe(dev, PTR_ERR(eswin_sdhci->clk_ahb),
> +				    "clk_ahb clock not found.\n");
> +		goto err_pltfm_free;
> +	}
> +
> +	clk_xin = devm_clk_get(dev, "clk_xin");
> +	if (IS_ERR(clk_xin)) {
> +		ret = dev_err_probe(dev, PTR_ERR(clk_xin),
> +				    "clk_xin clock not found.\n");
> +		goto err_pltfm_free;
> +	}
> +
> +	ret = clk_prepare_enable(eswin_sdhci->clk_ahb);
> +	if (ret) {
> +		dev_err(dev, "Unable to enable AHB clock.\n");
> +		goto err_pltfm_free;
> +	}
> +
> +	ret = clk_prepare_enable(clk_xin);
> +	if (ret) {
> +		dev_err(dev, "Unable to enable SD clock.\n");
> +		goto clk_dis_ahb;
> +	}
> +
> +	ret = eswin_sdhci_reset_init(dev, eswin_sdhci);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to reset\n");
> +		goto clk_disable_all;
> +	}
> +
> +	eswin_sdhci->crg_regmap = syscon_regmap_lookup_by_phandle(
> +		pdev->dev.of_node, "eswin,syscrg_csr");
> +	if (IS_ERR(eswin_sdhci->crg_regmap)) {
> +		dev_dbg(&pdev->dev, "No syscrg_csr phandle specified\n");
> +		goto clk_disable_all;
> +	}
> +
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,syscrg_csr",
> +					 1, &eswin_sdhci->crg_core_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get crg_core_clk (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,syscrg_csr",
> +					 2, &eswin_sdhci->crg_aclk_ctrl);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get crg_aclk_ctrl (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,syscrg_csr",
> +					 3, &eswin_sdhci->crg_cfg_ctrl);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get crg_cfg_ctrl (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +
> +	eswin_sdhci->hsp_regmap = syscon_regmap_lookup_by_phandle(
> +		dev->of_node, "eswin,hsp_sp_csr");
> +	if (IS_ERR(eswin_sdhci->hsp_regmap)) {
> +		dev_dbg(dev, "No hsp_sp_csr phandle specified\n");
> +		goto clk_disable_all;
> +	}
> +
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,hsp_sp_csr",
> +					 2, &eswin_sdhci->hsp_int_status);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get hsp_int_status (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +	ret = of_property_read_u32_index(pdev->dev.of_node, "eswin,hsp_sp_csr",
> +					 3, &eswin_sdhci->hsp_pwr_ctrl);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't get hsp_pwr_ctrl (%d)\n", ret);
> +		goto clk_disable_all;
> +	}
> +
> +	regmap_write(eswin_sdhci->hsp_regmap, eswin_sdhci->hsp_int_status,
> +		     MSHC_INT_CLK_STABLE);
> +	regmap_write(eswin_sdhci->hsp_regmap, eswin_sdhci->hsp_pwr_ctrl,
> +		     MSHC_HOST_VAL_STABLE);
> +
> +	if (!of_property_read_u32(dev->of_node, "delay_code", &val))
> +		eswin_sdhci->phy.delay_code = val;
> +
> +	if (!of_property_read_u32(dev->of_node, "drive-impedance-ohm", &val))
> +		eswin_sdhci->phy.drive_impedance =
> +			eswin_convert_drive_impedance_ohm(pdev, val);
> +
> +	if (of_property_read_bool(dev->of_node, "enable-cmd-pullup"))
> +		eswin_sdhci->phy.enable_cmd_pullup = ENABLE;
> +	else
> +		eswin_sdhci->phy.enable_cmd_pullup = DISABLE;
> +
> +	if (of_property_read_bool(dev->of_node, "enable-data-pullup"))
> +		eswin_sdhci->phy.enable_data_pullup = ENABLE;
> +	else
> +		eswin_sdhci->phy.enable_data_pullup = DISABLE;
> +
> +	if (of_property_read_bool(dev->of_node, "enable-strobe-pulldown"))
> +		eswin_sdhci->phy.enable_strobe_pulldown = ENABLE;
> +	else
> +		eswin_sdhci->phy.enable_strobe_pulldown = DISABLE;
> +
> +	sdhci_get_of_property(pdev);
> +
> +	pltfm_host->clk = clk_xin;
> +
> +	ret = eswin_sdhci_register_sdclk(eswin_sdhci, clk_xin, dev);
> +	if (ret)
> +		goto clk_disable_all;
> +
> +	eswin_sdhci_dt_parse_clk_phases(dev, &eswin_sdhci->clk_data);
> +
> +	ret = mmc_of_parse(host->mmc);
> +	if (ret) {
> +		ret = dev_err_probe(dev, ret, "parsing dt failed.\n");
> +		goto unreg_clk;
> +	}
> +
> +	if (of_device_is_compatible(dev->of_node, "eswin,sdhci-5.1")) {
> +		host->mmc_host_ops.hs400_enhanced_strobe =
> +			eswin_sdhci_hs400_enhanced_strobe;
> +		eswin_sdhci->has_cqe = true;
> +		host->mmc->caps2 |= MMC_CAP2_CQE;
> +
> +		if (!of_property_read_bool(dev->of_node, "disable-cqe-dcmd"))
> +			host->mmc->caps2 |= MMC_CAP2_CQE_DCMD;
> +	}
> +
> +	sdhci_enable_v4_mode(eswin_sdhci->host);
> +
> +	ret = eswin_sdhci_add_host(eswin_sdhci);
> +	if (ret)
> +		goto unreg_clk;
> +
> +	return 0;
> +
> +unreg_clk:
> +	eswin_sdhci_unregister_sdclk(dev);
> +clk_disable_all:
> +	clk_disable_unprepare(clk_xin);
> +clk_dis_ahb:
> +	clk_disable_unprepare(eswin_sdhci->clk_ahb);
> +err_pltfm_free:
> +	sdhci_pltfm_free(pdev);
> +	return ret;
> +}
> +
> +static void eswin_sdhci_remove(struct platform_device *pdev)
> +{
> +	int ret;
> +	struct sdhci_host *host = platform_get_drvdata(pdev);
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct eswin_sdhci_data *eswin_sdhci = sdhci_pltfm_priv(pltfm_host);
> +	struct clk *clk_ahb = eswin_sdhci->clk_ahb;
> +
> +	sdhci_pltfm_remove(pdev);
> +
> +	if (eswin_sdhci->txrx_rst) {
> +		ret = reset_control_assert(eswin_sdhci->txrx_rst);
> +		WARN_ON(ret != 0);
> +	}
> +
> +	if (eswin_sdhci->phy_rst) {
> +		ret = reset_control_assert(eswin_sdhci->phy_rst);
> +		WARN_ON(ret != 0);
> +	}
> +
> +	if (eswin_sdhci->prstn) {
> +		ret = reset_control_assert(eswin_sdhci->prstn);
> +		WARN_ON(ret != 0);
> +	}
> +
> +	if (eswin_sdhci->arstn) {
> +		ret = reset_control_assert(eswin_sdhci->arstn);
> +		WARN_ON(ret != 0);
> +	}
> +	eswin_sdhci_unregister_sdclk(&pdev->dev);
> +	clk_disable_unprepare(clk_ahb);
> +}
> +
> +static void emmc_hard_reset(struct sdhci_host *host)
> +{
> +	unsigned int val;
> +
> +	val = sdhci_readw(host, VENDOR_EMMC_CTRL_R);
> +	val |= EMMC_RST_N_OE;
> +	sdhci_writew(host, val, VENDOR_EMMC_CTRL_R);
> +	val &= ~EMMC_RST_N;
> +	sdhci_writew(host, val, VENDOR_EMMC_CTRL_R);
> +	mdelay(20);
> +	val |= EMMC_RST_N;
> +	sdhci_writew(host, val, VENDOR_EMMC_CTRL_R);
> +}
> +
> +static void eswin_sdhci_shutdown(struct platform_device *pdev)
> +{
> +	struct sdhci_host *host = platform_get_drvdata(pdev);
> +
> +	if (!host)
> +		return;
> +
> +	emmc_hard_reset(host);
> +	host->ops->reset(host, SDHCI_RESET_ALL);
> +	platform_set_drvdata(pdev, NULL);
> +}
> +
> +static const struct dev_pm_ops eswin_sdhci_pmops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(eswin_sdhci_suspend, eswin_sdhci_resume)};
> +
> +static struct platform_driver eswin_sdhci_driver = {
> +	.driver = {
> +		.name = "sdhci-eic7700",
> +		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
> +		.of_match_table = eswin_sdhci_of_match,
> +		.pm = &eswin_sdhci_pmops,
> +	},
> +	.probe = eswin_sdhci_probe,
> +	.remove = eswin_sdhci_remove,
> +	.shutdown = eswin_sdhci_shutdown,
> +};
> +
> +module_platform_driver(eswin_sdhci_driver);
> +
> +MODULE_DESCRIPTION("Driver for the ESWIN SDHCI Controller");
> +MODULE_AUTHOR("Shuang Liang <liangshuang@eswincomputing.com>");
> +MODULE_AUTHOR("Xuyang Dong <dongxuyang@eswincomputing.com>");
> +MODULE_LICENSE("GPL");


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2025-05-22  7:48 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-16  9:12 [PATCH v1 0/2] Add driver support for ESWIN eic7700 SoC sdhci controller dongxuyang
2025-05-16  9:16 ` [PATCH v1 1/2] dt-bindings: sdhci: eswin: Documentation for eic7700 SoC dongxuyang
2025-05-16 10:26   ` Rob Herring (Arm)
2025-05-16 13:12   ` Krzysztof Kozlowski
2025-05-16  9:17 ` [PATCH v1 2/2] sdhci: eswin: Add eic7700 sdhci driver dongxuyang
2025-05-16 13:17   ` Krzysztof Kozlowski
2025-05-17  1:57   ` kernel test robot
2025-05-22  7:48   ` Adrian Hunter

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox