devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/2] Add support for Eswin EIC7700 SD/eMMC controller
@ 2025-10-11 11:10 hehuan1
  2025-10-11 11:11 ` [PATCH v4 1/2] dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700 hehuan1
  2025-10-11 11:12 ` [PATCH v4 2/2] mmc: sdhci-of-dwcmshc: Add support for " hehuan1
  0 siblings, 2 replies; 6+ messages in thread
From: hehuan1 @ 2025-10-11 11:10 UTC (permalink / raw)
  To: ulf.hansson, robh, krzk+dt, conor+dt, jszhang, adrian.hunter,
	p.zabel, linux-mmc, devicetree, linux-kernel
  Cc: ningyu, linmin, pinkesh.vaghela, xuxiang, luyulin, dongxuyang,
	zhangsenchuan, weishangjuan, lizhi2, caohang, hehuan1

From: Huan He <hehuan1@eswincomputing.com>

Updates:

  Changes in v4:
  - Update sdhci-of-dwcmshc.c
    - Address the compile error from kernel test robot
      - Remove duplicate implementation of dwcmshc_enable_card_clk()
      - Add missing dwcmshc_disable_card_clk() function implementation
  - Link to v3: https://lore.kernel.org/all/20251010093807.1579-1-hehuan1@eswincomputing.com/

  Changes in v3:
  - Update snps,dwcmshc-sdhci.yaml
    - Delete clock-output-names, '#clock-cells' and eswin,syscrg-csr
    - Update description for eswin,hsp-sp-csr
    - Update drive-impedance-ohm
    - Update the item of reset-names
  - Update sdhci-of-dwcmshc.c
    - Add descriptions for PHY registers
    - Simplify clock management(remove custom clock provider, use
      standard clk API)
    - Replace magic numbers with GENMASK() or FIELD_PREP() macros
    - Add comments explaining HSP stability assertion writes
    - Adjust line wrapping to fit within 100-column
    - Delete forward declarations by moving function definitions
    - Rename variable is_sdio to is_sd
    - Replace unclear macros with meaningful alternatives
  - Link to v2: https://lore.kernel.org/all/20250912093451.125-1-hehuan1@eswincomputing.com/

  Changes in v2:
  - Delete the previous separate driver and yaml binding file
  - Update snps,dwcmshc-sdhci.yaml to add support for Eswin EIC7700
    - Add the new compautible string: "eswin,eic7700-dwcmshc"
    - Add new properties: clock-output-names, '#clock-cells',
      drive-impedance-ohm, eswin,hsp-sp-csr and eswin,syscrg-csr
    - Add customized reset-names for EIC7700 platform
  - Update sdhci-of-dwcmshc.c to add support for Eswin EIC7700
    - Add a new struct eic7700_priv to hold Eswin-specific data,
      including clock phases, register mappings, and drive
      impedance configuration
    - Implement EIC7700-specific sdhci_ops
      - set_clock: support core clock configuration with phase delay
      - reset: add PHY reset and configuration
      - set_uhs_signaling: support HS400 DLL lock
      - platform_execute_tuning: implement delay line tuning and phase
        code adjustment
    - Add initialization routine (eic7700_init)
    - Integrate the new platform data and ops into the driver's match table
  - Link to v1: https://lore.kernel.org/all/20250516091259.774-1-dongxuyang@eswincomputing.com/

Huan He (2):
  dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700
  mmc: sdhci-of-dwcmshc: Add support for Eswin EIC7700

 .../bindings/mmc/snps,dwcmshc-sdhci.yaml      |  57 +-
 drivers/mmc/host/sdhci-of-dwcmshc.c           | 526 +++++++++++++++++-
 2 files changed, 555 insertions(+), 28 deletions(-)

-- 
2.25.1


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

* [PATCH v4 1/2] dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700
  2025-10-11 11:10 [PATCH v4 0/2] Add support for Eswin EIC7700 SD/eMMC controller hehuan1
@ 2025-10-11 11:11 ` hehuan1
  2025-10-13 11:40   ` Rob Herring
  2025-10-11 11:12 ` [PATCH v4 2/2] mmc: sdhci-of-dwcmshc: Add support for " hehuan1
  1 sibling, 1 reply; 6+ messages in thread
From: hehuan1 @ 2025-10-11 11:11 UTC (permalink / raw)
  To: ulf.hansson, robh, krzk+dt, conor+dt, jszhang, adrian.hunter,
	p.zabel, linux-mmc, devicetree, linux-kernel
  Cc: ningyu, linmin, pinkesh.vaghela, xuxiang, luyulin, dongxuyang,
	zhangsenchuan, weishangjuan, lizhi2, caohang, hehuan1,
	Conor Dooley

From: Huan He <hehuan1@eswincomputing.com>

EIC7700 use Synopsys dwcmshc IP for SD/eMMC controllers.
Add Eswin EIC7700 support in sdhci-of-dwcmshc.yaml.

Signed-off-by: Huan He <hehuan1@eswincomputing.com>
Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
---
 .../bindings/mmc/snps,dwcmshc-sdhci.yaml      | 57 +++++++++++++++++--
 1 file changed, 51 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml
index f882219a0a26..edd6c8e90cad 100644
--- a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml
+++ b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml
@@ -30,6 +30,7 @@ properties:
           - sophgo,sg2002-dwcmshc
           - sophgo,sg2042-dwcmshc
           - thead,th1520-dwcmshc
+          - eswin,eic7700-dwcmshc
 
   reg:
     maxItems: 1
@@ -52,17 +53,30 @@ properties:
     maxItems: 5
 
   reset-names:
-    items:
-      - const: core
-      - const: bus
-      - const: axi
-      - const: block
-      - const: timer
+    maxItems: 5
 
   rockchip,txclk-tapnum:
     description: Specify the number of delay for tx sampling.
     $ref: /schemas/types.yaml#/definitions/uint8
 
+  eswin,hsp-sp-csr:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    items:
+      - description: Phandle to HSP(High-Speed Peripheral) device
+      - description: Offset of the stability status register for
+                     internal clock
+      - description: Offset of the stability register for host regulator
+                     voltage.
+    description: |
+      HSP CSR is to control and get status of different high-speed
+      peripherals (such as Ethernet, USB, SATA, etc.) via register,
+      which can close module's clock, reset module independently
+      and tune board-level's parameters of PHY, etc.
+
+  eswin,drive-impedance-ohms:
+    description: Specifies the drive impedance in Ohm.
+    enum: [33, 40, 50, 66, 100]
+
 required:
   - compatible
   - reg
@@ -110,6 +124,37 @@ allOf:
             - const: block
             - const: timer
 
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: eswin,eic7700-dwcmshc
+    then:
+      properties:
+        resets:
+          minItems: 4
+          maxItems: 4
+        reset-names:
+          items:
+            - const: axi
+            - const: phy
+            - const: prstn
+            - const: txrx
+      required:
+        - eswin,hsp-sp-csr
+        - eswin,drive-impedance-ohms
+    else:
+      properties:
+        resets:
+          maxItems: 5
+        reset-names:
+          items:
+            - const: core
+            - const: bus
+            - const: axi
+            - const: block
+            - const: timer
+
   - if:
       properties:
         compatible:
-- 
2.25.1


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

* [PATCH v4 2/2] mmc: sdhci-of-dwcmshc: Add support for Eswin EIC7700
  2025-10-11 11:10 [PATCH v4 0/2] Add support for Eswin EIC7700 SD/eMMC controller hehuan1
  2025-10-11 11:11 ` [PATCH v4 1/2] dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700 hehuan1
@ 2025-10-11 11:12 ` hehuan1
  2025-10-14  8:42   ` Dan Carpenter
  2025-10-15 13:09   ` Adrian Hunter
  1 sibling, 2 replies; 6+ messages in thread
From: hehuan1 @ 2025-10-11 11:12 UTC (permalink / raw)
  To: ulf.hansson, robh, krzk+dt, conor+dt, jszhang, adrian.hunter,
	p.zabel, linux-mmc, devicetree, linux-kernel
  Cc: ningyu, linmin, pinkesh.vaghela, xuxiang, luyulin, dongxuyang,
	zhangsenchuan, weishangjuan, lizhi2, caohang, hehuan1,
	kernel test robot

From: Huan He <hehuan1@eswincomputing.com>

Add support for the mmc controller in the Eswin EIC7700 with the new
compatible "eswin,eic7700-dwcmshc". Implement custom sdhci_ops for
set_clock, reset, set_uhs_signaling, platform_execute_tuning.

Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202510110904.0H6EStUB-lkp@intel.com/
Closes: https://lore.kernel.org/oe-kbuild-all/202510110829.1yKzYvIP-lkp@intel.com/
Signed-off-by: Huan He <hehuan1@eswincomputing.com>
---
 drivers/mmc/host/sdhci-of-dwcmshc.c | 526 ++++++++++++++++++++++++++--
 1 file changed, 504 insertions(+), 22 deletions(-)

diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c
index eebd45389956..28bb2067f945 100644
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -11,6 +11,7 @@
 #include <linux/arm-smccc.h>
 #include <linux/bitfield.h>
 #include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/dma-mapping.h>
 #include <linux/iopoll.h>
 #include <linux/kernel.h>
@@ -19,8 +20,11 @@
 #include <linux/platform_device.h>
 #include <linux/pm_domain.h>
 #include <linux/pm_runtime.h>
+#include <linux/regmap.h>
 #include <linux/reset.h>
 #include <linux/sizes.h>
+#include <linux/mfd/syscon.h>
+#include <linux/units.h>
 
 #include "sdhci-pltfm.h"
 #include "cqhci.h"
@@ -39,6 +43,7 @@
 #define DWCMSHC_CARD_IS_EMMC		BIT(0)
 #define DWCMSHC_ENHANCED_STROBE		BIT(8)
 #define DWCMSHC_EMMC_ATCTRL		0x40
+#define DWCMSHC_AT_STAT			0x44
 /* Tuning and auto-tuning fields in AT_CTRL_R control register */
 #define AT_CTRL_AT_EN			BIT(0) /* autotuning is enabled */
 #define AT_CTRL_CI_SEL			BIT(1) /* interval to drive center phase select */
@@ -194,6 +199,19 @@
 #define PHY_DLLDL_CNFG_SLV_INPSEL_MASK	GENMASK(6, 5) /* bits [6:5] */
 #define PHY_DLLDL_CNFG_SLV_INPSEL	0x3 /* clock source select for slave DL */
 
+/* PHY DLL offset setting register */
+#define PHY_DLL_OFFST_R			(DWC_MSHC_PTR_PHY_R + 0x29)
+/* DLL LBT setting register */
+#define PHY_DLLBT_CNFG_R		(DWC_MSHC_PTR_PHY_R + 0x2c)
+/* DLL Status register */
+#define PHY_DLL_STATUS_R		(DWC_MSHC_PTR_PHY_R + 0x2e)
+#define DLL_LOCK_STS			BIT(0)/* DLL is locked and ready */
+/*
+ * Captures the value of DLL's lock error status information. Value is valid
+ * only when LOCK_STS is set.
+ */
+#define DLL_ERROR_STS			BIT(1)
+
 #define FLAG_IO_FIXED_1V8	BIT(0)
 
 #define BOUNDARY_OK(addr, len) \
@@ -206,6 +224,31 @@
 /* SMC call for BlueField-3 eMMC RST_N */
 #define BLUEFIELD_SMC_SET_EMMC_RST_N	0x82000007
 
+/* Eswin specific Registers */
+#define EIC7700_CARD_CLK_STABLE		BIT(28)
+#define EIC7700_INT_BCLK_STABLE		BIT(16)
+#define EIC7700_INT_ACLK_STABLE		BIT(8)
+#define EIC7700_INT_TMCLK_STABLE	BIT(0)
+#define EIC7700_INT_CLK_STABLE		(EIC7700_CARD_CLK_STABLE | \
+					 EIC7700_INT_ACLK_STABLE | \
+					 EIC7700_INT_BCLK_STABLE | \
+					 EIC7700_INT_TMCLK_STABLE)
+#define EIC7700_HOST_VAL_STABLE		BIT(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 MAX_PHASE_CODE			0xff
+#define TUNING_RANGE_THRESHOLD		40
+#define PHY_CLK_MAX_DELAY_MASK		0x7f
+#define PHY_DELAY_CODE_MAX		0x7f
+#define PHY_DELAY_CODE_EMMC		0x17
+#define PHY_DELAY_CODE_SD		0x55
+
 enum dwcmshc_rk_type {
 	DWCMSHC_RK3568,
 	DWCMSHC_RK3588,
@@ -217,6 +260,11 @@ struct rk35xx_priv {
 	u8 txclk_tapnum;
 };
 
+struct eic7700_priv {
+	struct reset_control *reset;
+	unsigned int drive_impedance;
+};
+
 #define DWCMSHC_MAX_OTHER_CLKS 3
 
 struct dwcmshc_priv {
@@ -238,6 +286,28 @@ struct dwcmshc_pltfm_data {
 	void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
 };
 
+static void dwcmshc_disable_card_clk(struct sdhci_host *host)
+{
+	u16 ctrl;
+
+	ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+	if (ctrl & SDHCI_CLOCK_CARD_EN) {
+		ctrl &= ~SDHCI_CLOCK_CARD_EN;
+		sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
+	}
+}
+
+static void dwcmshc_enable_card_clk(struct sdhci_host *host)
+{
+	u16 ctrl;
+
+	ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+	if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) {
+		ctrl |= SDHCI_CLOCK_CARD_EN;
+		sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
+	}
+}
+
 static int dwcmshc_get_enable_other_clks(struct device *dev,
 					 struct dwcmshc_priv *priv,
 					 int num_clks,
@@ -1095,6 +1165,413 @@ static int sg2042_init(struct device *dev, struct sdhci_host *host,
 					     ARRAY_SIZE(clk_ids), clk_ids);
 }
 
+static void sdhci_eic7700_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	u16 clk;
+
+	host->mmc->actual_clock = clock;
+
+	if (clock == 0) {
+		sdhci_set_clock(host, clock);
+		return;
+	}
+	dwcmshc_disable_card_clk(host);
+
+	clk_disable_unprepare(pltfm_host->clk);
+	clk_set_rate(pltfm_host->clk, clock);
+	clk_prepare_enable(pltfm_host->clk);
+
+	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+	clk |= SDHCI_CLOCK_INT_EN;
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+	dwcmshc_enable_card_clk(host);
+}
+
+static void sdhci_eic7700_config_phy_delay(struct sdhci_host *host, int delay)
+{
+	delay &= PHY_CLK_MAX_DELAY_MASK;
+
+	/* phy clk delay line config */
+	sdhci_writeb(host, PHY_SDCLKDL_CNFG_UPDATE, PHY_SDCLKDL_CNFG_R);
+	sdhci_writeb(host, delay, PHY_SDCLKDL_DC_R);
+	sdhci_writeb(host, 0x0, PHY_SDCLKDL_CNFG_R);
+}
+
+static void sdhci_eic7700_config_phy(struct sdhci_host *host)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
+	u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
+	struct eic7700_priv *priv = dwc_priv->priv;
+	unsigned int val, drv;
+
+	drv = FIELD_PREP(PHY_CNFG_PAD_SP_MASK, priv->drive_impedance & 0xF);
+	drv |= FIELD_PREP(PHY_CNFG_PAD_SN_MASK, (priv->drive_impedance >> 4) & 0xF);
+
+	if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
+		val = sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
+		val |= DWCMSHC_CARD_IS_EMMC;
+		sdhci_writew(host, val, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
+	}
+
+	/* reset phy, config phy's pad */
+	sdhci_writel(host, drv | (~PHY_CNFG_RSTN_DEASSERT), PHY_CNFG_R);
+
+	/* configure phy pads */
+	val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+	val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+	val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
+	val |= PHY_PAD_RXSEL_1V8;
+	sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
+	sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
+	sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
+
+	/* Clock PAD Setting */
+	val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+	val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+	sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
+
+	/* PHY strobe PAD setting (EMMC only) */
+	if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
+		val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+		val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+		val |= PHY_PAD_RXSEL_1V8;
+		sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
+	}
+	usleep_range(2000, 3000);
+	sdhci_writel(host, drv | PHY_CNFG_RSTN_DEASSERT, PHY_CNFG_R);
+	sdhci_eic7700_config_phy_delay(host, dwc_priv->delay_line);
+}
+
+static void sdhci_eic7700_reset(struct sdhci_host *host, u8 mask)
+{
+	sdhci_reset(host, mask);
+
+	/* after reset all, the phy's config will be clear */
+	if (mask == SDHCI_RESET_ALL)
+		sdhci_eic7700_config_phy(host);
+}
+
+static int sdhci_eic7700_reset_init(struct device *dev, struct eic7700_priv *priv)
+{
+	int ret;
+
+	priv->reset = devm_reset_control_array_get_optional_exclusive(dev);
+	if (IS_ERR(priv->reset)) {
+		ret = PTR_ERR(priv->reset);
+		dev_err(dev, "failed to get reset control %d\n", ret);
+		return ret;
+	}
+
+	ret = reset_control_assert(priv->reset);
+	if (ret) {
+		dev_err(dev, "Failed to assert reset signals: %d\n", ret);
+		return ret;
+	}
+	usleep_range(2000, 2100);
+	ret = reset_control_deassert(priv->reset);
+	if (ret) {
+		dev_err(dev, "Failed to deassert reset signals: %d\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static unsigned int eic7700_convert_drive_impedance_ohm(struct device *dev, 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(dev, "Invalid value %u for drive-impedance-ohms.\n", dr_ohm);
+	return PHYCTRL_DR_50OHM;
+}
+
+static int sdhci_eic7700_delay_tuning(struct sdhci_host *host, u32 opcode)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
+	int delay_min = -1;
+	int delay_max = -1;
+	int cmd_error = 0;
+	int delay = 0;
+	int i = 0;
+	int ret;
+
+	for (i = 0; i <= PHY_DELAY_CODE_MAX; i++) {
+		sdhci_eic7700_config_phy_delay(host, i);
+		ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
+		if (ret) {
+			host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+			usleep_range(200, 210);
+			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));
+		sdhci_eic7700_config_phy_delay(host, dwc_priv->delay_line);
+		return ret;
+	}
+
+	delay = (delay_min + delay_max) / 2;
+	sdhci_eic7700_config_phy_delay(host, delay);
+
+	return 0;
+}
+
+static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO;
+	int phase_code = -1;
+	int code_range = -1;
+	bool is_sd = false;
+	int code_min = -1;
+	int code_max = -1;
+	int cmd_error = 0;
+	int ret = 0;
+	int i = 0;
+
+	if ((host->mmc->caps2 & sd_caps) == sd_caps)
+		is_sd = true;
+
+	for (i = 0; i <= MAX_PHASE_CODE; i++) {
+		/* Centered Phase code */
+		sdhci_writew(host, i, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
+		ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
+		host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+
+		if (ret) {
+			/* SDIO specific range tracking */
+			if (is_sd && 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;
+			}
+			/* EMMC breaks after first valid range */
+			if (!is_sd && code_min != -1 && code_max != -1)
+				break;
+		} else {
+			/* Track valid phase code range */
+			if (code_min == -1) {
+				code_min = i;
+				if (!is_sd)
+					continue;
+			}
+			code_max = i;
+			if (is_sd && i == MAX_PHASE_CODE) {
+				if (code_max - code_min > code_range) {
+					code_range = code_max - code_min;
+					phase_code = (code_min + code_max) / 2;
+				}
+			}
+		}
+	}
+
+	/* Handle tuning failure case */
+	if ((is_sd && phase_code == -1) ||
+	    (!is_sd && code_min == -1 && code_max == -1)) {
+		pr_err("%s: phase code tuning failed!\n", mmc_hostname(host->mmc));
+		sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
+		return -EIO;
+	}
+	if (!is_sd)
+		phase_code = (code_min + code_max) / 2;
+
+	sdhci_writew(host, phase_code, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
+
+	/* SDIO specific final verification */
+	if (is_sd) {
+		ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
+		host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+		if (ret) {
+			pr_err("%s: Final phase code 0x%x verification failed!\n",
+			       mmc_hostname(host->mmc), phase_code);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int sdhci_eic7700_executing_tuning(struct sdhci_host *host, u32 opcode)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
+	int ret = 0;
+	u16 ctrl;
+	u32 val;
+
+	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+	ctrl &= ~SDHCI_CTRL_TUNED_CLK;
+	sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+	val = sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
+	val |= AT_CTRL_SW_TUNE_EN;
+	sdhci_writew(host, val, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
+
+	sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
+	sdhci_writew(host, 0x0, SDHCI_CMD_DATA);
+
+	if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
+		ret = sdhci_eic7700_delay_tuning(host, opcode);
+		if (ret)
+			return ret;
+	}
+
+	ret = sdhci_eic7700_phase_code_tuning(host, opcode);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void sdhci_eic7700_set_uhs_signaling(struct sdhci_host *host, unsigned int timing)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	u8 status;
+	u32 val;
+	int ret;
+
+	dwcmshc_set_uhs_signaling(host, timing);
+
+	/* here need make dll locked when in hs400 at 200MHz */
+	if (timing == MMC_TIMING_MMC_HS400 && host->clock == 200000000) {
+		val = sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
+		val &= ~(FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_DLY));
+		/* 2-cycle latency */
+		val |= FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, 0x2);
+		sdhci_writew(host, val, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
+
+		sdhci_writeb(host, FIELD_PREP(PHY_DLL_CNFG1_SLVDLY_MASK, PHY_DLL_CNFG1_SLVDLY) |
+			     0x3, PHY_DLL_CNFG1_R);/* DLL wait cycle input */
+		/* DLL jump step input */
+		sdhci_writeb(host, 0x02, PHY_DLL_CNFG2_R);
+		sdhci_writeb(host, FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK,
+					      PHY_DLLDL_CNFG_SLV_INPSEL), PHY_DLLDL_CNFG_R);
+		/* Sets the value of DLL's offset input */
+		sdhci_writeb(host, 0x00, PHY_DLL_OFFST_R);
+		/* Sets the value of DLL's olbt loadval input. Controls the Ibt
+		 * timer's timeout value at which DLL runs a revalidation cycle.
+		 */
+		sdhci_writew(host, 0xffff, PHY_DLLBT_CNFG_R);
+		sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R);
+		usleep_range(100, 110);
+
+		ret = read_poll_timeout(sdhci_readb, status, status & DLL_LOCK_STS, 100, 1000000,
+					false, host, PHY_DLL_STATUS_R);
+		if (ret) {
+			pr_err("%s: DLL lock timeout! 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);
+		}
+	}
+}
+
+static void sdhci_eic7700_set_uhs_wrapper(struct sdhci_host *host, unsigned int timing)
+{
+	u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO;
+
+	if ((host->mmc->caps2 & sd_caps) == sd_caps)
+		sdhci_set_uhs_signaling(host, timing);
+	else
+		sdhci_eic7700_set_uhs_signaling(host, timing);
+}
+
+static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
+{
+	u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
+	unsigned int val, hsp_int_status, hsp_pwr_ctrl;
+	struct of_phandle_args args;
+	struct eic7700_priv *priv;
+	struct regmap *hsp_regmap;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(struct eic7700_priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dwc_priv->priv = priv;
+
+	ret = sdhci_eic7700_reset_init(dev, dwc_priv->priv);
+	if (ret) {
+		dev_err(dev, "failed to reset\n");
+		return ret;
+	}
+
+	ret = of_parse_phandle_with_fixed_args(dev->of_node, "eswin,hsp-sp-csr", 2, 0, &args);
+	if (ret) {
+		dev_err(dev, "Fail to parse 'eswin,hsp-sp-csr' phandle (%d)\n", ret);
+		return ret;
+	}
+
+	hsp_regmap = syscon_node_to_regmap(args.np);
+	if (IS_ERR(hsp_regmap)) {
+		dev_err(dev, "Failed to get regmap for 'eswin,hsp-sp-csr'\n");
+		of_node_put(args.np);
+		return ret;
+	}
+	hsp_int_status = args.args[0];
+	hsp_pwr_ctrl = args.args[1];
+	of_node_put(args.np);
+	/*
+	 * Assert clock stability: write EIC7700_INT_CLK_STABLE to hsp_int_status.
+	 * This signals to the eMMC controller that platform clocks (card, ACLK,
+	 * BCLK, TMCLK) are enabled and stable.
+	 */
+	regmap_write(hsp_regmap, hsp_int_status, EIC7700_INT_CLK_STABLE);
+	/*
+	 * Assert voltage stability: write EIC7700_HOST_VAL_STABLE to hsp_pwr_ctrl.
+	 * This signals that VDD is stable and permits transition to high-speed
+	 * modes (e.g., UHS-I).
+	 */
+	regmap_write(hsp_regmap, hsp_pwr_ctrl, EIC7700_HOST_VAL_STABLE);
+
+	if ((host->mmc->caps2 & emmc_caps) == emmc_caps)
+		dwc_priv->delay_line = PHY_DELAY_CODE_EMMC;
+	else
+		dwc_priv->delay_line = PHY_DELAY_CODE_SD;
+
+	if (!of_property_read_u32(dev->of_node, "eswin,drive-impedance-ohms", &val))
+		priv->drive_impedance = eic7700_convert_drive_impedance_ohm(dev, val);
+	return 0;
+}
+
 static const struct sdhci_ops sdhci_dwcmshc_ops = {
 	.set_clock		= sdhci_set_clock,
 	.set_bus_width		= sdhci_set_bus_width,
@@ -1169,6 +1646,18 @@ static const struct sdhci_ops sdhci_dwcmshc_sg2042_ops = {
 	.platform_execute_tuning = th1520_execute_tuning,
 };
 
+static const struct sdhci_ops sdhci_dwcmshc_eic7700_ops = {
+	.set_clock = sdhci_eic7700_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 = sdhci_eic7700_reset,
+	.set_uhs_signaling = sdhci_eic7700_set_uhs_wrapper,
+	.set_power = sdhci_set_power_and_bus_voltage,
+	.irq = dwcmshc_cqe_irq_handler,
+	.platform_execute_tuning = sdhci_eic7700_executing_tuning,
+};
+
 static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata = {
 	.pdata = {
 		.ops = &sdhci_dwcmshc_ops,
@@ -1238,6 +1727,17 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_sg2042_pdata = {
 	.init = sg2042_init,
 };
 
+static const struct dwcmshc_pltfm_data sdhci_dwcmshc_eic7700_pdata = {
+	.pdata = {
+		.ops = &sdhci_dwcmshc_eic7700_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,
+	},
+	.init = eic7700_init,
+};
+
 static const struct cqhci_host_ops dwcmshc_cqhci_ops = {
 	.enable		= dwcmshc_sdhci_cqe_enable,
 	.disable	= sdhci_cqe_disable,
@@ -1338,6 +1838,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
 		.compatible = "sophgo,sg2042-dwcmshc",
 		.data = &sdhci_dwcmshc_sg2042_pdata,
 	},
+	{
+		.compatible = "eswin,eic7700-dwcmshc",
+		.data = &sdhci_dwcmshc_eic7700_pdata,
+	},
 	{},
 };
 MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids);
@@ -1469,17 +1973,6 @@ static int dwcmshc_probe(struct platform_device *pdev)
 	return err;
 }
 
-static void dwcmshc_disable_card_clk(struct sdhci_host *host)
-{
-	u16 ctrl;
-
-	ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
-	if (ctrl & SDHCI_CLOCK_CARD_EN) {
-		ctrl &= ~SDHCI_CLOCK_CARD_EN;
-		sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
-	}
-}
-
 static void dwcmshc_remove(struct platform_device *pdev)
 {
 	struct sdhci_host *host = platform_get_drvdata(pdev);
@@ -1570,17 +2063,6 @@ static int dwcmshc_resume(struct device *dev)
 	return ret;
 }
 
-static void dwcmshc_enable_card_clk(struct sdhci_host *host)
-{
-	u16 ctrl;
-
-	ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
-	if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) {
-		ctrl |= SDHCI_CLOCK_CARD_EN;
-		sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
-	}
-}
-
 static int dwcmshc_runtime_suspend(struct device *dev)
 {
 	struct sdhci_host *host = dev_get_drvdata(dev);
-- 
2.25.1


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

* Re: [PATCH v4 1/2] dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700
  2025-10-11 11:11 ` [PATCH v4 1/2] dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700 hehuan1
@ 2025-10-13 11:40   ` Rob Herring
  0 siblings, 0 replies; 6+ messages in thread
From: Rob Herring @ 2025-10-13 11:40 UTC (permalink / raw)
  To: hehuan1
  Cc: ulf.hansson, krzk+dt, conor+dt, jszhang, adrian.hunter, p.zabel,
	linux-mmc, devicetree, linux-kernel, ningyu, linmin,
	pinkesh.vaghela, xuxiang, luyulin, dongxuyang, zhangsenchuan,
	weishangjuan, lizhi2, caohang, Conor Dooley

On Sat, Oct 11, 2025 at 07:11:50PM +0800, hehuan1@eswincomputing.com wrote:
> From: Huan He <hehuan1@eswincomputing.com>
> 
> EIC7700 use Synopsys dwcmshc IP for SD/eMMC controllers.
> Add Eswin EIC7700 support in sdhci-of-dwcmshc.yaml.
> 
> Signed-off-by: Huan He <hehuan1@eswincomputing.com>
> Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
> ---
>  .../bindings/mmc/snps,dwcmshc-sdhci.yaml      | 57 +++++++++++++++++--
>  1 file changed, 51 insertions(+), 6 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml
> index f882219a0a26..edd6c8e90cad 100644
> --- a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml
> +++ b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml
> @@ -30,6 +30,7 @@ properties:
>            - sophgo,sg2002-dwcmshc
>            - sophgo,sg2042-dwcmshc
>            - thead,th1520-dwcmshc
> +          - eswin,eic7700-dwcmshc
>  
>    reg:
>      maxItems: 1
> @@ -52,17 +53,30 @@ properties:
>      maxItems: 5
>  
>    reset-names:
> -    items:
> -      - const: core
> -      - const: bus
> -      - const: axi
> -      - const: block
> -      - const: timer
> +    maxItems: 5
>  
>    rockchip,txclk-tapnum:
>      description: Specify the number of delay for tx sampling.
>      $ref: /schemas/types.yaml#/definitions/uint8
>  
> +  eswin,hsp-sp-csr:
> +    $ref: /schemas/types.yaml#/definitions/phandle-array
> +    items:

This should be:

items:
  - items:
      - description: ...
      ...

> +      - description: Phandle to HSP(High-Speed Peripheral) device
> +      - description: Offset of the stability status register for
> +                     internal clock
> +      - description: Offset of the stability register for host regulator
> +                     voltage.
> +    description: |

Don't need '|' if no formatting to preserve.

> +      HSP CSR is to control and get status of different high-speed
> +      peripherals (such as Ethernet, USB, SATA, etc.) via register,
> +      which can close module's clock, reset module independently
> +      and tune board-level's parameters of PHY, etc.

Wrap lines at 80 chars.

> +
> +  eswin,drive-impedance-ohms:
> +    description: Specifies the drive impedance in Ohm.
> +    enum: [33, 40, 50, 66, 100]
> +
>  required:
>    - compatible
>    - reg
> @@ -110,6 +124,37 @@ allOf:
>              - const: block
>              - const: timer
>  
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            const: eswin,eic7700-dwcmshc
> +    then:
> +      properties:
> +        resets:
> +          minItems: 4
> +          maxItems: 4
> +        reset-names:
> +          items:
> +            - const: axi
> +            - const: phy
> +            - const: prstn
> +            - const: txrx
> +      required:
> +        - eswin,hsp-sp-csr
> +        - eswin,drive-impedance-ohms
> +    else:
> +      properties:
> +        resets:
> +          maxItems: 5
> +        reset-names:
> +          items:
> +            - const: core
> +            - const: bus
> +            - const: axi
> +            - const: block
> +            - const: timer
> +
>    - if:
>        properties:
>          compatible:
> -- 
> 2.25.1
> 

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

* Re: [PATCH v4 2/2] mmc: sdhci-of-dwcmshc: Add support for Eswin EIC7700
  2025-10-11 11:12 ` [PATCH v4 2/2] mmc: sdhci-of-dwcmshc: Add support for " hehuan1
@ 2025-10-14  8:42   ` Dan Carpenter
  2025-10-15 13:09   ` Adrian Hunter
  1 sibling, 0 replies; 6+ messages in thread
From: Dan Carpenter @ 2025-10-14  8:42 UTC (permalink / raw)
  To: oe-kbuild, hehuan1, ulf.hansson, robh, krzk+dt, conor+dt, jszhang,
	adrian.hunter, p.zabel, linux-mmc, devicetree, linux-kernel
  Cc: lkp, oe-kbuild-all, ningyu, linmin, pinkesh.vaghela, xuxiang,
	luyulin, dongxuyang, zhangsenchuan, weishangjuan, lizhi2, caohang,
	hehuan1

Hi,

kernel test robot noticed the following build warnings:

https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/hehuan1-eswincomputing-com/dt-bindings-mmc-sdhci-of-dwcmshc-Add-Eswin-EIC7700/20251011-191312
base:   linus/master
patch link:    https://lore.kernel.org/r/20251011111216.570-1-hehuan1%40eswincomputing.com
patch subject: [PATCH v4 2/2] mmc: sdhci-of-dwcmshc: Add support for Eswin EIC7700
config: s390-randconfig-r073-20251013 (https://download.01.org/0day-ci/archive/20251013/202510131636.lJ5QxMpK-lkp@intel.com/config)
compiler: s390-linux-gcc (GCC) 12.5.0

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>
| Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
| Closes: https://lore.kernel.org/r/202510131636.lJ5QxMpK-lkp@intel.com/

New smatch warnings:
drivers/mmc/host/sdhci-of-dwcmshc.c:1547 eic7700_init() warn: missing error code? 'ret'

vim +/ret +1547 drivers/mmc/host/sdhci-of-dwcmshc.c

3c5c200f02eee8 Huan He 2025-10-11  1516  static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
3c5c200f02eee8 Huan He 2025-10-11  1517  {
3c5c200f02eee8 Huan He 2025-10-11  1518  	u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
3c5c200f02eee8 Huan He 2025-10-11  1519  	unsigned int val, hsp_int_status, hsp_pwr_ctrl;
3c5c200f02eee8 Huan He 2025-10-11  1520  	struct of_phandle_args args;
3c5c200f02eee8 Huan He 2025-10-11  1521  	struct eic7700_priv *priv;
3c5c200f02eee8 Huan He 2025-10-11  1522  	struct regmap *hsp_regmap;
3c5c200f02eee8 Huan He 2025-10-11  1523  	int ret;
3c5c200f02eee8 Huan He 2025-10-11  1524  
3c5c200f02eee8 Huan He 2025-10-11  1525  	priv = devm_kzalloc(dev, sizeof(struct eic7700_priv), GFP_KERNEL);
3c5c200f02eee8 Huan He 2025-10-11  1526  	if (!priv)
3c5c200f02eee8 Huan He 2025-10-11  1527  		return -ENOMEM;
3c5c200f02eee8 Huan He 2025-10-11  1528  
3c5c200f02eee8 Huan He 2025-10-11  1529  	dwc_priv->priv = priv;
3c5c200f02eee8 Huan He 2025-10-11  1530  
3c5c200f02eee8 Huan He 2025-10-11  1531  	ret = sdhci_eic7700_reset_init(dev, dwc_priv->priv);
3c5c200f02eee8 Huan He 2025-10-11  1532  	if (ret) {
3c5c200f02eee8 Huan He 2025-10-11  1533  		dev_err(dev, "failed to reset\n");
3c5c200f02eee8 Huan He 2025-10-11  1534  		return ret;
3c5c200f02eee8 Huan He 2025-10-11  1535  	}
3c5c200f02eee8 Huan He 2025-10-11  1536  
3c5c200f02eee8 Huan He 2025-10-11  1537  	ret = of_parse_phandle_with_fixed_args(dev->of_node, "eswin,hsp-sp-csr", 2, 0, &args);
3c5c200f02eee8 Huan He 2025-10-11  1538  	if (ret) {
3c5c200f02eee8 Huan He 2025-10-11  1539  		dev_err(dev, "Fail to parse 'eswin,hsp-sp-csr' phandle (%d)\n", ret);
3c5c200f02eee8 Huan He 2025-10-11  1540  		return ret;
3c5c200f02eee8 Huan He 2025-10-11  1541  	}
3c5c200f02eee8 Huan He 2025-10-11  1542  
3c5c200f02eee8 Huan He 2025-10-11  1543  	hsp_regmap = syscon_node_to_regmap(args.np);
3c5c200f02eee8 Huan He 2025-10-11  1544  	if (IS_ERR(hsp_regmap)) {
3c5c200f02eee8 Huan He 2025-10-11  1545  		dev_err(dev, "Failed to get regmap for 'eswin,hsp-sp-csr'\n");
3c5c200f02eee8 Huan He 2025-10-11  1546  		of_node_put(args.np);
3c5c200f02eee8 Huan He 2025-10-11 @1547  		return ret;

return PTR_ERR(hsp_regmap);

3c5c200f02eee8 Huan He 2025-10-11  1548  	}
3c5c200f02eee8 Huan He 2025-10-11  1549  	hsp_int_status = args.args[0];
3c5c200f02eee8 Huan He 2025-10-11  1550  	hsp_pwr_ctrl = args.args[1];
3c5c200f02eee8 Huan He 2025-10-11  1551  	of_node_put(args.np);
3c5c200f02eee8 Huan He 2025-10-11  1552  	/*
3c5c200f02eee8 Huan He 2025-10-11  1553  	 * Assert clock stability: write EIC7700_INT_CLK_STABLE to hsp_int_status.
3c5c200f02eee8 Huan He 2025-10-11  1554  	 * This signals to the eMMC controller that platform clocks (card, ACLK,
3c5c200f02eee8 Huan He 2025-10-11  1555  	 * BCLK, TMCLK) are enabled and stable.
3c5c200f02eee8 Huan He 2025-10-11  1556  	 */

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


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

* Re: [PATCH v4 2/2] mmc: sdhci-of-dwcmshc: Add support for Eswin EIC7700
  2025-10-11 11:12 ` [PATCH v4 2/2] mmc: sdhci-of-dwcmshc: Add support for " hehuan1
  2025-10-14  8:42   ` Dan Carpenter
@ 2025-10-15 13:09   ` Adrian Hunter
  1 sibling, 0 replies; 6+ messages in thread
From: Adrian Hunter @ 2025-10-15 13:09 UTC (permalink / raw)
  To: hehuan1, ulf.hansson, robh, krzk+dt, conor+dt, jszhang, p.zabel,
	linux-mmc, devicetree, linux-kernel
  Cc: ningyu, linmin, pinkesh.vaghela, xuxiang, luyulin, dongxuyang,
	zhangsenchuan, weishangjuan, lizhi2, caohang, kernel test robot

On 11/10/2025 14:12, hehuan1@eswincomputing.com wrote:
> From: Huan He <hehuan1@eswincomputing.com>
> 
> Add support for the mmc controller in the Eswin EIC7700 with the new
> compatible "eswin,eic7700-dwcmshc". Implement custom sdhci_ops for
> set_clock, reset, set_uhs_signaling, platform_execute_tuning.
> 
> Reported-by: kernel test robot <lkp@intel.com>
> Closes: https://lore.kernel.org/oe-kbuild-all/202510110904.0H6EStUB-lkp@intel.com/
> Closes: https://lore.kernel.org/oe-kbuild-all/202510110829.1yKzYvIP-lkp@intel.com/

The test robot tags are only really meaningful if the issue is fixed
in a separate patch/commit (i.e. not just a new version of
the same patch/commit)

> Signed-off-by: Huan He <hehuan1@eswincomputing.com>

Looks good.  A few minor comments further below.

> ---
>  drivers/mmc/host/sdhci-of-dwcmshc.c | 526 ++++++++++++++++++++++++++--
>  1 file changed, 504 insertions(+), 22 deletions(-)
> 
> diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c
> index eebd45389956..28bb2067f945 100644
> --- a/drivers/mmc/host/sdhci-of-dwcmshc.c
> +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
> @@ -11,6 +11,7 @@
>  #include <linux/arm-smccc.h>
>  #include <linux/bitfield.h>
>  #include <linux/clk.h>
> +#include <linux/clk-provider.h>
>  #include <linux/dma-mapping.h>
>  #include <linux/iopoll.h>
>  #include <linux/kernel.h>
> @@ -19,8 +20,11 @@
>  #include <linux/platform_device.h>
>  #include <linux/pm_domain.h>
>  #include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
>  #include <linux/reset.h>
>  #include <linux/sizes.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/units.h>
>  
>  #include "sdhci-pltfm.h"
>  #include "cqhci.h"
> @@ -39,6 +43,7 @@
>  #define DWCMSHC_CARD_IS_EMMC		BIT(0)
>  #define DWCMSHC_ENHANCED_STROBE		BIT(8)
>  #define DWCMSHC_EMMC_ATCTRL		0x40
> +#define DWCMSHC_AT_STAT			0x44
>  /* Tuning and auto-tuning fields in AT_CTRL_R control register */
>  #define AT_CTRL_AT_EN			BIT(0) /* autotuning is enabled */
>  #define AT_CTRL_CI_SEL			BIT(1) /* interval to drive center phase select */
> @@ -194,6 +199,19 @@
>  #define PHY_DLLDL_CNFG_SLV_INPSEL_MASK	GENMASK(6, 5) /* bits [6:5] */
>  #define PHY_DLLDL_CNFG_SLV_INPSEL	0x3 /* clock source select for slave DL */
>  
> +/* PHY DLL offset setting register */
> +#define PHY_DLL_OFFST_R			(DWC_MSHC_PTR_PHY_R + 0x29)
> +/* DLL LBT setting register */
> +#define PHY_DLLBT_CNFG_R		(DWC_MSHC_PTR_PHY_R + 0x2c)
> +/* DLL Status register */
> +#define PHY_DLL_STATUS_R		(DWC_MSHC_PTR_PHY_R + 0x2e)
> +#define DLL_LOCK_STS			BIT(0)/* DLL is locked and ready */
> +/*
> + * Captures the value of DLL's lock error status information. Value is valid
> + * only when LOCK_STS is set.
> + */
> +#define DLL_ERROR_STS			BIT(1)
> +
>  #define FLAG_IO_FIXED_1V8	BIT(0)
>  
>  #define BOUNDARY_OK(addr, len) \
> @@ -206,6 +224,31 @@
>  /* SMC call for BlueField-3 eMMC RST_N */
>  #define BLUEFIELD_SMC_SET_EMMC_RST_N	0x82000007
>  
> +/* Eswin specific Registers */
> +#define EIC7700_CARD_CLK_STABLE		BIT(28)
> +#define EIC7700_INT_BCLK_STABLE		BIT(16)
> +#define EIC7700_INT_ACLK_STABLE		BIT(8)
> +#define EIC7700_INT_TMCLK_STABLE	BIT(0)
> +#define EIC7700_INT_CLK_STABLE		(EIC7700_CARD_CLK_STABLE | \
> +					 EIC7700_INT_ACLK_STABLE | \
> +					 EIC7700_INT_BCLK_STABLE | \
> +					 EIC7700_INT_TMCLK_STABLE)
> +#define EIC7700_HOST_VAL_STABLE		BIT(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 MAX_PHASE_CODE			0xff
> +#define TUNING_RANGE_THRESHOLD		40
> +#define PHY_CLK_MAX_DELAY_MASK		0x7f
> +#define PHY_DELAY_CODE_MAX		0x7f
> +#define PHY_DELAY_CODE_EMMC		0x17
> +#define PHY_DELAY_CODE_SD		0x55
> +
>  enum dwcmshc_rk_type {
>  	DWCMSHC_RK3568,
>  	DWCMSHC_RK3588,
> @@ -217,6 +260,11 @@ struct rk35xx_priv {
>  	u8 txclk_tapnum;
>  };
>  
> +struct eic7700_priv {
> +	struct reset_control *reset;
> +	unsigned int drive_impedance;
> +};
> +
>  #define DWCMSHC_MAX_OTHER_CLKS 3
>  
>  struct dwcmshc_priv {
> @@ -238,6 +286,28 @@ struct dwcmshc_pltfm_data {
>  	void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
>  };
>  
> +static void dwcmshc_disable_card_clk(struct sdhci_host *host)
> +{
> +	u16 ctrl;
> +
> +	ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> +	if (ctrl & SDHCI_CLOCK_CARD_EN) {
> +		ctrl &= ~SDHCI_CLOCK_CARD_EN;
> +		sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
> +	}
> +}
> +
> +static void dwcmshc_enable_card_clk(struct sdhci_host *host)
> +{
> +	u16 ctrl;
> +
> +	ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> +	if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) {
> +		ctrl |= SDHCI_CLOCK_CARD_EN;
> +		sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
> +	}
> +}
> +
>  static int dwcmshc_get_enable_other_clks(struct device *dev,
>  					 struct dwcmshc_priv *priv,
>  					 int num_clks,
> @@ -1095,6 +1165,413 @@ static int sg2042_init(struct device *dev, struct sdhci_host *host,
>  					     ARRAY_SIZE(clk_ids), clk_ids);
>  }
>  
> +static void sdhci_eic7700_set_clock(struct sdhci_host *host, unsigned int clock)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	u16 clk;
> +
> +	host->mmc->actual_clock = clock;
> +
> +	if (clock == 0) {
> +		sdhci_set_clock(host, clock);
> +		return;
> +	}
> +	dwcmshc_disable_card_clk(host);
> +
> +	clk_disable_unprepare(pltfm_host->clk);
> +	clk_set_rate(pltfm_host->clk, clock);
> +	clk_prepare_enable(pltfm_host->clk);

Seems unusual to have to disable and re-enable a clock
to change the rate.

> +
> +	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> +	clk |= SDHCI_CLOCK_INT_EN;
> +	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> +
> +	dwcmshc_enable_card_clk(host);
> +}
> +
> +static void sdhci_eic7700_config_phy_delay(struct sdhci_host *host, int delay)
> +{
> +	delay &= PHY_CLK_MAX_DELAY_MASK;
> +
> +	/* phy clk delay line config */
> +	sdhci_writeb(host, PHY_SDCLKDL_CNFG_UPDATE, PHY_SDCLKDL_CNFG_R);
> +	sdhci_writeb(host, delay, PHY_SDCLKDL_DC_R);
> +	sdhci_writeb(host, 0x0, PHY_SDCLKDL_CNFG_R);
> +}
> +
> +static void sdhci_eic7700_config_phy(struct sdhci_host *host)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
> +	u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
> +	struct eic7700_priv *priv = dwc_priv->priv;
> +	unsigned int val, drv;
> +
> +	drv = FIELD_PREP(PHY_CNFG_PAD_SP_MASK, priv->drive_impedance & 0xF);
> +	drv |= FIELD_PREP(PHY_CNFG_PAD_SN_MASK, (priv->drive_impedance >> 4) & 0xF);
> +
> +	if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
> +		val = sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
> +		val |= DWCMSHC_CARD_IS_EMMC;
> +		sdhci_writew(host, val, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
> +	}
> +
> +	/* reset phy, config phy's pad */
> +	sdhci_writel(host, drv | (~PHY_CNFG_RSTN_DEASSERT), PHY_CNFG_R);

() not needed around ~PHY_CNFG_RSTN_DEASSERT

> +
> +	/* configure phy pads */
> +	val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
> +	val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
> +	val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
> +	val |= PHY_PAD_RXSEL_1V8;
> +	sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
> +	sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
> +	sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
> +
> +	/* Clock PAD Setting */
> +	val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
> +	val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
> +	sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
> +
> +	/* PHY strobe PAD setting (EMMC only) */
> +	if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
> +		val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
> +		val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
> +		val |= PHY_PAD_RXSEL_1V8;
> +		sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
> +	}
> +	usleep_range(2000, 3000);
> +	sdhci_writel(host, drv | PHY_CNFG_RSTN_DEASSERT, PHY_CNFG_R);
> +	sdhci_eic7700_config_phy_delay(host, dwc_priv->delay_line);
> +}
> +
> +static void sdhci_eic7700_reset(struct sdhci_host *host, u8 mask)
> +{
> +	sdhci_reset(host, mask);
> +
> +	/* after reset all, the phy's config will be clear */
> +	if (mask == SDHCI_RESET_ALL)
> +		sdhci_eic7700_config_phy(host);
> +}
> +
> +static int sdhci_eic7700_reset_init(struct device *dev, struct eic7700_priv *priv)
> +{
> +	int ret;
> +
> +	priv->reset = devm_reset_control_array_get_optional_exclusive(dev);
> +	if (IS_ERR(priv->reset)) {
> +		ret = PTR_ERR(priv->reset);
> +		dev_err(dev, "failed to get reset control %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = reset_control_assert(priv->reset);
> +	if (ret) {
> +		dev_err(dev, "Failed to assert reset signals: %d\n", ret);
> +		return ret;
> +	}
> +	usleep_range(2000, 2100);
> +	ret = reset_control_deassert(priv->reset);
> +	if (ret) {
> +		dev_err(dev, "Failed to deassert reset signals: %d\n", ret);
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static unsigned int eic7700_convert_drive_impedance_ohm(struct device *dev, 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(dev, "Invalid value %u for drive-impedance-ohms.\n", dr_ohm);
> +	return PHYCTRL_DR_50OHM;
> +}
> +
> +static int sdhci_eic7700_delay_tuning(struct sdhci_host *host, u32 opcode)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
> +	int delay_min = -1;
> +	int delay_max = -1;
> +	int cmd_error = 0;
> +	int delay = 0;
> +	int i = 0;
> +	int ret;
> +
> +	for (i = 0; i <= PHY_DELAY_CODE_MAX; i++) {
> +		sdhci_eic7700_config_phy_delay(host, i);
> +		ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
> +		if (ret) {
> +			host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
> +			usleep_range(200, 210);
> +			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));
> +		sdhci_eic7700_config_phy_delay(host, dwc_priv->delay_line);
> +		return ret;
> +	}
> +
> +	delay = (delay_min + delay_max) / 2;
> +	sdhci_eic7700_config_phy_delay(host, delay);
> +
> +	return 0;
> +}
> +
> +static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
> +	u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO;
> +	int phase_code = -1;
> +	int code_range = -1;
> +	bool is_sd = false;
> +	int code_min = -1;
> +	int code_max = -1;
> +	int cmd_error = 0;
> +	int ret = 0;
> +	int i = 0;
> +
> +	if ((host->mmc->caps2 & sd_caps) == sd_caps)
> +		is_sd = true;
> +
> +	for (i = 0; i <= MAX_PHASE_CODE; i++) {
> +		/* Centered Phase code */
> +		sdhci_writew(host, i, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
> +		ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
> +		host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
> +
> +		if (ret) {
> +			/* SDIO specific range tracking */

This is includes the MMC_CAP2_NO_SDIO case so the comment
is contradictory.  Could just say "SD" instead of "SDIO"

> +			if (is_sd && 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;
> +			}
> +			/* EMMC breaks after first valid range */
> +			if (!is_sd && code_min != -1 && code_max != -1)
> +				break;
> +		} else {
> +			/* Track valid phase code range */
> +			if (code_min == -1) {
> +				code_min = i;
> +				if (!is_sd)
> +					continue;
> +			}
> +			code_max = i;
> +			if (is_sd && i == MAX_PHASE_CODE) {
> +				if (code_max - code_min > code_range) {
> +					code_range = code_max - code_min;
> +					phase_code = (code_min + code_max) / 2;
> +				}
> +			}
> +		}
> +	}
> +
> +	/* Handle tuning failure case */
> +	if ((is_sd && phase_code == -1) ||
> +	    (!is_sd && code_min == -1 && code_max == -1)) {
> +		pr_err("%s: phase code tuning failed!\n", mmc_hostname(host->mmc));
> +		sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
> +		return -EIO;
> +	}
> +	if (!is_sd)
> +		phase_code = (code_min + code_max) / 2;
> +
> +	sdhci_writew(host, phase_code, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
> +
> +	/* SDIO specific final verification */

Ditto

> +	if (is_sd) {
> +		ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
> +		host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
> +		if (ret) {
> +			pr_err("%s: Final phase code 0x%x verification failed!\n",
> +			       mmc_hostname(host->mmc), phase_code);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int sdhci_eic7700_executing_tuning(struct sdhci_host *host, u32 opcode)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
> +	u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
> +	int ret = 0;
> +	u16 ctrl;
> +	u32 val;
> +
> +	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +	ctrl &= ~SDHCI_CTRL_TUNED_CLK;
> +	sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
> +
> +	val = sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
> +	val |= AT_CTRL_SW_TUNE_EN;
> +	sdhci_writew(host, val, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
> +
> +	sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
> +	sdhci_writew(host, 0x0, SDHCI_CMD_DATA);
> +
> +	if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
> +		ret = sdhci_eic7700_delay_tuning(host, opcode);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = sdhci_eic7700_phase_code_tuning(host, opcode);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static void sdhci_eic7700_set_uhs_signaling(struct sdhci_host *host, unsigned int timing)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
> +	u8 status;
> +	u32 val;
> +	int ret;
> +
> +	dwcmshc_set_uhs_signaling(host, timing);
> +
> +	/* here need make dll locked when in hs400 at 200MHz */
> +	if (timing == MMC_TIMING_MMC_HS400 && host->clock == 200000000) {
> +		val = sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
> +		val &= ~(FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_DLY));
> +		/* 2-cycle latency */
> +		val |= FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, 0x2);
> +		sdhci_writew(host, val, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
> +
> +		sdhci_writeb(host, FIELD_PREP(PHY_DLL_CNFG1_SLVDLY_MASK, PHY_DLL_CNFG1_SLVDLY) |
> +			     0x3, PHY_DLL_CNFG1_R);/* DLL wait cycle input */
> +		/* DLL jump step input */
> +		sdhci_writeb(host, 0x02, PHY_DLL_CNFG2_R);
> +		sdhci_writeb(host, FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK,
> +					      PHY_DLLDL_CNFG_SLV_INPSEL), PHY_DLLDL_CNFG_R);
> +		/* Sets the value of DLL's offset input */
> +		sdhci_writeb(host, 0x00, PHY_DLL_OFFST_R);
> +		/* Sets the value of DLL's olbt loadval input. Controls the Ibt
> +		 * timer's timeout value at which DLL runs a revalidation cycle.
> +		 */

Prefer multi-line comment style like this:

		/*
		 * Sets the value of DLL's olbt loadval input. Controls the Ibt
		 * timer's timeout value at which DLL runs a revalidation cycle.
		 */

> +		sdhci_writew(host, 0xffff, PHY_DLLBT_CNFG_R);
> +		sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R);
> +		usleep_range(100, 110);
> +
> +		ret = read_poll_timeout(sdhci_readb, status, status & DLL_LOCK_STS, 100, 1000000,
> +					false, host, PHY_DLL_STATUS_R);
> +		if (ret) {
> +			pr_err("%s: DLL lock timeout! 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);
> +		}
> +	}
> +}
> +
> +static void sdhci_eic7700_set_uhs_wrapper(struct sdhci_host *host, unsigned int timing)
> +{
> +	u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO;
> +
> +	if ((host->mmc->caps2 & sd_caps) == sd_caps)
> +		sdhci_set_uhs_signaling(host, timing);
> +	else
> +		sdhci_eic7700_set_uhs_signaling(host, timing);
> +}
> +
> +static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
> +{
> +	u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
> +	unsigned int val, hsp_int_status, hsp_pwr_ctrl;
> +	struct of_phandle_args args;
> +	struct eic7700_priv *priv;
> +	struct regmap *hsp_regmap;
> +	int ret;
> +
> +	priv = devm_kzalloc(dev, sizeof(struct eic7700_priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	dwc_priv->priv = priv;
> +
> +	ret = sdhci_eic7700_reset_init(dev, dwc_priv->priv);
> +	if (ret) {
> +		dev_err(dev, "failed to reset\n");
> +		return ret;
> +	}
> +
> +	ret = of_parse_phandle_with_fixed_args(dev->of_node, "eswin,hsp-sp-csr", 2, 0, &args);
> +	if (ret) {
> +		dev_err(dev, "Fail to parse 'eswin,hsp-sp-csr' phandle (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	hsp_regmap = syscon_node_to_regmap(args.np);
> +	if (IS_ERR(hsp_regmap)) {
> +		dev_err(dev, "Failed to get regmap for 'eswin,hsp-sp-csr'\n");
> +		of_node_put(args.np);
> +		return ret;

As the robot pointed out:

		return PTR_ERR(hsp_regmap);

> +	}
> +	hsp_int_status = args.args[0];
> +	hsp_pwr_ctrl = args.args[1];
> +	of_node_put(args.np);
> +	/*
> +	 * Assert clock stability: write EIC7700_INT_CLK_STABLE to hsp_int_status.
> +	 * This signals to the eMMC controller that platform clocks (card, ACLK,
> +	 * BCLK, TMCLK) are enabled and stable.
> +	 */
> +	regmap_write(hsp_regmap, hsp_int_status, EIC7700_INT_CLK_STABLE);
> +	/*
> +	 * Assert voltage stability: write EIC7700_HOST_VAL_STABLE to hsp_pwr_ctrl.
> +	 * This signals that VDD is stable and permits transition to high-speed
> +	 * modes (e.g., UHS-I).
> +	 */
> +	regmap_write(hsp_regmap, hsp_pwr_ctrl, EIC7700_HOST_VAL_STABLE);
> +
> +	if ((host->mmc->caps2 & emmc_caps) == emmc_caps)
> +		dwc_priv->delay_line = PHY_DELAY_CODE_EMMC;
> +	else
> +		dwc_priv->delay_line = PHY_DELAY_CODE_SD;
> +
> +	if (!of_property_read_u32(dev->of_node, "eswin,drive-impedance-ohms", &val))
> +		priv->drive_impedance = eic7700_convert_drive_impedance_ohm(dev, val);
> +	return 0;
> +}
> +
>  static const struct sdhci_ops sdhci_dwcmshc_ops = {
>  	.set_clock		= sdhci_set_clock,
>  	.set_bus_width		= sdhci_set_bus_width,
> @@ -1169,6 +1646,18 @@ static const struct sdhci_ops sdhci_dwcmshc_sg2042_ops = {
>  	.platform_execute_tuning = th1520_execute_tuning,
>  };
>  
> +static const struct sdhci_ops sdhci_dwcmshc_eic7700_ops = {
> +	.set_clock = sdhci_eic7700_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 = sdhci_eic7700_reset,
> +	.set_uhs_signaling = sdhci_eic7700_set_uhs_wrapper,
> +	.set_power = sdhci_set_power_and_bus_voltage,
> +	.irq = dwcmshc_cqe_irq_handler,
> +	.platform_execute_tuning = sdhci_eic7700_executing_tuning,
> +};
> +
>  static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata = {
>  	.pdata = {
>  		.ops = &sdhci_dwcmshc_ops,
> @@ -1238,6 +1727,17 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_sg2042_pdata = {
>  	.init = sg2042_init,
>  };
>  
> +static const struct dwcmshc_pltfm_data sdhci_dwcmshc_eic7700_pdata = {
> +	.pdata = {
> +		.ops = &sdhci_dwcmshc_eic7700_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,
> +	},
> +	.init = eic7700_init,
> +};
> +
>  static const struct cqhci_host_ops dwcmshc_cqhci_ops = {
>  	.enable		= dwcmshc_sdhci_cqe_enable,
>  	.disable	= sdhci_cqe_disable,
> @@ -1338,6 +1838,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
>  		.compatible = "sophgo,sg2042-dwcmshc",
>  		.data = &sdhci_dwcmshc_sg2042_pdata,
>  	},
> +	{
> +		.compatible = "eswin,eic7700-dwcmshc",
> +		.data = &sdhci_dwcmshc_eic7700_pdata,
> +	},
>  	{},
>  };
>  MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids);
> @@ -1469,17 +1973,6 @@ static int dwcmshc_probe(struct platform_device *pdev)
>  	return err;
>  }
>  
> -static void dwcmshc_disable_card_clk(struct sdhci_host *host)
> -{
> -	u16 ctrl;
> -
> -	ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> -	if (ctrl & SDHCI_CLOCK_CARD_EN) {
> -		ctrl &= ~SDHCI_CLOCK_CARD_EN;
> -		sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
> -	}
> -}
> -
>  static void dwcmshc_remove(struct platform_device *pdev)
>  {
>  	struct sdhci_host *host = platform_get_drvdata(pdev);
> @@ -1570,17 +2063,6 @@ static int dwcmshc_resume(struct device *dev)
>  	return ret;
>  }
>  
> -static void dwcmshc_enable_card_clk(struct sdhci_host *host)
> -{
> -	u16 ctrl;
> -
> -	ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> -	if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) {
> -		ctrl |= SDHCI_CLOCK_CARD_EN;
> -		sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
> -	}
> -}
> -
>  static int dwcmshc_runtime_suspend(struct device *dev)
>  {
>  	struct sdhci_host *host = dev_get_drvdata(dev);


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

end of thread, other threads:[~2025-10-15 13:09 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-11 11:10 [PATCH v4 0/2] Add support for Eswin EIC7700 SD/eMMC controller hehuan1
2025-10-11 11:11 ` [PATCH v4 1/2] dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700 hehuan1
2025-10-13 11:40   ` Rob Herring
2025-10-11 11:12 ` [PATCH v4 2/2] mmc: sdhci-of-dwcmshc: Add support for " hehuan1
2025-10-14  8:42   ` Dan Carpenter
2025-10-15 13:09   ` Adrian Hunter

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).