xref: /rk3399_rockchip-uboot/drivers/mmc/rockchip_dw_mmc.c (revision 15737894e442cc5c7b68e4b7cf721b52173acb3e)
1 /*
2  * Copyright (c) 2013 Google, Inc
3  *
4  * SPDX-License-Identifier:	GPL-2.0+
5  */
6 
7 #include <common.h>
8 #include <clk.h>
9 #include <dm.h>
10 #include <dt-structs.h>
11 #include <dwmmc.h>
12 #include <errno.h>
13 #include <mapmem.h>
14 #include <pwrseq.h>
15 #include <syscon.h>
16 #include <asm/gpio.h>
17 #include <asm/arch/clock.h>
18 #include <asm/arch/periph.h>
19 #include <linux/err.h>
20 
21 DECLARE_GLOBAL_DATA_PTR;
22 
23 struct rockchip_mmc_plat {
24 #if CONFIG_IS_ENABLED(OF_PLATDATA)
25 	struct dtd_rockchip_rk3288_dw_mshc dtplat;
26 #endif
27 	struct mmc_config cfg;
28 	struct mmc mmc;
29 };
30 
31 struct rockchip_dwmmc_priv {
32 	struct clk clk;
33 	struct clk sample_clk;
34 	struct dwmci_host host;
35 	int fifo_depth;
36 	bool fifo_mode;
37 	u32 minmax[2];
38 };
39 
40 #ifdef CONFIG_SPL_BUILD
41 __weak void mmc_gpio_init_direct(void) {}
42 #endif
43 
44 static uint rockchip_dwmmc_get_mmc_clk(struct dwmci_host *host, uint freq)
45 {
46 	struct udevice *dev = host->priv;
47 	struct rockchip_dwmmc_priv *priv = dev_get_priv(dev);
48 	int ret;
49 
50 	/*
51 	 * If DDR52 8bit mode(only emmc work in 8bit mode),
52 	 * divider must be set 1
53 	 */
54 	if (mmc_card_ddr52(host->mmc) && host->mmc->bus_width == 8)
55 		freq *= 2;
56 
57 	ret = clk_set_rate(&priv->clk, freq);
58 	if (ret < 0) {
59 		debug("%s: err=%d\n", __func__, ret);
60 		return ret;
61 	}
62 
63 	return freq;
64 }
65 
66 static int rockchip_dwmmc_ofdata_to_platdata(struct udevice *dev)
67 {
68 #if !CONFIG_IS_ENABLED(OF_PLATDATA)
69 	struct rockchip_dwmmc_priv *priv = dev_get_priv(dev);
70 	struct dwmci_host *host = &priv->host;
71 
72 	host->name = dev->name;
73 	host->ioaddr = dev_read_addr_ptr(dev);
74 	host->buswidth = dev_read_u32_default(dev, "bus-width", 4);
75 	host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk;
76 	host->priv = dev;
77 
78 	/* use non-removeable as sdcard and emmc as judgement */
79 	if (dev_read_bool(dev, "non-removable"))
80 		host->dev_index = 0;
81 	else
82 		host->dev_index = 1;
83 
84 	priv->fifo_depth = dev_read_u32_default(dev, "fifo-depth", 0);
85 
86 	if (priv->fifo_depth < 0)
87 		return -EINVAL;
88 	priv->fifo_mode = dev_read_bool(dev, "fifo-mode");
89 
90 	/*
91 	 * 'clock-freq-min-max' is deprecated
92 	 * (see https://github.com/torvalds/linux/commit/b023030f10573de738bbe8df63d43acab64c9f7b)
93 	 */
94 	if (dev_read_u32_array(dev, "clock-freq-min-max", priv->minmax, 2)) {
95 		int val = dev_read_u32_default(dev, "max-frequency", -EINVAL);
96 
97 		if (val < 0)
98 			return val;
99 
100 		priv->minmax[0] = 400000;  /* 400 kHz */
101 		priv->minmax[1] = val;
102 	} else {
103 		debug("%s: 'clock-freq-min-max' property was deprecated.\n",
104 		      __func__);
105 	}
106 #endif
107 	return 0;
108 }
109 
110 static int rockchip_dwmmc_execute_tuning(struct dwmci_host *host, u32 opcode)
111 {
112 	int i = 0;
113 	int ret = -1;
114 	struct mmc *mmc = host->mmc;
115 	struct udevice *dev = host->priv;
116 	struct rockchip_dwmmc_priv *priv = dev_get_priv(dev);
117 
118 	if (IS_ERR(&priv->sample_clk))
119 		return -EIO;
120 
121 	if (mmc->default_phase > 0 && mmc->default_phase < 360) {
122 		ret = clk_set_phase(&priv->sample_clk, mmc->default_phase);
123 		if (ret)
124 			printf("set clk phase fail\n");
125 		else
126 			ret = mmc_send_tuning(mmc, opcode);
127 		mmc->default_phase = 0;
128 	}
129 	/*
130 	 * If use default_phase to tune successfully, return.
131 	 * Otherwise, use the othe phase to tune.
132 	 */
133 	if (!ret)
134 		return ret;
135 
136 	for (i = 0; i < 5; i++) {
137 		/* mmc->init_retry must be 0, 1, 2, 3 */
138 		if (mmc->init_retry == 4)
139 			mmc->init_retry = 0;
140 
141 		ret = clk_set_phase(&priv->sample_clk, 90 * mmc->init_retry);
142 		if (ret) {
143 			printf("set clk phase fail\n");
144 			break;
145 		}
146 		ret = mmc_send_tuning(mmc, opcode);
147 		debug("Tuning phase is %d, ret is %d\n", mmc->init_retry * 90, ret);
148 		mmc->init_retry++;
149 		if (!ret)
150 			break;
151 	}
152 
153 	return ret;
154 }
155 
156 static int rockchip_dwmmc_probe(struct udevice *dev)
157 {
158 	struct rockchip_mmc_plat *plat = dev_get_platdata(dev);
159 	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
160 	struct rockchip_dwmmc_priv *priv = dev_get_priv(dev);
161 	struct dwmci_host *host = &priv->host;
162 	struct udevice *pwr_dev __maybe_unused;
163 	int ret;
164 
165 #ifdef CONFIG_SPL_BUILD
166 	mmc_gpio_init_direct();
167 #endif
168 #if CONFIG_IS_ENABLED(OF_PLATDATA)
169 	struct dtd_rockchip_rk3288_dw_mshc *dtplat = &plat->dtplat;
170 
171 	host->name = dev->name;
172 	host->ioaddr = map_sysmem(dtplat->reg[0], dtplat->reg[1]);
173 	host->buswidth = dtplat->bus_width;
174 	host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk;
175 	host->execute_tuning = rockchip_dwmmc_execute_tuning;
176 	host->priv = dev;
177 	host->dev_index = 0;
178 	priv->fifo_depth = dtplat->fifo_depth;
179 	priv->fifo_mode = 0;
180 	priv->minmax[0] = 400000;  /*  400 kHz */
181 	priv->minmax[1] = dtplat->max_frequency;
182 
183 	ret = clk_get_by_index_platdata(dev, 0, dtplat->clocks, &priv->clk);
184 	if (ret < 0)
185 		return ret;
186 #else
187 	ret = clk_get_by_index(dev, 0, &priv->clk);
188 	if (ret < 0)
189 		return ret;
190 
191 	ret = clk_get_by_name(dev, "ciu-sample", &priv->sample_clk);
192 	if (ret < 0)
193 		debug("MMC: sample clock not found, not support hs200!\n");
194 	host->execute_tuning = rockchip_dwmmc_execute_tuning;
195 #endif
196 	host->fifoth_val = MSIZE(DWMCI_MSIZE) |
197 		RX_WMARK(priv->fifo_depth / 2 - 1) |
198 		TX_WMARK(priv->fifo_depth / 2);
199 
200 	host->fifo_mode = priv->fifo_mode;
201 
202 #ifdef CONFIG_ROCKCHIP_RK3128
203 	host->stride_pio = true;
204 #else
205 	host->stride_pio = false;
206 #endif
207 
208 #ifdef CONFIG_PWRSEQ
209 	/* Enable power if needed */
210 	ret = uclass_get_device_by_phandle(UCLASS_PWRSEQ, dev, "mmc-pwrseq",
211 					   &pwr_dev);
212 	if (!ret) {
213 		ret = pwrseq_set_power(pwr_dev, true);
214 		if (ret)
215 			return ret;
216 	}
217 #endif
218 	dwmci_setup_cfg(&plat->cfg, host, priv->minmax[1], priv->minmax[0]);
219 	if (dev_read_bool(dev, "mmc-hs200-1_8v"))
220 		plat->cfg.host_caps |= MMC_MODE_HS200;
221 	plat->mmc.default_phase =
222 		dev_read_u32_default(dev, "default-sample-phase", 0);
223 	plat->mmc.init_retry = 0;
224 	host->mmc = &plat->mmc;
225 	host->mmc->priv = &priv->host;
226 	host->mmc->dev = dev;
227 	upriv->mmc = host->mmc;
228 
229 	return dwmci_probe(dev);
230 }
231 
232 static int rockchip_dwmmc_bind(struct udevice *dev)
233 {
234 	struct rockchip_mmc_plat *plat = dev_get_platdata(dev);
235 
236 	return dwmci_bind(dev, &plat->mmc, &plat->cfg);
237 }
238 
239 static const struct udevice_id rockchip_dwmmc_ids[] = {
240 	{ .compatible = "rockchip,rk3288-dw-mshc" },
241 	{ .compatible = "rockchip,rk2928-dw-mshc" },
242 	{ }
243 };
244 
245 U_BOOT_DRIVER(rockchip_dwmmc_drv) = {
246 	.name		= "rockchip_rk3288_dw_mshc",
247 	.id		= UCLASS_MMC,
248 	.of_match	= rockchip_dwmmc_ids,
249 	.ofdata_to_platdata = rockchip_dwmmc_ofdata_to_platdata,
250 	.ops		= &dm_dwmci_ops,
251 	.bind		= rockchip_dwmmc_bind,
252 	.probe		= rockchip_dwmmc_probe,
253 	.priv_auto_alloc_size = sizeof(struct rockchip_dwmmc_priv),
254 	.platdata_auto_alloc_size = sizeof(struct rockchip_mmc_plat),
255 };
256 
257 #ifdef CONFIG_PWRSEQ
258 static int rockchip_dwmmc_pwrseq_set_power(struct udevice *dev, bool enable)
259 {
260 	struct gpio_desc reset;
261 	int ret;
262 
263 	ret = gpio_request_by_name(dev, "reset-gpios", 0, &reset, GPIOD_IS_OUT);
264 	if (ret)
265 		return ret;
266 	dm_gpio_set_value(&reset, 1);
267 	udelay(1);
268 	dm_gpio_set_value(&reset, 0);
269 	udelay(200);
270 
271 	return 0;
272 }
273 
274 static const struct pwrseq_ops rockchip_dwmmc_pwrseq_ops = {
275 	.set_power	= rockchip_dwmmc_pwrseq_set_power,
276 };
277 
278 static const struct udevice_id rockchip_dwmmc_pwrseq_ids[] = {
279 	{ .compatible = "mmc-pwrseq-emmc" },
280 	{ }
281 };
282 
283 U_BOOT_DRIVER(rockchip_dwmmc_pwrseq_drv) = {
284 	.name		= "mmc_pwrseq_emmc",
285 	.id		= UCLASS_PWRSEQ,
286 	.of_match	= rockchip_dwmmc_pwrseq_ids,
287 	.ops		= &rockchip_dwmmc_pwrseq_ops,
288 };
289 #endif
290