diff options
Diffstat (limited to 'drivers/mmc/rockchip_sdhci.c')
-rw-r--r-- | drivers/mmc/rockchip_sdhci.c | 117 |
1 files changed, 111 insertions, 6 deletions
diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c index b91df05de4..f3f9d83ba3 100644 --- a/drivers/mmc/rockchip_sdhci.c +++ b/drivers/mmc/rockchip_sdhci.c @@ -22,6 +22,8 @@ #include <asm/arch-rockchip/clock.h> #include <asm/arch-rockchip/hardware.h> +/* DWCMSHC specific Mode Select value */ +#define DWCMSHC_CTRL_HS400 0x7 /* 400KHz is max freq for card ID etc. Use that as min */ #define EMMC_MIN_FREQ 400000 #define KHz (1000) @@ -42,6 +44,17 @@ ((((x) >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK) ==\ PHYCTRL_DLLRDY_DONE) +#define ARASAN_VENDOR_REGISTER 0x78 +#define ARASAN_VENDOR_ENHANCED_STROBE BIT(0) + +/* DWC IP vendor area 1 pointer */ +#define DWCMSHC_P_VENDOR_AREA1 0xe8 +#define DWCMSHC_AREA1_MASK GENMASK(11, 0) +/* Offset inside the vendor area 1 */ +#define DWCMSHC_EMMC_CONTROL 0x2c +#define DWCMSHC_CARD_IS_EMMC BIT(0) +#define DWCMSHC_ENHANCED_STROBE BIT(8) + /* Rockchip specific Registers */ #define DWCMSHC_EMMC_DLL_CTRL 0x800 #define DWCMSHC_EMMC_DLL_CTRL_RESET BIT(1) @@ -57,8 +70,14 @@ #define DWCMSHC_EMMC_DLL_INC_VALUE 2 #define DWCMSHC_EMMC_DLL_INC 8 #define DWCMSHC_EMMC_DLL_DLYENA BIT(27) -#define DLL_TXCLK_TAPNUM_DEFAULT 0x10 -#define DLL_STRBIN_TAPNUM_DEFAULT 0x3 +#define DLL_TXCLK_TAPNUM_DEFAULT 0xA + +#define DLL_STRBIN_TAPNUM_DEFAULT 0x8 +#define DLL_STRBIN_TAPNUM_FROM_SW BIT(24) +#define DLL_STRBIN_DELAY_NUM_SEL BIT(26) +#define DLL_STRBIN_DELAY_NUM_OFFSET 16 +#define DLL_STRBIN_DELAY_NUM_DEFAULT 0x16 + #define DLL_TXCLK_TAPNUM_FROM_SW BIT(24) #define DWCMSHC_EMMC_DLL_LOCKED BIT(8) #define DWCMSHC_EMMC_DLL_TIMEOUT BIT(9) @@ -117,6 +136,19 @@ struct sdhci_data { * Return: 0 if successful, -ve on error */ int (*set_ios_post)(struct sdhci_host *host); + + /** + * set_enhanced_strobe() - Set HS400 Enhanced Strobe config + * + * This is the set_enhanced_strobe() SDHCI operation that should + * be used for the hardware this driver data is associated with. + * Normally, this is used to set any host-specific configuration + * necessary for HS400 ES. + * + * @host: SDHCI host structure + * Return: 0 if successful, -ve on error + */ + int (*set_enhanced_strobe)(struct sdhci_host *host); }; static int rk3399_emmc_phy_init(struct udevice *dev) @@ -206,6 +238,21 @@ static int rk3399_emmc_get_phy(struct udevice *dev) return 0; } +static int rk3399_sdhci_set_enhanced_strobe(struct sdhci_host *host) +{ + struct mmc *mmc = host->mmc; + u32 vendor; + + vendor = sdhci_readl(host, ARASAN_VENDOR_REGISTER); + if (mmc->selected_mode == MMC_HS_400_ES) + vendor |= ARASAN_VENDOR_ENHANCED_STROBE; + else + vendor &= ~ARASAN_VENDOR_ENHANCED_STROBE; + sdhci_writel(host, vendor, ARASAN_VENDOR_REGISTER); + + return 0; +} + static void rk3399_sdhci_set_control_reg(struct sdhci_host *host) { struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); @@ -217,6 +264,15 @@ static void rk3399_sdhci_set_control_reg(struct sdhci_host *host) rk3399_emmc_phy_power_off(priv->phy); sdhci_set_control_reg(host); + + /* + * Reinitializing the device tries to set it to lower-speed modes + * first, which fails if the Enhanced Strobe bit is set, making + * the device impossible to use. Set the correct value here to + * let reinitialization attempts succeed. + */ + if (CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)) + rk3399_sdhci_set_enhanced_strobe(host); }; static int rk3399_sdhci_set_ios_post(struct sdhci_host *host) @@ -287,7 +343,8 @@ static int rk3568_sdhci_emmc_set_clock(struct sdhci_host *host, unsigned int clo sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_TXCLK); extra = DWCMSHC_EMMC_DLL_DLYENA | - DLL_STRBIN_TAPNUM_DEFAULT; + DLL_STRBIN_TAPNUM_DEFAULT | + DLL_STRBIN_TAPNUM_FROM_SW; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); } else { /* reset the clock phase when the frequency is lower than 100MHz */ @@ -295,7 +352,15 @@ static int rk3568_sdhci_emmc_set_clock(struct sdhci_host *host, unsigned int clo extra = DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK); sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK); - sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_STRBIN); + /* + * Before switching to hs400es mode, the driver will enable + * enhanced strobe first. PHY needs to configure the parameters + * of enhanced strobe first. + */ + extra = DWCMSHC_EMMC_DLL_DLYENA | + DLL_STRBIN_DELAY_NUM_SEL | + DLL_STRBIN_DELAY_NUM_DEFAULT << DLL_STRBIN_DELAY_NUM_OFFSET; + sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); } return 0; @@ -306,11 +371,30 @@ static int rk3568_emmc_get_phy(struct udevice *dev) return 0; } +static int rk3568_sdhci_set_enhanced_strobe(struct sdhci_host *host) +{ + struct mmc *mmc = host->mmc; + u32 vendor; + int reg; + + reg = (sdhci_readl(host, DWCMSHC_P_VENDOR_AREA1) & DWCMSHC_AREA1_MASK) + + DWCMSHC_EMMC_CONTROL; + + vendor = sdhci_readl(host, reg); + if (mmc->selected_mode == MMC_HS_400_ES) + vendor |= DWCMSHC_ENHANCED_STROBE; + else + vendor &= ~DWCMSHC_ENHANCED_STROBE; + sdhci_writel(host, vendor, reg); + + return 0; +} + static int rk3568_sdhci_set_ios_post(struct sdhci_host *host) { struct mmc *mmc = host->mmc; uint clock = mmc->tran_speed; - u32 reg; + u32 reg, vendor_reg; if (!clock) clock = mmc->clock; @@ -320,8 +404,15 @@ static int rk3568_sdhci_set_ios_post(struct sdhci_host *host) if (mmc->selected_mode == MMC_HS_400 || mmc->selected_mode == MMC_HS_400_ES) { reg = sdhci_readw(host, SDHCI_HOST_CONTROL2); reg &= ~SDHCI_CTRL_UHS_MASK; - reg |= SDHCI_CTRL_HS400; + reg |= DWCMSHC_CTRL_HS400; sdhci_writew(host, reg, SDHCI_HOST_CONTROL2); + + vendor_reg = (sdhci_readl(host, DWCMSHC_P_VENDOR_AREA1) & DWCMSHC_AREA1_MASK) + + DWCMSHC_EMMC_CONTROL; + /* set CARD_IS_EMMC bit to enable Data Strobe for HS400 */ + reg = sdhci_readw(host, vendor_reg); + reg |= DWCMSHC_CARD_IS_EMMC; + sdhci_writew(host, reg, vendor_reg); } else { sdhci_set_uhs_timing(host); } @@ -409,10 +500,22 @@ static int rockchip_sdhci_execute_tuning(struct mmc *mmc, u8 opcode) return ret; } +static int rockchip_sdhci_set_enhanced_strobe(struct sdhci_host *host) +{ + struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); + struct sdhci_data *data = (struct sdhci_data *)dev_get_driver_data(priv->dev); + + if (data->set_enhanced_strobe) + return data->set_enhanced_strobe(host); + + return -ENOTSUPP; +} + static struct sdhci_ops rockchip_sdhci_ops = { .set_ios_post = rockchip_sdhci_set_ios_post, .platform_execute_tuning = &rockchip_sdhci_execute_tuning, .set_control_reg = rockchip_sdhci_set_control_reg, + .set_enhanced_strobe = rockchip_sdhci_set_enhanced_strobe, }; static int rockchip_sdhci_probe(struct udevice *dev) @@ -495,12 +598,14 @@ static const struct sdhci_data rk3399_data = { .emmc_phy_init = rk3399_emmc_phy_init, .set_control_reg = rk3399_sdhci_set_control_reg, .set_ios_post = rk3399_sdhci_set_ios_post, + .set_enhanced_strobe = rk3399_sdhci_set_enhanced_strobe, }; static const struct sdhci_data rk3568_data = { .get_phy = rk3568_emmc_get_phy, .emmc_phy_init = rk3568_emmc_phy_init, .set_ios_post = rk3568_sdhci_set_ios_post, + .set_enhanced_strobe = rk3568_sdhci_set_enhanced_strobe, }; static const struct udevice_id sdhci_ids[] = { |