Commit c6f361cb authored by Yifeng Zhao's avatar Yifeng Zhao Committed by Ulf Hansson
Browse files

mmc: sdhci-of-dwcmshc: add support for rk3588



Add support for RK3588's DWCMSHC controller, which is used for
providing the rootfs on the RK3588 evaluation board.

Signed-off-by: default avatarYifeng Zhao <yifeng.zhao@rock-chips.com>
[port from vendor BSP]
Acked-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
Signed-off-by: default avatarSebastian Reichel <sebastian.reichel@collabora.com>
Link: https://lore.kernel.org/r/20220504213251.264819-12-sebastian.reichel@collabora.com


Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent 86e1a8e1
Loading
Loading
Loading
Loading
+103 −18
Original line number Original line Diff line number Diff line
@@ -31,6 +31,7 @@
/* Offset inside the  vendor area 1 */
/* Offset inside the  vendor area 1 */
#define DWCMSHC_HOST_CTRL3		0x8
#define DWCMSHC_HOST_CTRL3		0x8
#define DWCMSHC_EMMC_CONTROL		0x2c
#define DWCMSHC_EMMC_CONTROL		0x2c
#define DWCMSHC_CARD_IS_EMMC		BIT(0)
#define DWCMSHC_ENHANCED_STROBE		BIT(8)
#define DWCMSHC_ENHANCED_STROBE		BIT(8)
#define DWCMSHC_EMMC_ATCTRL		0x40
#define DWCMSHC_EMMC_ATCTRL		0x40


@@ -39,7 +40,7 @@
#define DWCMSHC_EMMC_DLL_RXCLK		0x804
#define DWCMSHC_EMMC_DLL_RXCLK		0x804
#define DWCMSHC_EMMC_DLL_TXCLK		0x808
#define DWCMSHC_EMMC_DLL_TXCLK		0x808
#define DWCMSHC_EMMC_DLL_STRBIN		0x80c
#define DWCMSHC_EMMC_DLL_STRBIN		0x80c
#define DLL_STRBIN_TAPNUM_FROM_SW	BIT(24)
#define DECMSHC_EMMC_DLL_CMDOUT		0x810
#define DWCMSHC_EMMC_DLL_STATUS0	0x840
#define DWCMSHC_EMMC_DLL_STATUS0	0x840
#define DWCMSHC_EMMC_DLL_START		BIT(0)
#define DWCMSHC_EMMC_DLL_START		BIT(0)
#define DWCMSHC_EMMC_DLL_LOCKED		BIT(8)
#define DWCMSHC_EMMC_DLL_LOCKED		BIT(8)
@@ -48,11 +49,21 @@
#define DWCMSHC_EMMC_DLL_START_POINT	16
#define DWCMSHC_EMMC_DLL_START_POINT	16
#define DWCMSHC_EMMC_DLL_INC		8
#define DWCMSHC_EMMC_DLL_INC		8
#define DWCMSHC_EMMC_DLL_DLYENA		BIT(27)
#define DWCMSHC_EMMC_DLL_DLYENA		BIT(27)
#define DLL_TXCLK_TAPNUM_DEFAULT	0x8
#define DLL_TXCLK_TAPNUM_DEFAULT	0x10
#define DLL_STRBIN_TAPNUM_DEFAULT	0x8
#define DLL_TXCLK_TAPNUM_90_DEGREES	0xA
#define DLL_TXCLK_TAPNUM_FROM_SW	BIT(24)
#define DLL_TXCLK_TAPNUM_FROM_SW	BIT(24)
#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_RXCLK_NO_INVERTER		1
#define DLL_RXCLK_NO_INVERTER		1
#define DLL_RXCLK_INVERTER		0
#define DLL_RXCLK_INVERTER		0
#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_LOCK_WO_TMOUT(x) \
#define DLL_LOCK_WO_TMOUT(x) \
	((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \
	((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \
	(((x) & DWCMSHC_EMMC_DLL_TIMEOUT) == 0))
	(((x) & DWCMSHC_EMMC_DLL_TIMEOUT) == 0))
@@ -61,10 +72,16 @@
#define BOUNDARY_OK(addr, len) \
#define BOUNDARY_OK(addr, len) \
	((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1)))
	((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1)))


enum dwcmshc_rk_type {
	DWCMSHC_RK3568,
	DWCMSHC_RK3588,
};

struct rk35xx_priv {
struct rk35xx_priv {
	/* Rockchip specified optional clocks */
	/* Rockchip specified optional clocks */
	struct clk_bulk_data rockchip_clks[RK35xx_MAX_CLKS];
	struct clk_bulk_data rockchip_clks[RK35xx_MAX_CLKS];
	struct reset_control *reset;
	struct reset_control *reset;
	enum dwcmshc_rk_type devtype;
	u8 txclk_tapnum;
	u8 txclk_tapnum;
};
};


@@ -133,7 +150,9 @@ static void dwcmshc_request(struct mmc_host *mmc, struct mmc_request *mrq)
static void dwcmshc_set_uhs_signaling(struct sdhci_host *host,
static void dwcmshc_set_uhs_signaling(struct sdhci_host *host,
				      unsigned int timing)
				      unsigned int timing)
{
{
	u16 ctrl_2;
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
	struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
	u16 ctrl, ctrl_2;


	ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
	ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
	/* Select Bus Speed Mode for host */
	/* Select Bus Speed Mode for host */
@@ -151,8 +170,15 @@ static void dwcmshc_set_uhs_signaling(struct sdhci_host *host,
	else if ((timing == MMC_TIMING_UHS_DDR50) ||
	else if ((timing == MMC_TIMING_UHS_DDR50) ||
		 (timing == MMC_TIMING_MMC_DDR52))
		 (timing == MMC_TIMING_MMC_DDR52))
		ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
		ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
	else if (timing == MMC_TIMING_MMC_HS400)
	else if (timing == MMC_TIMING_MMC_HS400) {
		/* set CARD_IS_EMMC bit to enable Data Strobe for HS400 */
		ctrl = sdhci_readw(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
		ctrl |= DWCMSHC_CARD_IS_EMMC;
		sdhci_writew(host, ctrl, priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);

		ctrl_2 |= DWCMSHC_CTRL_HS400;
		ctrl_2 |= DWCMSHC_CTRL_HS400;
	}

	sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
	sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
}
}


@@ -185,17 +211,11 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock


	host->mmc->actual_clock = 0;
	host->mmc->actual_clock = 0;


	/*
	if (clock == 0) {
	 * DO NOT TOUCH THIS SETTING. RX clk inverter unit is enabled
		/* Disable interface clock at initial state. */
	 * by default, but it shouldn't be enabled. We should anyway
		sdhci_set_clock(host, clock);
	 * disable it before issuing any cmds.
	 */
	extra = DWCMSHC_EMMC_DLL_DLYENA |
		DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL;
	sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK);

	if (clock == 0)
		return;
		return;
	}


	/* Rockchip platform only support 375KHz for identify mode */
	/* Rockchip platform only support 375KHz for identify mode */
	if (clock <= 400000)
	if (clock <= 400000)
@@ -213,9 +233,21 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
	extra &= ~BIT(0);
	extra &= ~BIT(0);
	sdhci_writel(host, extra, reg);
	sdhci_writel(host, extra, reg);


	if (clock <= 400000) {
	if (clock <= 52000000) {
		/* Disable DLL to reset sample clock */
		/* Disable DLL and reset both of sample and drive clock */
		sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL);
		sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL);
		sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_RXCLK);
		sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK);
		sdhci_writel(host, 0, DECMSHC_EMMC_DLL_CMDOUT);
		/*
		 * 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;
		return;
	}
	}


@@ -224,6 +256,15 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
	udelay(1);
	udelay(1);
	sdhci_writel(host, 0x0, DWCMSHC_EMMC_DLL_CTRL);
	sdhci_writel(host, 0x0, DWCMSHC_EMMC_DLL_CTRL);


	/*
	 * We shouldn't set DLL_RXCLK_NO_INVERTER for identify mode but
	 * we must set it in higher speed mode.
	 */
	extra = DWCMSHC_EMMC_DLL_DLYENA;
	if (priv->devtype == DWCMSHC_RK3568)
		extra |= DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL;
	sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK);

	/* Init DLL settings */
	/* Init DLL settings */
	extra = 0x5 << DWCMSHC_EMMC_DLL_START_POINT |
	extra = 0x5 << DWCMSHC_EMMC_DLL_START_POINT |
		0x2 << DWCMSHC_EMMC_DLL_INC |
		0x2 << DWCMSHC_EMMC_DLL_INC |
@@ -246,8 +287,20 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
	    host->mmc->ios.timing == MMC_TIMING_MMC_HS400)
	    host->mmc->ios.timing == MMC_TIMING_MMC_HS400)
		txclk_tapnum = priv->txclk_tapnum;
		txclk_tapnum = priv->txclk_tapnum;


	if ((priv->devtype == DWCMSHC_RK3588) && host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
		txclk_tapnum = DLL_TXCLK_TAPNUM_90_DEGREES;

		extra = DLL_CMDOUT_SRC_CLK_NEG |
			DLL_CMDOUT_EN_SRC_CLK_NEG |
			DWCMSHC_EMMC_DLL_DLYENA |
			DLL_CMDOUT_TAPNUM_90_DEGREES |
			DLL_CMDOUT_TAPNUM_FROM_SW;
		sdhci_writel(host, extra, DECMSHC_EMMC_DLL_CMDOUT);
	}

	extra = DWCMSHC_EMMC_DLL_DLYENA |
	extra = DWCMSHC_EMMC_DLL_DLYENA |
		DLL_TXCLK_TAPNUM_FROM_SW |
		DLL_TXCLK_TAPNUM_FROM_SW |
		DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL |
		txclk_tapnum;
		txclk_tapnum;
	sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_TXCLK);
	sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_TXCLK);


@@ -345,7 +398,25 @@ static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc
	return 0;
	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[] = {
static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
	{
		.compatible = "rockchip,rk3588-dwcmshc",
		.data = &sdhci_dwcmshc_rk35xx_pdata,
	},
	{
	{
		.compatible = "rockchip,rk3568-dwcmshc",
		.compatible = "rockchip,rk3568-dwcmshc",
		.data = &sdhci_dwcmshc_rk35xx_pdata,
		.data = &sdhci_dwcmshc_rk35xx_pdata,
@@ -433,6 +504,11 @@ static int dwcmshc_probe(struct platform_device *pdev)
			goto err_clk;
			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;
		priv->priv = rk_priv;


		err = dwcmshc_rk35xx_init(host, priv);
		err = dwcmshc_rk35xx_init(host, priv);
@@ -442,12 +518,21 @@ static int dwcmshc_probe(struct platform_device *pdev)


	host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
	host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;


	err = sdhci_add_host(host);
	err = sdhci_setup_host(host);
	if (err)
	if (err)
		goto err_clk;
		goto err_clk;


	if (rk_priv)
		dwcmshc_rk35xx_postinit(host, priv);

	err = __sdhci_add_host(host);
	if (err)
		goto err_setup_host;

	return 0;
	return 0;


err_setup_host:
	sdhci_cleanup_host(host);
err_clk:
err_clk:
	clk_disable_unprepare(pltfm_host->clk);
	clk_disable_unprepare(pltfm_host->clk);
	clk_disable_unprepare(priv->bus_clk);
	clk_disable_unprepare(priv->bus_clk);