sunxi_mmc.c 15.1 KB
Newer Older
I
Ian Campbell 已提交
1 2 3 4 5 6 7 8 9 10 11
/*
 * (C) Copyright 2007-2011
 * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
 * Aaron <leafy.myeh@allwinnertech.com>
 *
 * MMC driver for allwinner sunxi platform.
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
12
#include <dm.h>
13
#include <errno.h>
I
Ian Campbell 已提交
14 15 16 17 18
#include <malloc.h>
#include <mmc.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/cpu.h>
19
#include <asm/arch/gpio.h>
I
Ian Campbell 已提交
20
#include <asm/arch/mmc.h>
21
#include <asm-generic/gpio.h>
I
Ian Campbell 已提交
22

23 24 25 26 27
struct sunxi_mmc_plat {
	struct mmc_config cfg;
	struct mmc mmc;
};

28
struct sunxi_mmc_priv {
I
Ian Campbell 已提交
29 30 31
	unsigned mmc_no;
	uint32_t *mclkreg;
	unsigned fatal_err;
32
	struct gpio_desc cd_gpio;	/* Change Detect GPIO */
I
Ian Campbell 已提交
33 34 35 36
	struct sunxi_mmc *reg;
	struct mmc_config cfg;
};

37
#if !CONFIG_IS_ENABLED(DM_MMC)
I
Ian Campbell 已提交
38
/* support 4 mmc hosts */
39
struct sunxi_mmc_priv mmc_host[4];
I
Ian Campbell 已提交
40

41 42 43 44 45 46 47 48
static int sunxi_mmc_getcd_gpio(int sdc_no)
{
	switch (sdc_no) {
	case 0: return sunxi_name_to_gpio(CONFIG_MMC0_CD_PIN);
	case 1: return sunxi_name_to_gpio(CONFIG_MMC1_CD_PIN);
	case 2: return sunxi_name_to_gpio(CONFIG_MMC2_CD_PIN);
	case 3: return sunxi_name_to_gpio(CONFIG_MMC3_CD_PIN);
	}
49
	return -EINVAL;
50 51
}

I
Ian Campbell 已提交
52 53
static int mmc_resource_init(int sdc_no)
{
54
	struct sunxi_mmc_priv *priv = &mmc_host[sdc_no];
I
Ian Campbell 已提交
55
	struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
56
	int cd_pin, ret = 0;
I
Ian Campbell 已提交
57 58 59 60 61

	debug("init mmc %d resource\n", sdc_no);

	switch (sdc_no) {
	case 0:
62 63
		priv->reg = (struct sunxi_mmc *)SUNXI_MMC0_BASE;
		priv->mclkreg = &ccm->sd0_clk_cfg;
I
Ian Campbell 已提交
64 65
		break;
	case 1:
66 67
		priv->reg = (struct sunxi_mmc *)SUNXI_MMC1_BASE;
		priv->mclkreg = &ccm->sd1_clk_cfg;
I
Ian Campbell 已提交
68 69
		break;
	case 2:
70 71
		priv->reg = (struct sunxi_mmc *)SUNXI_MMC2_BASE;
		priv->mclkreg = &ccm->sd2_clk_cfg;
I
Ian Campbell 已提交
72 73
		break;
	case 3:
74 75
		priv->reg = (struct sunxi_mmc *)SUNXI_MMC3_BASE;
		priv->mclkreg = &ccm->sd3_clk_cfg;
I
Ian Campbell 已提交
76 77 78 79 80
		break;
	default:
		printf("Wrong mmc number %d\n", sdc_no);
		return -1;
	}
81
	priv->mmc_no = sdc_no;
I
Ian Campbell 已提交
82

83
	cd_pin = sunxi_mmc_getcd_gpio(sdc_no);
84
	if (cd_pin >= 0) {
85
		ret = gpio_request(cd_pin, "mmc_cd");
86 87
		if (!ret) {
			sunxi_gpio_set_pull(cd_pin, SUNXI_GPIO_PULL_UP);
88
			ret = gpio_direction_input(cd_pin);
89
		}
90
	}
91 92

	return ret;
I
Ian Campbell 已提交
93
}
94
#endif
I
Ian Campbell 已提交
95

96
static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
97 98
{
	unsigned int pll, pll_hz, div, n, oclk_dly, sclk_dly;
M
Maxime Ripard 已提交
99 100 101 102 103 104 105 106 107 108 109 110
	bool new_mode = false;
	u32 val = 0;

	if (IS_ENABLED(CONFIG_MMC_SUNXI_HAS_NEW_MODE) && (priv->mmc_no == 2))
		new_mode = true;

	/*
	 * The MMC clock has an extra /2 post-divider when operating in the new
	 * mode.
	 */
	if (new_mode)
		hz = hz * 2;
111 112 113 114 115

	if (hz <= 24000000) {
		pll = CCM_MMC_CTRL_OSCM24;
		pll_hz = 24000000;
	} else {
116 117 118 119
#ifdef CONFIG_MACH_SUN9I
		pll = CCM_MMC_CTRL_PLL_PERIPH0;
		pll_hz = clock_get_pll4_periph0();
#else
120 121
		pll = CCM_MMC_CTRL_PLL6;
		pll_hz = clock_get_pll6();
122
#endif
123 124 125 126 127 128 129 130 131 132 133 134 135
	}

	div = pll_hz / hz;
	if (pll_hz % hz)
		div++;

	n = 0;
	while (div > 16) {
		n++;
		div = (div + 1) / 2;
	}

	if (n > 3) {
136 137
		printf("mmc %u error cannot set clock to %u\n", priv->mmc_no,
		       hz);
138 139 140 141 142 143
		return -1;
	}

	/* determine delays */
	if (hz <= 400000) {
		oclk_dly = 0;
144
		sclk_dly = 0;
145 146 147
	} else if (hz <= 25000000) {
		oclk_dly = 0;
		sclk_dly = 5;
148
#ifdef CONFIG_MACH_SUN9I
149
	} else if (hz <= 50000000) {
150 151
		oclk_dly = 5;
		sclk_dly = 4;
152 153 154 155
	} else {
		/* hz > 50000000 */
		oclk_dly = 2;
		sclk_dly = 4;
156 157 158 159 160 161 162 163 164
#else
	} else if (hz <= 50000000) {
		oclk_dly = 3;
		sclk_dly = 4;
	} else {
		/* hz > 50000000 */
		oclk_dly = 1;
		sclk_dly = 4;
#endif
165 166
	}

M
Maxime Ripard 已提交
167 168 169 170 171 172 173 174 175 176 177 178
	if (new_mode) {
#ifdef CONFIG_MMC_SUNXI_HAS_NEW_MODE
		val = CCM_MMC_CTRL_MODE_SEL_NEW;
		writel(SUNXI_MMC_NTSR_MODE_SEL_NEW, &priv->reg->ntsr);
#endif
	} else {
		val = CCM_MMC_CTRL_OCLK_DLY(oclk_dly) |
			CCM_MMC_CTRL_SCLK_DLY(sclk_dly);
	}

	writel(CCM_MMC_CTRL_ENABLE| pll | CCM_MMC_CTRL_N(n) |
	       CCM_MMC_CTRL_M(div) | val, priv->mclkreg);
179 180

	debug("mmc %u set mod-clk req %u parent %u n %u m %u rate %u\n",
181
	      priv->mmc_no, hz, pll_hz, 1u << n, div, pll_hz / (1u << n) / div);
182 183 184 185

	return 0;
}

186
static int mmc_update_clk(struct sunxi_mmc_priv *priv)
I
Ian Campbell 已提交
187 188 189 190 191 192 193
{
	unsigned int cmd;
	unsigned timeout_msecs = 2000;

	cmd = SUNXI_MMC_CMD_START |
	      SUNXI_MMC_CMD_UPCLK_ONLY |
	      SUNXI_MMC_CMD_WAIT_PRE_OVER;
194 195
	writel(cmd, &priv->reg->cmd);
	while (readl(&priv->reg->cmd) & SUNXI_MMC_CMD_START) {
I
Ian Campbell 已提交
196 197 198 199 200 201
		if (!timeout_msecs--)
			return -1;
		udelay(1000);
	}

	/* clock update sets various irq status bits, clear these */
202
	writel(readl(&priv->reg->rint), &priv->reg->rint);
I
Ian Campbell 已提交
203 204 205 206

	return 0;
}

207
static int mmc_config_clock(struct sunxi_mmc_priv *priv, struct mmc *mmc)
I
Ian Campbell 已提交
208
{
209
	unsigned rval = readl(&priv->reg->clkcr);
I
Ian Campbell 已提交
210 211 212

	/* Disable Clock */
	rval &= ~SUNXI_MMC_CLK_ENABLE;
213
	writel(rval, &priv->reg->clkcr);
214
	if (mmc_update_clk(priv))
I
Ian Campbell 已提交
215 216
		return -1;

217
	/* Set mod_clk to new rate */
218
	if (mmc_set_mod_clk(priv, mmc->clock))
219 220 221
		return -1;

	/* Clear internal divider */
I
Ian Campbell 已提交
222
	rval &= ~SUNXI_MMC_CLK_DIVIDER_MASK;
223
	writel(rval, &priv->reg->clkcr);
224

I
Ian Campbell 已提交
225 226
	/* Re-enable Clock */
	rval |= SUNXI_MMC_CLK_ENABLE;
227
	writel(rval, &priv->reg->clkcr);
228
	if (mmc_update_clk(priv))
I
Ian Campbell 已提交
229 230 231 232 233
		return -1;

	return 0;
}

234 235
static int sunxi_mmc_set_ios_common(struct sunxi_mmc_priv *priv,
				    struct mmc *mmc)
I
Ian Campbell 已提交
236
{
237 238
	debug("set ios: bus_width: %x, clock: %d\n",
	      mmc->bus_width, mmc->clock);
I
Ian Campbell 已提交
239 240

	/* Change clock first */
241
	if (mmc->clock && mmc_config_clock(priv, mmc) != 0) {
242
		priv->fatal_err = 1;
243
		return -EINVAL;
I
Ian Campbell 已提交
244 245 246 247
	}

	/* Change bus width */
	if (mmc->bus_width == 8)
248
		writel(0x2, &priv->reg->width);
I
Ian Campbell 已提交
249
	else if (mmc->bus_width == 4)
250
		writel(0x1, &priv->reg->width);
I
Ian Campbell 已提交
251
	else
252
		writel(0x0, &priv->reg->width);
253 254

	return 0;
I
Ian Campbell 已提交
255 256
}

257
#if !CONFIG_IS_ENABLED(DM_MMC)
258
static int sunxi_mmc_core_init(struct mmc *mmc)
I
Ian Campbell 已提交
259
{
260
	struct sunxi_mmc_priv *priv = mmc->priv;
I
Ian Campbell 已提交
261 262

	/* Reset controller */
263
	writel(SUNXI_MMC_GCTRL_RESET, &priv->reg->gctrl);
H
Hans de Goede 已提交
264
	udelay(1000);
I
Ian Campbell 已提交
265 266 267

	return 0;
}
268
#endif
I
Ian Campbell 已提交
269

270 271
static int mmc_trans_data_by_cpu(struct sunxi_mmc_priv *priv, struct mmc *mmc,
				 struct mmc_data *data)
I
Ian Campbell 已提交
272 273 274 275 276 277
{
	const int reading = !!(data->flags & MMC_DATA_READ);
	const uint32_t status_bit = reading ? SUNXI_MMC_STATUS_FIFO_EMPTY :
					      SUNXI_MMC_STATUS_FIFO_FULL;
	unsigned i;
	unsigned *buff = (unsigned int *)(reading ? data->dest : data->src);
278
	unsigned byte_cnt = data->blocksize * data->blocks;
279 280 281
	unsigned timeout_usecs = (byte_cnt >> 8) * 1000;
	if (timeout_usecs < 2000000)
		timeout_usecs = 2000000;
I
Ian Campbell 已提交
282

H
Hans de Goede 已提交
283
	/* Always read / write data through the CPU */
284
	setbits_le32(&priv->reg->gctrl, SUNXI_MMC_GCTRL_ACCESS_BY_AHB);
H
Hans de Goede 已提交
285

I
Ian Campbell 已提交
286
	for (i = 0; i < (byte_cnt >> 2); i++) {
287
		while (readl(&priv->reg->status) & status_bit) {
288
			if (!timeout_usecs--)
I
Ian Campbell 已提交
289
				return -1;
290
			udelay(1);
I
Ian Campbell 已提交
291 292 293
		}

		if (reading)
294
			buff[i] = readl(&priv->reg->fifo);
I
Ian Campbell 已提交
295
		else
296
			writel(buff[i], &priv->reg->fifo);
I
Ian Campbell 已提交
297 298 299 300 301
	}

	return 0;
}

302 303
static int mmc_rint_wait(struct sunxi_mmc_priv *priv, struct mmc *mmc,
			 uint timeout_msecs, uint done_bit, const char *what)
I
Ian Campbell 已提交
304 305 306 307
{
	unsigned int status;

	do {
308
		status = readl(&priv->reg->rint);
I
Ian Campbell 已提交
309 310 311 312
		if (!timeout_msecs-- ||
		    (status & SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT)) {
			debug("%s timeout %x\n", what,
			      status & SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT);
313
			return -ETIMEDOUT;
I
Ian Campbell 已提交
314 315 316 317 318 319 320
		}
		udelay(1000);
	} while (!(status & done_bit));

	return 0;
}

321 322 323
static int sunxi_mmc_send_cmd_common(struct sunxi_mmc_priv *priv,
				     struct mmc *mmc, struct mmc_cmd *cmd,
				     struct mmc_data *data)
I
Ian Campbell 已提交
324 325 326 327 328 329 330
{
	unsigned int cmdval = SUNXI_MMC_CMD_START;
	unsigned int timeout_msecs;
	int error = 0;
	unsigned int status = 0;
	unsigned int bytecnt = 0;

331
	if (priv->fatal_err)
I
Ian Campbell 已提交
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
		return -1;
	if (cmd->resp_type & MMC_RSP_BUSY)
		debug("mmc cmd %d check rsp busy\n", cmd->cmdidx);
	if (cmd->cmdidx == 12)
		return 0;

	if (!cmd->cmdidx)
		cmdval |= SUNXI_MMC_CMD_SEND_INIT_SEQ;
	if (cmd->resp_type & MMC_RSP_PRESENT)
		cmdval |= SUNXI_MMC_CMD_RESP_EXPIRE;
	if (cmd->resp_type & MMC_RSP_136)
		cmdval |= SUNXI_MMC_CMD_LONG_RESPONSE;
	if (cmd->resp_type & MMC_RSP_CRC)
		cmdval |= SUNXI_MMC_CMD_CHK_RESPONSE_CRC;

	if (data) {
348
		if ((u32)(long)data->dest & 0x3) {
I
Ian Campbell 已提交
349 350 351 352 353 354 355 356 357
			error = -1;
			goto out;
		}

		cmdval |= SUNXI_MMC_CMD_DATA_EXPIRE|SUNXI_MMC_CMD_WAIT_PRE_OVER;
		if (data->flags & MMC_DATA_WRITE)
			cmdval |= SUNXI_MMC_CMD_WRITE;
		if (data->blocks > 1)
			cmdval |= SUNXI_MMC_CMD_AUTO_STOP;
358 359
		writel(data->blocksize, &priv->reg->blksz);
		writel(data->blocks * data->blocksize, &priv->reg->bytecnt);
I
Ian Campbell 已提交
360 361
	}

362
	debug("mmc %d, cmd %d(0x%08x), arg 0x%08x\n", priv->mmc_no,
I
Ian Campbell 已提交
363
	      cmd->cmdidx, cmdval | cmd->cmdidx, cmd->cmdarg);
364
	writel(cmd->cmdarg, &priv->reg->arg);
I
Ian Campbell 已提交
365 366

	if (!data)
367
		writel(cmdval | cmd->cmdidx, &priv->reg->cmd);
I
Ian Campbell 已提交
368 369 370 371 372 373 374 375 376 377 378

	/*
	 * transfer data and check status
	 * STATREG[2] : FIFO empty
	 * STATREG[3] : FIFO full
	 */
	if (data) {
		int ret = 0;

		bytecnt = data->blocksize * data->blocks;
		debug("trans data %d bytes\n", bytecnt);
379
		writel(cmdval | cmd->cmdidx, &priv->reg->cmd);
380
		ret = mmc_trans_data_by_cpu(priv, mmc, data);
I
Ian Campbell 已提交
381
		if (ret) {
382
			error = readl(&priv->reg->rint) &
I
Ian Campbell 已提交
383
				SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT;
384
			error = -ETIMEDOUT;
I
Ian Campbell 已提交
385 386 387 388
			goto out;
		}
	}

389 390
	error = mmc_rint_wait(priv, mmc, 1000, SUNXI_MMC_RINT_COMMAND_DONE,
			      "cmd");
I
Ian Campbell 已提交
391 392 393 394
	if (error)
		goto out;

	if (data) {
H
Hans de Goede 已提交
395
		timeout_msecs = 120;
I
Ian Campbell 已提交
396
		debug("cacl timeout %x msec\n", timeout_msecs);
397
		error = mmc_rint_wait(priv, mmc, timeout_msecs,
I
Ian Campbell 已提交
398 399 400 401 402 403 404 405 406 407 408
				      data->blocks > 1 ?
				      SUNXI_MMC_RINT_AUTO_COMMAND_DONE :
				      SUNXI_MMC_RINT_DATA_OVER,
				      "data");
		if (error)
			goto out;
	}

	if (cmd->resp_type & MMC_RSP_BUSY) {
		timeout_msecs = 2000;
		do {
409
			status = readl(&priv->reg->status);
I
Ian Campbell 已提交
410 411
			if (!timeout_msecs--) {
				debug("busy timeout\n");
412
				error = -ETIMEDOUT;
I
Ian Campbell 已提交
413 414 415 416 417 418 419
				goto out;
			}
			udelay(1000);
		} while (status & SUNXI_MMC_STATUS_CARD_DATA_BUSY);
	}

	if (cmd->resp_type & MMC_RSP_136) {
420 421 422 423
		cmd->response[0] = readl(&priv->reg->resp3);
		cmd->response[1] = readl(&priv->reg->resp2);
		cmd->response[2] = readl(&priv->reg->resp1);
		cmd->response[3] = readl(&priv->reg->resp0);
I
Ian Campbell 已提交
424 425 426 427
		debug("mmc resp 0x%08x 0x%08x 0x%08x 0x%08x\n",
		      cmd->response[3], cmd->response[2],
		      cmd->response[1], cmd->response[0]);
	} else {
428
		cmd->response[0] = readl(&priv->reg->resp0);
I
Ian Campbell 已提交
429 430 431 432
		debug("mmc resp 0x%08x\n", cmd->response[0]);
	}
out:
	if (error < 0) {
433
		writel(SUNXI_MMC_GCTRL_RESET, &priv->reg->gctrl);
434
		mmc_update_clk(priv);
I
Ian Campbell 已提交
435
	}
436 437 438
	writel(0xffffffff, &priv->reg->rint);
	writel(readl(&priv->reg->gctrl) | SUNXI_MMC_GCTRL_FIFO_RESET,
	       &priv->reg->gctrl);
I
Ian Campbell 已提交
439 440 441 442

	return error;
}

443
#if !CONFIG_IS_ENABLED(DM_MMC)
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
static int sunxi_mmc_set_ios_legacy(struct mmc *mmc)
{
	struct sunxi_mmc_priv *priv = mmc->priv;

	return sunxi_mmc_set_ios_common(priv, mmc);
}

static int sunxi_mmc_send_cmd_legacy(struct mmc *mmc, struct mmc_cmd *cmd,
				     struct mmc_data *data)
{
	struct sunxi_mmc_priv *priv = mmc->priv;

	return sunxi_mmc_send_cmd_common(priv, mmc, cmd, data);
}

static int sunxi_mmc_getcd_legacy(struct mmc *mmc)
460
{
461
	struct sunxi_mmc_priv *priv = mmc->priv;
462
	int cd_pin;
463

464
	cd_pin = sunxi_mmc_getcd_gpio(priv->mmc_no);
465
	if (cd_pin < 0)
466 467
		return 1;

468
	return !gpio_get_value(cd_pin);
469 470
}

I
Ian Campbell 已提交
471
static const struct mmc_ops sunxi_mmc_ops = {
472 473
	.send_cmd	= sunxi_mmc_send_cmd_legacy,
	.set_ios	= sunxi_mmc_set_ios_legacy,
474
	.init		= sunxi_mmc_core_init,
475
	.getcd		= sunxi_mmc_getcd_legacy,
I
Ian Campbell 已提交
476 477
};

478
struct mmc *sunxi_mmc_init(int sdc_no)
I
Ian Campbell 已提交
479
{
480
	struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
481 482
	struct sunxi_mmc_priv *priv = &mmc_host[sdc_no];
	struct mmc_config *cfg = &priv->cfg;
483
	int ret;
I
Ian Campbell 已提交
484

485
	memset(priv, '\0', sizeof(struct sunxi_mmc_priv));
I
Ian Campbell 已提交
486 487 488 489 490 491

	cfg->name = "SUNXI SD/MMC";
	cfg->ops  = &sunxi_mmc_ops;

	cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
	cfg->host_caps = MMC_MODE_4BIT;
492
#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN8I)
493 494 495
	if (sdc_no == 2)
		cfg->host_caps = MMC_MODE_8BIT;
#endif
R
Rob Herring 已提交
496
	cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
I
Ian Campbell 已提交
497 498 499 500 501
	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;

	cfg->f_min = 400000;
	cfg->f_max = 52000000;

502 503 504
	if (mmc_resource_init(sdc_no) != 0)
		return NULL;

505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
	/* config ahb clock */
	debug("init mmc %d clock and io\n", sdc_no);
	setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MMC(sdc_no));

#ifdef CONFIG_SUNXI_GEN_SUN6I
	/* unassert reset */
	setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MMC(sdc_no));
#endif
#if defined(CONFIG_MACH_SUN9I)
	/* sun9i has a mmc-common module, also set the gate and reset there */
	writel(SUNXI_MMC_COMMON_CLK_GATE | SUNXI_MMC_COMMON_RESET,
	       SUNXI_MMC_COMMON_BASE + 4 * sdc_no);
#endif
	ret = mmc_set_mod_clk(priv, 24000000);
	if (ret)
		return NULL;
I
Ian Campbell 已提交
521

522
	return mmc_create(cfg, mmc_host);
I
Ian Campbell 已提交
523
}
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
#else

static int sunxi_mmc_set_ios(struct udevice *dev)
{
	struct sunxi_mmc_plat *plat = dev_get_platdata(dev);
	struct sunxi_mmc_priv *priv = dev_get_priv(dev);

	return sunxi_mmc_set_ios_common(priv, &plat->mmc);
}

static int sunxi_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
			      struct mmc_data *data)
{
	struct sunxi_mmc_plat *plat = dev_get_platdata(dev);
	struct sunxi_mmc_priv *priv = dev_get_priv(dev);

	return sunxi_mmc_send_cmd_common(priv, &plat->mmc, cmd, data);
}

static int sunxi_mmc_getcd(struct udevice *dev)
{
	struct sunxi_mmc_priv *priv = dev_get_priv(dev);

	if (dm_gpio_is_valid(&priv->cd_gpio))
		return dm_gpio_get_value(&priv->cd_gpio);

	return 1;
}

static const struct dm_mmc_ops sunxi_mmc_ops = {
	.send_cmd	= sunxi_mmc_send_cmd,
	.set_ios	= sunxi_mmc_set_ios,
	.get_cd		= sunxi_mmc_getcd,
};

static int sunxi_mmc_probe(struct udevice *dev)
{
	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
	struct sunxi_mmc_plat *plat = dev_get_platdata(dev);
	struct sunxi_mmc_priv *priv = dev_get_priv(dev);
	struct mmc_config *cfg = &plat->cfg;
	struct ofnode_phandle_args args;
	u32 *gate_reg;
	int bus_width, ret;

	cfg->name = dev->name;
	bus_width = dev_read_u32_default(dev, "bus-width", 1);

	cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
	cfg->host_caps = 0;
	if (bus_width == 8)
		cfg->host_caps |= MMC_MODE_8BIT;
	if (bus_width >= 4)
		cfg->host_caps |= MMC_MODE_4BIT;
	cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;

	cfg->f_min = 400000;
	cfg->f_max = 52000000;

	priv->reg = (void *)dev_read_addr(dev);

	/* We don't have a sunxi clock driver so find the clock address here */
	ret = dev_read_phandle_with_args(dev, "clocks", "#clock-cells", 0,
					  1, &args);
	if (ret)
		return ret;
	priv->mclkreg = (u32 *)ofnode_get_addr(args.node);

	ret = dev_read_phandle_with_args(dev, "clocks", "#clock-cells", 0,
					  0, &args);
	if (ret)
		return ret;
	gate_reg = (u32 *)ofnode_get_addr(args.node);
	setbits_le32(gate_reg, 1 << args.args[0]);
	priv->mmc_no = args.args[0] - 8;

	ret = mmc_set_mod_clk(priv, 24000000);
	if (ret)
		return ret;

	/* This GPIO is optional */
	if (!gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio,
				  GPIOD_IS_IN)) {
		int cd_pin = gpio_get_number(&priv->cd_gpio);

		sunxi_gpio_set_pull(cd_pin, SUNXI_GPIO_PULL_UP);
	}

	upriv->mmc = &plat->mmc;

	/* Reset controller */
	writel(SUNXI_MMC_GCTRL_RESET, &priv->reg->gctrl);
	udelay(1000);

	return 0;
}

static int sunxi_mmc_bind(struct udevice *dev)
{
	struct sunxi_mmc_plat *plat = dev_get_platdata(dev);

	return mmc_bind(dev, &plat->mmc, &plat->cfg);
}

static const struct udevice_id sunxi_mmc_ids[] = {
	{ .compatible = "allwinner,sun5i-a13-mmc" },
	{ }
};

U_BOOT_DRIVER(sunxi_mmc_drv) = {
	.name		= "sunxi_mmc",
	.id		= UCLASS_MMC,
	.of_match	= sunxi_mmc_ids,
	.bind		= sunxi_mmc_bind,
	.probe		= sunxi_mmc_probe,
	.ops		= &sunxi_mmc_ops,
	.platdata_auto_alloc_size = sizeof(struct sunxi_mmc_plat),
	.priv_auto_alloc_size = sizeof(struct sunxi_mmc_priv),
};
#endif