xref: /rk3399_rockchip-uboot/drivers/mmc/rockchip_dw_mmc.c (revision 1a0c3c4ddd5c97ff2f7160660c9ef4d7cb7fbe92)
1a8cb4fb5SSimon Glass /*
2a8cb4fb5SSimon Glass  * Copyright (c) 2013 Google, Inc
3a8cb4fb5SSimon Glass  *
4a8cb4fb5SSimon Glass  * SPDX-License-Identifier:	GPL-2.0+
5a8cb4fb5SSimon Glass  */
6a8cb4fb5SSimon Glass 
7a8cb4fb5SSimon Glass #include <common.h>
8a8cb4fb5SSimon Glass #include <clk.h>
9a8cb4fb5SSimon Glass #include <dm.h>
10bfeb443eSSimon Glass #include <dt-structs.h>
11a8cb4fb5SSimon Glass #include <dwmmc.h>
12a8cb4fb5SSimon Glass #include <errno.h>
13bfeb443eSSimon Glass #include <mapmem.h>
14e1efec4eSSimon Glass #include <pwrseq.h>
15a8cb4fb5SSimon Glass #include <syscon.h>
16e1efec4eSSimon Glass #include <asm/gpio.h>
17a8cb4fb5SSimon Glass #include <asm/arch/clock.h>
18a8cb4fb5SSimon Glass #include <asm/arch/periph.h>
19a8cb4fb5SSimon Glass #include <linux/err.h>
20a8cb4fb5SSimon Glass 
21a8cb4fb5SSimon Glass DECLARE_GLOBAL_DATA_PTR;
22a8cb4fb5SSimon Glass 
23f6e41d17SSimon Glass struct rockchip_mmc_plat {
24bfeb443eSSimon Glass #if CONFIG_IS_ENABLED(OF_PLATDATA)
25bfeb443eSSimon Glass 	struct dtd_rockchip_rk3288_dw_mshc dtplat;
26bfeb443eSSimon Glass #endif
27f6e41d17SSimon Glass 	struct mmc_config cfg;
28f6e41d17SSimon Glass 	struct mmc mmc;
29f6e41d17SSimon Glass };
30f6e41d17SSimon Glass 
31a8cb4fb5SSimon Glass struct rockchip_dwmmc_priv {
32135aa950SStephen Warren 	struct clk clk;
334455fdd9SZiyuan Xu 	struct clk sample_clk;
34a8cb4fb5SSimon Glass 	struct dwmci_host host;
356809b04fSSimon Glass 	int fifo_depth;
366809b04fSSimon Glass 	bool fifo_mode;
376809b04fSSimon Glass 	u32 minmax[2];
38a8cb4fb5SSimon Glass };
39a8cb4fb5SSimon Glass 
40*1a0c3c4dSJason Zhu #ifdef CONFIG_USING_KERNEL_DTB
41*1a0c3c4dSJason Zhu int board_mmc_dm_reinit(struct udevice *dev)
42*1a0c3c4dSJason Zhu {
43*1a0c3c4dSJason Zhu 	struct rockchip_dwmmc_priv *priv = dev_get_priv(dev);
44*1a0c3c4dSJason Zhu 
45*1a0c3c4dSJason Zhu 	if (!priv || !&priv->clk)
46*1a0c3c4dSJason Zhu 		return 0;
47*1a0c3c4dSJason Zhu 
48*1a0c3c4dSJason Zhu 	if (!memcmp(dev->name, "dwmmc", strlen("dwmmc")))
49*1a0c3c4dSJason Zhu 		return clk_get_by_index(dev, 0, &priv->clk);
50*1a0c3c4dSJason Zhu 	else
51*1a0c3c4dSJason Zhu 		return 0;
52*1a0c3c4dSJason Zhu }
53*1a0c3c4dSJason Zhu #endif
54*1a0c3c4dSJason Zhu 
55ace0ade6SJason Zhu #ifdef CONFIG_SPL_BUILD
56ace0ade6SJason Zhu __weak void mmc_gpio_init_direct(void) {}
57ace0ade6SJason Zhu #endif
58ace0ade6SJason Zhu 
59a8cb4fb5SSimon Glass static uint rockchip_dwmmc_get_mmc_clk(struct dwmci_host *host, uint freq)
60a8cb4fb5SSimon Glass {
61a8cb4fb5SSimon Glass 	struct udevice *dev = host->priv;
62a8cb4fb5SSimon Glass 	struct rockchip_dwmmc_priv *priv = dev_get_priv(dev);
63a8cb4fb5SSimon Glass 	int ret;
64a8cb4fb5SSimon Glass 
6524527ef9SZiyuan Xu 	/*
6624527ef9SZiyuan Xu 	 * If DDR52 8bit mode(only emmc work in 8bit mode),
6724527ef9SZiyuan Xu 	 * divider must be set 1
6824527ef9SZiyuan Xu 	 */
6924527ef9SZiyuan Xu 	if (mmc_card_ddr52(host->mmc) && host->mmc->bus_width == 8)
7024527ef9SZiyuan Xu 		freq *= 2;
7124527ef9SZiyuan Xu 
72135aa950SStephen Warren 	ret = clk_set_rate(&priv->clk, freq);
73a8cb4fb5SSimon Glass 	if (ret < 0) {
74419b0801SKever Yang 		debug("%s: err=%d\n", __func__, ret);
75a8cb4fb5SSimon Glass 		return ret;
76a8cb4fb5SSimon Glass 	}
77a8cb4fb5SSimon Glass 
78a8cb4fb5SSimon Glass 	return freq;
79a8cb4fb5SSimon Glass }
80a8cb4fb5SSimon Glass 
81a8cb4fb5SSimon Glass static int rockchip_dwmmc_ofdata_to_platdata(struct udevice *dev)
82a8cb4fb5SSimon Glass {
83bfeb443eSSimon Glass #if !CONFIG_IS_ENABLED(OF_PLATDATA)
84a8cb4fb5SSimon Glass 	struct rockchip_dwmmc_priv *priv = dev_get_priv(dev);
85a8cb4fb5SSimon Glass 	struct dwmci_host *host = &priv->host;
86a8cb4fb5SSimon Glass 
87a8cb4fb5SSimon Glass 	host->name = dev->name;
881c23e4e1SPhilipp Tomsich 	host->ioaddr = dev_read_addr_ptr(dev);
89fd1bf8dfSPhilipp Tomsich 	host->buswidth = dev_read_u32_default(dev, "bus-width", 4);
90a8cb4fb5SSimon Glass 	host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk;
91a8cb4fb5SSimon Glass 	host->priv = dev;
92a8cb4fb5SSimon Glass 
93ace2198bShuang lin 	/* use non-removeable as sdcard and emmc as judgement */
94fd1bf8dfSPhilipp Tomsich 	if (dev_read_bool(dev, "non-removable"))
956579385bShuang lin 		host->dev_index = 0;
966579385bShuang lin 	else
97ace2198bShuang lin 		host->dev_index = 1;
98a8cb4fb5SSimon Glass 
99fd1bf8dfSPhilipp Tomsich 	priv->fifo_depth = dev_read_u32_default(dev, "fifo-depth", 0);
100fd1bf8dfSPhilipp Tomsich 
1016809b04fSSimon Glass 	if (priv->fifo_depth < 0)
1026809b04fSSimon Glass 		return -EINVAL;
103fd1bf8dfSPhilipp Tomsich 	priv->fifo_mode = dev_read_bool(dev, "fifo-mode");
104ff71f9acSPhilipp Tomsich 
105ff71f9acSPhilipp Tomsich 	/*
106ff71f9acSPhilipp Tomsich 	 * 'clock-freq-min-max' is deprecated
107ff71f9acSPhilipp Tomsich 	 * (see https://github.com/torvalds/linux/commit/b023030f10573de738bbe8df63d43acab64c9f7b)
108ff71f9acSPhilipp Tomsich 	 */
109fd1bf8dfSPhilipp Tomsich 	if (dev_read_u32_array(dev, "clock-freq-min-max", priv->minmax, 2)) {
110fd1bf8dfSPhilipp Tomsich 		int val = dev_read_u32_default(dev, "max-frequency", -EINVAL);
111ff71f9acSPhilipp Tomsich 
112ff71f9acSPhilipp Tomsich 		if (val < 0)
113ff71f9acSPhilipp Tomsich 			return val;
114ff71f9acSPhilipp Tomsich 
115ff71f9acSPhilipp Tomsich 		priv->minmax[0] = 400000;  /* 400 kHz */
116ff71f9acSPhilipp Tomsich 		priv->minmax[1] = val;
117ff71f9acSPhilipp Tomsich 	} else {
118ff71f9acSPhilipp Tomsich 		debug("%s: 'clock-freq-min-max' property was deprecated.\n",
119ff71f9acSPhilipp Tomsich 		      __func__);
120ff71f9acSPhilipp Tomsich 	}
121bfeb443eSSimon Glass #endif
122a8cb4fb5SSimon Glass 	return 0;
123a8cb4fb5SSimon Glass }
124a8cb4fb5SSimon Glass 
1254455fdd9SZiyuan Xu static int rockchip_dwmmc_execute_tuning(struct dwmci_host *host, u32 opcode)
1264455fdd9SZiyuan Xu {
1274a6b8656SJason Zhu 	int i = 0;
1284a6b8656SJason Zhu 	int ret = -1;
1294a6b8656SJason Zhu 	struct mmc *mmc = host->mmc;
1304455fdd9SZiyuan Xu 	struct udevice *dev = host->priv;
1314455fdd9SZiyuan Xu 	struct rockchip_dwmmc_priv *priv = dev_get_priv(dev);
1324455fdd9SZiyuan Xu 
1334455fdd9SZiyuan Xu 	if (IS_ERR(&priv->sample_clk))
1344455fdd9SZiyuan Xu 		return -EIO;
1354455fdd9SZiyuan Xu 
1364a6b8656SJason Zhu 	if (mmc->default_phase > 0 && mmc->default_phase < 360) {
1374a6b8656SJason Zhu 		ret = clk_set_phase(&priv->sample_clk, mmc->default_phase);
1384a6b8656SJason Zhu 		if (ret)
1394a6b8656SJason Zhu 			printf("set clk phase fail\n");
1404a6b8656SJason Zhu 		else
1414a6b8656SJason Zhu 			ret = mmc_send_tuning(mmc, opcode);
1424a6b8656SJason Zhu 		mmc->default_phase = 0;
1434455fdd9SZiyuan Xu 	}
1444455fdd9SZiyuan Xu 	/*
1454a6b8656SJason Zhu 	 * If use default_phase to tune successfully, return.
1464a6b8656SJason Zhu 	 * Otherwise, use the othe phase to tune.
1474455fdd9SZiyuan Xu 	 */
1484a6b8656SJason Zhu 	if (!ret)
1494a6b8656SJason Zhu 		return ret;
1504455fdd9SZiyuan Xu 
1514a6b8656SJason Zhu 	for (i = 0; i < 5; i++) {
1524a6b8656SJason Zhu 		/* mmc->init_retry must be 0, 1, 2, 3 */
1534a6b8656SJason Zhu 		if (mmc->init_retry == 4)
1544a6b8656SJason Zhu 			mmc->init_retry = 0;
1554a6b8656SJason Zhu 
1564a6b8656SJason Zhu 		ret = clk_set_phase(&priv->sample_clk, 90 * mmc->init_retry);
1574a6b8656SJason Zhu 		if (ret) {
1584a6b8656SJason Zhu 			printf("set clk phase fail\n");
1594a6b8656SJason Zhu 			break;
1604a6b8656SJason Zhu 		}
1614a6b8656SJason Zhu 		ret = mmc_send_tuning(mmc, opcode);
1624a6b8656SJason Zhu 		debug("Tuning phase is %d, ret is %d\n", mmc->init_retry * 90, ret);
1634a6b8656SJason Zhu 		mmc->init_retry++;
1644a6b8656SJason Zhu 		if (!ret)
1654a6b8656SJason Zhu 			break;
1664455fdd9SZiyuan Xu 	}
1674455fdd9SZiyuan Xu 
1684455fdd9SZiyuan Xu 	return ret;
1694455fdd9SZiyuan Xu }
1704455fdd9SZiyuan Xu 
171a8cb4fb5SSimon Glass static int rockchip_dwmmc_probe(struct udevice *dev)
172a8cb4fb5SSimon Glass {
173f6e41d17SSimon Glass 	struct rockchip_mmc_plat *plat = dev_get_platdata(dev);
174a8cb4fb5SSimon Glass 	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
175a8cb4fb5SSimon Glass 	struct rockchip_dwmmc_priv *priv = dev_get_priv(dev);
176a8cb4fb5SSimon Glass 	struct dwmci_host *host = &priv->host;
177e1efec4eSSimon Glass 	struct udevice *pwr_dev __maybe_unused;
178a8cb4fb5SSimon Glass 	int ret;
179a8cb4fb5SSimon Glass 
180ace0ade6SJason Zhu #ifdef CONFIG_SPL_BUILD
181ace0ade6SJason Zhu 	mmc_gpio_init_direct();
182ace0ade6SJason Zhu #endif
183bfeb443eSSimon Glass #if CONFIG_IS_ENABLED(OF_PLATDATA)
184bfeb443eSSimon Glass 	struct dtd_rockchip_rk3288_dw_mshc *dtplat = &plat->dtplat;
185bfeb443eSSimon Glass 
186bfeb443eSSimon Glass 	host->name = dev->name;
187bfeb443eSSimon Glass 	host->ioaddr = map_sysmem(dtplat->reg[0], dtplat->reg[1]);
188bfeb443eSSimon Glass 	host->buswidth = dtplat->bus_width;
189bfeb443eSSimon Glass 	host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk;
1904455fdd9SZiyuan Xu 	host->execute_tuning = rockchip_dwmmc_execute_tuning;
191bfeb443eSSimon Glass 	host->priv = dev;
192bfeb443eSSimon Glass 	host->dev_index = 0;
193bfeb443eSSimon Glass 	priv->fifo_depth = dtplat->fifo_depth;
194bfeb443eSSimon Glass 	priv->fifo_mode = 0;
19580935298SKever Yang 	priv->minmax[0] = 400000;  /*  400 kHz */
19680935298SKever Yang 	priv->minmax[1] = dtplat->max_frequency;
197bfeb443eSSimon Glass 
198bfeb443eSSimon Glass 	ret = clk_get_by_index_platdata(dev, 0, dtplat->clocks, &priv->clk);
199bfeb443eSSimon Glass 	if (ret < 0)
200bfeb443eSSimon Glass 		return ret;
201bfeb443eSSimon Glass #else
202419b0801SKever Yang 	ret = clk_get_by_index(dev, 0, &priv->clk);
203898d6439SSimon Glass 	if (ret < 0)
204a8cb4fb5SSimon Glass 		return ret;
20508c9dc10SJason Zhu 
20608c9dc10SJason Zhu 	ret = clk_get_by_name(dev, "ciu-sample", &priv->sample_clk);
20708c9dc10SJason Zhu 	if (ret < 0)
2081e72694fSKever Yang 		debug("MMC: sample clock not found, not support hs200!\n");
2094455fdd9SZiyuan Xu 	host->execute_tuning = rockchip_dwmmc_execute_tuning;
210bfeb443eSSimon Glass #endif
2115ef89808SJason Zhu 	host->fifoth_val = MSIZE(DWMCI_MSIZE) |
2126809b04fSSimon Glass 		RX_WMARK(priv->fifo_depth / 2 - 1) |
2136809b04fSSimon Glass 		TX_WMARK(priv->fifo_depth / 2);
21428637248Shuang lin 
2156809b04fSSimon Glass 	host->fifo_mode = priv->fifo_mode;
21628637248Shuang lin 
217bda599f7SShawn Lin #ifdef CONFIG_ROCKCHIP_RK3128
218bda599f7SShawn Lin 	host->stride_pio = true;
219bda599f7SShawn Lin #else
220bda599f7SShawn Lin 	host->stride_pio = false;
221bda599f7SShawn Lin #endif
222bda599f7SShawn Lin 
223e1efec4eSSimon Glass #ifdef CONFIG_PWRSEQ
224e1efec4eSSimon Glass 	/* Enable power if needed */
225e1efec4eSSimon Glass 	ret = uclass_get_device_by_phandle(UCLASS_PWRSEQ, dev, "mmc-pwrseq",
226e1efec4eSSimon Glass 					   &pwr_dev);
227e1efec4eSSimon Glass 	if (!ret) {
228e1efec4eSSimon Glass 		ret = pwrseq_set_power(pwr_dev, true);
229e1efec4eSSimon Glass 		if (ret)
230e1efec4eSSimon Glass 			return ret;
231e1efec4eSSimon Glass 	}
232e1efec4eSSimon Glass #endif
233e5113c33SJaehoon Chung 	dwmci_setup_cfg(&plat->cfg, host, priv->minmax[1], priv->minmax[0]);
23416cc62c8SJason Zhu 	if (dev_read_bool(dev, "mmc-hs200-1_8v"))
23516cc62c8SJason Zhu 		plat->cfg.host_caps |= MMC_MODE_HS200;
23609494d3aSJason Zhu 	plat->mmc.default_phase =
23709494d3aSJason Zhu 		dev_read_u32_default(dev, "default-sample-phase", 0);
238e860ec32SJason Zhu 	plat->mmc.init_retry = 0;
239f6e41d17SSimon Glass 	host->mmc = &plat->mmc;
240f6e41d17SSimon Glass 	host->mmc->priv = &priv->host;
241cffe5d86SSimon Glass 	host->mmc->dev = dev;
242a8cb4fb5SSimon Glass 	upriv->mmc = host->mmc;
243a8cb4fb5SSimon Glass 
24442b37d8dSSimon Glass 	return dwmci_probe(dev);
245a8cb4fb5SSimon Glass }
246a8cb4fb5SSimon Glass 
247f6e41d17SSimon Glass static int rockchip_dwmmc_bind(struct udevice *dev)
248f6e41d17SSimon Glass {
249f6e41d17SSimon Glass 	struct rockchip_mmc_plat *plat = dev_get_platdata(dev);
250f6e41d17SSimon Glass 
25124f5aec3SMasahiro Yamada 	return dwmci_bind(dev, &plat->mmc, &plat->cfg);
252f6e41d17SSimon Glass }
253f6e41d17SSimon Glass 
254a8cb4fb5SSimon Glass static const struct udevice_id rockchip_dwmmc_ids[] = {
255a8cb4fb5SSimon Glass 	{ .compatible = "rockchip,rk3288-dw-mshc" },
256337e8c3eSPaweł Jarosz 	{ .compatible = "rockchip,rk2928-dw-mshc" },
257a8cb4fb5SSimon Glass 	{ }
258a8cb4fb5SSimon Glass };
259a8cb4fb5SSimon Glass 
260a8cb4fb5SSimon Glass U_BOOT_DRIVER(rockchip_dwmmc_drv) = {
261bfeb443eSSimon Glass 	.name		= "rockchip_rk3288_dw_mshc",
262a8cb4fb5SSimon Glass 	.id		= UCLASS_MMC,
263a8cb4fb5SSimon Glass 	.of_match	= rockchip_dwmmc_ids,
264a8cb4fb5SSimon Glass 	.ofdata_to_platdata = rockchip_dwmmc_ofdata_to_platdata,
26542b37d8dSSimon Glass 	.ops		= &dm_dwmci_ops,
266f6e41d17SSimon Glass 	.bind		= rockchip_dwmmc_bind,
267a8cb4fb5SSimon Glass 	.probe		= rockchip_dwmmc_probe,
268a8cb4fb5SSimon Glass 	.priv_auto_alloc_size = sizeof(struct rockchip_dwmmc_priv),
269f6e41d17SSimon Glass 	.platdata_auto_alloc_size = sizeof(struct rockchip_mmc_plat),
270a8cb4fb5SSimon Glass };
271e1efec4eSSimon Glass 
272e1efec4eSSimon Glass #ifdef CONFIG_PWRSEQ
273e1efec4eSSimon Glass static int rockchip_dwmmc_pwrseq_set_power(struct udevice *dev, bool enable)
274e1efec4eSSimon Glass {
275e1efec4eSSimon Glass 	struct gpio_desc reset;
276e1efec4eSSimon Glass 	int ret;
277e1efec4eSSimon Glass 
278e1efec4eSSimon Glass 	ret = gpio_request_by_name(dev, "reset-gpios", 0, &reset, GPIOD_IS_OUT);
279e1efec4eSSimon Glass 	if (ret)
280e1efec4eSSimon Glass 		return ret;
281e1efec4eSSimon Glass 	dm_gpio_set_value(&reset, 1);
282e1efec4eSSimon Glass 	udelay(1);
283e1efec4eSSimon Glass 	dm_gpio_set_value(&reset, 0);
284e1efec4eSSimon Glass 	udelay(200);
285e1efec4eSSimon Glass 
286e1efec4eSSimon Glass 	return 0;
287e1efec4eSSimon Glass }
288e1efec4eSSimon Glass 
289e1efec4eSSimon Glass static const struct pwrseq_ops rockchip_dwmmc_pwrseq_ops = {
290e1efec4eSSimon Glass 	.set_power	= rockchip_dwmmc_pwrseq_set_power,
291e1efec4eSSimon Glass };
292e1efec4eSSimon Glass 
293e1efec4eSSimon Glass static const struct udevice_id rockchip_dwmmc_pwrseq_ids[] = {
294e1efec4eSSimon Glass 	{ .compatible = "mmc-pwrseq-emmc" },
295e1efec4eSSimon Glass 	{ }
296e1efec4eSSimon Glass };
297e1efec4eSSimon Glass 
298e1efec4eSSimon Glass U_BOOT_DRIVER(rockchip_dwmmc_pwrseq_drv) = {
299e1efec4eSSimon Glass 	.name		= "mmc_pwrseq_emmc",
300e1efec4eSSimon Glass 	.id		= UCLASS_PWRSEQ,
301e1efec4eSSimon Glass 	.of_match	= rockchip_dwmmc_pwrseq_ids,
302e1efec4eSSimon Glass 	.ops		= &rockchip_dwmmc_pwrseq_ops,
303e1efec4eSSimon Glass };
304e1efec4eSSimon Glass #endif
305