From nobody Sun Feb 8 01:33:49 2026 Received: from zg8tmja2lje4os43os4xodqa.icoremail.net (zg8tmja2lje4os43os4xodqa.icoremail.net [206.189.79.184]) by smtp.subspace.kernel.org (Postfix) with ESMTP id A641F2E4274; Sun, 19 Oct 2025 11:53:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=206.189.79.184 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760874794; cv=none; b=W5T2XtetGnASAdFsME+WjhBAtA2BN+VxsqJwISlTlmV7i6gtauE7ZzGlFv4u0nZJmGOMCT3pPGYyv7iRo8AfS/R4ImBuf7fxlnZBovKDFgn1BfbxbSjUfLS65cgiVXIcbAQaCGLMYuIMEXrtaKU2dmL1FfFDYWee1TCjMZapsT4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760874794; c=relaxed/simple; bh=sDW89jNy0IdOGVAN2W7BI2wZuL27rXm7OI9loBvaoBE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rM0mLgtCd67bcbSkjsSDN4kwOrpEC/xnvbv2dnzl4X0d6UWqfq4y06XVUP7cMchc0z1WIWZN5x3guIrmJvj0ok/9vsumFqMztnQsJyV556ibb6HjgG9xsQqpWAy7Hhvhcbx44N7+LwYtDyuU1emdMnuW8K9krsDYNTb5RanXjOY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=eswincomputing.com; spf=pass smtp.mailfrom=eswincomputing.com; arc=none smtp.client-ip=206.189.79.184 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=eswincomputing.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=eswincomputing.com Received: from E0005154LT.eswin.cn (unknown [10.100.73.96]) by app2 (Coremail) with SMTP id TQJkCgBnBpUJ0fRoNUQYAQ--.7701S2; Sun, 19 Oct 2025 19:52:47 +0800 (CST) From: hehuan1@eswincomputing.com To: ulf.hansson@linaro.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, jszhang@kernel.org, adrian.hunter@intel.com, p.zabel@pengutronix.de, linux-mmc@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Cc: ningyu@eswincomputing.com, linmin@eswincomputing.com, pinkesh.vaghela@einfochips.com, xuxiang@eswincomputing.com, luyulin@eswincomputing.com, dongxuyang@eswincomputing.com, zhangsenchuan@eswincomputing.com, weishangjuan@eswincomputing.com, lizhi2@eswincomputing.com, caohang@eswincomputing.com, hehuan1@eswincomputing.com, Conor Dooley Subject: [PATCH v5 1/2] dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700 Date: Sun, 19 Oct 2025 19:52:38 +0800 Message-ID: <20251019115238.320-1-hehuan1@eswincomputing.com> X-Mailer: git-send-email 2.49.0.windows.1 In-Reply-To: <20251019115133.300-1-hehuan1@eswincomputing.com> References: <20251019115133.300-1-hehuan1@eswincomputing.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: TQJkCgBnBpUJ0fRoNUQYAQ--.7701S2 X-Coremail-Antispam: 1UD129KBjvJXoW7Cr45ZF17Jr48tryruFW7urg_yoW5Jr1fpa 95GFZrGr1fJr1fZw48J3Wvk3W3t3Z7Jr1Yyr17Jr43JanYqFyUtrZIkwn8Ka45CFyxXFya 9ay2vry5Aa42vr7anT9S1TB71UUUUUDqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDU0xBIdaVrnRJUUUBq14x267AKxVW5JVWrJwAFc2x0x2IEx4CE42xK8VAvwI8IcIk0 rVWrJVCq3wAFIxvE14AKwVWUJVWUGwA2ocxC64kIII0Yj41l84x0c7CEw4AK67xGY2AK02 1l84ACjcxK6xIIjxv20xvE14v26r1j6r1xM28EF7xvwVC0I7IYx2IY6xkF7I0E14v26r4j 6F4UM28EF7xvwVC2z280aVAFwI0_Cr1j6rxdM28EF7xvwVC2z280aVCY1x0267AKxVW0oV Cq3wAS0I0E0xvYzxvE52x082IY62kv0487Mc02F40EFcxC0VAKzVAqx4xG6I80ewAv7VC0 I7IYx2IY67AKxVWUXVWUAwAv7VC2z280aVAFwI0_Jr0_Gr1lOx8S6xCaFVCjc4AY6r1j6r 4UM4x0Y48IcxkI7VAKI48JM4x0x7Aq67IIx4CEVc8vx2IErcIFxwACI402YVCY1x02628v n2kIc2xKxwAKzVCY07xG64k0F24lc7CjxVAaw2AFwI0_GFv_Wrylc2xSY4AK6svPMxAIw2 8IcxkI7VAKI48JMxC20s026xCaFVCjc4AY6r1j6r4UMI8I3I0E5I8CrVAFwI0_Jr0_Jr4l x2IqxVCjr7xvwVAFwI0_JrI_JrWlx4CE17CEb7AF67AKxVW8ZVWrXwCIc40Y0x0EwIxGrw CI42IY6xIIjxv20xvE14v26r1j6r1xMIIF0xvE2Ix0cI8IcVCY1x0267AKxVW8JVWxJwCI 42IY6xAIw20EY4v20xvaj40_Jr0_JF4lIxAIcVC2z280aVAFwI0_Jr0_Gr1lIxAIcVC2z2 80aVCY1x0267AKxVW8JVW8JrUvcSsGvfC2KfnxnUUI43ZEXa7sRiBT5PUUUUU== X-CM-SenderInfo: 5khk3tzqr6v25zlqu0xpsx3x1qjou0bp/ Content-Type: text/plain; charset="utf-8" From: Huan He EIC7700 use Synopsys dwcmshc IP for SD/eMMC controllers. Add Eswin EIC7700 support in sdhci-of-dwcmshc.yaml. Signed-off-by: Huan He Reviewed-by: Conor Dooley --- .../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..7e7c55dc2440 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 =20 reg: maxItems: 1 @@ -52,17 +53,30 @@ properties: maxItems: 5 =20 reset-names: - items: - - const: core - - const: bus - - const: axi - - const: block - - const: timer + maxItems: 5 =20 rockchip,txclk-tapnum: description: Specify the number of delay for tx sampling. $ref: /schemas/types.yaml#/definitions/uint8 =20 + eswin,hsp-sp-csr: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: Phandle to HSP(High-Speed Peripheral) device + - description: Offset of the stability status register for inter= nal + clock. + - description: Offset of the stability register for host regulat= or + voltage. + description: + HSP CSR is to control and get status of different high-speed periphe= rals + (such as Ethernet, USB, SATA, etc.) via register, which can 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 =20 + - 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: --=20 2.25.1 From nobody Sun Feb 8 01:33:49 2026 Received: from zg8tmja2lje4os43os4xodqa.icoremail.net (zg8tmja2lje4os43os4xodqa.icoremail.net [206.189.79.184]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 7BD862877D7; Sun, 19 Oct 2025 11:53:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=206.189.79.184 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760874819; cv=none; b=dDlszYdCILN8rHRhJ5ImJpTp2p6Mbp+LsC7LLCiqnRVoaRplnqEpLpQZX0sTFfgZbamuMexb+txplwcgsLw9qNnUnyC5sHpAaKfF8kzU1ix3UX5pX3JWHOY4mKjAd2Q8hTDPe/ljK8Rn/seY/uMHuBjCYhjeDgFMvy3bWKKKUpE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760874819; c=relaxed/simple; bh=BiF8XxnWQbEhdFS6YZEa+rRDYu1z/lo8wxXVlzmOZzw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=k6tdi+F+Q3qhvJpg6WUMo5cYPzpQLutAWri6JedarM1izezH/jDbC30eLc22GiTlWCMErrW+RXQLlUjTDzmczOVqucni4dVELCTH9C3MeJQZK5TN/ciiaNiExtH5TaCeSDv5nnjxNwzAlObGSocYF56mZF5QwyQ+ns0v1kvXP+I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=eswincomputing.com; spf=pass smtp.mailfrom=eswincomputing.com; arc=none smtp.client-ip=206.189.79.184 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=eswincomputing.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=eswincomputing.com Received: from E0005154LT.eswin.cn (unknown [10.100.73.96]) by app1 (Coremail) with SMTP id TAJkCgAXOxEw0fRoeFUYAQ--.63592S2; Sun, 19 Oct 2025 19:53:29 +0800 (CST) From: hehuan1@eswincomputing.com To: ulf.hansson@linaro.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, jszhang@kernel.org, adrian.hunter@intel.com, p.zabel@pengutronix.de, linux-mmc@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Cc: ningyu@eswincomputing.com, linmin@eswincomputing.com, pinkesh.vaghela@einfochips.com, xuxiang@eswincomputing.com, luyulin@eswincomputing.com, dongxuyang@eswincomputing.com, zhangsenchuan@eswincomputing.com, weishangjuan@eswincomputing.com, lizhi2@eswincomputing.com, caohang@eswincomputing.com, hehuan1@eswincomputing.com Subject: [PATCH v5 2/2] mmc: sdhci-of-dwcmshc: Add support for Eswin EIC7700 Date: Sun, 19 Oct 2025 19:53:16 +0800 Message-ID: <20251019115316.337-1-hehuan1@eswincomputing.com> X-Mailer: git-send-email 2.49.0.windows.1 In-Reply-To: <20251019115133.300-1-hehuan1@eswincomputing.com> References: <20251019115133.300-1-hehuan1@eswincomputing.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: TAJkCgAXOxEw0fRoeFUYAQ--.63592S2 X-Coremail-Antispam: 1UD129KBjvAXoW3tr13Jw48KF4rJFyUCw4UJwb_yoW8Zr13Co WfWF13Jw4fJw4xuFWFkFn7KF1j9rZ7C3ySkw4a9w4ku3WkC3WUJrWSka13Xw1aqryFyr1k Jrs7Jry3XrWfAFn5n29KB7ZKAUJUUUU5529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UjIYCTnIWjp_UUUYN7AC8VAFwI0_Xr0_Wr1l1xkIjI8I6I8E6xAIw20EY4v20xva j40_Wr0E3s1l1IIY67AEw4v_Jr0_Jr4l8cAvFVAK0II2c7xJM28CjxkF64kEwVA0rcxSw2 x7M28EF7xvwVC0I7IYx2IY67AKxVWUJVWUCwA2z4x0Y4vE2Ix0cI8IcVCY1x0267AKxVWx JVW8Jr1l84ACjcxK6I8E87Iv67AKxVWxJr0_GcWl84ACjcxK6I8E87Iv6xkF7I0E14v26r xl6s0DM2AIxVAIcxkEcVAq07x20xvEncxIr21l5I8CrVACY4xI64kE6c02F40Ex7xfMcIj 6xIIjxv20xvE14v26r1Y6r17McIj6I8E87Iv67AKxVWUJVW8JwAm72CE4IkC6x0Yz7v_Jr 0_Gr1lF7xvr2IYc2Ij64vIr41lF7I21c0EjII2zVCS5cI20VAGYxC7M4IIrI8v6xkF7I0E 8cxan2IY04v7M4kE6xkIj40Ew7xC0wCY1x0262kKe7AKxVW8ZVWrXwCY02Avz4vE-syl42 xK82IYc2Ij64vIr41l4I8I3I0E4IkC6x0Yz7v_Jr0_Gr1lx2IqxVAqx4xG67AKxVWUJVWU GwC20s026x8GjcxK67AKxVWUGVWUWwC2zVAF1VAY17CE14v26r4a6rW5MIIYrxkI7VAKI4 8JMIIF0xvE2Ix0cI8IcVAFwI0_Jr0_JF4lIxAIcVC0I7IYx2IY6xkF7I0E14v26F4j6r4U JwCI42IY6xAIw20EY4v20xvaj40_Jr0_JF4lIxAIcVC2z280aVAFwI0_Jr0_Gr1lIxAIcV C2z280aVCY1x0267AKxVW8JVW8JrUvcSsGvfC2KfnxnUUI43ZEXa7sRiBT5PUUUUU== X-CM-SenderInfo: 5khk3tzqr6v25zlqu0xpsx3x1qjou0bp/ Content-Type: text/plain; charset="utf-8" From: Huan He 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. Signed-off-by: Huan He Acked-by: Adrian Hunter --- drivers/mmc/host/sdhci-of-dwcmshc.c | 502 +++++++++++++++++++++++++++- 1 file changed, 491 insertions(+), 11 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-o= f-dwcmshc.c index eebd45389956..c8726e6e0905 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -19,8 +20,11 @@ #include #include #include +#include #include #include +#include +#include =20 #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 = */ =20 +/* 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 val= id + * only when LOCK_STS is set. + */ +#define DLL_ERROR_STS BIT(1) + #define FLAG_IO_FIXED_1V8 BIT(0) =20 #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 =20 +/* 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; }; =20 +struct eic7700_priv { + struct reset_control *reset; + unsigned int drive_impedance; +}; + #define DWCMSHC_MAX_OTHER_CLKS 3 =20 struct dwcmshc_priv { @@ -238,6 +286,17 @@ struct dwcmshc_pltfm_data { void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv); }; =20 +static void dwcmshc_enable_card_clk(struct sdhci_host *host) +{ + u16 ctrl; + + ctrl =3D sdhci_readw(host, SDHCI_CLOCK_CONTROL); + if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) { + ctrl |=3D 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 +1154,411 @@ static int sg2042_init(struct device *dev, struct s= dhci_host *host, ARRAY_SIZE(clk_ids), clk_ids); } =20 +static void sdhci_eic7700_set_clock(struct sdhci_host *host, unsigned int = clock) +{ + struct sdhci_pltfm_host *pltfm_host =3D sdhci_priv(host); + u16 clk; + + host->mmc->actual_clock =3D clock; + + if (clock =3D=3D 0) { + sdhci_set_clock(host, clock); + return; + } + + clk_set_rate(pltfm_host->clk, clock); + + clk =3D sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk |=3D 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 de= lay) +{ + delay &=3D 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 =3D sdhci_priv(host); + struct dwcmshc_priv *dwc_priv =3D sdhci_pltfm_priv(pltfm_host); + u32 emmc_caps =3D MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; + struct eic7700_priv *priv =3D dwc_priv->priv; + unsigned int val, drv; + + drv =3D FIELD_PREP(PHY_CNFG_PAD_SP_MASK, priv->drive_impedance & 0xF); + drv |=3D FIELD_PREP(PHY_CNFG_PAD_SN_MASK, (priv->drive_impedance >> 4) & = 0xF); + + if ((host->mmc->caps2 & emmc_caps) =3D=3D emmc_caps) { + val =3D sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC= _CONTROL); + val |=3D DWCMSHC_CARD_IS_EMMC; + sdhci_writew(host, val, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_C= ONTROL); + } + + /* reset phy, config phy's pad */ + sdhci_writel(host, drv | ~PHY_CNFG_RSTN_DEASSERT, PHY_CNFG_R); + + /* configure phy pads */ + val =3D FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG20= 42); + val |=3D FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2= 042); + val |=3D FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP); + val |=3D 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 =3D FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG20= 42); + val |=3D FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2= 042); + sdhci_writew(host, val, PHY_CLKPAD_CNFG_R); + + /* PHY strobe PAD setting (EMMC only) */ + if ((host->mmc->caps2 & emmc_caps) =3D=3D emmc_caps) { + val =3D FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2= 042); + val |=3D FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG= 2042); + val |=3D 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 =3D=3D SDHCI_RESET_ALL) + sdhci_eic7700_config_phy(host); +} + +static int sdhci_eic7700_reset_init(struct device *dev, struct eic7700_pri= v *priv) +{ + int ret; + + priv->reset =3D devm_reset_control_array_get_optional_exclusive(dev); + if (IS_ERR(priv->reset)) { + ret =3D PTR_ERR(priv->reset); + dev_err(dev, "failed to get reset control %d\n", ret); + return ret; + } + + ret =3D 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 =3D 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 =3D sdhci_priv(host); + struct dwcmshc_priv *dwc_priv =3D sdhci_pltfm_priv(pltfm_host); + int delay_min =3D -1; + int delay_max =3D -1; + int cmd_error =3D 0; + int delay =3D 0; + int i =3D 0; + int ret; + + for (i =3D 0; i <=3D PHY_DELAY_CODE_MAX; i++) { + sdhci_eic7700_config_phy_delay(host, i); + ret =3D 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 !=3D -1 && delay_max !=3D -1) + break; + } else { + if (delay_min =3D=3D -1) { + delay_min =3D i; + continue; + } else { + delay_max =3D i; + continue; + } + } + } + if (delay_min =3D=3D -1 && delay_max =3D=3D -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 =3D (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 op= code) +{ + struct sdhci_pltfm_host *pltfm_host =3D sdhci_priv(host); + struct dwcmshc_priv *priv =3D sdhci_pltfm_priv(pltfm_host); + u32 sd_caps =3D MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO; + int phase_code =3D -1; + int code_range =3D -1; + bool is_sd =3D false; + int code_min =3D -1; + int code_max =3D -1; + int cmd_error =3D 0; + int ret =3D 0; + int i =3D 0; + + if ((host->mmc->caps2 & sd_caps) =3D=3D sd_caps) + is_sd =3D true; + + for (i =3D 0; i <=3D MAX_PHASE_CODE; i++) { + /* Centered Phase code */ + sdhci_writew(host, i, priv->vendor_specific_area1 + DWCMSHC_AT_STAT); + ret =3D mmc_send_tuning(host->mmc, opcode, &cmd_error); + host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + + if (ret) { + /* SD specific range tracking */ + if (is_sd && code_min !=3D -1 && code_max !=3D -1) { + if (code_max - code_min > code_range) { + code_range =3D code_max - code_min; + phase_code =3D (code_min + code_max) / 2; + if (code_range > TUNING_RANGE_THRESHOLD) + break; + } + code_min =3D -1; + code_max =3D -1; + } + /* EMMC breaks after first valid range */ + if (!is_sd && code_min !=3D -1 && code_max !=3D -1) + break; + } else { + /* Track valid phase code range */ + if (code_min =3D=3D -1) { + code_min =3D i; + if (!is_sd) + continue; + } + code_max =3D i; + if (is_sd && i =3D=3D MAX_PHASE_CODE) { + if (code_max - code_min > code_range) { + code_range =3D code_max - code_min; + phase_code =3D (code_min + code_max) / 2; + } + } + } + } + + /* Handle tuning failure case */ + if ((is_sd && phase_code =3D=3D -1) || + (!is_sd && code_min =3D=3D -1 && code_max =3D=3D -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 =3D (code_min + code_max) / 2; + + sdhci_writew(host, phase_code, priv->vendor_specific_area1 + DWCMSHC_AT_S= TAT); + + /* SD specific final verification */ + if (is_sd) { + ret =3D 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 opc= ode) +{ + struct sdhci_pltfm_host *pltfm_host =3D sdhci_priv(host); + struct dwcmshc_priv *priv =3D sdhci_pltfm_priv(pltfm_host); + u32 emmc_caps =3D MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; + int ret =3D 0; + u16 ctrl; + u32 val; + + ctrl =3D sdhci_readw(host, SDHCI_HOST_CONTROL2); + ctrl &=3D ~SDHCI_CTRL_TUNED_CLK; + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + + val =3D sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCT= RL); + val |=3D 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) =3D=3D emmc_caps) { + ret =3D sdhci_eic7700_delay_tuning(host, opcode); + if (ret) + return ret; + } + + ret =3D sdhci_eic7700_phase_code_tuning(host, opcode); + if (ret) + return ret; + + return 0; +} + +static void sdhci_eic7700_set_uhs_signaling(struct sdhci_host *host, unsig= ned int timing) +{ + struct sdhci_pltfm_host *pltfm_host =3D sdhci_priv(host); + struct dwcmshc_priv *priv =3D 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 =3D=3D MMC_TIMING_MMC_HS400 && host->clock =3D=3D 200000000) { + val =3D sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATC= TRL); + val &=3D ~(FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_= DLY)); + /* 2-cycle latency */ + val |=3D FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, 0x2); + sdhci_writew(host, val, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTR= L); + + sdhci_writeb(host, FIELD_PREP(PHY_DLL_CNFG1_SLVDLY_MASK, PHY_DLL_CNFG1_S= LVDLY) | + 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 =3D read_poll_timeout(sdhci_readb, status, status & DLL_LOCK_STS, 10= 0, 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 =3D 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, unsigne= d int timing) +{ + u32 sd_caps =3D MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO; + + if ((host->mmc->caps2 & sd_caps) =3D=3D 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, struc= t dwcmshc_priv *dwc_priv) +{ + u32 emmc_caps =3D 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 =3D devm_kzalloc(dev, sizeof(struct eic7700_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dwc_priv->priv =3D priv; + + ret =3D sdhci_eic7700_reset_init(dev, dwc_priv->priv); + if (ret) { + dev_err(dev, "failed to reset\n"); + return ret; + } + + ret =3D 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 =3D 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 PTR_ERR(hsp_regmap); + } + hsp_int_status =3D args.args[0]; + hsp_pwr_ctrl =3D 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_ctr= l. + * 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) =3D=3D emmc_caps) + dwc_priv->delay_line =3D PHY_DELAY_CODE_EMMC; + else + dwc_priv->delay_line =3D PHY_DELAY_CODE_SD; + + if (!of_property_read_u32(dev->of_node, "eswin,drive-impedance-ohms", &va= l)) + priv->drive_impedance =3D eic7700_convert_drive_impedance_ohm(dev, val); + return 0; +} + static const struct sdhci_ops sdhci_dwcmshc_ops =3D { .set_clock =3D sdhci_set_clock, .set_bus_width =3D sdhci_set_bus_width, @@ -1169,6 +1633,18 @@ static const struct sdhci_ops sdhci_dwcmshc_sg2042_o= ps =3D { .platform_execute_tuning =3D th1520_execute_tuning, }; =20 +static const struct sdhci_ops sdhci_dwcmshc_eic7700_ops =3D { + .set_clock =3D sdhci_eic7700_set_clock, + .get_max_clock =3D sdhci_pltfm_clk_get_max_clock, + .get_timeout_clock =3D sdhci_pltfm_clk_get_max_clock, + .set_bus_width =3D sdhci_set_bus_width, + .reset =3D sdhci_eic7700_reset, + .set_uhs_signaling =3D sdhci_eic7700_set_uhs_wrapper, + .set_power =3D sdhci_set_power_and_bus_voltage, + .irq =3D dwcmshc_cqe_irq_handler, + .platform_execute_tuning =3D sdhci_eic7700_executing_tuning, +}; + static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata =3D { .pdata =3D { .ops =3D &sdhci_dwcmshc_ops, @@ -1238,6 +1714,17 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc= _sg2042_pdata =3D { .init =3D sg2042_init, }; =20 +static const struct dwcmshc_pltfm_data sdhci_dwcmshc_eic7700_pdata =3D { + .pdata =3D { + .ops =3D &sdhci_dwcmshc_eic7700_ops, + .quirks =3D SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, + .quirks2 =3D SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, + }, + .init =3D eic7700_init, +}; + static const struct cqhci_host_ops dwcmshc_cqhci_ops =3D { .enable =3D dwcmshc_sdhci_cqe_enable, .disable =3D sdhci_cqe_disable, @@ -1338,6 +1825,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_id= s[] =3D { .compatible =3D "sophgo,sg2042-dwcmshc", .data =3D &sdhci_dwcmshc_sg2042_pdata, }, + { + .compatible =3D "eswin,eic7700-dwcmshc", + .data =3D &sdhci_dwcmshc_eic7700_pdata, + }, {}, }; MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids); @@ -1570,17 +2061,6 @@ static int dwcmshc_resume(struct device *dev) return ret; } =20 -static void dwcmshc_enable_card_clk(struct sdhci_host *host) -{ - u16 ctrl; - - ctrl =3D sdhci_readw(host, SDHCI_CLOCK_CONTROL); - if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) { - ctrl |=3D SDHCI_CLOCK_CARD_EN; - sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL); - } -} - static int dwcmshc_runtime_suspend(struct device *dev) { struct sdhci_host *host =3D dev_get_drvdata(dev); --=20 2.25.1