Add support for Eswin EIC7700 SD/eMMC controller
Subsystems (3)
v4 cover Add support for Eswin EIC7700 SD/eMMC controller
From: Huan He <[email protected]>
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/[email protected]/
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/[email protected]/
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/[email protected]/
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(-)
Signature · 1 line
2.25.1
PATCH v4 1/2 dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700
From: Huan He <[email protected]>
EIC7700 use Synopsys dwcmshc IP for SD/eMMC controllers. Add Eswin EIC7700 support in sdhci-of-dwcmshc.yaml.
--- .../bindings/mmc/snps,dwcmshc-sdhci.yaml | 57 +++++++++++++++++-- 1 file changed, 51 insertions(+), 6 deletions(-)
Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml +51 -6
@@ -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:
Signature · 1 line
2.25.1
Rob Herring
maintainerRe: [PATCH v4 1/2] dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700
Quoted 32 lines · On Sat, Oct 11, 2025 at 07:11:50PM +0800, [email protected] wrote:
From: Huan He <[email protected]>
EIC7700 use Synopsys dwcmshc IP for SD/eMMC controllers. Add Eswin EIC7700 support in
sdhci-of-dwcmshc.yaml.Signed-off-by Huan He <[email protected]>Reviewed-by Conor Dooley <[email protected]>--- .../bindings/mmc/snps,dwcmshc-sdhci.yaml | 57 +++++++++++++++++-- 1 file changed, 51 insertions(+), 6 deletions(-)Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml +1
-5@@ -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
Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml properties: · line 64
Quoted 10 lines
Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml +4
-1- - 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: ...
...
Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml properties: · line 70
Quoted 6 lines
Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml +6
-0+ - 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.
Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml properties: · line 74
Quoted 4 lines
Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml +4
-0+ 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.
Quoted 49 lines
Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml +36
-0+ + 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:Signature · 1 line
2.25.1
PATCH v4 2/2 mmc: sdhci-of-dwcmshc: Add support for Eswin EIC7700
In reply to [email protected]From: Huan He <[email protected]>
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.
--- drivers/mmc/host/sdhci-of-dwcmshc.c | 526 ++++++++++++++++++++++++++-- 1 file changed, 504 insertions(+), 22 deletions(-)
drivers/mmc/host/sdhci-of-dwcmshc.c +504 -22
@@ -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 3struct 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);
Signature · 1 line
2.25.1
Dan Carpenter
build botRe: [PATCH v4 2/2] mmc: sdhci-of-dwcmshc: Add support for Eswin EIC7700
BCC: [email protected] CC: [email protected] In-Reply-To: <[email protected]> References: <[email protected]> TO…
BCC: [email protected]
CC: [email protected]
In-Reply-To: <[email protected]>
References: <[email protected]>
TO: [email protected]
TO: [email protected]
TO: [email protected]
TO: [email protected]
TO: [email protected]
TO: [email protected]
TO: [email protected]
TO: [email protected]
TO: [email protected]
TO: [email protected]
TO: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: [email protected]
CC: kernel test robot <[email protected]>
Hi,
kernel test robot noticed the following build warnings:
[auto build test WARNING on linus/master]
[also build test WARNING on ulf-hansson-mmc-mirror/next v6.18-rc1 next-20251010] [cannot apply to robh/for-next pza/reset/next pza/imx-drm/next]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/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
:::::: branch date: 2 days ago
:::::: commit date: 2 days ago
config: s390-randconfig-r073-20251013 (https://download.01.org/0day-ci/archive/20251013/[email protected]/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 <[email protected]> | Reported-by: Dan Carpenter <[email protected]> | Closes: https://lore.kernel.org/r/[email protected]/
New smatch warnings:drivers/mmc/host/sdhci-of-dwcmshc.c:1547 eic7700_init() warn: missing error code? 'ret'
Old smatch warnings:drivers/mmc/host/sdhci-of-dwcmshc.c:1079 cv18xx_sdhci_execute_tuning() error: uninitialized symbol 'target_min'. drivers/mmc/host/sdhci-of-dwcmshc.c:1079 cv18xx_sdhci_execute_tuning() error: uninitialized symbol 'target_max'.
vim +/ret +1547 drivers/mmc/host/sdhci-of-dwcmshc.c
3c5c200f02eee8 Huan He 2025-10-11 1515
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;
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 */
3c5c200f02eee8 Huan He 2025-10-11 1557 regmap_write(hsp_regmap, hsp_int_status, EIC7700_INT_CLK_STABLE);
3c5c200f02eee8 Huan He 2025-10-11 1558 /*
3c5c200f02eee8 Huan He 2025-10-11 1559 * Assert voltage stability: write EIC7700_HOST_VAL_STABLE to hsp_pwr_ctrl.
3c5c200f02eee8 Huan He 2025-10-11 1560 * This signals that VDD is stable and permits transition to high-speed
3c5c200f02eee8 Huan He 2025-10-11 1561 * modes (e.g., UHS-I).
3c5c200f02eee8 Huan He 2025-10-11 1562 */
3c5c200f02eee8 Huan He 2025-10-11 1563 regmap_write(hsp_regmap, hsp_pwr_ctrl, EIC7700_HOST_VAL_STABLE);
3c5c200f02eee8 Huan He 2025-10-11 1564
3c5c200f02eee8 Huan He 2025-10-11 1565 if ((host->mmc->caps2 & emmc_caps) == emmc_caps)
3c5c200f02eee8 Huan He 2025-10-11 1566 dwc_priv->delay_line = PHY_DELAY_CODE_EMMC;
3c5c200f02eee8 Huan He 2025-10-11 1567 else
3c5c200f02eee8 Huan He 2025-10-11 1568 dwc_priv->delay_line = PHY_DELAY_CODE_SD;
3c5c200f02eee8 Huan He 2025-10-11 1569
3c5c200f02eee8 Huan He 2025-10-11 1570 if (!of_property_read_u32(dev->of_node, "eswin,drive-impedance-ohms", &val))
3c5c200f02eee8 Huan He 2025-10-11 1571 priv->drive_impedance = eic7700_convert_drive_impedance_ohm(dev, val);
3c5c200f02eee8 Huan He 2025-10-11 1572 return 0;
3c5c200f02eee8 Huan He 2025-10-11 1573 }
3c5c200f02eee8 Huan He 2025-10-11 1574 Signature · 2 lines
0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
Adrian Hunter
maintainerRe: [PATCH v4 2/2] mmc: sdhci-of-dwcmshc: Add support for Eswin EIC7700
In reply to [email protected]Quoted 9 lines · On 11/10/2025 14:12, [email protected] wrote:
From: Huan He <[email protected]>
Add support for the mmc controller in the Eswin EIC7700 with the new compatible "eswin,eic7700-dwcmshc". Implement custom
sdhci_opsforset_clock, reset,set_uhs_signaling,platform_execute_tuning.
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)
Quoted 1 line
Signed-off-by Huan He <[email protected]>
Looks good. A few minor comments further below.
Quoted 139 lines
--- drivers/mmc/host/sdhci-of-dwcmshc.c | 526 ++++++++++++++++++++++++++-- 1 file changed, 504 insertions(+), 22 deletions(-)drivers/mmc/host/sdhci-of-dwcmshc.c +76
-0@@ -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 3struct 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;
drivers/mmc/host/sdhci-of-dwcmshc.c static int sg2042_init(struct device *dev, struct sdhci_host *host, · line 1183
Quoted 10 lines
drivers/mmc/host/sdhci-of-dwcmshc.c +10
-0+ +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.
Quoted 27 lines
drivers/mmc/host/sdhci-of-dwcmshc.c +27
-0+ + 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 );
drivers/mmc/host/sdhci-of-dwcmshc.c static int sg2042_init(struct device *dev, struct sdhci_host *host, · line 1220
Quoted 10 lines
drivers/mmc/host/sdhci-of-dwcmshc.c +10
-0+ 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
Quoted 137 lines
drivers/mmc/host/sdhci-of-dwcmshc.c +137
-0+ +/* 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)
drivers/mmc/host/sdhci-of-dwcmshc.c static int sg2042_init(struct device *dev, struct sdhci_host *host, · line 1367
Quoted 10 lines
drivers/mmc/host/sdhci-of-dwcmshc.c +10
-0+ 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"
Quoted 33 lines
drivers/mmc/host/sdhci-of-dwcmshc.c +33
-0+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 )) {
drivers/mmc/host/sdhci-of-dwcmshc.c static int sg2042_init(struct device *dev, struct sdhci_host *host, · line 1410
Quoted 10 lines
drivers/mmc/host/sdhci-of-dwcmshc.c +10
-0+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
Quoted 65 lines
drivers/mmc/host/sdhci-of-dwcmshc.c +65
-0+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) |
drivers/mmc/host/sdhci-of-dwcmshc.c static int sg2042_init(struct device *dev, struct sdhci_host *host, · line 1485
Quoted 10 lines
drivers/mmc/host/sdhci-of-dwcmshc.c +10
-0+ 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. */
Quoted 52 lines
drivers/mmc/host/sdhci-of-dwcmshc.c +52
-0+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);
drivers/mmc/host/sdhci-of-dwcmshc.c static int sg2042_init(struct device *dev, struct sdhci_host *host, · line 1547
Quoted 10 lines
drivers/mmc/host/sdhci-of-dwcmshc.c +10
-0+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);
Quoted 114 lines
drivers/mmc/host/sdhci-of-dwcmshc.c +54
-22+ } + 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);