Add support for RK3588 to the rockchip sdhci driver. Use driver data to handle differences between RK3568 and RK3588:
- Set "Receive original clock source is auto gating" for RK3588. - Set "Receive clock source is no-inverted" only on RK3568 and "Transmit clock source is invertion of original clock input" for RK3588. - Use different txclk_tapnum for HS400 modes on RK3588. - Configure the CMDOUT reg for HS400 modes for RK3588. This is based on the mainline linux and vendor kernel driver and have successfully been tested with rock5b-rk3588_defconfig and CONFIG_MMC_HS200_SUPPORT=y CONFIG_MMC_HS400_SUPPORT=y CONFIG_MMC_HS400_ES_SUPPORT=y CONFIG_MMC_SPEED_MODE_SET=y using the following command to switch mode and then read 512 MiB of data from eMMC into memory, => mmc dev 0 0 <mode> && mmc info && mmc read 10000000 2000 10000 for each of the modes below. 0 = MMC legacy 1 = MMC High Speed (26MHz) 3 = MMC High Speed (52MHz) 4 = MMC DDR52 (52MHz) 10 = HS200 (200MHz) 11 = HS400 (200MHz) 12 = HS400ES (200MHz) Signed-off-by: Yifeng Zhao <yifeng.z...@rock-chips.com> Signed-off-by: Jonas Karlman <jo...@kwiboo.se> Reviewed-by: Shawn Lin <shawn....@rock-chips.com> --- v2: - Add Signed-off-by tag - Update commit message - Rename quirks to flags - Save HS200/HS400 txclk_tapnum as driver data - Remove use of rockchip,txclk-tapnum prop - Collect r-b tag drivers/mmc/rockchip_sdhci.c | 57 +++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c index 12a616d3dfb8..2857dcc9ec4f 100644 --- a/drivers/mmc/rockchip_sdhci.c +++ b/drivers/mmc/rockchip_sdhci.c @@ -56,6 +56,7 @@ #define DWCMSHC_EMMC_DLL_RXCLK 0x804 #define DWCMSHC_EMMC_DLL_TXCLK 0x808 #define DWCMSHC_EMMC_DLL_STRBIN 0x80c +#define DWCMSHC_EMMC_DLL_CMDOUT 0x810 #define DWCMSHC_EMMC_DLL_STATUS0 0x840 #define DWCMSHC_EMMC_DLL_STATUS1 0x844 #define DWCMSHC_EMMC_DLL_START BIT(0) @@ -70,18 +71,27 @@ #define DLL_RXCLK_NO_INVERTER BIT(29) #define DLL_RXCLK_ORI_GATE BIT(31) #define DLL_TXCLK_TAPNUM_DEFAULT 0x10 +#define DLL_TXCLK_TAPNUM_90_DEGREES 0x9 #define DLL_TXCLK_TAPNUM_FROM_SW BIT(24) +#define DLL_TXCLK_NO_INVERTER BIT(29) #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 #define DLL_STRBIN_DELAY_NUM_DEFAULT 0x10 +#define DLL_CMDOUT_TAPNUM_90_DEGREES 0x8 +#define DLL_CMDOUT_TAPNUM_FROM_SW BIT(24) +#define DLL_CMDOUT_SRC_CLK_NEG BIT(28) +#define DLL_CMDOUT_EN_SRC_CLK_NEG BIT(29) +#define DLL_CMDOUT_BOTH_CLK_EDGE BIT(30) #define DLL_LOCK_WO_TMOUT(x) \ ((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \ (((x) & DWCMSHC_EMMC_DLL_TIMEOUT) == 0)) #define ROCKCHIP_MAX_CLKS 3 +#define FLAG_INVERTER_FLAG_IN_RXCLK BIT(0) + struct rockchip_sdhc_plat { struct mmc_config cfg; struct mmc mmc; @@ -144,6 +154,10 @@ struct sdhci_data { * Return: 0 if successful, -ve on error */ int (*set_enhanced_strobe)(struct sdhci_host *host); + + u32 flags; + u8 hs200_txclk_tapnum; + u8 hs400_txclk_tapnum; }; static void rk3399_emmc_phy_power_on(struct rockchip_emmc_phy *phy, u32 clock) @@ -294,8 +308,11 @@ static void rk3568_sdhci_set_clock(struct sdhci_host *host, u32 div) static int rk3568_sdhci_config_dll(struct sdhci_host *host, u32 clock, bool enable) { + struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); + struct sdhci_data *data = (struct sdhci_data *)dev_get_driver_data(priv->dev); + struct mmc *mmc = host->mmc; int val, ret; - u32 extra; + u32 extra, txclk_tapnum; if (!enable) return 0; @@ -318,12 +335,28 @@ static int rk3568_sdhci_config_dll(struct sdhci_host *host, u32 clock, bool enab if (ret) return ret; - extra = DWCMSHC_EMMC_DLL_DLYENA | DLL_RXCLK_NO_INVERTER; + extra = DWCMSHC_EMMC_DLL_DLYENA | DLL_RXCLK_ORI_GATE; + if (data->flags & FLAG_INVERTER_FLAG_IN_RXCLK) + extra |= DLL_RXCLK_NO_INVERTER; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK); + txclk_tapnum = data->hs200_txclk_tapnum; + if (mmc->selected_mode == MMC_HS_400 || + mmc->selected_mode == MMC_HS_400_ES) { + txclk_tapnum = data->hs400_txclk_tapnum; + + extra = DLL_CMDOUT_SRC_CLK_NEG | + DLL_CMDOUT_BOTH_CLK_EDGE | + DWCMSHC_EMMC_DLL_DLYENA | + DLL_CMDOUT_TAPNUM_90_DEGREES | + DLL_CMDOUT_TAPNUM_FROM_SW; + sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_CMDOUT); + } + extra = DWCMSHC_EMMC_DLL_DLYENA | - DLL_TXCLK_TAPNUM_DEFAULT | - DLL_TXCLK_TAPNUM_FROM_SW; + DLL_TXCLK_TAPNUM_FROM_SW | + DLL_TXCLK_NO_INVERTER | + txclk_tapnum; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_TXCLK); extra = DWCMSHC_EMMC_DLL_DLYENA | @@ -339,6 +372,7 @@ static int rk3568_sdhci_config_dll(struct sdhci_host *host, u32 clock, bool enab sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_CTRL); sdhci_writel(host, DLL_RXCLK_ORI_GATE, DWCMSHC_EMMC_DLL_RXCLK); sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK); + sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CMDOUT); /* * Before switching to hs400es mode, the driver will enable * enhanced strobe first. PHY needs to configure the parameters @@ -594,6 +628,17 @@ static const struct sdhci_data rk3568_data = { .set_ios_post = rk3568_sdhci_set_ios_post, .set_clock = rk3568_sdhci_set_clock, .config_dll = rk3568_sdhci_config_dll, + .flags = FLAG_INVERTER_FLAG_IN_RXCLK, + .hs200_txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT, + .hs400_txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT, +}; + +static const struct sdhci_data rk3588_data = { + .set_ios_post = rk3568_sdhci_set_ios_post, + .set_clock = rk3568_sdhci_set_clock, + .config_dll = rk3568_sdhci_config_dll, + .hs200_txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT, + .hs400_txclk_tapnum = DLL_TXCLK_TAPNUM_90_DEGREES, }; static const struct udevice_id sdhci_ids[] = { @@ -605,6 +650,10 @@ static const struct udevice_id sdhci_ids[] = { .compatible = "rockchip,rk3568-dwcmshc", .data = (ulong)&rk3568_data, }, + { + .compatible = "rockchip,rk3588-dwcmshc", + .data = (ulong)&rk3588_data, + }, { } }; -- 2.40.0