xref: /rk3399_rockchip-uboot/drivers/mmc/sunxi_mmc.c (revision 1d1bd42eb08b267a0884a0fa04483daeda169f6d)
1e24ea55cSIan Campbell /*
2e24ea55cSIan Campbell  * (C) Copyright 2007-2011
3e24ea55cSIan Campbell  * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
4e24ea55cSIan Campbell  * Aaron <leafy.myeh@allwinnertech.com>
5e24ea55cSIan Campbell  *
6e24ea55cSIan Campbell  * MMC driver for allwinner sunxi platform.
7e24ea55cSIan Campbell  *
8e24ea55cSIan Campbell  * SPDX-License-Identifier:	GPL-2.0+
9e24ea55cSIan Campbell  */
10e24ea55cSIan Campbell 
11e24ea55cSIan Campbell #include <common.h>
12e24ea55cSIan Campbell #include <malloc.h>
13e24ea55cSIan Campbell #include <mmc.h>
14e24ea55cSIan Campbell #include <asm/io.h>
15e24ea55cSIan Campbell #include <asm/arch/clock.h>
16e24ea55cSIan Campbell #include <asm/arch/cpu.h>
17e24ea55cSIan Campbell #include <asm/arch/mmc.h>
18e24ea55cSIan Campbell 
19e24ea55cSIan Campbell struct sunxi_mmc_host {
20e24ea55cSIan Campbell 	unsigned mmc_no;
21e24ea55cSIan Campbell 	uint32_t *mclkreg;
22e24ea55cSIan Campbell 	unsigned fatal_err;
23e24ea55cSIan Campbell 	unsigned mod_clk;
24e24ea55cSIan Campbell 	struct sunxi_mmc *reg;
25e24ea55cSIan Campbell 	struct mmc_config cfg;
26e24ea55cSIan Campbell };
27e24ea55cSIan Campbell 
28e24ea55cSIan Campbell /* support 4 mmc hosts */
29e24ea55cSIan Campbell struct sunxi_mmc_host mmc_host[4];
30e24ea55cSIan Campbell 
31e24ea55cSIan Campbell static int mmc_resource_init(int sdc_no)
32e24ea55cSIan Campbell {
33e24ea55cSIan Campbell 	struct sunxi_mmc_host *mmchost = &mmc_host[sdc_no];
34e24ea55cSIan Campbell 	struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
35e24ea55cSIan Campbell 
36e24ea55cSIan Campbell 	debug("init mmc %d resource\n", sdc_no);
37e24ea55cSIan Campbell 
38e24ea55cSIan Campbell 	switch (sdc_no) {
39e24ea55cSIan Campbell 	case 0:
40e24ea55cSIan Campbell 		mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC0_BASE;
41e24ea55cSIan Campbell 		mmchost->mclkreg = &ccm->sd0_clk_cfg;
42e24ea55cSIan Campbell 		break;
43e24ea55cSIan Campbell 	case 1:
44e24ea55cSIan Campbell 		mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC1_BASE;
45e24ea55cSIan Campbell 		mmchost->mclkreg = &ccm->sd1_clk_cfg;
46e24ea55cSIan Campbell 		break;
47e24ea55cSIan Campbell 	case 2:
48e24ea55cSIan Campbell 		mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC2_BASE;
49e24ea55cSIan Campbell 		mmchost->mclkreg = &ccm->sd2_clk_cfg;
50e24ea55cSIan Campbell 		break;
51e24ea55cSIan Campbell 	case 3:
52e24ea55cSIan Campbell 		mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC3_BASE;
53e24ea55cSIan Campbell 		mmchost->mclkreg = &ccm->sd3_clk_cfg;
54e24ea55cSIan Campbell 		break;
55e24ea55cSIan Campbell 	default:
56e24ea55cSIan Campbell 		printf("Wrong mmc number %d\n", sdc_no);
57e24ea55cSIan Campbell 		return -1;
58e24ea55cSIan Campbell 	}
59e24ea55cSIan Campbell 	mmchost->mmc_no = sdc_no;
60e24ea55cSIan Campbell 
61e24ea55cSIan Campbell 	return 0;
62e24ea55cSIan Campbell }
63e24ea55cSIan Campbell 
64e24ea55cSIan Campbell static int mmc_clk_io_on(int sdc_no)
65e24ea55cSIan Campbell {
66e24ea55cSIan Campbell 	unsigned int pll_clk;
67e24ea55cSIan Campbell 	unsigned int divider;
68e24ea55cSIan Campbell 	struct sunxi_mmc_host *mmchost = &mmc_host[sdc_no];
69e24ea55cSIan Campbell 	struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
70e24ea55cSIan Campbell 
71e24ea55cSIan Campbell 	debug("init mmc %d clock and io\n", sdc_no);
72e24ea55cSIan Campbell 
73e24ea55cSIan Campbell 	/* config ahb clock */
74e24ea55cSIan Campbell 	setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MMC(sdc_no));
75e24ea55cSIan Campbell 
76*1d1bd42eSHans de Goede #if defined(CONFIG_SUN6I)
77*1d1bd42eSHans de Goede 	/* unassert reset */
78*1d1bd42eSHans de Goede 	setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MMC(sdc_no));
79*1d1bd42eSHans de Goede #endif
80*1d1bd42eSHans de Goede 
81e24ea55cSIan Campbell 	/* config mod clock */
82e24ea55cSIan Campbell 	pll_clk = clock_get_pll6();
83e24ea55cSIan Campbell 	/* should be close to 100 MHz but no more, so round up */
84e24ea55cSIan Campbell 	divider = ((pll_clk + 99999999) / 100000000) - 1;
85e24ea55cSIan Campbell 	writel(CCM_MMC_CTRL_ENABLE | CCM_MMC_CTRL_PLL6 | divider,
86e24ea55cSIan Campbell 	       mmchost->mclkreg);
87e24ea55cSIan Campbell 	mmchost->mod_clk = pll_clk / (divider + 1);
88e24ea55cSIan Campbell 
89e24ea55cSIan Campbell 	return 0;
90e24ea55cSIan Campbell }
91e24ea55cSIan Campbell 
92e24ea55cSIan Campbell static int mmc_update_clk(struct mmc *mmc)
93e24ea55cSIan Campbell {
94e24ea55cSIan Campbell 	struct sunxi_mmc_host *mmchost = mmc->priv;
95e24ea55cSIan Campbell 	unsigned int cmd;
96e24ea55cSIan Campbell 	unsigned timeout_msecs = 2000;
97e24ea55cSIan Campbell 
98e24ea55cSIan Campbell 	cmd = SUNXI_MMC_CMD_START |
99e24ea55cSIan Campbell 	      SUNXI_MMC_CMD_UPCLK_ONLY |
100e24ea55cSIan Campbell 	      SUNXI_MMC_CMD_WAIT_PRE_OVER;
101e24ea55cSIan Campbell 	writel(cmd, &mmchost->reg->cmd);
102e24ea55cSIan Campbell 	while (readl(&mmchost->reg->cmd) & SUNXI_MMC_CMD_START) {
103e24ea55cSIan Campbell 		if (!timeout_msecs--)
104e24ea55cSIan Campbell 			return -1;
105e24ea55cSIan Campbell 		udelay(1000);
106e24ea55cSIan Campbell 	}
107e24ea55cSIan Campbell 
108e24ea55cSIan Campbell 	/* clock update sets various irq status bits, clear these */
109e24ea55cSIan Campbell 	writel(readl(&mmchost->reg->rint), &mmchost->reg->rint);
110e24ea55cSIan Campbell 
111e24ea55cSIan Campbell 	return 0;
112e24ea55cSIan Campbell }
113e24ea55cSIan Campbell 
114e24ea55cSIan Campbell static int mmc_config_clock(struct mmc *mmc, unsigned div)
115e24ea55cSIan Campbell {
116e24ea55cSIan Campbell 	struct sunxi_mmc_host *mmchost = mmc->priv;
117e24ea55cSIan Campbell 	unsigned rval = readl(&mmchost->reg->clkcr);
118e24ea55cSIan Campbell 
119e24ea55cSIan Campbell 	/* Disable Clock */
120e24ea55cSIan Campbell 	rval &= ~SUNXI_MMC_CLK_ENABLE;
121e24ea55cSIan Campbell 	writel(rval, &mmchost->reg->clkcr);
122e24ea55cSIan Campbell 	if (mmc_update_clk(mmc))
123e24ea55cSIan Campbell 		return -1;
124e24ea55cSIan Campbell 
125e24ea55cSIan Campbell 	/* Change Divider Factor */
126e24ea55cSIan Campbell 	rval &= ~SUNXI_MMC_CLK_DIVIDER_MASK;
127e24ea55cSIan Campbell 	rval |= div;
128e24ea55cSIan Campbell 	writel(rval, &mmchost->reg->clkcr);
129e24ea55cSIan Campbell 	if (mmc_update_clk(mmc))
130e24ea55cSIan Campbell 		return -1;
131e24ea55cSIan Campbell 	/* Re-enable Clock */
132e24ea55cSIan Campbell 	rval |= SUNXI_MMC_CLK_ENABLE;
133e24ea55cSIan Campbell 	writel(rval, &mmchost->reg->clkcr);
134e24ea55cSIan Campbell 
135e24ea55cSIan Campbell 	if (mmc_update_clk(mmc))
136e24ea55cSIan Campbell 		return -1;
137e24ea55cSIan Campbell 
138e24ea55cSIan Campbell 	return 0;
139e24ea55cSIan Campbell }
140e24ea55cSIan Campbell 
141e24ea55cSIan Campbell static void mmc_set_ios(struct mmc *mmc)
142e24ea55cSIan Campbell {
143e24ea55cSIan Campbell 	struct sunxi_mmc_host *mmchost = mmc->priv;
144e24ea55cSIan Campbell 	unsigned int clkdiv = 0;
145e24ea55cSIan Campbell 
146e24ea55cSIan Campbell 	debug("set ios: bus_width: %x, clock: %d, mod_clk: %d\n",
147e24ea55cSIan Campbell 	      mmc->bus_width, mmc->clock, mmchost->mod_clk);
148e24ea55cSIan Campbell 
149e24ea55cSIan Campbell 	/* Change clock first */
150e24ea55cSIan Campbell 	clkdiv = (mmchost->mod_clk + (mmc->clock >> 1)) / mmc->clock / 2;
151e24ea55cSIan Campbell 	if (mmc->clock) {
152e24ea55cSIan Campbell 		if (mmc_config_clock(mmc, clkdiv)) {
153e24ea55cSIan Campbell 			mmchost->fatal_err = 1;
154e24ea55cSIan Campbell 			return;
155e24ea55cSIan Campbell 		}
156e24ea55cSIan Campbell 	}
157e24ea55cSIan Campbell 
158e24ea55cSIan Campbell 	/* Change bus width */
159e24ea55cSIan Campbell 	if (mmc->bus_width == 8)
160e24ea55cSIan Campbell 		writel(0x2, &mmchost->reg->width);
161e24ea55cSIan Campbell 	else if (mmc->bus_width == 4)
162e24ea55cSIan Campbell 		writel(0x1, &mmchost->reg->width);
163e24ea55cSIan Campbell 	else
164e24ea55cSIan Campbell 		writel(0x0, &mmchost->reg->width);
165e24ea55cSIan Campbell }
166e24ea55cSIan Campbell 
167e24ea55cSIan Campbell static int mmc_core_init(struct mmc *mmc)
168e24ea55cSIan Campbell {
169e24ea55cSIan Campbell 	struct sunxi_mmc_host *mmchost = mmc->priv;
170e24ea55cSIan Campbell 
171e24ea55cSIan Campbell 	/* Reset controller */
172e24ea55cSIan Campbell 	writel(SUNXI_MMC_GCTRL_RESET, &mmchost->reg->gctrl);
173b6ae6765SHans de Goede 	udelay(1000);
174e24ea55cSIan Campbell 
175e24ea55cSIan Campbell 	return 0;
176e24ea55cSIan Campbell }
177e24ea55cSIan Campbell 
178e24ea55cSIan Campbell static int mmc_trans_data_by_cpu(struct mmc *mmc, struct mmc_data *data)
179e24ea55cSIan Campbell {
180e24ea55cSIan Campbell 	struct sunxi_mmc_host *mmchost = mmc->priv;
181e24ea55cSIan Campbell 	const int reading = !!(data->flags & MMC_DATA_READ);
182e24ea55cSIan Campbell 	const uint32_t status_bit = reading ? SUNXI_MMC_STATUS_FIFO_EMPTY :
183e24ea55cSIan Campbell 					      SUNXI_MMC_STATUS_FIFO_FULL;
184e24ea55cSIan Campbell 	unsigned i;
185e24ea55cSIan Campbell 	unsigned byte_cnt = data->blocksize * data->blocks;
186e24ea55cSIan Campbell 	unsigned timeout_msecs = 2000;
187e24ea55cSIan Campbell 	unsigned *buff = (unsigned int *)(reading ? data->dest : data->src);
188e24ea55cSIan Campbell 
189b6ae6765SHans de Goede 	/* Always read / write data through the CPU */
190b6ae6765SHans de Goede 	setbits_le32(&mmchost->reg->gctrl, SUNXI_MMC_GCTRL_ACCESS_BY_AHB);
191b6ae6765SHans de Goede 
192e24ea55cSIan Campbell 	for (i = 0; i < (byte_cnt >> 2); i++) {
193e24ea55cSIan Campbell 		while (readl(&mmchost->reg->status) & status_bit) {
194e24ea55cSIan Campbell 			if (!timeout_msecs--)
195e24ea55cSIan Campbell 				return -1;
196e24ea55cSIan Campbell 			udelay(1000);
197e24ea55cSIan Campbell 		}
198e24ea55cSIan Campbell 
199e24ea55cSIan Campbell 		if (reading)
200*1d1bd42eSHans de Goede 			buff[i] = readl(&mmchost->reg->fifo);
201e24ea55cSIan Campbell 		else
202*1d1bd42eSHans de Goede 			writel(buff[i], &mmchost->reg->fifo);
203e24ea55cSIan Campbell 	}
204e24ea55cSIan Campbell 
205e24ea55cSIan Campbell 	return 0;
206e24ea55cSIan Campbell }
207e24ea55cSIan Campbell 
208e24ea55cSIan Campbell static int mmc_rint_wait(struct mmc *mmc, unsigned int timeout_msecs,
209e24ea55cSIan Campbell 			 unsigned int done_bit, const char *what)
210e24ea55cSIan Campbell {
211e24ea55cSIan Campbell 	struct sunxi_mmc_host *mmchost = mmc->priv;
212e24ea55cSIan Campbell 	unsigned int status;
213e24ea55cSIan Campbell 
214e24ea55cSIan Campbell 	do {
215e24ea55cSIan Campbell 		status = readl(&mmchost->reg->rint);
216e24ea55cSIan Campbell 		if (!timeout_msecs-- ||
217e24ea55cSIan Campbell 		    (status & SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT)) {
218e24ea55cSIan Campbell 			debug("%s timeout %x\n", what,
219e24ea55cSIan Campbell 			      status & SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT);
220e24ea55cSIan Campbell 			return TIMEOUT;
221e24ea55cSIan Campbell 		}
222e24ea55cSIan Campbell 		udelay(1000);
223e24ea55cSIan Campbell 	} while (!(status & done_bit));
224e24ea55cSIan Campbell 
225e24ea55cSIan Campbell 	return 0;
226e24ea55cSIan Campbell }
227e24ea55cSIan Campbell 
228e24ea55cSIan Campbell static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
229e24ea55cSIan Campbell 			struct mmc_data *data)
230e24ea55cSIan Campbell {
231e24ea55cSIan Campbell 	struct sunxi_mmc_host *mmchost = mmc->priv;
232e24ea55cSIan Campbell 	unsigned int cmdval = SUNXI_MMC_CMD_START;
233e24ea55cSIan Campbell 	unsigned int timeout_msecs;
234e24ea55cSIan Campbell 	int error = 0;
235e24ea55cSIan Campbell 	unsigned int status = 0;
236e24ea55cSIan Campbell 	unsigned int bytecnt = 0;
237e24ea55cSIan Campbell 
238e24ea55cSIan Campbell 	if (mmchost->fatal_err)
239e24ea55cSIan Campbell 		return -1;
240e24ea55cSIan Campbell 	if (cmd->resp_type & MMC_RSP_BUSY)
241e24ea55cSIan Campbell 		debug("mmc cmd %d check rsp busy\n", cmd->cmdidx);
242e24ea55cSIan Campbell 	if (cmd->cmdidx == 12)
243e24ea55cSIan Campbell 		return 0;
244e24ea55cSIan Campbell 
245e24ea55cSIan Campbell 	if (!cmd->cmdidx)
246e24ea55cSIan Campbell 		cmdval |= SUNXI_MMC_CMD_SEND_INIT_SEQ;
247e24ea55cSIan Campbell 	if (cmd->resp_type & MMC_RSP_PRESENT)
248e24ea55cSIan Campbell 		cmdval |= SUNXI_MMC_CMD_RESP_EXPIRE;
249e24ea55cSIan Campbell 	if (cmd->resp_type & MMC_RSP_136)
250e24ea55cSIan Campbell 		cmdval |= SUNXI_MMC_CMD_LONG_RESPONSE;
251e24ea55cSIan Campbell 	if (cmd->resp_type & MMC_RSP_CRC)
252e24ea55cSIan Campbell 		cmdval |= SUNXI_MMC_CMD_CHK_RESPONSE_CRC;
253e24ea55cSIan Campbell 
254e24ea55cSIan Campbell 	if (data) {
255e24ea55cSIan Campbell 		if ((u32) data->dest & 0x3) {
256e24ea55cSIan Campbell 			error = -1;
257e24ea55cSIan Campbell 			goto out;
258e24ea55cSIan Campbell 		}
259e24ea55cSIan Campbell 
260e24ea55cSIan Campbell 		cmdval |= SUNXI_MMC_CMD_DATA_EXPIRE|SUNXI_MMC_CMD_WAIT_PRE_OVER;
261e24ea55cSIan Campbell 		if (data->flags & MMC_DATA_WRITE)
262e24ea55cSIan Campbell 			cmdval |= SUNXI_MMC_CMD_WRITE;
263e24ea55cSIan Campbell 		if (data->blocks > 1)
264e24ea55cSIan Campbell 			cmdval |= SUNXI_MMC_CMD_AUTO_STOP;
265e24ea55cSIan Campbell 		writel(data->blocksize, &mmchost->reg->blksz);
266e24ea55cSIan Campbell 		writel(data->blocks * data->blocksize, &mmchost->reg->bytecnt);
267e24ea55cSIan Campbell 	}
268e24ea55cSIan Campbell 
269e24ea55cSIan Campbell 	debug("mmc %d, cmd %d(0x%08x), arg 0x%08x\n", mmchost->mmc_no,
270e24ea55cSIan Campbell 	      cmd->cmdidx, cmdval | cmd->cmdidx, cmd->cmdarg);
271e24ea55cSIan Campbell 	writel(cmd->cmdarg, &mmchost->reg->arg);
272e24ea55cSIan Campbell 
273e24ea55cSIan Campbell 	if (!data)
274e24ea55cSIan Campbell 		writel(cmdval | cmd->cmdidx, &mmchost->reg->cmd);
275e24ea55cSIan Campbell 
276e24ea55cSIan Campbell 	/*
277e24ea55cSIan Campbell 	 * transfer data and check status
278e24ea55cSIan Campbell 	 * STATREG[2] : FIFO empty
279e24ea55cSIan Campbell 	 * STATREG[3] : FIFO full
280e24ea55cSIan Campbell 	 */
281e24ea55cSIan Campbell 	if (data) {
282e24ea55cSIan Campbell 		int ret = 0;
283e24ea55cSIan Campbell 
284e24ea55cSIan Campbell 		bytecnt = data->blocksize * data->blocks;
285e24ea55cSIan Campbell 		debug("trans data %d bytes\n", bytecnt);
286e24ea55cSIan Campbell 		writel(cmdval | cmd->cmdidx, &mmchost->reg->cmd);
287e24ea55cSIan Campbell 		ret = mmc_trans_data_by_cpu(mmc, data);
288e24ea55cSIan Campbell 		if (ret) {
289e24ea55cSIan Campbell 			error = readl(&mmchost->reg->rint) & \
290e24ea55cSIan Campbell 				SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT;
291e24ea55cSIan Campbell 			error = TIMEOUT;
292e24ea55cSIan Campbell 			goto out;
293e24ea55cSIan Campbell 		}
294e24ea55cSIan Campbell 	}
295e24ea55cSIan Campbell 
296e24ea55cSIan Campbell 	error = mmc_rint_wait(mmc, 0xfffff, SUNXI_MMC_RINT_COMMAND_DONE, "cmd");
297e24ea55cSIan Campbell 	if (error)
298e24ea55cSIan Campbell 		goto out;
299e24ea55cSIan Campbell 
300e24ea55cSIan Campbell 	if (data) {
301b6ae6765SHans de Goede 		timeout_msecs = 120;
302e24ea55cSIan Campbell 		debug("cacl timeout %x msec\n", timeout_msecs);
303e24ea55cSIan Campbell 		error = mmc_rint_wait(mmc, timeout_msecs,
304e24ea55cSIan Campbell 				      data->blocks > 1 ?
305e24ea55cSIan Campbell 				      SUNXI_MMC_RINT_AUTO_COMMAND_DONE :
306e24ea55cSIan Campbell 				      SUNXI_MMC_RINT_DATA_OVER,
307e24ea55cSIan Campbell 				      "data");
308e24ea55cSIan Campbell 		if (error)
309e24ea55cSIan Campbell 			goto out;
310e24ea55cSIan Campbell 	}
311e24ea55cSIan Campbell 
312e24ea55cSIan Campbell 	if (cmd->resp_type & MMC_RSP_BUSY) {
313e24ea55cSIan Campbell 		timeout_msecs = 2000;
314e24ea55cSIan Campbell 		do {
315e24ea55cSIan Campbell 			status = readl(&mmchost->reg->status);
316e24ea55cSIan Campbell 			if (!timeout_msecs--) {
317e24ea55cSIan Campbell 				debug("busy timeout\n");
318e24ea55cSIan Campbell 				error = TIMEOUT;
319e24ea55cSIan Campbell 				goto out;
320e24ea55cSIan Campbell 			}
321e24ea55cSIan Campbell 			udelay(1000);
322e24ea55cSIan Campbell 		} while (status & SUNXI_MMC_STATUS_CARD_DATA_BUSY);
323e24ea55cSIan Campbell 	}
324e24ea55cSIan Campbell 
325e24ea55cSIan Campbell 	if (cmd->resp_type & MMC_RSP_136) {
326e24ea55cSIan Campbell 		cmd->response[0] = readl(&mmchost->reg->resp3);
327e24ea55cSIan Campbell 		cmd->response[1] = readl(&mmchost->reg->resp2);
328e24ea55cSIan Campbell 		cmd->response[2] = readl(&mmchost->reg->resp1);
329e24ea55cSIan Campbell 		cmd->response[3] = readl(&mmchost->reg->resp0);
330e24ea55cSIan Campbell 		debug("mmc resp 0x%08x 0x%08x 0x%08x 0x%08x\n",
331e24ea55cSIan Campbell 		      cmd->response[3], cmd->response[2],
332e24ea55cSIan Campbell 		      cmd->response[1], cmd->response[0]);
333e24ea55cSIan Campbell 	} else {
334e24ea55cSIan Campbell 		cmd->response[0] = readl(&mmchost->reg->resp0);
335e24ea55cSIan Campbell 		debug("mmc resp 0x%08x\n", cmd->response[0]);
336e24ea55cSIan Campbell 	}
337e24ea55cSIan Campbell out:
338e24ea55cSIan Campbell 	if (error < 0) {
339e24ea55cSIan Campbell 		writel(SUNXI_MMC_GCTRL_RESET, &mmchost->reg->gctrl);
340e24ea55cSIan Campbell 		mmc_update_clk(mmc);
341e24ea55cSIan Campbell 	}
342e24ea55cSIan Campbell 	writel(0xffffffff, &mmchost->reg->rint);
343e24ea55cSIan Campbell 	writel(readl(&mmchost->reg->gctrl) | SUNXI_MMC_GCTRL_FIFO_RESET,
344e24ea55cSIan Campbell 	       &mmchost->reg->gctrl);
345e24ea55cSIan Campbell 
346e24ea55cSIan Campbell 	return error;
347e24ea55cSIan Campbell }
348e24ea55cSIan Campbell 
349e24ea55cSIan Campbell static const struct mmc_ops sunxi_mmc_ops = {
350e24ea55cSIan Campbell 	.send_cmd	= mmc_send_cmd,
351e24ea55cSIan Campbell 	.set_ios	= mmc_set_ios,
352e24ea55cSIan Campbell 	.init		= mmc_core_init,
353e24ea55cSIan Campbell };
354e24ea55cSIan Campbell 
355e24ea55cSIan Campbell int sunxi_mmc_init(int sdc_no)
356e24ea55cSIan Campbell {
357e24ea55cSIan Campbell 	struct mmc_config *cfg = &mmc_host[sdc_no].cfg;
358e24ea55cSIan Campbell 
359e24ea55cSIan Campbell 	memset(&mmc_host[sdc_no], 0, sizeof(struct sunxi_mmc_host));
360e24ea55cSIan Campbell 
361e24ea55cSIan Campbell 	cfg->name = "SUNXI SD/MMC";
362e24ea55cSIan Campbell 	cfg->ops  = &sunxi_mmc_ops;
363e24ea55cSIan Campbell 
364e24ea55cSIan Campbell 	cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
365e24ea55cSIan Campbell 	cfg->host_caps = MMC_MODE_4BIT;
366e24ea55cSIan Campbell 	cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
367a3e8c220SWills Wang #if defined(CONFIG_SUN6I) || defined(CONFIG_SUN7I) || defined(CONFIG_SUN8I)
368a3e8c220SWills Wang 	cfg->host_caps |= MMC_MODE_HC;
369a3e8c220SWills Wang #endif
370e24ea55cSIan Campbell 	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
371e24ea55cSIan Campbell 
372e24ea55cSIan Campbell 	cfg->f_min = 400000;
373e24ea55cSIan Campbell 	cfg->f_max = 52000000;
374e24ea55cSIan Campbell 
375e24ea55cSIan Campbell 	mmc_resource_init(sdc_no);
376e24ea55cSIan Campbell 	mmc_clk_io_on(sdc_no);
377e24ea55cSIan Campbell 
378e24ea55cSIan Campbell 	if (mmc_create(cfg, &mmc_host[sdc_no]) == NULL)
379e24ea55cSIan Campbell 		return -1;
380e24ea55cSIan Campbell 
381e24ea55cSIan Campbell 	return 0;
382e24ea55cSIan Campbell }
383