[PATCH v1 1/2] mmc: rockchip_sdhci: add phy and clock config for rk3399

Yifeng Zhao yifeng.zhao at rock-chips.com
Mon Jun 7 09:38:14 CEST 2021


Add clock, phy and other configuration, it is convenient to support
new controller. Here a short summary of the changes:
- support HS200 and HS400 config by dts
- Remove OF_PLATDATA related code
- Reorder header inclusion
- Add data width config for 1, 4, 8 bits
- Add phy ops
- add ops set_ios_post to modify the parameters of phy when the
  clock changes

Signed-off-by: Yifeng Zhao <yifeng.zhao at rock-chips.com>
---

 drivers/mmc/rockchip_sdhci.c | 334 +++++++++++++++++++++++++++++++----
 1 file changed, 303 insertions(+), 31 deletions(-)

diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c
index d95f8b2a15..05ed998eca 100644
--- a/drivers/mmc/rockchip_sdhci.c
+++ b/drivers/mmc/rockchip_sdhci.c
@@ -6,33 +6,285 @@
  */
 
 #include <common.h>
+#include <clk.h>
 #include <dm.h>
+#include <dm/ofnode.h>
 #include <dt-structs.h>
+#include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/libfdt.h>
 #include <malloc.h>
 #include <mapmem.h>
 #include <sdhci.h>
-#include <clk.h>
+#include <syscon.h>
 
+#include <asm/arch-rockchip/clock.h>
+#include <asm/arch-rockchip/hardware.h>
 /* 400KHz is max freq for card ID etc. Use that as min */
 #define EMMC_MIN_FREQ	400000
+#define KHz	(1000)
+#define MHz	(1000 * KHz)
+
+#define PHYCTRL_CALDONE_MASK		0x1
+#define PHYCTRL_CALDONE_SHIFT		0x6
+#define PHYCTRL_CALDONE_DONE		0x1
+#define PHYCTRL_DLLRDY_MASK		0x1
+#define PHYCTRL_DLLRDY_SHIFT		0x5
+#define PHYCTRL_DLLRDY_DONE		0x1
+#define PHYCTRL_FREQSEL_200M		0x0
+#define PHYCTRL_FREQSEL_50M		0x1
+#define PHYCTRL_FREQSEL_100M		0x2
+#define PHYCTRL_FREQSEL_150M		0x3
 
 struct rockchip_sdhc_plat {
-#if CONFIG_IS_ENABLED(OF_PLATDATA)
-	struct dtd_rockchip_rk3399_sdhci_5_1 dtplat;
-#endif
 	struct mmc_config cfg;
 	struct mmc mmc;
 };
 
+struct rockchip_emmc_phy {
+	u32 emmcphy_con[7];
+	u32 reserved;
+	u32 emmcphy_status;
+};
+
 struct rockchip_sdhc {
 	struct sdhci_host host;
+	struct udevice *dev;
 	void *base;
+	struct rockchip_emmc_phy *phy;
+	struct clk emmc_clk;
+};
+
+struct sdhci_data {
+	int (*emmc_set_clock)(struct sdhci_host *host, unsigned int clock);
+	int (*emmc_phy_init)(struct udevice *dev);
+	int (*get_phy)(struct udevice *dev);
+};
+
+static int rk3399_emmc_phy_init(struct udevice *dev)
+{
+	return 0;
+}
+
+static void rk3399_emmc_phy_power_on(struct rockchip_emmc_phy *phy, u32 clock)
+{
+	u32 caldone, dllrdy, freqsel;
+	uint start;
+
+	writel(RK_CLRSETBITS(7 << 4, 0), &phy->emmcphy_con[6]);
+	writel(RK_CLRSETBITS(1 << 11, 1 << 11), &phy->emmcphy_con[0]);
+	writel(RK_CLRSETBITS(0xf << 7, 6 << 7), &phy->emmcphy_con[0]);
+
+	/*
+	 * According to the user manual, calpad calibration
+	 * cycle takes more than 2us without the minimal recommended
+	 * value, so we may need a little margin here
+	 */
+	udelay(3);
+	writel(RK_CLRSETBITS(1, 1), &phy->emmcphy_con[6]);
+
+	/*
+	 * According to the user manual, it asks driver to
+	 * wait 5us for calpad busy trimming. But it seems that
+	 * 5us of caldone isn't enough for all cases.
+	 */
+	udelay(500);
+	caldone = readl(&phy->emmcphy_status);
+	caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK;
+	if (caldone != PHYCTRL_CALDONE_DONE) {
+		printf("%s: caldone timeout.\n", __func__);
+		return;
+	}
+
+	/* Set the frequency of the DLL operation */
+	if (clock < 75 * MHz)
+		freqsel = PHYCTRL_FREQSEL_50M;
+	else if (clock < 125 * MHz)
+		freqsel = PHYCTRL_FREQSEL_100M;
+	else if (clock < 175 * MHz)
+		freqsel = PHYCTRL_FREQSEL_150M;
+	else
+		freqsel = PHYCTRL_FREQSEL_200M;
+
+	/* Set the frequency of the DLL operation */
+	writel(RK_CLRSETBITS(3 << 12, freqsel << 12), &phy->emmcphy_con[0]);
+	writel(RK_CLRSETBITS(1 << 1, 1 << 1), &phy->emmcphy_con[6]);
+
+	start = get_timer(0);
+
+	do {
+		udelay(1);
+		dllrdy = readl(&phy->emmcphy_status);
+		dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK;
+		if (dllrdy == PHYCTRL_DLLRDY_DONE)
+			break;
+	} while (get_timer(start) < 50000);
+
+	if (dllrdy != PHYCTRL_DLLRDY_DONE)
+		printf("%s: dllrdy timeout.\n", __func__);
+}
+
+static void rk3399_emmc_phy_power_off(struct rockchip_emmc_phy *phy)
+{
+	writel(RK_CLRSETBITS(1, 0), &phy->emmcphy_con[6]);
+	writel(RK_CLRSETBITS(1 << 1, 0), &phy->emmcphy_con[6]);
+}
+
+static int rockchip_emmc_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+	unsigned int div, clk = 0, timeout;
+	unsigned int input_clk;
+	struct rockchip_sdhc *priv =
+			container_of(host, struct rockchip_sdhc, host);
+
+	/* Wait max 20 ms */
+	timeout = 2000;
+	while (sdhci_readl(host, SDHCI_PRESENT_STATE) &
+			   (SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT)) {
+		if (timeout == 0) {
+			printf("%s: Timeout to wait cmd & data inhibit\n",
+			       __func__);
+			return -EBUSY;
+		}
+
+		timeout--;
+		udelay(10);
+	}
+	sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+
+	if (clock == 0)
+		return 0;
+
+	input_clk = clk_set_rate(&priv->emmc_clk, clock);
+	if (IS_ERR_VALUE(input_clk))
+		input_clk = host->max_clk;
+
+	if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) {
+		/*
+		 * Check if the Host Controller supports Programmable Clock
+		 * Mode.
+		 */
+		if (host->clk_mul) {
+			for (div = 1; div <= 1024; div++) {
+				if ((input_clk / div) <= clock)
+					break;
+			}
+
+			/*
+			 * Set Programmable Clock Mode in the Clock
+			 * Control register.
+			 */
+			clk = SDHCI_PROG_CLOCK_MODE;
+			div--;
+		} else {
+			/* Version 3.00 divisors must be a multiple of 2. */
+			if (input_clk <= clock) {
+				div = 1;
+			} else {
+				for (div = 2;
+				     div < SDHCI_MAX_DIV_SPEC_300;
+				     div += 2) {
+					if ((input_clk / div) <= clock)
+						break;
+				}
+			}
+			div >>= 1;
+		}
+	} else {
+		/* Version 2.00 divisors must be a power of 2. */
+		for (div = 1; div < SDHCI_MAX_DIV_SPEC_200; div *= 2) {
+			if ((input_clk / div) <= clock)
+				break;
+		}
+		div >>= 1;
+	}
+
+	clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
+	clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
+		<< SDHCI_DIVIDER_HI_SHIFT;
+	clk |= SDHCI_CLOCK_INT_EN;
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+	/* Wait max 20 ms */
+	timeout = 2000;
+	while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
+		& SDHCI_CLOCK_INT_STABLE)) {
+		if (timeout == 0) {
+			printf("%s: Internal clock never stabilised.\n",
+			       __func__);
+			return -EBUSY;
+		}
+		timeout--;
+		udelay(10);
+	}
+	clk |= SDHCI_CLOCK_CARD_EN;
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+	host->clock = clock;
+
+	return 0;
+}
+
+static int rk3399_emmc_get_phy(struct udevice *dev)
+{
+	struct rockchip_sdhc *priv = dev_get_priv(dev);
+
+	ofnode phy_node;
+	void *grf_base;
+	u32 grf_phy_offset, phandle;
+
+	phandle = dev_read_u32_default(dev, "phys", 0);
+	phy_node = ofnode_get_by_phandle(phandle);
+	if (!ofnode_valid(phy_node)) {
+		debug("Not found emmc phy device\n");
+		return -ENODEV;
+	}
+
+	grf_base = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
+	if (grf_base < 0)
+		printf("%s Get syscon grf failed", __func__);
+	grf_phy_offset = ofnode_read_u32_default(phy_node, "reg", 0);
+
+	priv->phy = (struct rockchip_emmc_phy *)(grf_base + grf_phy_offset);
+
+	return 0;
+}
+
+static int rk3399_sdhci_emmc_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+	struct rockchip_sdhc *priv =
+			container_of(host, struct rockchip_sdhc, host);
+	int cycle_phy = host->clock != clock &&
+			clock > EMMC_MIN_FREQ;
+
+	if (cycle_phy)
+		rk3399_emmc_phy_power_off(priv->phy);
+
+	rockchip_emmc_set_clock(host, clock);
+
+	if (cycle_phy)
+		rk3399_emmc_phy_power_on(priv->phy, clock);
+
+	return 0;
+}
+
+static int sdhci_set_ios_post(struct sdhci_host *host)
+{
+	struct mmc *mmc = host->mmc;
+	struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host);
+	struct sdhci_data *data = (struct sdhci_data *)dev_get_driver_data(priv->dev);
+
+	data->emmc_set_clock(host, mmc->clock);
+
+	return 0;
+}
+
+static struct sdhci_ops rockchip_sdhci_ops = {
+	.set_ios_post	= sdhci_set_ios_post,
 };
 
-static int arasan_sdhci_probe(struct udevice *dev)
+static int rockchip_sdhci_probe(struct udevice *dev)
 {
+	struct sdhci_data *data = (struct sdhci_data *)dev_get_driver_data(dev);
 	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
 	struct rockchip_sdhc_plat *plat = dev_get_plat(dev);
 	struct rockchip_sdhc *prv = dev_get_priv(dev);
@@ -40,17 +292,22 @@ static int arasan_sdhci_probe(struct udevice *dev)
 	int max_frequency, ret;
 	struct clk clk;
 
-#if CONFIG_IS_ENABLED(OF_PLATDATA)
-	struct dtd_rockchip_rk3399_sdhci_5_1 *dtplat = &plat->dtplat;
-
-	host->name = dev->name;
-	host->ioaddr = map_sysmem(dtplat->reg[0], dtplat->reg[1]);
-	max_frequency = dtplat->max_frequency;
-	ret = clk_get_by_driver_info(dev, dtplat->clocks, &clk);
-#else
 	max_frequency = dev_read_u32_default(dev, "max-frequency", 0);
+	switch (dev_read_u32_default(dev, "bus-width", 4)) {
+	case 8:
+		host->host_caps |= MMC_MODE_8BIT;
+		break;
+	case 4:
+		host->host_caps |= MMC_MODE_4BIT;
+		break;
+	case 1:
+		break;
+	default:
+		printf("Invalid \"bus-width\" value\n");
+		return -EINVAL;
+	}
 	ret = clk_get_by_index(dev, 0, &clk);
-#endif
+
 	if (!ret) {
 		ret = clk_set_rate(&clk, max_frequency);
 		if (IS_ERR_VALUE(ret))
@@ -59,16 +316,25 @@ static int arasan_sdhci_probe(struct udevice *dev)
 		printf("%s fail to get clk\n", __func__);
 	}
 
+	prv->emmc_clk = clk;
+	prv->dev = dev;
+	ret = data->get_phy(dev);
+	if (ret)
+		return ret;
+
+	ret = data->emmc_phy_init(dev);
+	if (ret)
+		return ret;
+
+	host->ops = &rockchip_sdhci_ops;
+
 	host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD;
 	host->max_clk = max_frequency;
-	/*
-	 * The sdhci-driver only supports 4bit and 8bit, as sdhci_setup_cfg
-	 * doesn't allow us to clear MMC_MODE_4BIT.  Consequently, we don't
-	 * check for other bus-width values.
-	 */
-	if (host->bus_width == 8)
-		host->host_caps |= MMC_MODE_8BIT;
 
+	if (dev_read_bool(dev, "mmc-hs200-1_8v"))
+		host->host_caps |= MMC_MODE_HS200;
+	else if (dev_read_bool(dev, "mmc-hs400-1_8v"))
+		host->host_caps |= MMC_MODE_HS400;
 	host->mmc = &plat->mmc;
 	host->mmc->priv = &prv->host;
 	host->mmc->dev = dev;
@@ -81,15 +347,12 @@ static int arasan_sdhci_probe(struct udevice *dev)
 	return sdhci_probe(dev);
 }
 
-static int arasan_sdhci_of_to_plat(struct udevice *dev)
+static int sdhci_of_to_plat(struct udevice *dev)
 {
-#if !CONFIG_IS_ENABLED(OF_PLATDATA)
 	struct sdhci_host *host = dev_get_priv(dev);
 
 	host->name = dev->name;
 	host->ioaddr = dev_read_addr_ptr(dev);
-	host->bus_width = dev_read_u32_default(dev, "bus-width", 4);
-#endif
 
 	return 0;
 }
@@ -101,19 +364,28 @@ static int rockchip_sdhci_bind(struct udevice *dev)
 	return sdhci_bind(dev, &plat->mmc, &plat->cfg);
 }
 
-static const struct udevice_id arasan_sdhci_ids[] = {
-	{ .compatible = "arasan,sdhci-5.1" },
+static const struct sdhci_data rk3399_data = {
+	.emmc_set_clock = rk3399_sdhci_emmc_set_clock,
+	.get_phy = rk3399_emmc_get_phy,
+	.emmc_phy_init = rk3399_emmc_phy_init,
+};
+
+static const struct udevice_id sdhci_ids[] = {
+	{
+		.compatible = "arasan,sdhci-5.1",
+		.data = (ulong)&rk3399_data,
+	},
 	{ }
 };
 
 U_BOOT_DRIVER(arasan_sdhci_drv) = {
-	.name		= "rockchip_rk3399_sdhci_5_1",
+	.name		= "rockchip_sdhci_5_1",
 	.id		= UCLASS_MMC,
-	.of_match	= arasan_sdhci_ids,
-	.of_to_plat = arasan_sdhci_of_to_plat,
+	.of_match	= sdhci_ids,
+	.of_to_plat	= sdhci_of_to_plat,
 	.ops		= &sdhci_ops,
 	.bind		= rockchip_sdhci_bind,
-	.probe		= arasan_sdhci_probe,
+	.probe		= rockchip_sdhci_probe,
 	.priv_auto	= sizeof(struct rockchip_sdhc),
 	.plat_auto	= sizeof(struct rockchip_sdhc_plat),
 };
-- 
2.17.1





More information about the U-Boot mailing list