summaryrefslogtreecommitdiff
path: root/drivers/mmc/host/sdhci-of-dwcmshc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/sdhci-of-dwcmshc.c')
-rw-r--r--drivers/mmc/host/sdhci-of-dwcmshc.c1245
1 files changed, 993 insertions, 252 deletions
diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c
index 39edf04fedcf..51949cde0958 100644
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -8,20 +8,27 @@
*/
#include <linux/acpi.h>
+#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>
#include <linux/module.h>
#include <linux/of.h>
#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"
+#include "sdhci-cqhci.h"
#define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16)
@@ -37,6 +44,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 */
@@ -80,6 +88,8 @@
#define DWCMSHC_EMMC_DLL_TXCLK 0x808
#define DWCMSHC_EMMC_DLL_STRBIN 0x80c
#define DECMSHC_EMMC_DLL_CMDOUT 0x810
+#define DECMSHC_EMMC_MISC_CON 0x81C
+#define MISC_INTCLK_EN BIT(1)
#define DWCMSHC_EMMC_DLL_STATUS0 0x840
#define DWCMSHC_EMMC_DLL_START BIT(0)
#define DWCMSHC_EMMC_DLL_LOCKED BIT(8)
@@ -92,7 +102,7 @@
#define DLL_TXCLK_TAPNUM_DEFAULT 0x10
#define DLL_TXCLK_TAPNUM_90_DEGREES 0xA
#define DLL_TXCLK_TAPNUM_FROM_SW BIT(24)
-#define DLL_STRBIN_TAPNUM_DEFAULT 0x8
+#define DLL_STRBIN_TAPNUM_DEFAULT 0x4
#define DLL_STRBIN_TAPNUM_FROM_SW BIT(24)
#define DLL_STRBIN_DELAY_NUM_SEL BIT(26)
#define DLL_STRBIN_DELAY_NUM_OFFSET 16
@@ -108,18 +118,20 @@
#define DLL_LOCK_WO_TMOUT(x) \
((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \
(((x) & DWCMSHC_EMMC_DLL_TIMEOUT) == 0))
-#define RK35xx_MAX_CLKS 3
/* PHY register area pointer */
#define DWC_MSHC_PTR_PHY_R 0x300
/* PHY general configuration */
-#define PHY_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x00)
-#define PHY_CNFG_RSTN_DEASSERT 0x1 /* Deassert PHY reset */
-#define PHY_CNFG_PAD_SP_MASK GENMASK(19, 16) /* bits [19:16] */
-#define PHY_CNFG_PAD_SP 0x0c /* PMOS TX drive strength */
-#define PHY_CNFG_PAD_SN_MASK GENMASK(23, 20) /* bits [23:20] */
-#define PHY_CNFG_PAD_SN 0x0c /* NMOS TX drive strength */
+#define PHY_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x00)
+#define PHY_CNFG_RSTN_DEASSERT 0x1 /* Deassert PHY reset */
+#define PHY_CNFG_PHY_PWRGOOD_MASK BIT_MASK(1) /* bit [1] */
+#define PHY_CNFG_PAD_SP_MASK GENMASK(19, 16) /* bits [19:16] */
+#define PHY_CNFG_PAD_SP 0x0c /* PMOS TX drive strength */
+#define PHY_CNFG_PAD_SP_SG2042 0x09 /* PMOS TX drive strength for SG2042 */
+#define PHY_CNFG_PAD_SN_MASK GENMASK(23, 20) /* bits [23:20] */
+#define PHY_CNFG_PAD_SN 0x0c /* NMOS TX drive strength */
+#define PHY_CNFG_PAD_SN_SG2042 0x08 /* NMOS TX drive strength for SG2042 */
/* PHY command/response pad settings */
#define PHY_CMDPAD_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x04)
@@ -148,10 +160,12 @@
#define PHY_PAD_TXSLEW_CTRL_P 0x3 /* Slew control for P-Type pad TX */
#define PHY_PAD_TXSLEW_CTRL_N_MASK GENMASK(12, 9) /* bits [12:9] */
#define PHY_PAD_TXSLEW_CTRL_N 0x3 /* Slew control for N-Type pad TX */
+#define PHY_PAD_TXSLEW_CTRL_N_SG2042 0x2 /* Slew control for N-Type pad TX for SG2042 */
/* PHY CLK delay line settings */
#define PHY_SDCLKDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x1d)
-#define PHY_SDCLKDL_CNFG_UPDATE BIT(4) /* set before writing to SDCLKDL_DC */
+#define PHY_SDCLKDL_CNFG_EXTDLY_EN BIT(0)
+#define PHY_SDCLKDL_CNFG_UPDATE BIT(4) /* set before writing to SDCLKDL_DC */
/* PHY CLK delay line delay code */
#define PHY_SDCLKDL_DC_R (DWC_MSHC_PTR_PHY_R + 0x1e)
@@ -159,10 +173,14 @@
#define PHY_SDCLKDL_DC_DEFAULT 0x32 /* default delay code */
#define PHY_SDCLKDL_DC_HS400 0x18 /* delay code for HS400 mode */
+#define PHY_SMPLDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x20)
+#define PHY_SMPLDL_CNFG_BYPASS_EN BIT(1)
+
/* PHY drift_cclk_rx delay line configuration setting */
#define PHY_ATDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x21)
#define PHY_ATDL_CNFG_INPSEL_MASK GENMASK(3, 2) /* bits [3:2] */
#define PHY_ATDL_CNFG_INPSEL 0x3 /* delay line input source */
+#define PHY_ATDL_CNFG_INPSEL_SG2042 0x2 /* delay line input source for SG2042 */
/* PHY DLL control settings */
#define PHY_DLL_CTRL_R (DWC_MSHC_PTR_PHY_R + 0x24)
@@ -184,6 +202,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) \
@@ -193,29 +224,111 @@
SDHCI_TRNS_BLK_CNT_EN | \
SDHCI_TRNS_DMA)
+/* 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,
};
struct rk35xx_priv {
- /* Rockchip specified optional clocks */
- struct clk_bulk_data rockchip_clks[RK35xx_MAX_CLKS];
struct reset_control *reset;
enum dwcmshc_rk_type devtype;
u8 txclk_tapnum;
};
+struct eic7700_priv {
+ struct reset_control *reset;
+ unsigned int drive_impedance;
+};
+
+#define DWCMSHC_MAX_OTHER_CLKS 3
+
struct dwcmshc_priv {
struct clk *bus_clk;
int vendor_specific_area1; /* P_VENDOR_SPECIFIC_AREA1 reg */
int vendor_specific_area2; /* P_VENDOR_SPECIFIC_AREA2 reg */
+ int num_other_clks;
+ struct clk_bulk_data other_clks[DWCMSHC_MAX_OTHER_CLKS];
+
void *priv; /* pointer to SoC private stuff */
u16 delay_line;
u16 flags;
};
+struct dwcmshc_pltfm_data {
+ const struct sdhci_pltfm_data pdata;
+ const struct cqhci_host_ops *cqhci_host_ops;
+ int (*init)(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
+ void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
+};
+
+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,
+ const char * const clk_ids[])
+{
+ int err;
+
+ if (num_clks > DWCMSHC_MAX_OTHER_CLKS)
+ return -EINVAL;
+
+ for (int i = 0; i < num_clks; i++)
+ priv->other_clks[i].id = clk_ids[i];
+
+ err = devm_clk_bulk_get_optional(dev, num_clks, priv->other_clks);
+ if (err) {
+ dev_err(dev, "failed to get clocks %d\n", err);
+ return err;
+ }
+
+ err = clk_bulk_prepare_enable(num_clks, priv->other_clks);
+ if (err)
+ dev_err(dev, "failed to enable clocks %d\n", err);
+
+ priv->num_other_clks = num_clks;
+
+ return err;
+}
+
/*
* If DMA addr spans 128MB boundary, we split the DMA transfer into two
* so that each DMA transfer doesn't exceed the boundary.
@@ -239,6 +352,19 @@ static void dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc,
sdhci_adma_write_desc(host, desc, addr, len, cmd);
}
+static void dwcmshc_reset(struct sdhci_host *host, u8 mask)
+{
+ sdhci_reset(host, mask);
+
+ /* The dwcmshc does not comply with the SDHCI specification
+ * regarding the "Software Reset for CMD line should clear 'Command
+ * Complete' in the Normal Interrupt Status Register." Clear the bit
+ * here to compensate for this quirk.
+ */
+ if (mask & SDHCI_RESET_CMD)
+ sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS);
+}
+
static unsigned int dwcmshc_get_max_clock(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -279,12 +405,17 @@ static void dwcmshc_request(struct mmc_host *mmc, struct mmc_request *mrq)
sdhci_request(mmc, mrq);
}
-static void dwcmshc_phy_1_8v_init(struct sdhci_host *host)
+static void dwcmshc_phy_init(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ u32 rxsel = PHY_PAD_RXSEL_3V3;
u32 val;
+ if (priv->flags & FLAG_IO_FIXED_1V8 ||
+ host->mmc->ios.timing & MMC_SIGNAL_VOLTAGE_180)
+ rxsel = PHY_PAD_RXSEL_1V8;
+
/* deassert phy reset & set tx drive strength */
val = PHY_CNFG_RSTN_DEASSERT;
val |= FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP);
@@ -304,7 +435,7 @@ static void dwcmshc_phy_1_8v_init(struct sdhci_host *host)
sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R);
/* configure phy pads */
- val = PHY_PAD_RXSEL_1V8;
+ val = rxsel;
val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
@@ -316,65 +447,22 @@ static void dwcmshc_phy_1_8v_init(struct sdhci_host *host)
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
- val = PHY_PAD_RXSEL_1V8;
+ val = rxsel;
val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLDOWN);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
/* enable data strobe mode */
- sdhci_writeb(host, FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK, PHY_DLLDL_CNFG_SLV_INPSEL),
- PHY_DLLDL_CNFG_R);
-
- /* enable phy dll */
- sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R);
-}
-
-static void dwcmshc_phy_3_3v_init(struct sdhci_host *host)
-{
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
- u32 val;
-
- /* deassert phy reset & set tx drive strength */
- val = PHY_CNFG_RSTN_DEASSERT;
- val |= FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP);
- val |= FIELD_PREP(PHY_CNFG_PAD_SN_MASK, PHY_CNFG_PAD_SN);
- sdhci_writel(host, val, PHY_CNFG_R);
-
- /* disable delay line */
- sdhci_writeb(host, PHY_SDCLKDL_CNFG_UPDATE, PHY_SDCLKDL_CNFG_R);
-
- /* set delay line */
- sdhci_writeb(host, priv->delay_line, PHY_SDCLKDL_DC_R);
- sdhci_writeb(host, PHY_DLL_CNFG2_JUMPSTEP, PHY_DLL_CNFG2_R);
-
- /* enable delay lane */
- val = sdhci_readb(host, PHY_SDCLKDL_CNFG_R);
- val &= ~(PHY_SDCLKDL_CNFG_UPDATE);
- sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R);
-
- /* configure phy pads */
- val = PHY_PAD_RXSEL_3V3;
- val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
- val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
- val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
- sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
- sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
- sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
-
- val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
- val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
- sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
+ if (rxsel == PHY_PAD_RXSEL_1V8) {
+ u8 sel = FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK, PHY_DLLDL_CNFG_SLV_INPSEL);
- val = PHY_PAD_RXSEL_3V3;
- val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLDOWN);
- val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
- val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
- sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
+ sdhci_writeb(host, sel, PHY_DLLDL_CNFG_R);
+ }
/* enable phy dll */
sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R);
+
}
static void th1520_sdhci_set_phy(struct sdhci_host *host)
@@ -384,11 +472,7 @@ static void th1520_sdhci_set_phy(struct sdhci_host *host)
u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
u16 emmc_ctrl;
- /* Before power on, set PHY configs */
- if (priv->flags & FLAG_IO_FIXED_1V8)
- dwcmshc_phy_1_8v_init(host);
- else
- dwcmshc_phy_3_3v_init(host);
+ dwcmshc_phy_init(host);
if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
emmc_ctrl = sdhci_readw(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
@@ -553,6 +637,73 @@ static void dwcmshc_cqhci_dumpregs(struct mmc_host *mmc)
sdhci_dumpregs(mmc_priv(mmc));
}
+static void rk35xx_sdhci_cqe_pre_enable(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
+ u32 reg;
+
+ /* Set Send Status Command Idle Timer to 10.66us (256 * 1 / 24) */
+ reg = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_SSC1);
+ reg = (reg & ~CQHCI_SSC1_CIT_MASK) | 0x0100;
+ sdhci_writel(host, reg, dwc_priv->vendor_specific_area2 + CQHCI_SSC1);
+
+ reg = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
+ reg |= CQHCI_ENABLE;
+ sdhci_writel(host, reg, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
+}
+
+static void rk35xx_sdhci_cqe_enable(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ u32 reg;
+
+ reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
+ while (reg & SDHCI_DATA_AVAILABLE) {
+ sdhci_readl(host, SDHCI_BUFFER);
+ reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
+ }
+
+ sdhci_writew(host, DWCMSHC_SDHCI_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE);
+
+ sdhci_cqe_enable(mmc);
+}
+
+static void rk35xx_sdhci_cqe_disable(struct mmc_host *mmc, bool recovery)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ unsigned long flags;
+ u32 ctrl;
+
+ /*
+ * During CQE command transfers, command complete bit gets latched.
+ * So s/w should clear command complete interrupt status when CQE is
+ * either halted or disabled. Otherwise unexpected SDCHI legacy
+ * interrupt gets triggered when CQE is halted/disabled.
+ */
+ spin_lock_irqsave(&host->lock, flags);
+ ctrl = sdhci_readl(host, SDHCI_INT_ENABLE);
+ ctrl |= SDHCI_INT_RESPONSE;
+ sdhci_writel(host, ctrl, SDHCI_INT_ENABLE);
+ sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS);
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ sdhci_cqe_disable(mmc, recovery);
+}
+
+static void rk35xx_sdhci_cqe_post_disable(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
+ u32 ctrl;
+
+ ctrl = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
+ ctrl &= ~CQHCI_ENABLE;
+ sdhci_writel(host, ctrl, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
+}
+
static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -580,10 +731,11 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
sdhci_set_clock(host, clock);
- /* Disable cmd conflict check */
+ /* Disable cmd conflict check and internal clock gate */
reg = dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3;
extra = sdhci_readl(host, reg);
extra &= ~BIT(0);
+ extra |= BIT(4);
sdhci_writel(host, extra, reg);
if (clock <= 52000000) {
@@ -671,6 +823,10 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask)
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
struct rk35xx_priv *priv = dwc_priv->priv;
+ u32 extra = sdhci_readl(host, DECMSHC_EMMC_MISC_CON);
+
+ if ((host->mmc->caps2 & MMC_CAP2_CQE) && (mask & SDHCI_RESET_ALL))
+ cqhci_deactivate(host->mmc);
if (mask & SDHCI_RESET_ALL && priv->reset) {
reset_control_assert(priv->reset);
@@ -679,6 +835,89 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask)
}
sdhci_reset(host, mask);
+
+ /* Enable INTERNAL CLOCK */
+ sdhci_writel(host, MISC_INTCLK_EN | extra, DECMSHC_EMMC_MISC_CON);
+}
+
+static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host,
+ struct dwcmshc_priv *dwc_priv)
+{
+ static const char * const clk_ids[] = {"axi", "block", "timer"};
+ struct rk35xx_priv *priv;
+ int err;
+
+ priv = devm_kzalloc(dev, sizeof(struct rk35xx_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ if (of_device_is_compatible(dev->of_node, "rockchip,rk3588-dwcmshc"))
+ priv->devtype = DWCMSHC_RK3588;
+ else
+ priv->devtype = DWCMSHC_RK3568;
+
+ priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc));
+ if (IS_ERR(priv->reset)) {
+ err = PTR_ERR(priv->reset);
+ dev_err(mmc_dev(host->mmc), "failed to get reset control %d\n", err);
+ return err;
+ }
+
+ err = dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv,
+ ARRAY_SIZE(clk_ids), clk_ids);
+ if (err)
+ return err;
+
+ if (of_property_read_u8(mmc_dev(host->mmc)->of_node, "rockchip,txclk-tapnum",
+ &priv->txclk_tapnum))
+ priv->txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT;
+
+ /* Disable cmd conflict check */
+ sdhci_writel(host, 0x0, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3);
+ /* Reset previous settings */
+ sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK);
+ sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_STRBIN);
+
+ dwc_priv->priv = priv;
+
+ return 0;
+}
+
+static void dwcmshc_rk35xx_postinit(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
+{
+ /*
+ * Don't support highspeed bus mode with low clk speed as we
+ * cannot use DLL for this condition.
+ */
+ if (host->mmc->f_max <= 52000000) {
+ dev_info(mmc_dev(host->mmc), "Disabling HS200/HS400, frequency too low (%d)\n",
+ host->mmc->f_max);
+ host->mmc->caps2 &= ~(MMC_CAP2_HS200 | MMC_CAP2_HS400);
+ host->mmc->caps &= ~(MMC_CAP_3_3V_DDR | MMC_CAP_1_8V_DDR);
+ }
+}
+
+static void dwcmshc_rk3576_postinit(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
+{
+ struct device *dev = mmc_dev(host->mmc);
+ int ret;
+
+ /*
+ * This works around the design of the RK3576's power domains, which
+ * makes the PD_NVM power domain, which the sdhci controller on the
+ * RK3576 is in, never come back the same way once it's run-time
+ * suspended once. This can happen during early kernel boot if no driver
+ * is using either PD_NVM or its child power domain PD_SDGMAC for a
+ * short moment, leading to it being turned off to save power. By
+ * keeping it on, sdhci suspending won't lead to PD_NVM becoming a
+ * candidate for getting turned off.
+ */
+ ret = dev_pm_genpd_rpm_always_on(dev, true);
+ if (ret && ret != -EOPNOTSUPP)
+ dev_warn(dev, "failed to set PD rpm always on, SoC may hang later: %pe\n",
+ ERR_PTR(ret));
+
+ dwcmshc_rk35xx_postinit(host, dwc_priv);
}
static int th1520_execute_tuning(struct sdhci_host *host, u32 opcode)
@@ -744,7 +983,7 @@ static void th1520_sdhci_reset(struct sdhci_host *host, u8 mask)
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
u16 ctrl_2;
- sdhci_reset(host, mask);
+ dwcmshc_reset(host, mask);
if (priv->flags & FLAG_IO_FIXED_1V8) {
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
@@ -755,13 +994,42 @@ static void th1520_sdhci_reset(struct sdhci_host *host, u8 mask)
}
}
+static int th1520_init(struct device *dev,
+ struct sdhci_host *host,
+ struct dwcmshc_priv *dwc_priv)
+{
+ dwc_priv->delay_line = PHY_SDCLKDL_DC_DEFAULT;
+
+ if (device_property_read_bool(dev, "mmc-ddr-1_8v") ||
+ device_property_read_bool(dev, "mmc-hs200-1_8v") ||
+ device_property_read_bool(dev, "mmc-hs400-1_8v"))
+ dwc_priv->flags |= FLAG_IO_FIXED_1V8;
+ else
+ dwc_priv->flags &= ~FLAG_IO_FIXED_1V8;
+
+ /*
+ * start_signal_voltage_switch() will try 3.3V first
+ * then 1.8V. Use SDHCI_SIGNALING_180 rather than
+ * SDHCI_SIGNALING_330 to avoid setting voltage to 3.3V
+ * in sdhci_start_signal_voltage_switch().
+ */
+ if (dwc_priv->flags & FLAG_IO_FIXED_1V8) {
+ host->flags &= ~SDHCI_SIGNALING_330;
+ host->flags |= SDHCI_SIGNALING_180;
+ }
+
+ sdhci_enable_v4_mode(host);
+
+ return 0;
+}
+
static void cv18xx_sdhci_reset(struct sdhci_host *host, u8 mask)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
u32 val, emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
- sdhci_reset(host, mask);
+ dwcmshc_reset(host, mask);
if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
val = sdhci_readl(host, priv->vendor_specific_area1 + CV18XX_SDHCI_MSHC_CTRL);
@@ -833,7 +1101,7 @@ static void cv18xx_sdhci_post_tuning(struct sdhci_host *host)
val |= SDHCI_INT_DATA_AVAIL;
sdhci_writel(host, val, SDHCI_INT_STATUS);
- sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+ dwcmshc_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
}
static int cv18xx_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
@@ -891,15 +1159,522 @@ static int cv18xx_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
return ret;
}
+static inline void sg2042_sdhci_phy_init(struct sdhci_host *host)
+{
+ u32 val;
+
+ /* Asset phy reset & set tx drive strength */
+ val = sdhci_readl(host, PHY_CNFG_R);
+ val &= ~PHY_CNFG_RSTN_DEASSERT;
+ val |= FIELD_PREP(PHY_CNFG_PHY_PWRGOOD_MASK, 1);
+ val |= FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP_SG2042);
+ val |= FIELD_PREP(PHY_CNFG_PAD_SN_MASK, PHY_CNFG_PAD_SN_SG2042);
+ sdhci_writel(host, val, PHY_CNFG_R);
+
+ /* Configure phy pads */
+ val = PHY_PAD_RXSEL_3V3;
+ val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
+ val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
+ val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+ sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
+ sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
+ sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
+
+ val = PHY_PAD_RXSEL_3V3;
+ val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
+ val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+ sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
+
+ val = PHY_PAD_RXSEL_3V3;
+ val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLDOWN);
+ val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
+ val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+ sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
+
+ /* Configure delay line */
+ /* Enable fixed delay */
+ sdhci_writeb(host, PHY_SDCLKDL_CNFG_EXTDLY_EN, PHY_SDCLKDL_CNFG_R);
+ /*
+ * Set delay line.
+ * Its recommended that bit UPDATE_DC[4] is 1 when SDCLKDL_DC is being written.
+ * Ensure UPDATE_DC[4] is '0' when not updating code.
+ */
+ val = sdhci_readb(host, PHY_SDCLKDL_CNFG_R);
+ val |= PHY_SDCLKDL_CNFG_UPDATE;
+ sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R);
+ /* Add 10 * 70ps = 0.7ns for output delay */
+ sdhci_writeb(host, 10, PHY_SDCLKDL_DC_R);
+ val = sdhci_readb(host, PHY_SDCLKDL_CNFG_R);
+ val &= ~(PHY_SDCLKDL_CNFG_UPDATE);
+ sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R);
+
+ /* Set SMPLDL_CNFG, Bypass */
+ sdhci_writeb(host, PHY_SMPLDL_CNFG_BYPASS_EN, PHY_SMPLDL_CNFG_R);
+
+ /* Set ATDL_CNFG, tuning clk not use for init */
+ val = FIELD_PREP(PHY_ATDL_CNFG_INPSEL_MASK, PHY_ATDL_CNFG_INPSEL_SG2042);
+ sdhci_writeb(host, val, PHY_ATDL_CNFG_R);
+
+ /* Deasset phy reset */
+ val = sdhci_readl(host, PHY_CNFG_R);
+ val |= PHY_CNFG_RSTN_DEASSERT;
+ sdhci_writel(host, val, PHY_CNFG_R);
+}
+
+static void sg2042_sdhci_reset(struct sdhci_host *host, u8 mask)
+{
+ sdhci_reset(host, mask);
+
+ if (mask & SDHCI_RESET_ALL)
+ sg2042_sdhci_phy_init(host);
+}
+
+static int sg2042_init(struct device *dev, struct sdhci_host *host,
+ struct dwcmshc_priv *dwc_priv)
+{
+ static const char * const clk_ids[] = {"timer"};
+
+ return dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv,
+ 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;
+ }
+
+ clk_set_rate(pltfm_host->clk, clock);
+
+ 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) {
+ /* SD 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);
+
+ /* SD 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 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,
.set_uhs_signaling = dwcmshc_set_uhs_signaling,
.get_max_clock = dwcmshc_get_max_clock,
+ .reset = dwcmshc_reset,
+ .adma_write_desc = dwcmshc_adma_write_desc,
+ .irq = dwcmshc_cqe_irq_handler,
+};
+
+#ifdef CONFIG_ACPI
+static void dwcmshc_bf3_hw_reset(struct sdhci_host *host)
+{
+ struct arm_smccc_res res = { 0 };
+
+ arm_smccc_smc(BLUEFIELD_SMC_SET_EMMC_RST_N, 0, 0, 0, 0, 0, 0, 0, &res);
+
+ if (res.a0)
+ pr_err("%s: RST_N failed.\n", mmc_hostname(host->mmc));
+}
+
+static const struct sdhci_ops sdhci_dwcmshc_bf3_ops = {
+ .set_clock = sdhci_set_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .set_uhs_signaling = dwcmshc_set_uhs_signaling,
+ .get_max_clock = dwcmshc_get_max_clock,
.reset = sdhci_reset,
.adma_write_desc = dwcmshc_adma_write_desc,
.irq = dwcmshc_cqe_irq_handler,
+ .hw_reset = dwcmshc_bf3_hw_reset,
};
+#endif
static const struct sdhci_ops sdhci_dwcmshc_rk35xx_ops = {
.set_clock = dwcmshc_rk3568_set_clock,
@@ -908,6 +1683,7 @@ static const struct sdhci_ops sdhci_dwcmshc_rk35xx_ops = {
.get_max_clock = rk35xx_get_max_clock,
.reset = rk35xx_sdhci_reset,
.adma_write_desc = dwcmshc_adma_write_desc,
+ .irq = dwcmshc_cqe_irq_handler,
};
static const struct sdhci_ops sdhci_dwcmshc_th1520_ops = {
@@ -917,7 +1693,7 @@ static const struct sdhci_ops sdhci_dwcmshc_th1520_ops = {
.get_max_clock = dwcmshc_get_max_clock,
.reset = th1520_sdhci_reset,
.adma_write_desc = dwcmshc_adma_write_desc,
- .voltage_switch = dwcmshc_phy_1_8v_init,
+ .voltage_switch = dwcmshc_phy_init,
.platform_execute_tuning = th1520_execute_tuning,
};
@@ -931,39 +1707,117 @@ static const struct sdhci_ops sdhci_dwcmshc_cv18xx_ops = {
.platform_execute_tuning = cv18xx_sdhci_execute_tuning,
};
-static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = {
- .ops = &sdhci_dwcmshc_ops,
- .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
- .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+static const struct sdhci_ops sdhci_dwcmshc_sg2042_ops = {
+ .set_clock = sdhci_set_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .set_uhs_signaling = dwcmshc_set_uhs_signaling,
+ .get_max_clock = dwcmshc_get_max_clock,
+ .reset = sg2042_sdhci_reset,
+ .adma_write_desc = dwcmshc_adma_write_desc,
+ .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,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+ },
};
#ifdef CONFIG_ACPI
-static const struct sdhci_pltfm_data sdhci_dwcmshc_bf3_pdata = {
- .ops = &sdhci_dwcmshc_ops,
- .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
- .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
- SDHCI_QUIRK2_ACMD23_BROKEN,
+static const struct dwcmshc_pltfm_data sdhci_dwcmshc_bf3_pdata = {
+ .pdata = {
+ .ops = &sdhci_dwcmshc_bf3_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_ACMD23_BROKEN,
+ },
};
#endif
-static const struct sdhci_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
- .ops = &sdhci_dwcmshc_rk35xx_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,
+static const struct cqhci_host_ops rk35xx_cqhci_ops = {
+ .pre_enable = rk35xx_sdhci_cqe_pre_enable,
+ .enable = rk35xx_sdhci_cqe_enable,
+ .disable = rk35xx_sdhci_cqe_disable,
+ .post_disable = rk35xx_sdhci_cqe_post_disable,
+ .dumpregs = dwcmshc_cqhci_dumpregs,
+ .set_tran_desc = dwcmshc_set_tran_desc,
+};
+
+static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
+ .pdata = {
+ .ops = &sdhci_dwcmshc_rk35xx_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,
+ },
+ .cqhci_host_ops = &rk35xx_cqhci_ops,
+ .init = dwcmshc_rk35xx_init,
+ .postinit = dwcmshc_rk35xx_postinit,
};
-static const struct sdhci_pltfm_data sdhci_dwcmshc_th1520_pdata = {
- .ops = &sdhci_dwcmshc_th1520_ops,
- .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
- .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk3576_pdata = {
+ .pdata = {
+ .ops = &sdhci_dwcmshc_rk35xx_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,
+ },
+ .cqhci_host_ops = &rk35xx_cqhci_ops,
+ .init = dwcmshc_rk35xx_init,
+ .postinit = dwcmshc_rk3576_postinit,
};
-static const struct sdhci_pltfm_data sdhci_dwcmshc_cv18xx_pdata = {
- .ops = &sdhci_dwcmshc_cv18xx_ops,
- .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
- .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+static const struct dwcmshc_pltfm_data sdhci_dwcmshc_th1520_pdata = {
+ .pdata = {
+ .ops = &sdhci_dwcmshc_th1520_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+ },
+ .init = th1520_init,
+};
+
+static const struct dwcmshc_pltfm_data sdhci_dwcmshc_cv18xx_pdata = {
+ .pdata = {
+ .ops = &sdhci_dwcmshc_cv18xx_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+ },
+};
+
+static const struct dwcmshc_pltfm_data sdhci_dwcmshc_sg2042_pdata = {
+ .pdata = {
+ .ops = &sdhci_dwcmshc_sg2042_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+ },
+ .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 = {
@@ -973,7 +1827,8 @@ static const struct cqhci_host_ops dwcmshc_cqhci_ops = {
.set_tran_desc = dwcmshc_set_tran_desc,
};
-static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev)
+static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev,
+ const struct dwcmshc_pltfm_data *pltfm_data)
{
struct cqhci_host *cq_host;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -1003,7 +1858,10 @@ static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *
}
cq_host->mmio = host->ioaddr + priv->vendor_specific_area2;
- cq_host->ops = &dwcmshc_cqhci_ops;
+ if (pltfm_data->cqhci_host_ops)
+ cq_host->ops = pltfm_data->cqhci_host_ops;
+ else
+ cq_host->ops = &dwcmshc_cqhci_ops;
/* Enable using of 128-bit task descriptors */
dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
@@ -1033,67 +1891,16 @@ dsbl_cqe_caps:
host->mmc->caps2 &= ~(MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD);
}
-static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
-{
- int err;
- struct rk35xx_priv *priv = dwc_priv->priv;
-
- priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc));
- if (IS_ERR(priv->reset)) {
- err = PTR_ERR(priv->reset);
- dev_err(mmc_dev(host->mmc), "failed to get reset control %d\n", err);
- return err;
- }
-
- priv->rockchip_clks[0].id = "axi";
- priv->rockchip_clks[1].id = "block";
- priv->rockchip_clks[2].id = "timer";
- err = devm_clk_bulk_get_optional(mmc_dev(host->mmc), RK35xx_MAX_CLKS,
- priv->rockchip_clks);
- if (err) {
- dev_err(mmc_dev(host->mmc), "failed to get clocks %d\n", err);
- return err;
- }
-
- err = clk_bulk_prepare_enable(RK35xx_MAX_CLKS, priv->rockchip_clks);
- if (err) {
- dev_err(mmc_dev(host->mmc), "failed to enable clocks %d\n", err);
- return err;
- }
-
- if (of_property_read_u8(mmc_dev(host->mmc)->of_node, "rockchip,txclk-tapnum",
- &priv->txclk_tapnum))
- priv->txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT;
-
- /* Disable cmd conflict check */
- sdhci_writel(host, 0x0, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3);
- /* Reset previous settings */
- sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK);
- sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_STRBIN);
-
- return 0;
-}
-
-static void dwcmshc_rk35xx_postinit(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
-{
- /*
- * Don't support highspeed bus mode with low clk speed as we
- * cannot use DLL for this condition.
- */
- if (host->mmc->f_max <= 52000000) {
- dev_info(mmc_dev(host->mmc), "Disabling HS200/HS400, frequency too low (%d)\n",
- host->mmc->f_max);
- host->mmc->caps2 &= ~(MMC_CAP2_HS200 | MMC_CAP2_HS400);
- host->mmc->caps &= ~(MMC_CAP_3_3V_DDR | MMC_CAP_1_8V_DDR);
- }
-}
-
static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
{
.compatible = "rockchip,rk3588-dwcmshc",
.data = &sdhci_dwcmshc_rk35xx_pdata,
},
{
+ .compatible = "rockchip,rk3576-dwcmshc",
+ .data = &sdhci_dwcmshc_rk3576_pdata,
+ },
+ {
.compatible = "rockchip,rk3568-dwcmshc",
.data = &sdhci_dwcmshc_rk35xx_pdata,
},
@@ -1113,6 +1920,14 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
.compatible = "thead,th1520-dwcmshc",
.data = &sdhci_dwcmshc_th1520_pdata,
},
+ {
+ .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);
@@ -1134,8 +1949,7 @@ static int dwcmshc_probe(struct platform_device *pdev)
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_host *host;
struct dwcmshc_priv *priv;
- struct rk35xx_priv *rk_priv = NULL;
- const struct sdhci_pltfm_data *pltfm_data;
+ const struct dwcmshc_pltfm_data *pltfm_data;
int err;
u32 extra, caps;
@@ -1145,7 +1959,7 @@ static int dwcmshc_probe(struct platform_device *pdev)
return -ENODEV;
}
- host = sdhci_pltfm_init(pdev, pltfm_data,
+ host = sdhci_pltfm_init(pdev, &pltfm_data->pdata,
sizeof(struct dwcmshc_priv));
if (IS_ERR(host))
return PTR_ERR(host);
@@ -1163,14 +1977,13 @@ static int dwcmshc_probe(struct platform_device *pdev)
if (dev->of_node) {
pltfm_host->clk = devm_clk_get(dev, "core");
- if (IS_ERR(pltfm_host->clk)) {
- err = PTR_ERR(pltfm_host->clk);
- dev_err(dev, "failed to get core clk: %d\n", err);
- goto free_pltfm;
- }
+ if (IS_ERR(pltfm_host->clk))
+ return dev_err_probe(dev, PTR_ERR(pltfm_host->clk),
+ "failed to get core clk\n");
+
err = clk_prepare_enable(pltfm_host->clk);
if (err)
- goto free_pltfm;
+ return err;
priv->bus_clk = devm_clk_get(dev, "bus");
if (!IS_ERR(priv->bus_clk))
@@ -1190,49 +2003,12 @@ static int dwcmshc_probe(struct platform_device *pdev)
host->mmc_host_ops.hs400_enhanced_strobe = dwcmshc_hs400_enhanced_strobe;
host->mmc_host_ops.execute_tuning = dwcmshc_execute_tuning;
- if (pltfm_data == &sdhci_dwcmshc_rk35xx_pdata) {
- rk_priv = devm_kzalloc(&pdev->dev, sizeof(struct rk35xx_priv), GFP_KERNEL);
- if (!rk_priv) {
- err = -ENOMEM;
- goto err_clk;
- }
-
- if (of_device_is_compatible(pdev->dev.of_node, "rockchip,rk3588-dwcmshc"))
- rk_priv->devtype = DWCMSHC_RK3588;
- else
- rk_priv->devtype = DWCMSHC_RK3568;
-
- priv->priv = rk_priv;
-
- err = dwcmshc_rk35xx_init(host, priv);
+ if (pltfm_data->init) {
+ err = pltfm_data->init(&pdev->dev, host, priv);
if (err)
goto err_clk;
}
- if (pltfm_data == &sdhci_dwcmshc_th1520_pdata) {
- priv->delay_line = PHY_SDCLKDL_DC_DEFAULT;
-
- if (device_property_read_bool(dev, "mmc-ddr-1_8v") ||
- device_property_read_bool(dev, "mmc-hs200-1_8v") ||
- device_property_read_bool(dev, "mmc-hs400-1_8v"))
- priv->flags |= FLAG_IO_FIXED_1V8;
- else
- priv->flags &= ~FLAG_IO_FIXED_1V8;
-
- /*
- * start_signal_voltage_switch() will try 3.3V first
- * then 1.8V. Use SDHCI_SIGNALING_180 rather than
- * SDHCI_SIGNALING_330 to avoid setting voltage to 3.3V
- * in sdhci_start_signal_voltage_switch().
- */
- if (priv->flags & FLAG_IO_FIXED_1V8) {
- host->flags &= ~SDHCI_SIGNALING_330;
- host->flags |= SDHCI_SIGNALING_180;
- }
-
- sdhci_enable_v4_mode(host);
- }
-
#ifdef CONFIG_ACPI
if (pltfm_data == &sdhci_dwcmshc_bf3_pdata)
sdhci_enable_v4_mode(host);
@@ -1257,11 +2033,11 @@ static int dwcmshc_probe(struct platform_device *pdev)
priv->vendor_specific_area2 =
sdhci_readw(host, DWCMSHC_P_VENDOR_AREA2);
- dwcmshc_cqhci_init(host, pdev);
+ dwcmshc_cqhci_init(host, pdev, pltfm_data);
}
- if (rk_priv)
- dwcmshc_rk35xx_postinit(host, priv);
+ if (pltfm_data->postinit)
+ pltfm_data->postinit(host, priv);
err = __sdhci_add_host(host);
if (err)
@@ -1279,11 +2055,7 @@ err_rpm:
err_clk:
clk_disable_unprepare(pltfm_host->clk);
clk_disable_unprepare(priv->bus_clk);
- if (rk_priv)
- clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
- rk_priv->rockchip_clks);
-free_pltfm:
- sdhci_pltfm_free(pdev);
+ clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks);
return err;
}
@@ -1303,7 +2075,6 @@ static void dwcmshc_remove(struct platform_device *pdev)
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
- struct rk35xx_priv *rk_priv = priv->priv;
pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
@@ -1315,19 +2086,14 @@ static void dwcmshc_remove(struct platform_device *pdev)
clk_disable_unprepare(pltfm_host->clk);
clk_disable_unprepare(priv->bus_clk);
- if (rk_priv)
- clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
- rk_priv->rockchip_clks);
- sdhci_pltfm_free(pdev);
+ clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks);
}
-#ifdef CONFIG_PM_SLEEP
static int dwcmshc_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
- struct rk35xx_priv *rk_priv = priv->priv;
int ret;
pm_runtime_resume(dev);
@@ -1346,9 +2112,7 @@ static int dwcmshc_suspend(struct device *dev)
if (!IS_ERR(priv->bus_clk))
clk_disable_unprepare(priv->bus_clk);
- if (rk_priv)
- clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
- rk_priv->rockchip_clks);
+ clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks);
return ret;
}
@@ -1358,7 +2122,6 @@ static int dwcmshc_resume(struct device *dev)
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
- struct rk35xx_priv *rk_priv = priv->priv;
int ret;
ret = clk_prepare_enable(pltfm_host->clk);
@@ -1371,29 +2134,24 @@ static int dwcmshc_resume(struct device *dev)
goto disable_clk;
}
- if (rk_priv) {
- ret = clk_bulk_prepare_enable(RK35xx_MAX_CLKS,
- rk_priv->rockchip_clks);
- if (ret)
- goto disable_bus_clk;
- }
+ ret = clk_bulk_prepare_enable(priv->num_other_clks, priv->other_clks);
+ if (ret)
+ goto disable_bus_clk;
ret = sdhci_resume_host(host);
if (ret)
- goto disable_rockchip_clks;
+ goto disable_other_clks;
if (host->mmc->caps2 & MMC_CAP2_CQE) {
ret = cqhci_resume(host->mmc);
if (ret)
- goto disable_rockchip_clks;
+ goto disable_other_clks;
}
return 0;
-disable_rockchip_clks:
- if (rk_priv)
- clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
- rk_priv->rockchip_clks);
+disable_other_clks:
+ clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks);
disable_bus_clk:
if (!IS_ERR(priv->bus_clk))
clk_disable_unprepare(priv->bus_clk);
@@ -1401,20 +2159,6 @@ disable_clk:
clk_disable_unprepare(pltfm_host->clk);
return ret;
}
-#endif
-
-#ifdef CONFIG_PM
-
-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)
{
@@ -1434,12 +2178,9 @@ static int dwcmshc_runtime_resume(struct device *dev)
return 0;
}
-#endif
-
static const struct dev_pm_ops dwcmshc_pmops = {
- SET_SYSTEM_SLEEP_PM_OPS(dwcmshc_suspend, dwcmshc_resume)
- SET_RUNTIME_PM_OPS(dwcmshc_runtime_suspend,
- dwcmshc_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(dwcmshc_suspend, dwcmshc_resume)
+ RUNTIME_PM_OPS(dwcmshc_runtime_suspend, dwcmshc_runtime_resume, NULL)
};
static struct platform_driver sdhci_dwcmshc_driver = {
@@ -1448,10 +2189,10 @@ static struct platform_driver sdhci_dwcmshc_driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = sdhci_dwcmshc_dt_ids,
.acpi_match_table = ACPI_PTR(sdhci_dwcmshc_acpi_ids),
- .pm = &dwcmshc_pmops,
+ .pm = pm_ptr(&dwcmshc_pmops),
},
.probe = dwcmshc_probe,
- .remove_new = dwcmshc_remove,
+ .remove = dwcmshc_remove,
};
module_platform_driver(sdhci_dwcmshc_driver);