* [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
* 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
* [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 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).