xref: /rk3399_rockchip-uboot/drivers/mmc/mmc.c (revision e56bff5b358874c5da2e9c1dd9c5bfa3a4bd113a)
1272cc70bSAndy Fleming /*
2272cc70bSAndy Fleming  * Copyright 2008, Freescale Semiconductor, Inc
3272cc70bSAndy Fleming  * Andy Fleming
4272cc70bSAndy Fleming  *
5272cc70bSAndy Fleming  * Based vaguely on the Linux code
6272cc70bSAndy Fleming  *
71a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
8272cc70bSAndy Fleming  */
9272cc70bSAndy Fleming 
10272cc70bSAndy Fleming #include <config.h>
11272cc70bSAndy Fleming #include <common.h>
12272cc70bSAndy Fleming #include <command.h>
138e3332e2SSjoerd Simons #include <dm.h>
148e3332e2SSjoerd Simons #include <dm/device-internal.h>
15d4622df3SStephen Warren #include <errno.h>
16272cc70bSAndy Fleming #include <mmc.h>
17272cc70bSAndy Fleming #include <part.h>
182051aefeSPeng Fan #include <power/regulator.h>
19272cc70bSAndy Fleming #include <malloc.h>
20cf92e05cSSimon Glass #include <memalign.h>
21272cc70bSAndy Fleming #include <linux/list.h>
229b1f942cSRabin Vincent #include <div64.h>
23da61fa5fSPaul Burton #include "mmc_private.h"
24272cc70bSAndy Fleming 
253697e599SPeng Fan static const unsigned int sd_au_size[] = {
263697e599SPeng Fan 	0,		SZ_16K / 512,		SZ_32K / 512,
273697e599SPeng Fan 	SZ_64K / 512,	SZ_128K / 512,		SZ_256K / 512,
283697e599SPeng Fan 	SZ_512K / 512,	SZ_1M / 512,		SZ_2M / 512,
293697e599SPeng Fan 	SZ_4M / 512,	SZ_8M / 512,		(SZ_8M + SZ_4M) / 512,
303697e599SPeng Fan 	SZ_16M / 512,	(SZ_16M + SZ_8M) / 512,	SZ_32M / 512,	SZ_64M / 512,
313697e599SPeng Fan };
323697e599SPeng Fan 
33e531136eSJason Zhu static char mmc_ext_csd[512];
34e531136eSJason Zhu 
35b5b838f1SMarek Vasut #if CONFIG_IS_ENABLED(MMC_TINY)
36b5b838f1SMarek Vasut static struct mmc mmc_static;
37b5b838f1SMarek Vasut struct mmc *find_mmc_device(int dev_num)
38b5b838f1SMarek Vasut {
39b5b838f1SMarek Vasut 	return &mmc_static;
40b5b838f1SMarek Vasut }
41b5b838f1SMarek Vasut 
42b5b838f1SMarek Vasut void mmc_do_preinit(void)
43b5b838f1SMarek Vasut {
44b5b838f1SMarek Vasut 	struct mmc *m = &mmc_static;
45b5b838f1SMarek Vasut #ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT
46b5b838f1SMarek Vasut 	mmc_set_preinit(m, 1);
47b5b838f1SMarek Vasut #endif
48b5b838f1SMarek Vasut 	if (m->preinit)
49b5b838f1SMarek Vasut 		mmc_start_init(m);
50b5b838f1SMarek Vasut }
51b5b838f1SMarek Vasut 
52b5b838f1SMarek Vasut struct blk_desc *mmc_get_blk_desc(struct mmc *mmc)
53b5b838f1SMarek Vasut {
54b5b838f1SMarek Vasut 	return &mmc->block_dev;
55b5b838f1SMarek Vasut }
56b5b838f1SMarek Vasut #endif
57b5b838f1SMarek Vasut 
58e7881d85SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC)
59750121c3SJeroen Hofstee __weak int board_mmc_getwp(struct mmc *mmc)
60d23d8d7eSNikita Kiryanov {
61d23d8d7eSNikita Kiryanov 	return -1;
62d23d8d7eSNikita Kiryanov }
63d23d8d7eSNikita Kiryanov 
64d23d8d7eSNikita Kiryanov int mmc_getwp(struct mmc *mmc)
65d23d8d7eSNikita Kiryanov {
66d23d8d7eSNikita Kiryanov 	int wp;
67d23d8d7eSNikita Kiryanov 
68d23d8d7eSNikita Kiryanov 	wp = board_mmc_getwp(mmc);
69d23d8d7eSNikita Kiryanov 
70d4e1da4eSPeter Korsgaard 	if (wp < 0) {
7193bfd616SPantelis Antoniou 		if (mmc->cfg->ops->getwp)
7293bfd616SPantelis Antoniou 			wp = mmc->cfg->ops->getwp(mmc);
73d4e1da4eSPeter Korsgaard 		else
74d4e1da4eSPeter Korsgaard 			wp = 0;
75d4e1da4eSPeter Korsgaard 	}
76d23d8d7eSNikita Kiryanov 
77d23d8d7eSNikita Kiryanov 	return wp;
78d23d8d7eSNikita Kiryanov }
79d23d8d7eSNikita Kiryanov 
80cee9ab7cSJeroen Hofstee __weak int board_mmc_getcd(struct mmc *mmc)
81cee9ab7cSJeroen Hofstee {
8211fdade2SStefano Babic 	return -1;
8311fdade2SStefano Babic }
848ca51e51SSimon Glass #endif
8511fdade2SStefano Babic 
868635ff9eSMarek Vasut #ifdef CONFIG_MMC_TRACE
87c0c76ebaSSimon Glass void mmmc_trace_before_send(struct mmc *mmc, struct mmc_cmd *cmd)
88c0c76ebaSSimon Glass {
89c0c76ebaSSimon Glass 	printf("CMD_SEND:%d\n", cmd->cmdidx);
90c0c76ebaSSimon Glass 	printf("\t\tARG\t\t\t 0x%08X\n", cmd->cmdarg);
91c0c76ebaSSimon Glass }
92c0c76ebaSSimon Glass 
93c0c76ebaSSimon Glass void mmmc_trace_after_send(struct mmc *mmc, struct mmc_cmd *cmd, int ret)
94c0c76ebaSSimon Glass {
955db2fe3aSRaffaele Recalcati 	int i;
965db2fe3aSRaffaele Recalcati 	u8 *ptr;
975db2fe3aSRaffaele Recalcati 
987863ce58SBin Meng 	if (ret) {
997863ce58SBin Meng 		printf("\t\tRET\t\t\t %d\n", ret);
1007863ce58SBin Meng 	} else {
1015db2fe3aSRaffaele Recalcati 		switch (cmd->resp_type) {
1025db2fe3aSRaffaele Recalcati 		case MMC_RSP_NONE:
1035db2fe3aSRaffaele Recalcati 			printf("\t\tMMC_RSP_NONE\n");
1045db2fe3aSRaffaele Recalcati 			break;
1055db2fe3aSRaffaele Recalcati 		case MMC_RSP_R1:
1065db2fe3aSRaffaele Recalcati 			printf("\t\tMMC_RSP_R1,5,6,7 \t 0x%08X \n",
1075db2fe3aSRaffaele Recalcati 				cmd->response[0]);
1085db2fe3aSRaffaele Recalcati 			break;
1095db2fe3aSRaffaele Recalcati 		case MMC_RSP_R1b:
1105db2fe3aSRaffaele Recalcati 			printf("\t\tMMC_RSP_R1b\t\t 0x%08X \n",
1115db2fe3aSRaffaele Recalcati 				cmd->response[0]);
1125db2fe3aSRaffaele Recalcati 			break;
1135db2fe3aSRaffaele Recalcati 		case MMC_RSP_R2:
1145db2fe3aSRaffaele Recalcati 			printf("\t\tMMC_RSP_R2\t\t 0x%08X \n",
1155db2fe3aSRaffaele Recalcati 				cmd->response[0]);
1165db2fe3aSRaffaele Recalcati 			printf("\t\t          \t\t 0x%08X \n",
1175db2fe3aSRaffaele Recalcati 				cmd->response[1]);
1185db2fe3aSRaffaele Recalcati 			printf("\t\t          \t\t 0x%08X \n",
1195db2fe3aSRaffaele Recalcati 				cmd->response[2]);
1205db2fe3aSRaffaele Recalcati 			printf("\t\t          \t\t 0x%08X \n",
1215db2fe3aSRaffaele Recalcati 				cmd->response[3]);
1225db2fe3aSRaffaele Recalcati 			printf("\n");
1235db2fe3aSRaffaele Recalcati 			printf("\t\t\t\t\tDUMPING DATA\n");
1245db2fe3aSRaffaele Recalcati 			for (i = 0; i < 4; i++) {
1255db2fe3aSRaffaele Recalcati 				int j;
1265db2fe3aSRaffaele Recalcati 				printf("\t\t\t\t\t%03d - ", i*4);
127146bec79SDirk Behme 				ptr = (u8 *)&cmd->response[i];
1285db2fe3aSRaffaele Recalcati 				ptr += 3;
1295db2fe3aSRaffaele Recalcati 				for (j = 0; j < 4; j++)
1305db2fe3aSRaffaele Recalcati 					printf("%02X ", *ptr--);
1315db2fe3aSRaffaele Recalcati 				printf("\n");
1325db2fe3aSRaffaele Recalcati 			}
1335db2fe3aSRaffaele Recalcati 			break;
1345db2fe3aSRaffaele Recalcati 		case MMC_RSP_R3:
1355db2fe3aSRaffaele Recalcati 			printf("\t\tMMC_RSP_R3,4\t\t 0x%08X \n",
1365db2fe3aSRaffaele Recalcati 				cmd->response[0]);
1375db2fe3aSRaffaele Recalcati 			break;
1385db2fe3aSRaffaele Recalcati 		default:
1395db2fe3aSRaffaele Recalcati 			printf("\t\tERROR MMC rsp not supported\n");
1405db2fe3aSRaffaele Recalcati 			break;
1415db2fe3aSRaffaele Recalcati 		}
1427863ce58SBin Meng 	}
143c0c76ebaSSimon Glass }
144c0c76ebaSSimon Glass 
145c0c76ebaSSimon Glass void mmc_trace_state(struct mmc *mmc, struct mmc_cmd *cmd)
146c0c76ebaSSimon Glass {
147c0c76ebaSSimon Glass 	int status;
148c0c76ebaSSimon Glass 
149c0c76ebaSSimon Glass 	status = (cmd->response[0] & MMC_STATUS_CURR_STATE) >> 9;
150c0c76ebaSSimon Glass 	printf("CURR STATE:%d\n", status);
151c0c76ebaSSimon Glass }
1525db2fe3aSRaffaele Recalcati #endif
153c0c76ebaSSimon Glass 
154e7881d85SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC)
155c0c76ebaSSimon Glass int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
156c0c76ebaSSimon Glass {
157c0c76ebaSSimon Glass 	int ret;
158c0c76ebaSSimon Glass 
159c0c76ebaSSimon Glass 	mmmc_trace_before_send(mmc, cmd);
160c0c76ebaSSimon Glass 	ret = mmc->cfg->ops->send_cmd(mmc, cmd, data);
161c0c76ebaSSimon Glass 	mmmc_trace_after_send(mmc, cmd, ret);
162c0c76ebaSSimon Glass 
1638635ff9eSMarek Vasut 	return ret;
164272cc70bSAndy Fleming }
1658ca51e51SSimon Glass #endif
166272cc70bSAndy Fleming 
167da61fa5fSPaul Burton int mmc_send_status(struct mmc *mmc, int timeout)
1685d4fc8d9SRaffaele Recalcati {
1695d4fc8d9SRaffaele Recalcati 	struct mmc_cmd cmd;
170d617c426SJan Kloetzke 	int err, retries = 5;
1715d4fc8d9SRaffaele Recalcati 
1725d4fc8d9SRaffaele Recalcati 	cmd.cmdidx = MMC_CMD_SEND_STATUS;
1735d4fc8d9SRaffaele Recalcati 	cmd.resp_type = MMC_RSP_R1;
174aaf3d41aSMarek Vasut 	if (!mmc_host_is_spi(mmc))
175aaf3d41aSMarek Vasut 		cmd.cmdarg = mmc->rca << 16;
1765d4fc8d9SRaffaele Recalcati 
1771677eef4SAndrew Gabbasov 	while (1) {
1785d4fc8d9SRaffaele Recalcati 		err = mmc_send_cmd(mmc, &cmd, NULL);
179d617c426SJan Kloetzke 		if (!err) {
180d617c426SJan Kloetzke 			if ((cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) &&
181d617c426SJan Kloetzke 			    (cmd.response[0] & MMC_STATUS_CURR_STATE) !=
182d617c426SJan Kloetzke 			     MMC_STATE_PRG)
1835d4fc8d9SRaffaele Recalcati 				break;
184d617c426SJan Kloetzke 			else if (cmd.response[0] & MMC_STATUS_MASK) {
18556196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
186d617c426SJan Kloetzke 				printf("Status Error: 0x%08X\n",
187d617c426SJan Kloetzke 					cmd.response[0]);
18856196826SPaul Burton #endif
189915ffa52SJaehoon Chung 				return -ECOMM;
190d617c426SJan Kloetzke 			}
191d617c426SJan Kloetzke 		} else if (--retries < 0)
192d617c426SJan Kloetzke 			return err;
1935d4fc8d9SRaffaele Recalcati 
1941677eef4SAndrew Gabbasov 		if (timeout-- <= 0)
1951677eef4SAndrew Gabbasov 			break;
1965d4fc8d9SRaffaele Recalcati 
1971677eef4SAndrew Gabbasov 		udelay(1000);
1981677eef4SAndrew Gabbasov 	}
1995d4fc8d9SRaffaele Recalcati 
200c0c76ebaSSimon Glass 	mmc_trace_state(mmc, &cmd);
2015b0c942fSJongman Heo 	if (timeout <= 0) {
20256196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
2035d4fc8d9SRaffaele Recalcati 		printf("Timeout waiting card ready\n");
20456196826SPaul Burton #endif
205915ffa52SJaehoon Chung 		return -ETIMEDOUT;
2065d4fc8d9SRaffaele Recalcati 	}
2075d4fc8d9SRaffaele Recalcati 
2085d4fc8d9SRaffaele Recalcati 	return 0;
2095d4fc8d9SRaffaele Recalcati }
2105d4fc8d9SRaffaele Recalcati 
211da61fa5fSPaul Burton int mmc_set_blocklen(struct mmc *mmc, int len)
212272cc70bSAndy Fleming {
213272cc70bSAndy Fleming 	struct mmc_cmd cmd;
214272cc70bSAndy Fleming 
215caa21a21SZiyuan Xu 	if (mmc_card_ddr(mmc))
216d22e3d46SJaehoon Chung 		return 0;
217d22e3d46SJaehoon Chung 
218272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SET_BLOCKLEN;
219272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
220272cc70bSAndy Fleming 	cmd.cmdarg = len;
221272cc70bSAndy Fleming 
222272cc70bSAndy Fleming 	return mmc_send_cmd(mmc, &cmd, NULL);
223272cc70bSAndy Fleming }
224272cc70bSAndy Fleming 
225ff8fef56SSascha Silbe static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start,
226fdbb873eSKim Phillips 			   lbaint_t blkcnt)
227272cc70bSAndy Fleming {
228272cc70bSAndy Fleming 	struct mmc_cmd cmd;
229272cc70bSAndy Fleming 	struct mmc_data data;
230272cc70bSAndy Fleming 
2314a1a06bcSAlagu Sankar 	if (blkcnt > 1)
2324a1a06bcSAlagu Sankar 		cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
2334a1a06bcSAlagu Sankar 	else
234272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
235272cc70bSAndy Fleming 
236272cc70bSAndy Fleming 	if (mmc->high_capacity)
2374a1a06bcSAlagu Sankar 		cmd.cmdarg = start;
238272cc70bSAndy Fleming 	else
2394a1a06bcSAlagu Sankar 		cmd.cmdarg = start * mmc->read_bl_len;
240272cc70bSAndy Fleming 
241272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
242272cc70bSAndy Fleming 
243272cc70bSAndy Fleming 	data.dest = dst;
2444a1a06bcSAlagu Sankar 	data.blocks = blkcnt;
245272cc70bSAndy Fleming 	data.blocksize = mmc->read_bl_len;
246272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
247272cc70bSAndy Fleming 
2484a1a06bcSAlagu Sankar 	if (mmc_send_cmd(mmc, &cmd, &data))
2494a1a06bcSAlagu Sankar 		return 0;
2504a1a06bcSAlagu Sankar 
2514a1a06bcSAlagu Sankar 	if (blkcnt > 1) {
2524a1a06bcSAlagu Sankar 		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
2534a1a06bcSAlagu Sankar 		cmd.cmdarg = 0;
2544a1a06bcSAlagu Sankar 		cmd.resp_type = MMC_RSP_R1b;
2554a1a06bcSAlagu Sankar 		if (mmc_send_cmd(mmc, &cmd, NULL)) {
25656196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
2574a1a06bcSAlagu Sankar 			printf("mmc fail to send stop cmd\n");
25856196826SPaul Burton #endif
2594a1a06bcSAlagu Sankar 			return 0;
2604a1a06bcSAlagu Sankar 		}
261272cc70bSAndy Fleming 	}
262272cc70bSAndy Fleming 
2634a1a06bcSAlagu Sankar 	return blkcnt;
264272cc70bSAndy Fleming }
265272cc70bSAndy Fleming 
26647f7fd3aSJason Zhu #ifdef CONFIG_SPL_BLK_READ_PREPARE
26747f7fd3aSJason Zhu static int mmc_read_blocks_prepare(struct mmc *mmc, void *dst, lbaint_t start,
26847f7fd3aSJason Zhu 				   lbaint_t blkcnt)
26947f7fd3aSJason Zhu {
27047f7fd3aSJason Zhu 	struct mmc_cmd cmd;
27147f7fd3aSJason Zhu 	struct mmc_data data;
27247f7fd3aSJason Zhu 
27347f7fd3aSJason Zhu 	if (blkcnt > 1)
27447f7fd3aSJason Zhu 		cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
27547f7fd3aSJason Zhu 	else
27647f7fd3aSJason Zhu 		cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
27747f7fd3aSJason Zhu 
27847f7fd3aSJason Zhu 	if (mmc->high_capacity)
27947f7fd3aSJason Zhu 		cmd.cmdarg = start;
28047f7fd3aSJason Zhu 	else
28147f7fd3aSJason Zhu 		cmd.cmdarg = start * mmc->read_bl_len;
28247f7fd3aSJason Zhu 
28347f7fd3aSJason Zhu 	cmd.resp_type = MMC_RSP_R1;
28447f7fd3aSJason Zhu 
28547f7fd3aSJason Zhu 	data.dest = dst;
28647f7fd3aSJason Zhu 	data.blocks = blkcnt;
28747f7fd3aSJason Zhu 	data.blocksize = mmc->read_bl_len;
28847f7fd3aSJason Zhu 	data.flags = MMC_DATA_READ;
28947f7fd3aSJason Zhu 
29047f7fd3aSJason Zhu 	if (mmc_send_cmd_prepare(mmc, &cmd, &data))
29147f7fd3aSJason Zhu 		return 0;
29247f7fd3aSJason Zhu 
29347f7fd3aSJason Zhu 	return blkcnt;
29447f7fd3aSJason Zhu }
29547f7fd3aSJason Zhu #endif
29647f7fd3aSJason Zhu 
29747f7fd3aSJason Zhu #ifdef CONFIG_SPL_BLK_READ_PREPARE
29847f7fd3aSJason Zhu #if CONFIG_IS_ENABLED(BLK)
29947f7fd3aSJason Zhu ulong mmc_bread_prepare(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, void *dst)
30047f7fd3aSJason Zhu #else
30147f7fd3aSJason Zhu ulong mmc_bread_prepare(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
30247f7fd3aSJason Zhu 			void *dst)
30347f7fd3aSJason Zhu #endif
30447f7fd3aSJason Zhu {
30547f7fd3aSJason Zhu #if CONFIG_IS_ENABLED(BLK)
30647f7fd3aSJason Zhu 	struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
30747f7fd3aSJason Zhu #endif
30847f7fd3aSJason Zhu 	int dev_num = block_dev->devnum;
30947f7fd3aSJason Zhu 	int timeout = 0;
31047f7fd3aSJason Zhu 	int err;
31147f7fd3aSJason Zhu 
31247f7fd3aSJason Zhu 	if (blkcnt == 0)
31347f7fd3aSJason Zhu 		return 0;
31447f7fd3aSJason Zhu 
31547f7fd3aSJason Zhu 	struct mmc *mmc = find_mmc_device(dev_num);
31647f7fd3aSJason Zhu 
31747f7fd3aSJason Zhu 	if (!mmc)
31847f7fd3aSJason Zhu 		return 0;
31947f7fd3aSJason Zhu 
32047f7fd3aSJason Zhu 	if (CONFIG_IS_ENABLED(MMC_TINY))
32147f7fd3aSJason Zhu 		err = mmc_switch_part(mmc, block_dev->hwpart);
32247f7fd3aSJason Zhu 	else
32347f7fd3aSJason Zhu 		err = blk_dselect_hwpart(block_dev, block_dev->hwpart);
32447f7fd3aSJason Zhu 
32547f7fd3aSJason Zhu 	if (err < 0)
32647f7fd3aSJason Zhu 		return 0;
32747f7fd3aSJason Zhu 
32847f7fd3aSJason Zhu 	if ((start + blkcnt) > block_dev->lba) {
32947f7fd3aSJason Zhu #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
33047f7fd3aSJason Zhu 		printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
33147f7fd3aSJason Zhu 		       start + blkcnt, block_dev->lba);
33247f7fd3aSJason Zhu #endif
33347f7fd3aSJason Zhu 		return 0;
33447f7fd3aSJason Zhu 	}
33547f7fd3aSJason Zhu 
33647f7fd3aSJason Zhu 	if (mmc_set_blocklen(mmc, mmc->read_bl_len)) {
33747f7fd3aSJason Zhu 		debug("%s: Failed to set blocklen\n", __func__);
33847f7fd3aSJason Zhu 		return 0;
33947f7fd3aSJason Zhu 	}
34047f7fd3aSJason Zhu 
34147f7fd3aSJason Zhu 	if (mmc_read_blocks_prepare(mmc, dst, start, blkcnt) != blkcnt) {
34247f7fd3aSJason Zhu 		debug("%s: Failed to read blocks\n", __func__);
34347f7fd3aSJason Zhu re_init_retry:
34447f7fd3aSJason Zhu 		timeout++;
34547f7fd3aSJason Zhu 		/*
34647f7fd3aSJason Zhu 		 * Try re-init seven times.
34747f7fd3aSJason Zhu 		 */
34847f7fd3aSJason Zhu 		if (timeout > 7) {
34947f7fd3aSJason Zhu 			printf("Re-init retry timeout\n");
35047f7fd3aSJason Zhu 			return 0;
35147f7fd3aSJason Zhu 		}
35247f7fd3aSJason Zhu 
35347f7fd3aSJason Zhu 		mmc->has_init = 0;
35447f7fd3aSJason Zhu 		if (mmc_init(mmc))
35547f7fd3aSJason Zhu 			return 0;
35647f7fd3aSJason Zhu 
35747f7fd3aSJason Zhu 		if (mmc_read_blocks_prepare(mmc, dst, start, blkcnt) != blkcnt) {
35847f7fd3aSJason Zhu 			printf("%s: Re-init mmc_read_blocks_prepare error\n",
35947f7fd3aSJason Zhu 			       __func__);
36047f7fd3aSJason Zhu 			goto re_init_retry;
36147f7fd3aSJason Zhu 		}
36247f7fd3aSJason Zhu 	}
36347f7fd3aSJason Zhu 
36447f7fd3aSJason Zhu 	return blkcnt;
36547f7fd3aSJason Zhu }
36647f7fd3aSJason Zhu #endif
36747f7fd3aSJason Zhu 
3687863dac1SJason Zhu #if CONFIG_IS_ENABLED(BLK)
3697863dac1SJason Zhu ulong mmc_bread(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, void *dst)
3707863dac1SJason Zhu #else
3717863dac1SJason Zhu ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
3727863dac1SJason Zhu 		void *dst)
3737863dac1SJason Zhu #endif
3747863dac1SJason Zhu {
3757863dac1SJason Zhu #if CONFIG_IS_ENABLED(BLK)
3767863dac1SJason Zhu 	struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
3777863dac1SJason Zhu #endif
3787863dac1SJason Zhu 	int dev_num = block_dev->devnum;
3797863dac1SJason Zhu 	int err;
3807863dac1SJason Zhu 	lbaint_t cur, blocks_todo = blkcnt;
3817863dac1SJason Zhu 
3827863dac1SJason Zhu #ifdef CONFIG_SPL_BLK_READ_PREPARE
3837863dac1SJason Zhu 	if (block_dev->op_flag == BLK_PRE_RW)
3847863dac1SJason Zhu #if CONFIG_IS_ENABLED(BLK)
3857863dac1SJason Zhu 		return mmc_bread_prepare(dev, start, blkcnt, dst);
3867863dac1SJason Zhu #else
3877863dac1SJason Zhu 		return mmc_bread_prepare(block_dev, start, blkcnt, dst);
3887863dac1SJason Zhu #endif
3897863dac1SJason Zhu #endif
3907863dac1SJason Zhu 	if (blkcnt == 0)
3917863dac1SJason Zhu 		return 0;
3927863dac1SJason Zhu 
3937863dac1SJason Zhu 	struct mmc *mmc = find_mmc_device(dev_num);
3947863dac1SJason Zhu 	if (!mmc)
3957863dac1SJason Zhu 		return 0;
3967863dac1SJason Zhu 
3977863dac1SJason Zhu 	if (CONFIG_IS_ENABLED(MMC_TINY))
3987863dac1SJason Zhu 		err = mmc_switch_part(mmc, block_dev->hwpart);
3997863dac1SJason Zhu 	else
4007863dac1SJason Zhu 		err = blk_dselect_hwpart(block_dev, block_dev->hwpart);
4017863dac1SJason Zhu 
4027863dac1SJason Zhu 	if (err < 0)
4037863dac1SJason Zhu 		return 0;
4047863dac1SJason Zhu 
4057863dac1SJason Zhu 	if ((start + blkcnt) > block_dev->lba) {
4067863dac1SJason Zhu #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
4077863dac1SJason Zhu 		printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
4087863dac1SJason Zhu 			start + blkcnt, block_dev->lba);
4097863dac1SJason Zhu #endif
4107863dac1SJason Zhu 		return 0;
4117863dac1SJason Zhu 	}
4127863dac1SJason Zhu 
4137863dac1SJason Zhu 	if (mmc_set_blocklen(mmc, mmc->read_bl_len)) {
4147863dac1SJason Zhu 		debug("%s: Failed to set blocklen\n", __func__);
4157863dac1SJason Zhu 		return 0;
4167863dac1SJason Zhu 	}
4177863dac1SJason Zhu 
4187863dac1SJason Zhu 	do {
4197863dac1SJason Zhu 		cur = (blocks_todo > mmc->cfg->b_max) ?
4207863dac1SJason Zhu 			mmc->cfg->b_max : blocks_todo;
4217863dac1SJason Zhu 		if (mmc_read_blocks(mmc, dst, start, cur) != cur) {
4227863dac1SJason Zhu 			debug("%s: Failed to read blocks\n", __func__);
4237863dac1SJason Zhu 			int timeout = 0;
4247863dac1SJason Zhu re_init_retry:
4257863dac1SJason Zhu 			timeout++;
4267863dac1SJason Zhu 			/*
4277863dac1SJason Zhu 			 * Try re-init seven times.
4287863dac1SJason Zhu 			 */
4297863dac1SJason Zhu 			if (timeout > 7) {
4307863dac1SJason Zhu 				printf("Re-init retry timeout\n");
4317863dac1SJason Zhu 				return 0;
4327863dac1SJason Zhu 			}
4337863dac1SJason Zhu 
4347863dac1SJason Zhu 			mmc->has_init = 0;
4357863dac1SJason Zhu 			if (mmc_init(mmc))
4367863dac1SJason Zhu 				return 0;
4377863dac1SJason Zhu 
4387863dac1SJason Zhu 			if (mmc_read_blocks(mmc, dst, start, cur) != cur) {
4397863dac1SJason Zhu 				printf("%s: Re-init mmc_read_blocks error\n",
4407863dac1SJason Zhu 				       __func__);
4417863dac1SJason Zhu 				goto re_init_retry;
4427863dac1SJason Zhu 			}
4437863dac1SJason Zhu 		}
4447863dac1SJason Zhu 		blocks_todo -= cur;
4457863dac1SJason Zhu 		start += cur;
4467863dac1SJason Zhu 		dst += cur * mmc->read_bl_len;
4477863dac1SJason Zhu 	} while (blocks_todo > 0);
4487863dac1SJason Zhu 
4497863dac1SJason Zhu 	return blkcnt;
4507863dac1SJason Zhu }
4517863dac1SJason Zhu 
45249dba033SZiyuan Xu void mmc_set_clock(struct mmc *mmc, uint clock)
45349dba033SZiyuan Xu {
45449dba033SZiyuan Xu 	if (clock > mmc->cfg->f_max)
45549dba033SZiyuan Xu 		clock = mmc->cfg->f_max;
45649dba033SZiyuan Xu 
45749dba033SZiyuan Xu 	if (clock < mmc->cfg->f_min)
45849dba033SZiyuan Xu 		clock = mmc->cfg->f_min;
45949dba033SZiyuan Xu 
46049dba033SZiyuan Xu 	mmc->clock = clock;
46149dba033SZiyuan Xu 
46249dba033SZiyuan Xu 	mmc_set_ios(mmc);
46349dba033SZiyuan Xu }
46449dba033SZiyuan Xu 
46549dba033SZiyuan Xu static void mmc_set_bus_width(struct mmc *mmc, uint width)
46649dba033SZiyuan Xu {
46749dba033SZiyuan Xu 	mmc->bus_width = width;
46849dba033SZiyuan Xu 
46949dba033SZiyuan Xu 	mmc_set_ios(mmc);
47049dba033SZiyuan Xu }
47149dba033SZiyuan Xu 
47281db2d36SZiyuan Xu static void mmc_set_timing(struct mmc *mmc, uint timing)
47381db2d36SZiyuan Xu {
47481db2d36SZiyuan Xu 	mmc->timing = timing;
47581db2d36SZiyuan Xu 	mmc_set_ios(mmc);
47681db2d36SZiyuan Xu }
47781db2d36SZiyuan Xu 
478fdbb873eSKim Phillips static int mmc_go_idle(struct mmc *mmc)
479272cc70bSAndy Fleming {
480272cc70bSAndy Fleming 	struct mmc_cmd cmd;
481272cc70bSAndy Fleming 	int err;
482272cc70bSAndy Fleming 
483272cc70bSAndy Fleming 	udelay(1000);
484272cc70bSAndy Fleming 
485272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_GO_IDLE_STATE;
486272cc70bSAndy Fleming 	cmd.cmdarg = 0;
487272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_NONE;
488272cc70bSAndy Fleming 
489272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
490272cc70bSAndy Fleming 
491272cc70bSAndy Fleming 	if (err)
492272cc70bSAndy Fleming 		return err;
493272cc70bSAndy Fleming 
494272cc70bSAndy Fleming 	udelay(2000);
495272cc70bSAndy Fleming 
496272cc70bSAndy Fleming 	return 0;
497272cc70bSAndy Fleming }
498272cc70bSAndy Fleming 
499479fbf72SJason Zhu #ifndef CONFIG_MMC_USE_PRE_CONFIG
500fdbb873eSKim Phillips static int sd_send_op_cond(struct mmc *mmc)
501272cc70bSAndy Fleming {
502272cc70bSAndy Fleming 	int timeout = 1000;
503272cc70bSAndy Fleming 	int err;
504272cc70bSAndy Fleming 	struct mmc_cmd cmd;
505272cc70bSAndy Fleming 
5061677eef4SAndrew Gabbasov 	while (1) {
507272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_APP_CMD;
508272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R1;
509272cc70bSAndy Fleming 		cmd.cmdarg = 0;
510272cc70bSAndy Fleming 
511272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
512272cc70bSAndy Fleming 
513272cc70bSAndy Fleming 		if (err)
514272cc70bSAndy Fleming 			return err;
515272cc70bSAndy Fleming 
516272cc70bSAndy Fleming 		cmd.cmdidx = SD_CMD_APP_SEND_OP_COND;
517272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R3;
518250de12bSStefano Babic 
519250de12bSStefano Babic 		/*
520250de12bSStefano Babic 		 * Most cards do not answer if some reserved bits
521250de12bSStefano Babic 		 * in the ocr are set. However, Some controller
522250de12bSStefano Babic 		 * can set bit 7 (reserved for low voltages), but
523250de12bSStefano Babic 		 * how to manage low voltages SD card is not yet
524250de12bSStefano Babic 		 * specified.
525250de12bSStefano Babic 		 */
526d52ebf10SThomas Chou 		cmd.cmdarg = mmc_host_is_spi(mmc) ? 0 :
52793bfd616SPantelis Antoniou 			(mmc->cfg->voltages & 0xff8000);
528272cc70bSAndy Fleming 
529272cc70bSAndy Fleming 		if (mmc->version == SD_VERSION_2)
530272cc70bSAndy Fleming 			cmd.cmdarg |= OCR_HCS;
531272cc70bSAndy Fleming 
532272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
533272cc70bSAndy Fleming 
534272cc70bSAndy Fleming 		if (err)
535272cc70bSAndy Fleming 			return err;
536272cc70bSAndy Fleming 
5371677eef4SAndrew Gabbasov 		if (cmd.response[0] & OCR_BUSY)
5381677eef4SAndrew Gabbasov 			break;
539272cc70bSAndy Fleming 
5401677eef4SAndrew Gabbasov 		if (timeout-- <= 0)
541915ffa52SJaehoon Chung 			return -EOPNOTSUPP;
542272cc70bSAndy Fleming 
5431677eef4SAndrew Gabbasov 		udelay(1000);
5441677eef4SAndrew Gabbasov 	}
5451677eef4SAndrew Gabbasov 
546272cc70bSAndy Fleming 	if (mmc->version != SD_VERSION_2)
547272cc70bSAndy Fleming 		mmc->version = SD_VERSION_1_0;
548272cc70bSAndy Fleming 
549d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc)) { /* read OCR for spi */
550d52ebf10SThomas Chou 		cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
551d52ebf10SThomas Chou 		cmd.resp_type = MMC_RSP_R3;
552d52ebf10SThomas Chou 		cmd.cmdarg = 0;
553d52ebf10SThomas Chou 
554d52ebf10SThomas Chou 		err = mmc_send_cmd(mmc, &cmd, NULL);
555d52ebf10SThomas Chou 
556d52ebf10SThomas Chou 		if (err)
557d52ebf10SThomas Chou 			return err;
558d52ebf10SThomas Chou 	}
559d52ebf10SThomas Chou 
560998be3ddSRabin Vincent 	mmc->ocr = cmd.response[0];
561272cc70bSAndy Fleming 
562272cc70bSAndy Fleming 	mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
563272cc70bSAndy Fleming 	mmc->rca = 0;
564272cc70bSAndy Fleming 
565272cc70bSAndy Fleming 	return 0;
566272cc70bSAndy Fleming }
567479fbf72SJason Zhu #endif
568272cc70bSAndy Fleming 
5695289b535SAndrew Gabbasov static int mmc_send_op_cond_iter(struct mmc *mmc, int use_arg)
570272cc70bSAndy Fleming {
5715289b535SAndrew Gabbasov 	struct mmc_cmd cmd;
572272cc70bSAndy Fleming 	int err;
573272cc70bSAndy Fleming 
5745289b535SAndrew Gabbasov 	cmd.cmdidx = MMC_CMD_SEND_OP_COND;
5755289b535SAndrew Gabbasov 	cmd.resp_type = MMC_RSP_R3;
5765289b535SAndrew Gabbasov 	cmd.cmdarg = 0;
5775a20397bSRob Herring 	if (use_arg && !mmc_host_is_spi(mmc))
5785a20397bSRob Herring 		cmd.cmdarg = OCR_HCS |
57993bfd616SPantelis Antoniou 			(mmc->cfg->voltages &
580a626c8d4SAndrew Gabbasov 			(mmc->ocr & OCR_VOLTAGE_MASK)) |
581a626c8d4SAndrew Gabbasov 			(mmc->ocr & OCR_ACCESS_MODE);
582e9550449SChe-Liang Chiou 
5835289b535SAndrew Gabbasov 	err = mmc_send_cmd(mmc, &cmd, NULL);
584e9550449SChe-Liang Chiou 	if (err)
585e9550449SChe-Liang Chiou 		return err;
5865289b535SAndrew Gabbasov 	mmc->ocr = cmd.response[0];
587e9550449SChe-Liang Chiou 	return 0;
588e9550449SChe-Liang Chiou }
589e9550449SChe-Liang Chiou 
590479fbf72SJason Zhu #ifndef CONFIG_MMC_USE_PRE_CONFIG
591750121c3SJeroen Hofstee static int mmc_send_op_cond(struct mmc *mmc)
592e9550449SChe-Liang Chiou {
593e9550449SChe-Liang Chiou 	int err, i;
594e9550449SChe-Liang Chiou 
595272cc70bSAndy Fleming 	/* Some cards seem to need this */
596272cc70bSAndy Fleming 	mmc_go_idle(mmc);
597272cc70bSAndy Fleming 
59831cacbabSRaffaele Recalcati  	/* Asking to the card its capabilities */
599e9550449SChe-Liang Chiou 	for (i = 0; i < 2; i++) {
6005289b535SAndrew Gabbasov 		err = mmc_send_op_cond_iter(mmc, i != 0);
60131cacbabSRaffaele Recalcati 		if (err)
60231cacbabSRaffaele Recalcati 			return err;
60331cacbabSRaffaele Recalcati 
604e9550449SChe-Liang Chiou 		/* exit if not busy (flag seems to be inverted) */
605a626c8d4SAndrew Gabbasov 		if (mmc->ocr & OCR_BUSY)
606bd47c135SAndrew Gabbasov 			break;
607e9550449SChe-Liang Chiou 	}
608bd47c135SAndrew Gabbasov 	mmc->op_cond_pending = 1;
609bd47c135SAndrew Gabbasov 	return 0;
610e9550449SChe-Liang Chiou }
611479fbf72SJason Zhu #endif
612750121c3SJeroen Hofstee static int mmc_complete_op_cond(struct mmc *mmc)
613e9550449SChe-Liang Chiou {
614e9550449SChe-Liang Chiou 	struct mmc_cmd cmd;
615e9550449SChe-Liang Chiou 	int timeout = 1000;
616e9550449SChe-Liang Chiou 	uint start;
617e9550449SChe-Liang Chiou 	int err;
618e9550449SChe-Liang Chiou 
619e9550449SChe-Liang Chiou 	mmc->op_cond_pending = 0;
620cc17c01fSAndrew Gabbasov 	if (!(mmc->ocr & OCR_BUSY)) {
621d188b113SYangbo Lu 		/* Some cards seem to need this */
622d188b113SYangbo Lu 		mmc_go_idle(mmc);
623d188b113SYangbo Lu 
624e9550449SChe-Liang Chiou 		start = get_timer(0);
6251677eef4SAndrew Gabbasov 		while (1) {
6265289b535SAndrew Gabbasov 			err = mmc_send_op_cond_iter(mmc, 1);
627272cc70bSAndy Fleming 			if (err)
628272cc70bSAndy Fleming 				return err;
6291677eef4SAndrew Gabbasov 			if (mmc->ocr & OCR_BUSY)
6301677eef4SAndrew Gabbasov 				break;
631e9550449SChe-Liang Chiou 			if (get_timer(start) > timeout)
632915ffa52SJaehoon Chung 				return -EOPNOTSUPP;
633e9550449SChe-Liang Chiou 			udelay(100);
6341677eef4SAndrew Gabbasov 		}
635cc17c01fSAndrew Gabbasov 	}
636272cc70bSAndy Fleming 
637d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc)) { /* read OCR for spi */
638d52ebf10SThomas Chou 		cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
639d52ebf10SThomas Chou 		cmd.resp_type = MMC_RSP_R3;
640d52ebf10SThomas Chou 		cmd.cmdarg = 0;
641d52ebf10SThomas Chou 
642d52ebf10SThomas Chou 		err = mmc_send_cmd(mmc, &cmd, NULL);
643d52ebf10SThomas Chou 
644d52ebf10SThomas Chou 		if (err)
645d52ebf10SThomas Chou 			return err;
646a626c8d4SAndrew Gabbasov 
647a626c8d4SAndrew Gabbasov 		mmc->ocr = cmd.response[0];
648d52ebf10SThomas Chou 	}
649d52ebf10SThomas Chou 
650272cc70bSAndy Fleming 	mmc->version = MMC_VERSION_UNKNOWN;
651272cc70bSAndy Fleming 
652272cc70bSAndy Fleming 	mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
653def816a2SStephen Warren 	mmc->rca = 1;
654272cc70bSAndy Fleming 
655272cc70bSAndy Fleming 	return 0;
656272cc70bSAndy Fleming }
657272cc70bSAndy Fleming 
658272cc70bSAndy Fleming 
659fdbb873eSKim Phillips static int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd)
660272cc70bSAndy Fleming {
661e531136eSJason Zhu 	static int initialized;
662272cc70bSAndy Fleming 	struct mmc_cmd cmd;
663272cc70bSAndy Fleming 	struct mmc_data data;
664272cc70bSAndy Fleming 	int err;
665272cc70bSAndy Fleming 
666e531136eSJason Zhu 	if (initialized) {
667e531136eSJason Zhu 		memcpy(ext_csd, mmc_ext_csd, 512);
668e531136eSJason Zhu 		return 0;
669e531136eSJason Zhu 	}
670e531136eSJason Zhu 
671e531136eSJason Zhu 	initialized = 1;
672e531136eSJason Zhu 
673272cc70bSAndy Fleming 	/* Get the Card Status Register */
674272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SEND_EXT_CSD;
675272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
676272cc70bSAndy Fleming 	cmd.cmdarg = 0;
677272cc70bSAndy Fleming 
678cdfd1ac6SYoshihiro Shimoda 	data.dest = (char *)ext_csd;
679272cc70bSAndy Fleming 	data.blocks = 1;
6808bfa195eSSimon Glass 	data.blocksize = MMC_MAX_BLOCK_LEN;
681272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
682272cc70bSAndy Fleming 
683272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, &data);
684e531136eSJason Zhu 	memcpy(mmc_ext_csd, ext_csd, 512);
6852056aa9fSJason Zhu #if defined(CONFIG_MMC_USE_PRE_CONFIG) && defined(CONFIG_SPL_BUILD)
6862056aa9fSJason Zhu 	char *mmc_ecsd_base = NULL;
6872056aa9fSJason Zhu 	ulong mmc_ecsd;
688272cc70bSAndy Fleming 
6892056aa9fSJason Zhu 	mmc_ecsd = dev_read_u32_default(mmc->dev, "mmc-ecsd", 0);
6902056aa9fSJason Zhu 	mmc_ecsd_base = (char *)mmc_ecsd;
6912056aa9fSJason Zhu 	if (mmc_ecsd_base) {
6922056aa9fSJason Zhu 		memcpy(mmc_ecsd_base, ext_csd, 512);
6932056aa9fSJason Zhu 		*(unsigned int *)(mmc_ecsd_base + 512) = 0x55aa55aa;
6942056aa9fSJason Zhu 	}
6952056aa9fSJason Zhu #endif
696272cc70bSAndy Fleming 	return err;
697272cc70bSAndy Fleming }
698272cc70bSAndy Fleming 
6999e8ce816SZiyuan Xu static int mmc_poll_for_busy(struct mmc *mmc, u8 send_status)
700272cc70bSAndy Fleming {
701272cc70bSAndy Fleming 	struct mmc_cmd cmd;
70255e5defdSZiyuan Xu 	u8 busy = true;
70355e5defdSZiyuan Xu 	uint start;
70455e5defdSZiyuan Xu 	int ret;
7055d4fc8d9SRaffaele Recalcati 	int timeout = 1000;
70655e5defdSZiyuan Xu 
70755e5defdSZiyuan Xu 	cmd.cmdidx = MMC_CMD_SEND_STATUS;
70855e5defdSZiyuan Xu 	cmd.resp_type = MMC_RSP_R1;
70955e5defdSZiyuan Xu 	cmd.cmdarg = mmc->rca << 16;
71055e5defdSZiyuan Xu 
71155e5defdSZiyuan Xu 	start = get_timer(0);
71255e5defdSZiyuan Xu 
7139e8ce816SZiyuan Xu 	if (!send_status && !mmc_can_card_busy(mmc)) {
7149e8ce816SZiyuan Xu 		mdelay(timeout);
7159e8ce816SZiyuan Xu 		return 0;
7169e8ce816SZiyuan Xu 	}
7179e8ce816SZiyuan Xu 
71855e5defdSZiyuan Xu 	do {
7199e8ce816SZiyuan Xu 		if (!send_status) {
72055e5defdSZiyuan Xu 			busy = mmc_card_busy(mmc);
72155e5defdSZiyuan Xu 		} else {
72255e5defdSZiyuan Xu 			ret = mmc_send_cmd(mmc, &cmd, NULL);
72355e5defdSZiyuan Xu 
72455e5defdSZiyuan Xu 			if (ret)
72555e5defdSZiyuan Xu 				return ret;
72655e5defdSZiyuan Xu 
72755e5defdSZiyuan Xu 			if (cmd.response[0] & MMC_STATUS_SWITCH_ERROR)
72855e5defdSZiyuan Xu 				return -EBADMSG;
72955e5defdSZiyuan Xu 			busy = (cmd.response[0] & MMC_STATUS_CURR_STATE) ==
73055e5defdSZiyuan Xu 				MMC_STATE_PRG;
73155e5defdSZiyuan Xu 		}
73255e5defdSZiyuan Xu 
73355e5defdSZiyuan Xu 		if (get_timer(start) > timeout && busy)
73455e5defdSZiyuan Xu 			return -ETIMEDOUT;
73555e5defdSZiyuan Xu 	} while (busy);
73655e5defdSZiyuan Xu 
73755e5defdSZiyuan Xu 	return 0;
73855e5defdSZiyuan Xu }
73955e5defdSZiyuan Xu 
74055e5defdSZiyuan Xu static int __mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value,
74155e5defdSZiyuan Xu 			u8 send_status)
74255e5defdSZiyuan Xu {
74355e5defdSZiyuan Xu 	struct mmc_cmd cmd;
744a9003dc6SMaxime Ripard 	int retries = 3;
7455d4fc8d9SRaffaele Recalcati 	int ret;
746272cc70bSAndy Fleming 
747272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SWITCH;
748272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1b;
749272cc70bSAndy Fleming 	cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
750272cc70bSAndy Fleming 				 (index << 16) |
751272cc70bSAndy Fleming 				 (value << 8);
752272cc70bSAndy Fleming 
75355e5defdSZiyuan Xu 	do {
7545d4fc8d9SRaffaele Recalcati 		ret = mmc_send_cmd(mmc, &cmd, NULL);
7555d4fc8d9SRaffaele Recalcati 
7569e8ce816SZiyuan Xu 		if (!ret)
7579e8ce816SZiyuan Xu 			return mmc_poll_for_busy(mmc, send_status);
75855e5defdSZiyuan Xu 	} while (--retries > 0 && ret);
75955e5defdSZiyuan Xu 
760a9003dc6SMaxime Ripard 	return ret;
761a9003dc6SMaxime Ripard }
762a9003dc6SMaxime Ripard 
76355e5defdSZiyuan Xu int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)
76455e5defdSZiyuan Xu {
76555e5defdSZiyuan Xu 	return __mmc_switch(mmc, set, index, value, true);
766272cc70bSAndy Fleming }
767272cc70bSAndy Fleming 
76849dba033SZiyuan Xu static int mmc_select_bus_width(struct mmc *mmc)
76949dba033SZiyuan Xu {
77049dba033SZiyuan Xu 	u32 ext_csd_bits[] = {
77149dba033SZiyuan Xu 		EXT_CSD_BUS_WIDTH_8,
77249dba033SZiyuan Xu 		EXT_CSD_BUS_WIDTH_4,
77349dba033SZiyuan Xu 	};
77449dba033SZiyuan Xu 	u32 bus_widths[] = {
77549dba033SZiyuan Xu 		MMC_BUS_WIDTH_8BIT,
77649dba033SZiyuan Xu 		MMC_BUS_WIDTH_4BIT,
77749dba033SZiyuan Xu 	};
77849dba033SZiyuan Xu 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
77949dba033SZiyuan Xu 	ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN);
78049dba033SZiyuan Xu 	u32 idx, bus_width = 0;
78149dba033SZiyuan Xu 	int err = 0;
78249dba033SZiyuan Xu 
78349dba033SZiyuan Xu 	if (mmc->version < MMC_VERSION_4 ||
78449dba033SZiyuan Xu 	    !(mmc->cfg->host_caps & (MMC_MODE_4BIT | MMC_MODE_8BIT)))
78549dba033SZiyuan Xu 		return 0;
78649dba033SZiyuan Xu 
78749dba033SZiyuan Xu 	err = mmc_send_ext_csd(mmc, ext_csd);
78849dba033SZiyuan Xu 
78949dba033SZiyuan Xu 	if (err)
79049dba033SZiyuan Xu 		return err;
79149dba033SZiyuan Xu 
79249dba033SZiyuan Xu 	idx = (mmc->cfg->host_caps & MMC_MODE_8BIT) ? 0 : 1;
79349dba033SZiyuan Xu 
79449dba033SZiyuan Xu 	/*
79549dba033SZiyuan Xu 	 * Unlike SD, MMC cards dont have a configuration register to notify
79649dba033SZiyuan Xu 	 * supported bus width. So bus test command should be run to identify
79749dba033SZiyuan Xu 	 * the supported bus width or compare the ext csd values of current
79849dba033SZiyuan Xu 	 * bus width and ext csd values of 1 bit mode read earlier.
79949dba033SZiyuan Xu 	 */
80049dba033SZiyuan Xu 	for (; idx < ARRAY_SIZE(bus_widths); idx++) {
80149dba033SZiyuan Xu 		/*
80249dba033SZiyuan Xu 		 * Host is capable of 8bit transfer, then switch
80349dba033SZiyuan Xu 		 * the device to work in 8bit transfer mode. If the
80449dba033SZiyuan Xu 		 * mmc switch command returns error then switch to
80549dba033SZiyuan Xu 		 * 4bit transfer mode. On success set the corresponding
80649dba033SZiyuan Xu 		 * bus width on the host.
80749dba033SZiyuan Xu 		 */
80849dba033SZiyuan Xu 		err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
80949dba033SZiyuan Xu 				 EXT_CSD_BUS_WIDTH, ext_csd_bits[idx]);
81049dba033SZiyuan Xu 		if (err)
81149dba033SZiyuan Xu 			continue;
81249dba033SZiyuan Xu 
81349dba033SZiyuan Xu 		bus_width = bus_widths[idx];
81449dba033SZiyuan Xu 		mmc_set_bus_width(mmc, bus_width);
81549dba033SZiyuan Xu 
81649dba033SZiyuan Xu 		err = mmc_send_ext_csd(mmc, test_csd);
81749dba033SZiyuan Xu 
81849dba033SZiyuan Xu 		if (err)
81949dba033SZiyuan Xu 			continue;
82049dba033SZiyuan Xu 
82149dba033SZiyuan Xu 		/* Only compare read only fields */
82249dba033SZiyuan Xu 		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] ==
82349dba033SZiyuan Xu 			test_csd[EXT_CSD_PARTITIONING_SUPPORT]) &&
82449dba033SZiyuan Xu 		    (ext_csd[EXT_CSD_HC_WP_GRP_SIZE] ==
82549dba033SZiyuan Xu 			test_csd[EXT_CSD_HC_WP_GRP_SIZE]) &&
82649dba033SZiyuan Xu 		    (ext_csd[EXT_CSD_REV] == test_csd[EXT_CSD_REV]) &&
82749dba033SZiyuan Xu 			(ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] ==
82849dba033SZiyuan Xu 			test_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) &&
82949dba033SZiyuan Xu 		    !memcmp(&ext_csd[EXT_CSD_SEC_CNT],
83049dba033SZiyuan Xu 			&test_csd[EXT_CSD_SEC_CNT], 4)) {
83149dba033SZiyuan Xu 			err = bus_width;
83249dba033SZiyuan Xu 			break;
83349dba033SZiyuan Xu 		} else {
83449dba033SZiyuan Xu 			err = -EBADMSG;
83549dba033SZiyuan Xu 		}
83649dba033SZiyuan Xu 	}
83749dba033SZiyuan Xu 
83849dba033SZiyuan Xu 	return err;
83949dba033SZiyuan Xu }
84049dba033SZiyuan Xu 
84149dba033SZiyuan Xu static const u8 tuning_blk_pattern_4bit[] = {
84249dba033SZiyuan Xu 	0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
84349dba033SZiyuan Xu 	0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
84449dba033SZiyuan Xu 	0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
84549dba033SZiyuan Xu 	0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
84649dba033SZiyuan Xu 	0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
84749dba033SZiyuan Xu 	0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
84849dba033SZiyuan Xu 	0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
84949dba033SZiyuan Xu 	0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
85049dba033SZiyuan Xu };
85149dba033SZiyuan Xu 
85249dba033SZiyuan Xu static const u8 tuning_blk_pattern_8bit[] = {
85349dba033SZiyuan Xu 	0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
85449dba033SZiyuan Xu 	0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
85549dba033SZiyuan Xu 	0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
85649dba033SZiyuan Xu 	0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
85749dba033SZiyuan Xu 	0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
85849dba033SZiyuan Xu 	0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
85949dba033SZiyuan Xu 	0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
86049dba033SZiyuan Xu 	0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
86149dba033SZiyuan Xu 	0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
86249dba033SZiyuan Xu 	0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
86349dba033SZiyuan Xu 	0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
86449dba033SZiyuan Xu 	0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
86549dba033SZiyuan Xu 	0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
86649dba033SZiyuan Xu 	0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
86749dba033SZiyuan Xu 	0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
86849dba033SZiyuan Xu 	0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
86949dba033SZiyuan Xu };
87049dba033SZiyuan Xu 
87149dba033SZiyuan Xu int mmc_send_tuning(struct mmc *mmc, u32 opcode)
87249dba033SZiyuan Xu {
87349dba033SZiyuan Xu 	struct mmc_cmd cmd;
87449dba033SZiyuan Xu 	struct mmc_data data;
87549dba033SZiyuan Xu 	const u8 *tuning_block_pattern;
87649dba033SZiyuan Xu 	int size, err = 0;
87749dba033SZiyuan Xu 	u8 *data_buf;
87849dba033SZiyuan Xu 
87949dba033SZiyuan Xu 	if (mmc->bus_width == MMC_BUS_WIDTH_8BIT) {
88049dba033SZiyuan Xu 		tuning_block_pattern = tuning_blk_pattern_8bit;
88149dba033SZiyuan Xu 		size = sizeof(tuning_blk_pattern_8bit);
88249dba033SZiyuan Xu 	} else if (mmc->bus_width == MMC_BUS_WIDTH_4BIT) {
88349dba033SZiyuan Xu 		tuning_block_pattern = tuning_blk_pattern_4bit;
88449dba033SZiyuan Xu 		size = sizeof(tuning_blk_pattern_4bit);
88549dba033SZiyuan Xu 	} else {
88649dba033SZiyuan Xu 		return -EINVAL;
88749dba033SZiyuan Xu 	}
88849dba033SZiyuan Xu 
88949dba033SZiyuan Xu 	data_buf = calloc(1, size);
89049dba033SZiyuan Xu 	if (!data_buf)
89149dba033SZiyuan Xu 		return -ENOMEM;
89249dba033SZiyuan Xu 
89349dba033SZiyuan Xu 	cmd.cmdidx = opcode;
89449dba033SZiyuan Xu 	cmd.resp_type = MMC_RSP_R1;
89549dba033SZiyuan Xu 	cmd.cmdarg = 0;
89649dba033SZiyuan Xu 
89749dba033SZiyuan Xu 	data.dest = (char *)data_buf;
89849dba033SZiyuan Xu 	data.blocksize = size;
89949dba033SZiyuan Xu 	data.blocks = 1;
90049dba033SZiyuan Xu 	data.flags = MMC_DATA_READ;
90149dba033SZiyuan Xu 
90249dba033SZiyuan Xu 	err = mmc_send_cmd(mmc, &cmd, &data);
90349dba033SZiyuan Xu 	if (err)
90449dba033SZiyuan Xu 		goto out;
90549dba033SZiyuan Xu 
90649dba033SZiyuan Xu 	if (memcmp(data_buf, tuning_block_pattern, size))
90749dba033SZiyuan Xu 		err = -EIO;
90849dba033SZiyuan Xu out:
90949dba033SZiyuan Xu 	free(data_buf);
91049dba033SZiyuan Xu 	return err;
91149dba033SZiyuan Xu }
91249dba033SZiyuan Xu 
91349dba033SZiyuan Xu static int mmc_execute_tuning(struct mmc *mmc)
91449dba033SZiyuan Xu {
91549dba033SZiyuan Xu #ifdef CONFIG_DM_MMC
91649dba033SZiyuan Xu 	struct dm_mmc_ops *ops = mmc_get_ops(mmc->dev);
91749dba033SZiyuan Xu #endif
91849dba033SZiyuan Xu 	u32 opcode;
91949dba033SZiyuan Xu 
92049dba033SZiyuan Xu 	if (IS_SD(mmc))
92149dba033SZiyuan Xu 		opcode = MMC_SEND_TUNING_BLOCK;
92249dba033SZiyuan Xu 	else
92349dba033SZiyuan Xu 		opcode = MMC_SEND_TUNING_BLOCK_HS200;
92449dba033SZiyuan Xu 
92549dba033SZiyuan Xu #ifndef CONFIG_DM_MMC
92649dba033SZiyuan Xu 	if (mmc->cfg->ops->execute_tuning) {
92749dba033SZiyuan Xu 		return mmc->cfg->ops->execute_tuning(mmc, opcode);
92849dba033SZiyuan Xu #else
92949dba033SZiyuan Xu 	if (ops->execute_tuning) {
93049dba033SZiyuan Xu 		return ops->execute_tuning(mmc->dev, opcode);
93149dba033SZiyuan Xu #endif
93249dba033SZiyuan Xu 	} else {
93349dba033SZiyuan Xu 		debug("Tuning feature required for HS200 mode.\n");
93449dba033SZiyuan Xu 		return -EIO;
93549dba033SZiyuan Xu 	}
93649dba033SZiyuan Xu }
93749dba033SZiyuan Xu 
93849dba033SZiyuan Xu static int mmc_hs200_tuning(struct mmc *mmc)
93949dba033SZiyuan Xu {
94049dba033SZiyuan Xu 	return mmc_execute_tuning(mmc);
94149dba033SZiyuan Xu }
94249dba033SZiyuan Xu 
943e61cd3d7SZiyuan Xu static int mmc_select_hs(struct mmc *mmc)
944e61cd3d7SZiyuan Xu {
945e61cd3d7SZiyuan Xu 	int ret;
946e61cd3d7SZiyuan Xu 
947e61cd3d7SZiyuan Xu 	ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
948e61cd3d7SZiyuan Xu 			 EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS);
949e61cd3d7SZiyuan Xu 
950e61cd3d7SZiyuan Xu 	if (!ret)
951e61cd3d7SZiyuan Xu 		mmc_set_timing(mmc, MMC_TIMING_MMC_HS);
952e61cd3d7SZiyuan Xu 
953e61cd3d7SZiyuan Xu 	return ret;
954e61cd3d7SZiyuan Xu }
955e61cd3d7SZiyuan Xu 
9565545757fSZiyuan Xu static int mmc_select_hs_ddr(struct mmc *mmc)
9575545757fSZiyuan Xu {
9585545757fSZiyuan Xu 	u32 ext_csd_bits;
9595545757fSZiyuan Xu 	int err = 0;
9605545757fSZiyuan Xu 
9615545757fSZiyuan Xu 	if (mmc->bus_width == MMC_BUS_WIDTH_1BIT)
9625545757fSZiyuan Xu 		return 0;
9635545757fSZiyuan Xu 
9645545757fSZiyuan Xu 	ext_csd_bits = (mmc->bus_width == MMC_BUS_WIDTH_8BIT) ?
9655545757fSZiyuan Xu 			EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4;
9665545757fSZiyuan Xu 
9675545757fSZiyuan Xu 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
9685545757fSZiyuan Xu 			 EXT_CSD_BUS_WIDTH, ext_csd_bits);
9695545757fSZiyuan Xu 	if (err)
9705545757fSZiyuan Xu 		return err;
9715545757fSZiyuan Xu 
9725545757fSZiyuan Xu 	mmc_set_timing(mmc, MMC_TIMING_MMC_DDR52);
9735545757fSZiyuan Xu 
9745545757fSZiyuan Xu 	return 0;
9755545757fSZiyuan Xu }
9765545757fSZiyuan Xu 
97749dba033SZiyuan Xu static int mmc_select_hs200(struct mmc *mmc)
97849dba033SZiyuan Xu {
97949dba033SZiyuan Xu 	int ret;
98049dba033SZiyuan Xu 
98149dba033SZiyuan Xu 	/*
98249dba033SZiyuan Xu 	 * Set the bus width(4 or 8) with host's support and
98349dba033SZiyuan Xu 	 * switch to HS200 mode if bus width is set successfully.
98449dba033SZiyuan Xu 	 */
98549dba033SZiyuan Xu 	ret = mmc_select_bus_width(mmc);
98649dba033SZiyuan Xu 
98749dba033SZiyuan Xu 	if (ret > 0) {
98849dba033SZiyuan Xu 		ret = __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
98949dba033SZiyuan Xu 				   EXT_CSD_HS_TIMING,
99049dba033SZiyuan Xu 				   EXT_CSD_TIMING_HS200, false);
99149dba033SZiyuan Xu 
99249dba033SZiyuan Xu 		if (ret)
99349dba033SZiyuan Xu 			return ret;
99449dba033SZiyuan Xu 
99549dba033SZiyuan Xu 		mmc_set_timing(mmc, MMC_TIMING_MMC_HS200);
99649dba033SZiyuan Xu 	}
99749dba033SZiyuan Xu 
99849dba033SZiyuan Xu 	return ret;
99949dba033SZiyuan Xu }
100049dba033SZiyuan Xu 
1001b673f29aSZiyuan Xu static int mmc_select_hs400(struct mmc *mmc)
1002b673f29aSZiyuan Xu {
1003b673f29aSZiyuan Xu 	int ret;
1004b673f29aSZiyuan Xu 
1005*e56bff5bSJason Zhu 	/* Reduce frequency to HS frequency */
1006*e56bff5bSJason Zhu 	mmc_set_clock(mmc, MMC_HIGH_52_MAX_DTR);
1007*e56bff5bSJason Zhu 
1008b673f29aSZiyuan Xu 	/* Switch card to HS mode */
1009b673f29aSZiyuan Xu 	ret = __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1010b673f29aSZiyuan Xu 			   EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, false);
1011b673f29aSZiyuan Xu 	if (ret)
1012b673f29aSZiyuan Xu 		return ret;
1013b673f29aSZiyuan Xu 
1014b673f29aSZiyuan Xu 	/* Set host controller to HS timing */
1015b673f29aSZiyuan Xu 	mmc_set_timing(mmc, MMC_TIMING_MMC_HS);
1016b673f29aSZiyuan Xu 
1017b673f29aSZiyuan Xu 	ret = mmc_send_status(mmc, 1000);
1018b673f29aSZiyuan Xu 	if (ret)
1019b673f29aSZiyuan Xu 		return ret;
1020b673f29aSZiyuan Xu 
1021b673f29aSZiyuan Xu 	/* Switch card to DDR */
1022b673f29aSZiyuan Xu 	ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1023b673f29aSZiyuan Xu 			 EXT_CSD_BUS_WIDTH,
1024b673f29aSZiyuan Xu 			 EXT_CSD_DDR_BUS_WIDTH_8);
1025b673f29aSZiyuan Xu 	if (ret)
1026b673f29aSZiyuan Xu 		return ret;
1027b673f29aSZiyuan Xu 
1028b673f29aSZiyuan Xu 	/* Switch card to HS400 */
1029b673f29aSZiyuan Xu 	ret = __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1030b673f29aSZiyuan Xu 			   EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400, false);
1031b673f29aSZiyuan Xu 	if (ret)
1032b673f29aSZiyuan Xu 		return ret;
1033b673f29aSZiyuan Xu 
1034b673f29aSZiyuan Xu 	/* Set host controller to HS400 timing and frequency */
1035b673f29aSZiyuan Xu 	mmc_set_timing(mmc, MMC_TIMING_MMC_HS400);
1036b673f29aSZiyuan Xu 
1037b673f29aSZiyuan Xu 	return ret;
1038b673f29aSZiyuan Xu }
1039b673f29aSZiyuan Xu 
1040227f658eSZiyuan Xu static u32 mmc_select_card_type(struct mmc *mmc, u8 *ext_csd)
1041227f658eSZiyuan Xu {
1042227f658eSZiyuan Xu 	u8 card_type;
1043227f658eSZiyuan Xu 	u32 host_caps, avail_type = 0;
1044227f658eSZiyuan Xu 
1045227f658eSZiyuan Xu 	card_type = ext_csd[EXT_CSD_CARD_TYPE];
1046227f658eSZiyuan Xu 	host_caps = mmc->cfg->host_caps;
1047227f658eSZiyuan Xu 
1048227f658eSZiyuan Xu 	if ((host_caps & MMC_MODE_HS) &&
1049227f658eSZiyuan Xu 	    (card_type & EXT_CSD_CARD_TYPE_26))
1050227f658eSZiyuan Xu 		avail_type |= EXT_CSD_CARD_TYPE_26;
1051227f658eSZiyuan Xu 
1052227f658eSZiyuan Xu 	if ((host_caps & MMC_MODE_HS) &&
1053227f658eSZiyuan Xu 	    (card_type & EXT_CSD_CARD_TYPE_52))
1054227f658eSZiyuan Xu 		avail_type |= EXT_CSD_CARD_TYPE_52;
1055227f658eSZiyuan Xu 
1056227f658eSZiyuan Xu 	/*
1057227f658eSZiyuan Xu 	 * For the moment, u-boot doesn't support signal voltage
1058227f658eSZiyuan Xu 	 * switch, therefor we assume that host support ddr52
1059227f658eSZiyuan Xu 	 * at 1.8v or 3.3v I/O(1.2v I/O not supported, hs200 and
1060227f658eSZiyuan Xu 	 * hs400 are the same).
1061227f658eSZiyuan Xu 	 */
1062227f658eSZiyuan Xu 	if ((host_caps & MMC_MODE_DDR_52MHz) &&
1063227f658eSZiyuan Xu 	    (card_type & EXT_CSD_CARD_TYPE_DDR_1_8V))
1064227f658eSZiyuan Xu 		avail_type |= EXT_CSD_CARD_TYPE_DDR_1_8V;
1065227f658eSZiyuan Xu 
1066227f658eSZiyuan Xu 	if ((host_caps & MMC_MODE_HS200) &&
1067227f658eSZiyuan Xu 	    (card_type & EXT_CSD_CARD_TYPE_HS200_1_8V))
1068227f658eSZiyuan Xu 		avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V;
1069227f658eSZiyuan Xu 
1070227f658eSZiyuan Xu 	/*
1071227f658eSZiyuan Xu 	 * If host can support HS400, it means that host can also
1072227f658eSZiyuan Xu 	 * support HS200.
1073227f658eSZiyuan Xu 	 */
1074227f658eSZiyuan Xu 	if ((host_caps & MMC_MODE_HS400) &&
1075227f658eSZiyuan Xu 	    (host_caps & MMC_MODE_8BIT) &&
1076227f658eSZiyuan Xu 	    (card_type & EXT_CSD_CARD_TYPE_HS400_1_8V))
1077227f658eSZiyuan Xu 		avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V |
1078227f658eSZiyuan Xu 				EXT_CSD_CARD_TYPE_HS400_1_8V;
1079227f658eSZiyuan Xu 
1080227f658eSZiyuan Xu 	if ((host_caps & MMC_MODE_HS400ES) &&
1081227f658eSZiyuan Xu 	    (host_caps & MMC_MODE_8BIT) &&
1082227f658eSZiyuan Xu 	    ext_csd[EXT_CSD_STROBE_SUPPORT] &&
1083227f658eSZiyuan Xu 	    (avail_type & EXT_CSD_CARD_TYPE_HS400_1_8V))
1084227f658eSZiyuan Xu 		avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V |
1085227f658eSZiyuan Xu 				EXT_CSD_CARD_TYPE_HS400_1_8V |
1086227f658eSZiyuan Xu 				EXT_CSD_CARD_TYPE_HS400ES;
1087227f658eSZiyuan Xu 
1088227f658eSZiyuan Xu 	return avail_type;
1089227f658eSZiyuan Xu }
1090227f658eSZiyuan Xu 
109149dba033SZiyuan Xu static void mmc_set_bus_speed(struct mmc *mmc, u8 avail_type)
109249dba033SZiyuan Xu {
109349dba033SZiyuan Xu 	int clock = 0;
109449dba033SZiyuan Xu 
109549dba033SZiyuan Xu 	if (mmc_card_hs(mmc))
109649dba033SZiyuan Xu 		clock = (avail_type & EXT_CSD_CARD_TYPE_52) ?
109749dba033SZiyuan Xu 			MMC_HIGH_52_MAX_DTR : MMC_HIGH_26_MAX_DTR;
109849dba033SZiyuan Xu 	else if (mmc_card_hs200(mmc) ||
109949dba033SZiyuan Xu 		 mmc_card_hs400(mmc) ||
110049dba033SZiyuan Xu 		 mmc_card_hs400es(mmc))
110149dba033SZiyuan Xu 		clock = MMC_HS200_MAX_DTR;
110249dba033SZiyuan Xu 
110349dba033SZiyuan Xu 	mmc_set_clock(mmc, clock);
110449dba033SZiyuan Xu }
110549dba033SZiyuan Xu 
1106fdbb873eSKim Phillips static int mmc_change_freq(struct mmc *mmc)
1107272cc70bSAndy Fleming {
11088bfa195eSSimon Glass 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
1109227f658eSZiyuan Xu 	u32 avail_type;
1110272cc70bSAndy Fleming 	int err;
1111272cc70bSAndy Fleming 
1112fc5b32fbSAndrew Gabbasov 	mmc->card_caps = 0;
1113272cc70bSAndy Fleming 
1114d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc))
1115d52ebf10SThomas Chou 		return 0;
1116d52ebf10SThomas Chou 
1117272cc70bSAndy Fleming 	/* Only version 4 supports high-speed */
1118272cc70bSAndy Fleming 	if (mmc->version < MMC_VERSION_4)
1119272cc70bSAndy Fleming 		return 0;
1120272cc70bSAndy Fleming 
1121fc5b32fbSAndrew Gabbasov 	mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT;
1122fc5b32fbSAndrew Gabbasov 
1123272cc70bSAndy Fleming 	err = mmc_send_ext_csd(mmc, ext_csd);
1124272cc70bSAndy Fleming 
1125272cc70bSAndy Fleming 	if (err)
1126272cc70bSAndy Fleming 		return err;
1127272cc70bSAndy Fleming 
1128227f658eSZiyuan Xu 	avail_type = mmc_select_card_type(mmc, ext_csd);
1129272cc70bSAndy Fleming 
113049dba033SZiyuan Xu 	if (avail_type & EXT_CSD_CARD_TYPE_HS200)
113149dba033SZiyuan Xu 		err = mmc_select_hs200(mmc);
11321f250d0aSJason Zhu 	else if (avail_type & EXT_CSD_CARD_TYPE_HS)
1133e61cd3d7SZiyuan Xu 		err = mmc_select_hs(mmc);
1134227f658eSZiyuan Xu 	else
1135227f658eSZiyuan Xu 		err = -EINVAL;
1136272cc70bSAndy Fleming 
1137272cc70bSAndy Fleming 	if (err)
1138a5e27b41SHeiko Schocher 		return err;
1139272cc70bSAndy Fleming 
114049dba033SZiyuan Xu 	mmc_set_bus_speed(mmc, avail_type);
1141272cc70bSAndy Fleming 
1142b673f29aSZiyuan Xu 	if (mmc_card_hs200(mmc)) {
114349dba033SZiyuan Xu 		err = mmc_hs200_tuning(mmc);
1144b673f29aSZiyuan Xu 		if (avail_type & EXT_CSD_CARD_TYPE_HS400 &&
1145b673f29aSZiyuan Xu 		    mmc->bus_width == MMC_BUS_WIDTH_8BIT) {
1146b673f29aSZiyuan Xu 			err = mmc_select_hs400(mmc);
1147b673f29aSZiyuan Xu 			mmc_set_bus_speed(mmc, avail_type);
1148b673f29aSZiyuan Xu 		}
1149b673f29aSZiyuan Xu 	} else if (!mmc_card_hs400es(mmc)) {
115049dba033SZiyuan Xu 		err = mmc_select_bus_width(mmc) > 0 ? 0 : err;
11515545757fSZiyuan Xu 		if (!err && avail_type & EXT_CSD_CARD_TYPE_DDR_52)
11525545757fSZiyuan Xu 			err = mmc_select_hs_ddr(mmc);
11535545757fSZiyuan Xu 	}
115449dba033SZiyuan Xu 
1155272cc70bSAndy Fleming 	return err;
1156272cc70bSAndy Fleming }
1157272cc70bSAndy Fleming 
1158f866a46dSStephen Warren static int mmc_set_capacity(struct mmc *mmc, int part_num)
1159f866a46dSStephen Warren {
1160f866a46dSStephen Warren 	switch (part_num) {
1161f866a46dSStephen Warren 	case 0:
1162f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_user;
1163f866a46dSStephen Warren 		break;
1164f866a46dSStephen Warren 	case 1:
1165f866a46dSStephen Warren 	case 2:
1166f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_boot;
1167f866a46dSStephen Warren 		break;
1168f866a46dSStephen Warren 	case 3:
1169f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_rpmb;
1170f866a46dSStephen Warren 		break;
1171f866a46dSStephen Warren 	case 4:
1172f866a46dSStephen Warren 	case 5:
1173f866a46dSStephen Warren 	case 6:
1174f866a46dSStephen Warren 	case 7:
1175f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_gp[part_num - 4];
1176f866a46dSStephen Warren 		break;
1177f866a46dSStephen Warren 	default:
1178f866a46dSStephen Warren 		return -1;
1179f866a46dSStephen Warren 	}
1180f866a46dSStephen Warren 
1181c40fdca6SSimon Glass 	mmc_get_blk_desc(mmc)->lba = lldiv(mmc->capacity, mmc->read_bl_len);
1182f866a46dSStephen Warren 
1183f866a46dSStephen Warren 	return 0;
1184f866a46dSStephen Warren }
1185f866a46dSStephen Warren 
11867dba0b93SSimon Glass int mmc_switch_part(struct mmc *mmc, unsigned int part_num)
1187bc897b1dSLei Wen {
1188f866a46dSStephen Warren 	int ret;
1189bc897b1dSLei Wen 
1190f866a46dSStephen Warren 	ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
1191bc897b1dSLei Wen 			 (mmc->part_config & ~PART_ACCESS_MASK)
1192bc897b1dSLei Wen 			 | (part_num & PART_ACCESS_MASK));
1193f866a46dSStephen Warren 
11946dc93e70SPeter Bigot 	/*
11956dc93e70SPeter Bigot 	 * Set the capacity if the switch succeeded or was intended
11966dc93e70SPeter Bigot 	 * to return to representing the raw device.
11976dc93e70SPeter Bigot 	 */
1198873cc1d7SStephen Warren 	if ((ret == 0) || ((ret == -ENODEV) && (part_num == 0))) {
11996dc93e70SPeter Bigot 		ret = mmc_set_capacity(mmc, part_num);
1200fdbb139fSSimon Glass 		mmc_get_blk_desc(mmc)->hwpart = part_num;
1201873cc1d7SStephen Warren 	}
12026dc93e70SPeter Bigot 
12036dc93e70SPeter Bigot 	return ret;
1204bc897b1dSLei Wen }
1205bc897b1dSLei Wen 
1206ac9da0e0SDiego Santa Cruz int mmc_hwpart_config(struct mmc *mmc,
1207ac9da0e0SDiego Santa Cruz 		      const struct mmc_hwpart_conf *conf,
1208ac9da0e0SDiego Santa Cruz 		      enum mmc_hwpart_conf_mode mode)
1209ac9da0e0SDiego Santa Cruz {
1210ac9da0e0SDiego Santa Cruz 	u8 part_attrs = 0;
1211ac9da0e0SDiego Santa Cruz 	u32 enh_size_mult;
1212ac9da0e0SDiego Santa Cruz 	u32 enh_start_addr;
1213ac9da0e0SDiego Santa Cruz 	u32 gp_size_mult[4];
1214ac9da0e0SDiego Santa Cruz 	u32 max_enh_size_mult;
1215ac9da0e0SDiego Santa Cruz 	u32 tot_enh_size_mult = 0;
12168dda5b0eSDiego Santa Cruz 	u8 wr_rel_set;
1217ac9da0e0SDiego Santa Cruz 	int i, pidx, err;
1218ac9da0e0SDiego Santa Cruz 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
1219ac9da0e0SDiego Santa Cruz 
1220ac9da0e0SDiego Santa Cruz 	if (mode < MMC_HWPART_CONF_CHECK || mode > MMC_HWPART_CONF_COMPLETE)
1221ac9da0e0SDiego Santa Cruz 		return -EINVAL;
1222ac9da0e0SDiego Santa Cruz 
1223ac9da0e0SDiego Santa Cruz 	if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4_41)) {
1224ac9da0e0SDiego Santa Cruz 		printf("eMMC >= 4.4 required for enhanced user data area\n");
1225ac9da0e0SDiego Santa Cruz 		return -EMEDIUMTYPE;
1226ac9da0e0SDiego Santa Cruz 	}
1227ac9da0e0SDiego Santa Cruz 
1228ac9da0e0SDiego Santa Cruz 	if (!(mmc->part_support & PART_SUPPORT)) {
1229ac9da0e0SDiego Santa Cruz 		printf("Card does not support partitioning\n");
1230ac9da0e0SDiego Santa Cruz 		return -EMEDIUMTYPE;
1231ac9da0e0SDiego Santa Cruz 	}
1232ac9da0e0SDiego Santa Cruz 
1233ac9da0e0SDiego Santa Cruz 	if (!mmc->hc_wp_grp_size) {
1234ac9da0e0SDiego Santa Cruz 		printf("Card does not define HC WP group size\n");
1235ac9da0e0SDiego Santa Cruz 		return -EMEDIUMTYPE;
1236ac9da0e0SDiego Santa Cruz 	}
1237ac9da0e0SDiego Santa Cruz 
1238ac9da0e0SDiego Santa Cruz 	/* check partition alignment and total enhanced size */
1239ac9da0e0SDiego Santa Cruz 	if (conf->user.enh_size) {
1240ac9da0e0SDiego Santa Cruz 		if (conf->user.enh_size % mmc->hc_wp_grp_size ||
1241ac9da0e0SDiego Santa Cruz 		    conf->user.enh_start % mmc->hc_wp_grp_size) {
1242ac9da0e0SDiego Santa Cruz 			printf("User data enhanced area not HC WP group "
1243ac9da0e0SDiego Santa Cruz 			       "size aligned\n");
1244ac9da0e0SDiego Santa Cruz 			return -EINVAL;
1245ac9da0e0SDiego Santa Cruz 		}
1246ac9da0e0SDiego Santa Cruz 		part_attrs |= EXT_CSD_ENH_USR;
1247ac9da0e0SDiego Santa Cruz 		enh_size_mult = conf->user.enh_size / mmc->hc_wp_grp_size;
1248ac9da0e0SDiego Santa Cruz 		if (mmc->high_capacity) {
1249ac9da0e0SDiego Santa Cruz 			enh_start_addr = conf->user.enh_start;
1250ac9da0e0SDiego Santa Cruz 		} else {
1251ac9da0e0SDiego Santa Cruz 			enh_start_addr = (conf->user.enh_start << 9);
1252ac9da0e0SDiego Santa Cruz 		}
1253ac9da0e0SDiego Santa Cruz 	} else {
1254ac9da0e0SDiego Santa Cruz 		enh_size_mult = 0;
1255ac9da0e0SDiego Santa Cruz 		enh_start_addr = 0;
1256ac9da0e0SDiego Santa Cruz 	}
1257ac9da0e0SDiego Santa Cruz 	tot_enh_size_mult += enh_size_mult;
1258ac9da0e0SDiego Santa Cruz 
1259ac9da0e0SDiego Santa Cruz 	for (pidx = 0; pidx < 4; pidx++) {
1260ac9da0e0SDiego Santa Cruz 		if (conf->gp_part[pidx].size % mmc->hc_wp_grp_size) {
1261ac9da0e0SDiego Santa Cruz 			printf("GP%i partition not HC WP group size "
1262ac9da0e0SDiego Santa Cruz 			       "aligned\n", pidx+1);
1263ac9da0e0SDiego Santa Cruz 			return -EINVAL;
1264ac9da0e0SDiego Santa Cruz 		}
1265ac9da0e0SDiego Santa Cruz 		gp_size_mult[pidx] = conf->gp_part[pidx].size / mmc->hc_wp_grp_size;
1266ac9da0e0SDiego Santa Cruz 		if (conf->gp_part[pidx].size && conf->gp_part[pidx].enhanced) {
1267ac9da0e0SDiego Santa Cruz 			part_attrs |= EXT_CSD_ENH_GP(pidx);
1268ac9da0e0SDiego Santa Cruz 			tot_enh_size_mult += gp_size_mult[pidx];
1269ac9da0e0SDiego Santa Cruz 		}
1270ac9da0e0SDiego Santa Cruz 	}
1271ac9da0e0SDiego Santa Cruz 
1272ac9da0e0SDiego Santa Cruz 	if (part_attrs && ! (mmc->part_support & ENHNCD_SUPPORT)) {
1273ac9da0e0SDiego Santa Cruz 		printf("Card does not support enhanced attribute\n");
1274ac9da0e0SDiego Santa Cruz 		return -EMEDIUMTYPE;
1275ac9da0e0SDiego Santa Cruz 	}
1276ac9da0e0SDiego Santa Cruz 
1277ac9da0e0SDiego Santa Cruz 	err = mmc_send_ext_csd(mmc, ext_csd);
1278ac9da0e0SDiego Santa Cruz 	if (err)
1279ac9da0e0SDiego Santa Cruz 		return err;
1280ac9da0e0SDiego Santa Cruz 
1281ac9da0e0SDiego Santa Cruz 	max_enh_size_mult =
1282ac9da0e0SDiego Santa Cruz 		(ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+2] << 16) +
1283ac9da0e0SDiego Santa Cruz 		(ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+1] << 8) +
1284ac9da0e0SDiego Santa Cruz 		ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT];
1285ac9da0e0SDiego Santa Cruz 	if (tot_enh_size_mult > max_enh_size_mult) {
1286ac9da0e0SDiego Santa Cruz 		printf("Total enhanced size exceeds maximum (%u > %u)\n",
1287ac9da0e0SDiego Santa Cruz 		       tot_enh_size_mult, max_enh_size_mult);
1288ac9da0e0SDiego Santa Cruz 		return -EMEDIUMTYPE;
1289ac9da0e0SDiego Santa Cruz 	}
1290ac9da0e0SDiego Santa Cruz 
12918dda5b0eSDiego Santa Cruz 	/* The default value of EXT_CSD_WR_REL_SET is device
12928dda5b0eSDiego Santa Cruz 	 * dependent, the values can only be changed if the
12938dda5b0eSDiego Santa Cruz 	 * EXT_CSD_HS_CTRL_REL bit is set. The values can be
12948dda5b0eSDiego Santa Cruz 	 * changed only once and before partitioning is completed. */
12958dda5b0eSDiego Santa Cruz 	wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET];
12968dda5b0eSDiego Santa Cruz 	if (conf->user.wr_rel_change) {
12978dda5b0eSDiego Santa Cruz 		if (conf->user.wr_rel_set)
12988dda5b0eSDiego Santa Cruz 			wr_rel_set |= EXT_CSD_WR_DATA_REL_USR;
12998dda5b0eSDiego Santa Cruz 		else
13008dda5b0eSDiego Santa Cruz 			wr_rel_set &= ~EXT_CSD_WR_DATA_REL_USR;
13018dda5b0eSDiego Santa Cruz 	}
13028dda5b0eSDiego Santa Cruz 	for (pidx = 0; pidx < 4; pidx++) {
13038dda5b0eSDiego Santa Cruz 		if (conf->gp_part[pidx].wr_rel_change) {
13048dda5b0eSDiego Santa Cruz 			if (conf->gp_part[pidx].wr_rel_set)
13058dda5b0eSDiego Santa Cruz 				wr_rel_set |= EXT_CSD_WR_DATA_REL_GP(pidx);
13068dda5b0eSDiego Santa Cruz 			else
13078dda5b0eSDiego Santa Cruz 				wr_rel_set &= ~EXT_CSD_WR_DATA_REL_GP(pidx);
13088dda5b0eSDiego Santa Cruz 		}
13098dda5b0eSDiego Santa Cruz 	}
13108dda5b0eSDiego Santa Cruz 
13118dda5b0eSDiego Santa Cruz 	if (wr_rel_set != ext_csd[EXT_CSD_WR_REL_SET] &&
13128dda5b0eSDiego Santa Cruz 	    !(ext_csd[EXT_CSD_WR_REL_PARAM] & EXT_CSD_HS_CTRL_REL)) {
13138dda5b0eSDiego Santa Cruz 		puts("Card does not support host controlled partition write "
13148dda5b0eSDiego Santa Cruz 		     "reliability settings\n");
13158dda5b0eSDiego Santa Cruz 		return -EMEDIUMTYPE;
13168dda5b0eSDiego Santa Cruz 	}
13178dda5b0eSDiego Santa Cruz 
1318ac9da0e0SDiego Santa Cruz 	if (ext_csd[EXT_CSD_PARTITION_SETTING] &
1319ac9da0e0SDiego Santa Cruz 	    EXT_CSD_PARTITION_SETTING_COMPLETED) {
1320ac9da0e0SDiego Santa Cruz 		printf("Card already partitioned\n");
1321ac9da0e0SDiego Santa Cruz 		return -EPERM;
1322ac9da0e0SDiego Santa Cruz 	}
1323ac9da0e0SDiego Santa Cruz 
1324ac9da0e0SDiego Santa Cruz 	if (mode == MMC_HWPART_CONF_CHECK)
1325ac9da0e0SDiego Santa Cruz 		return 0;
1326ac9da0e0SDiego Santa Cruz 
1327ac9da0e0SDiego Santa Cruz 	/* Partitioning requires high-capacity size definitions */
1328ac9da0e0SDiego Santa Cruz 	if (!(ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01)) {
1329ac9da0e0SDiego Santa Cruz 		err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1330ac9da0e0SDiego Santa Cruz 				 EXT_CSD_ERASE_GROUP_DEF, 1);
1331ac9da0e0SDiego Santa Cruz 
1332ac9da0e0SDiego Santa Cruz 		if (err)
1333ac9da0e0SDiego Santa Cruz 			return err;
1334ac9da0e0SDiego Santa Cruz 
1335ac9da0e0SDiego Santa Cruz 		ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1;
1336ac9da0e0SDiego Santa Cruz 
1337ac9da0e0SDiego Santa Cruz 		/* update erase group size to be high-capacity */
1338ac9da0e0SDiego Santa Cruz 		mmc->erase_grp_size =
1339ac9da0e0SDiego Santa Cruz 			ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024;
1340ac9da0e0SDiego Santa Cruz 
1341ac9da0e0SDiego Santa Cruz 	}
1342ac9da0e0SDiego Santa Cruz 
1343ac9da0e0SDiego Santa Cruz 	/* all OK, write the configuration */
1344ac9da0e0SDiego Santa Cruz 	for (i = 0; i < 4; i++) {
1345ac9da0e0SDiego Santa Cruz 		err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1346ac9da0e0SDiego Santa Cruz 				 EXT_CSD_ENH_START_ADDR+i,
1347ac9da0e0SDiego Santa Cruz 				 (enh_start_addr >> (i*8)) & 0xFF);
1348ac9da0e0SDiego Santa Cruz 		if (err)
1349ac9da0e0SDiego Santa Cruz 			return err;
1350ac9da0e0SDiego Santa Cruz 	}
1351ac9da0e0SDiego Santa Cruz 	for (i = 0; i < 3; i++) {
1352ac9da0e0SDiego Santa Cruz 		err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1353ac9da0e0SDiego Santa Cruz 				 EXT_CSD_ENH_SIZE_MULT+i,
1354ac9da0e0SDiego Santa Cruz 				 (enh_size_mult >> (i*8)) & 0xFF);
1355ac9da0e0SDiego Santa Cruz 		if (err)
1356ac9da0e0SDiego Santa Cruz 			return err;
1357ac9da0e0SDiego Santa Cruz 	}
1358ac9da0e0SDiego Santa Cruz 	for (pidx = 0; pidx < 4; pidx++) {
1359ac9da0e0SDiego Santa Cruz 		for (i = 0; i < 3; i++) {
1360ac9da0e0SDiego Santa Cruz 			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1361ac9da0e0SDiego Santa Cruz 					 EXT_CSD_GP_SIZE_MULT+pidx*3+i,
1362ac9da0e0SDiego Santa Cruz 					 (gp_size_mult[pidx] >> (i*8)) & 0xFF);
1363ac9da0e0SDiego Santa Cruz 			if (err)
1364ac9da0e0SDiego Santa Cruz 				return err;
1365ac9da0e0SDiego Santa Cruz 		}
1366ac9da0e0SDiego Santa Cruz 	}
1367ac9da0e0SDiego Santa Cruz 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1368ac9da0e0SDiego Santa Cruz 			 EXT_CSD_PARTITIONS_ATTRIBUTE, part_attrs);
1369ac9da0e0SDiego Santa Cruz 	if (err)
1370ac9da0e0SDiego Santa Cruz 		return err;
1371ac9da0e0SDiego Santa Cruz 
1372ac9da0e0SDiego Santa Cruz 	if (mode == MMC_HWPART_CONF_SET)
1373ac9da0e0SDiego Santa Cruz 		return 0;
1374ac9da0e0SDiego Santa Cruz 
13758dda5b0eSDiego Santa Cruz 	/* The WR_REL_SET is a write-once register but shall be
13768dda5b0eSDiego Santa Cruz 	 * written before setting PART_SETTING_COMPLETED. As it is
13778dda5b0eSDiego Santa Cruz 	 * write-once we can only write it when completing the
13788dda5b0eSDiego Santa Cruz 	 * partitioning. */
13798dda5b0eSDiego Santa Cruz 	if (wr_rel_set != ext_csd[EXT_CSD_WR_REL_SET]) {
13808dda5b0eSDiego Santa Cruz 		err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
13818dda5b0eSDiego Santa Cruz 				 EXT_CSD_WR_REL_SET, wr_rel_set);
13828dda5b0eSDiego Santa Cruz 		if (err)
13838dda5b0eSDiego Santa Cruz 			return err;
13848dda5b0eSDiego Santa Cruz 	}
13858dda5b0eSDiego Santa Cruz 
1386ac9da0e0SDiego Santa Cruz 	/* Setting PART_SETTING_COMPLETED confirms the partition
1387ac9da0e0SDiego Santa Cruz 	 * configuration but it only becomes effective after power
1388ac9da0e0SDiego Santa Cruz 	 * cycle, so we do not adjust the partition related settings
1389ac9da0e0SDiego Santa Cruz 	 * in the mmc struct. */
1390ac9da0e0SDiego Santa Cruz 
1391ac9da0e0SDiego Santa Cruz 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1392ac9da0e0SDiego Santa Cruz 			 EXT_CSD_PARTITION_SETTING,
1393ac9da0e0SDiego Santa Cruz 			 EXT_CSD_PARTITION_SETTING_COMPLETED);
1394ac9da0e0SDiego Santa Cruz 	if (err)
1395ac9da0e0SDiego Santa Cruz 		return err;
1396ac9da0e0SDiego Santa Cruz 
1397ac9da0e0SDiego Santa Cruz 	return 0;
1398ac9da0e0SDiego Santa Cruz }
1399ac9da0e0SDiego Santa Cruz 
1400e7881d85SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC)
140148972d90SThierry Reding int mmc_getcd(struct mmc *mmc)
140248972d90SThierry Reding {
140348972d90SThierry Reding 	int cd;
140448972d90SThierry Reding 
140548972d90SThierry Reding 	cd = board_mmc_getcd(mmc);
140648972d90SThierry Reding 
1407d4e1da4eSPeter Korsgaard 	if (cd < 0) {
140893bfd616SPantelis Antoniou 		if (mmc->cfg->ops->getcd)
140993bfd616SPantelis Antoniou 			cd = mmc->cfg->ops->getcd(mmc);
1410d4e1da4eSPeter Korsgaard 		else
1411d4e1da4eSPeter Korsgaard 			cd = 1;
1412d4e1da4eSPeter Korsgaard 	}
141348972d90SThierry Reding 
141448972d90SThierry Reding 	return cd;
141548972d90SThierry Reding }
14168ca51e51SSimon Glass #endif
141748972d90SThierry Reding 
1418fdbb873eSKim Phillips static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)
1419272cc70bSAndy Fleming {
1420272cc70bSAndy Fleming 	struct mmc_cmd cmd;
1421272cc70bSAndy Fleming 	struct mmc_data data;
1422272cc70bSAndy Fleming 
1423272cc70bSAndy Fleming 	/* Switch the frequency */
1424272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_SWITCH_FUNC;
1425272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
1426272cc70bSAndy Fleming 	cmd.cmdarg = (mode << 31) | 0xffffff;
1427272cc70bSAndy Fleming 	cmd.cmdarg &= ~(0xf << (group * 4));
1428272cc70bSAndy Fleming 	cmd.cmdarg |= value << (group * 4);
1429272cc70bSAndy Fleming 
1430272cc70bSAndy Fleming 	data.dest = (char *)resp;
1431272cc70bSAndy Fleming 	data.blocksize = 64;
1432272cc70bSAndy Fleming 	data.blocks = 1;
1433272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
1434272cc70bSAndy Fleming 
1435272cc70bSAndy Fleming 	return mmc_send_cmd(mmc, &cmd, &data);
1436272cc70bSAndy Fleming }
1437272cc70bSAndy Fleming 
1438272cc70bSAndy Fleming 
1439fdbb873eSKim Phillips static int sd_change_freq(struct mmc *mmc)
1440272cc70bSAndy Fleming {
1441272cc70bSAndy Fleming 	int err;
1442272cc70bSAndy Fleming 	struct mmc_cmd cmd;
1443f781dd38SAnton staaf 	ALLOC_CACHE_ALIGN_BUFFER(uint, scr, 2);
1444f781dd38SAnton staaf 	ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16);
1445272cc70bSAndy Fleming 	struct mmc_data data;
1446272cc70bSAndy Fleming 	int timeout;
1447272cc70bSAndy Fleming 
1448272cc70bSAndy Fleming 	mmc->card_caps = 0;
1449272cc70bSAndy Fleming 
1450d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc))
1451d52ebf10SThomas Chou 		return 0;
1452d52ebf10SThomas Chou 
1453272cc70bSAndy Fleming 	/* Read the SCR to find out if this card supports higher speeds */
1454272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_APP_CMD;
1455272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
1456272cc70bSAndy Fleming 	cmd.cmdarg = mmc->rca << 16;
1457272cc70bSAndy Fleming 
1458272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
1459272cc70bSAndy Fleming 
1460272cc70bSAndy Fleming 	if (err)
1461272cc70bSAndy Fleming 		return err;
1462272cc70bSAndy Fleming 
1463272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_APP_SEND_SCR;
1464272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
1465272cc70bSAndy Fleming 	cmd.cmdarg = 0;
1466272cc70bSAndy Fleming 
1467272cc70bSAndy Fleming 	timeout = 3;
1468272cc70bSAndy Fleming 
1469272cc70bSAndy Fleming retry_scr:
1470f781dd38SAnton staaf 	data.dest = (char *)scr;
1471272cc70bSAndy Fleming 	data.blocksize = 8;
1472272cc70bSAndy Fleming 	data.blocks = 1;
1473272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
1474272cc70bSAndy Fleming 
1475272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, &data);
1476272cc70bSAndy Fleming 
1477272cc70bSAndy Fleming 	if (err) {
1478272cc70bSAndy Fleming 		if (timeout--)
1479272cc70bSAndy Fleming 			goto retry_scr;
1480272cc70bSAndy Fleming 
1481272cc70bSAndy Fleming 		return err;
1482272cc70bSAndy Fleming 	}
1483272cc70bSAndy Fleming 
14844e3d89baSYauhen Kharuzhy 	mmc->scr[0] = __be32_to_cpu(scr[0]);
14854e3d89baSYauhen Kharuzhy 	mmc->scr[1] = __be32_to_cpu(scr[1]);
1486272cc70bSAndy Fleming 
1487272cc70bSAndy Fleming 	switch ((mmc->scr[0] >> 24) & 0xf) {
1488272cc70bSAndy Fleming 	case 0:
1489272cc70bSAndy Fleming 		mmc->version = SD_VERSION_1_0;
1490272cc70bSAndy Fleming 		break;
1491272cc70bSAndy Fleming 	case 1:
1492272cc70bSAndy Fleming 		mmc->version = SD_VERSION_1_10;
1493272cc70bSAndy Fleming 		break;
1494272cc70bSAndy Fleming 	case 2:
1495272cc70bSAndy Fleming 		mmc->version = SD_VERSION_2;
14961741c64dSJaehoon Chung 		if ((mmc->scr[0] >> 15) & 0x1)
14971741c64dSJaehoon Chung 			mmc->version = SD_VERSION_3;
1498272cc70bSAndy Fleming 		break;
1499272cc70bSAndy Fleming 	default:
1500272cc70bSAndy Fleming 		mmc->version = SD_VERSION_1_0;
1501272cc70bSAndy Fleming 		break;
1502272cc70bSAndy Fleming 	}
1503272cc70bSAndy Fleming 
1504b44c7083SAlagu Sankar 	if (mmc->scr[0] & SD_DATA_4BIT)
1505b44c7083SAlagu Sankar 		mmc->card_caps |= MMC_MODE_4BIT;
1506b44c7083SAlagu Sankar 
1507272cc70bSAndy Fleming 	/* Version 1.0 doesn't support switching */
1508272cc70bSAndy Fleming 	if (mmc->version == SD_VERSION_1_0)
1509272cc70bSAndy Fleming 		return 0;
1510272cc70bSAndy Fleming 
1511272cc70bSAndy Fleming 	timeout = 4;
1512272cc70bSAndy Fleming 	while (timeout--) {
1513272cc70bSAndy Fleming 		err = sd_switch(mmc, SD_SWITCH_CHECK, 0, 1,
1514f781dd38SAnton staaf 				(u8 *)switch_status);
1515272cc70bSAndy Fleming 
1516272cc70bSAndy Fleming 		if (err)
1517272cc70bSAndy Fleming 			return err;
1518272cc70bSAndy Fleming 
1519272cc70bSAndy Fleming 		/* The high-speed function is busy.  Try again */
15204e3d89baSYauhen Kharuzhy 		if (!(__be32_to_cpu(switch_status[7]) & SD_HIGHSPEED_BUSY))
1521272cc70bSAndy Fleming 			break;
1522272cc70bSAndy Fleming 	}
1523272cc70bSAndy Fleming 
1524272cc70bSAndy Fleming 	/* If high-speed isn't supported, we return */
15254e3d89baSYauhen Kharuzhy 	if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED))
1526272cc70bSAndy Fleming 		return 0;
1527272cc70bSAndy Fleming 
15282c3fbf4cSMacpaul Lin 	/*
15292c3fbf4cSMacpaul Lin 	 * If the host doesn't support SD_HIGHSPEED, do not switch card to
15302c3fbf4cSMacpaul Lin 	 * HIGHSPEED mode even if the card support SD_HIGHSPPED.
15312c3fbf4cSMacpaul Lin 	 * This can avoid furthur problem when the card runs in different
15322c3fbf4cSMacpaul Lin 	 * mode between the host.
15332c3fbf4cSMacpaul Lin 	 */
153493bfd616SPantelis Antoniou 	if (!((mmc->cfg->host_caps & MMC_MODE_HS_52MHz) &&
153593bfd616SPantelis Antoniou 		(mmc->cfg->host_caps & MMC_MODE_HS)))
15362c3fbf4cSMacpaul Lin 		return 0;
15372c3fbf4cSMacpaul Lin 
1538f781dd38SAnton staaf 	err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status);
1539272cc70bSAndy Fleming 
1540272cc70bSAndy Fleming 	if (err)
1541272cc70bSAndy Fleming 		return err;
1542272cc70bSAndy Fleming 
15434e3d89baSYauhen Kharuzhy 	if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) == 0x01000000)
1544272cc70bSAndy Fleming 		mmc->card_caps |= MMC_MODE_HS;
1545272cc70bSAndy Fleming 
1546272cc70bSAndy Fleming 	return 0;
1547272cc70bSAndy Fleming }
1548272cc70bSAndy Fleming 
15493697e599SPeng Fan static int sd_read_ssr(struct mmc *mmc)
15503697e599SPeng Fan {
15513697e599SPeng Fan 	int err, i;
15523697e599SPeng Fan 	struct mmc_cmd cmd;
15533697e599SPeng Fan 	ALLOC_CACHE_ALIGN_BUFFER(uint, ssr, 16);
15543697e599SPeng Fan 	struct mmc_data data;
15553697e599SPeng Fan 	int timeout = 3;
15563697e599SPeng Fan 	unsigned int au, eo, et, es;
15573697e599SPeng Fan 
15583697e599SPeng Fan 	cmd.cmdidx = MMC_CMD_APP_CMD;
15593697e599SPeng Fan 	cmd.resp_type = MMC_RSP_R1;
15603697e599SPeng Fan 	cmd.cmdarg = mmc->rca << 16;
15613697e599SPeng Fan 
15623697e599SPeng Fan 	err = mmc_send_cmd(mmc, &cmd, NULL);
15633697e599SPeng Fan 	if (err)
15643697e599SPeng Fan 		return err;
15653697e599SPeng Fan 
15663697e599SPeng Fan 	cmd.cmdidx = SD_CMD_APP_SD_STATUS;
15673697e599SPeng Fan 	cmd.resp_type = MMC_RSP_R1;
15683697e599SPeng Fan 	cmd.cmdarg = 0;
15693697e599SPeng Fan 
15703697e599SPeng Fan retry_ssr:
15713697e599SPeng Fan 	data.dest = (char *)ssr;
15723697e599SPeng Fan 	data.blocksize = 64;
15733697e599SPeng Fan 	data.blocks = 1;
15743697e599SPeng Fan 	data.flags = MMC_DATA_READ;
15753697e599SPeng Fan 
15763697e599SPeng Fan 	err = mmc_send_cmd(mmc, &cmd, &data);
15773697e599SPeng Fan 	if (err) {
15783697e599SPeng Fan 		if (timeout--)
15793697e599SPeng Fan 			goto retry_ssr;
15803697e599SPeng Fan 
15813697e599SPeng Fan 		return err;
15823697e599SPeng Fan 	}
15833697e599SPeng Fan 
15843697e599SPeng Fan 	for (i = 0; i < 16; i++)
15853697e599SPeng Fan 		ssr[i] = be32_to_cpu(ssr[i]);
15863697e599SPeng Fan 
15873697e599SPeng Fan 	au = (ssr[2] >> 12) & 0xF;
15883697e599SPeng Fan 	if ((au <= 9) || (mmc->version == SD_VERSION_3)) {
15893697e599SPeng Fan 		mmc->ssr.au = sd_au_size[au];
15903697e599SPeng Fan 		es = (ssr[3] >> 24) & 0xFF;
15913697e599SPeng Fan 		es |= (ssr[2] & 0xFF) << 8;
15923697e599SPeng Fan 		et = (ssr[3] >> 18) & 0x3F;
15933697e599SPeng Fan 		if (es && et) {
15943697e599SPeng Fan 			eo = (ssr[3] >> 16) & 0x3;
15953697e599SPeng Fan 			mmc->ssr.erase_timeout = (et * 1000) / es;
15963697e599SPeng Fan 			mmc->ssr.erase_offset = eo * 1000;
15973697e599SPeng Fan 		}
15983697e599SPeng Fan 	} else {
15993697e599SPeng Fan 		debug("Invalid Allocation Unit Size.\n");
16003697e599SPeng Fan 	}
16013697e599SPeng Fan 
16023697e599SPeng Fan 	return 0;
16033697e599SPeng Fan }
16043697e599SPeng Fan 
1605272cc70bSAndy Fleming /* frequency bases */
1606272cc70bSAndy Fleming /* divided by 10 to be nice to platforms without floating point */
16075f837c2cSMike Frysinger static const int fbase[] = {
1608272cc70bSAndy Fleming 	10000,
1609272cc70bSAndy Fleming 	100000,
1610272cc70bSAndy Fleming 	1000000,
1611272cc70bSAndy Fleming 	10000000,
1612272cc70bSAndy Fleming };
1613272cc70bSAndy Fleming 
1614272cc70bSAndy Fleming /* Multiplier values for TRAN_SPEED.  Multiplied by 10 to be nice
1615272cc70bSAndy Fleming  * to platforms without floating point.
1616272cc70bSAndy Fleming  */
161761fe076fSSimon Glass static const u8 multipliers[] = {
1618272cc70bSAndy Fleming 	0,	/* reserved */
1619272cc70bSAndy Fleming 	10,
1620272cc70bSAndy Fleming 	12,
1621272cc70bSAndy Fleming 	13,
1622272cc70bSAndy Fleming 	15,
1623272cc70bSAndy Fleming 	20,
1624272cc70bSAndy Fleming 	25,
1625272cc70bSAndy Fleming 	30,
1626272cc70bSAndy Fleming 	35,
1627272cc70bSAndy Fleming 	40,
1628272cc70bSAndy Fleming 	45,
1629272cc70bSAndy Fleming 	50,
1630272cc70bSAndy Fleming 	55,
1631272cc70bSAndy Fleming 	60,
1632272cc70bSAndy Fleming 	70,
1633272cc70bSAndy Fleming 	80,
1634272cc70bSAndy Fleming };
1635272cc70bSAndy Fleming 
1636e7881d85SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC)
1637fdbb873eSKim Phillips static void mmc_set_ios(struct mmc *mmc)
1638272cc70bSAndy Fleming {
163993bfd616SPantelis Antoniou 	if (mmc->cfg->ops->set_ios)
164093bfd616SPantelis Antoniou 		mmc->cfg->ops->set_ios(mmc);
1641272cc70bSAndy Fleming }
1642ad77484aSZiyuan Xu 
1643ad77484aSZiyuan Xu static bool mmc_card_busy(struct mmc *mmc)
1644ad77484aSZiyuan Xu {
1645ad77484aSZiyuan Xu 	if (!mmc->cfg->ops->card_busy)
1646ad77484aSZiyuan Xu 		return -ENOSYS;
1647ad77484aSZiyuan Xu 
1648ad77484aSZiyuan Xu 	return mmc->cfg->ops->card_busy(mmc);
1649ad77484aSZiyuan Xu }
1650ad77484aSZiyuan Xu 
1651ad77484aSZiyuan Xu static bool mmc_can_card_busy(struct mmc *)
1652ad77484aSZiyuan Xu {
1653ad77484aSZiyuan Xu 	return !!mmc->cfg->ops->card_busy;
1654ad77484aSZiyuan Xu }
16558ca51e51SSimon Glass #endif
1656272cc70bSAndy Fleming 
1657fdbb873eSKim Phillips static int mmc_startup(struct mmc *mmc)
1658272cc70bSAndy Fleming {
1659f866a46dSStephen Warren 	int err, i;
16603e3ff0acSZiyuan Xu 	uint mult, freq, tran_speed;
1661639b7827SYoshihiro Shimoda 	u64 cmult, csize, capacity;
1662272cc70bSAndy Fleming 	struct mmc_cmd cmd;
16638bfa195eSSimon Glass 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
16640c453bb7SDiego Santa Cruz 	bool has_parts = false;
16658a0cf490SDiego Santa Cruz 	bool part_completed;
1666c40fdca6SSimon Glass 	struct blk_desc *bdesc;
1667272cc70bSAndy Fleming 
1668d52ebf10SThomas Chou #ifdef CONFIG_MMC_SPI_CRC_ON
1669d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */
1670d52ebf10SThomas Chou 		cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF;
1671d52ebf10SThomas Chou 		cmd.resp_type = MMC_RSP_R1;
1672d52ebf10SThomas Chou 		cmd.cmdarg = 1;
1673d52ebf10SThomas Chou 		err = mmc_send_cmd(mmc, &cmd, NULL);
1674d52ebf10SThomas Chou 
1675d52ebf10SThomas Chou 		if (err)
1676d52ebf10SThomas Chou 			return err;
1677d52ebf10SThomas Chou 	}
1678d52ebf10SThomas Chou #endif
1679479fbf72SJason Zhu #ifndef CONFIG_MMC_USE_PRE_CONFIG
1680272cc70bSAndy Fleming 	/* Put the Card in Identify Mode */
1681d52ebf10SThomas Chou 	cmd.cmdidx = mmc_host_is_spi(mmc) ? MMC_CMD_SEND_CID :
1682d52ebf10SThomas Chou 		MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */
1683272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R2;
1684272cc70bSAndy Fleming 	cmd.cmdarg = 0;
1685272cc70bSAndy Fleming 
1686272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
1687272cc70bSAndy Fleming 
1688272cc70bSAndy Fleming 	if (err)
1689272cc70bSAndy Fleming 		return err;
1690272cc70bSAndy Fleming 
1691272cc70bSAndy Fleming 	memcpy(mmc->cid, cmd.response, 16);
1692272cc70bSAndy Fleming 
1693272cc70bSAndy Fleming 	/*
1694272cc70bSAndy Fleming 	 * For MMC cards, set the Relative Address.
1695272cc70bSAndy Fleming 	 * For SD cards, get the Relatvie Address.
1696272cc70bSAndy Fleming 	 * This also puts the cards into Standby State
1697272cc70bSAndy Fleming 	 */
1698d52ebf10SThomas Chou 	if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
1699272cc70bSAndy Fleming 		cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;
1700272cc70bSAndy Fleming 		cmd.cmdarg = mmc->rca << 16;
1701272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R6;
1702272cc70bSAndy Fleming 
1703272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
1704272cc70bSAndy Fleming 
1705272cc70bSAndy Fleming 		if (err)
1706272cc70bSAndy Fleming 			return err;
1707272cc70bSAndy Fleming 
1708272cc70bSAndy Fleming 		if (IS_SD(mmc))
1709998be3ddSRabin Vincent 			mmc->rca = (cmd.response[0] >> 16) & 0xffff;
1710d52ebf10SThomas Chou 	}
1711479fbf72SJason Zhu #endif
1712272cc70bSAndy Fleming 	/* Get the Card-Specific Data */
1713272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SEND_CSD;
1714272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R2;
1715272cc70bSAndy Fleming 	cmd.cmdarg = mmc->rca << 16;
1716272cc70bSAndy Fleming 
1717272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
1718272cc70bSAndy Fleming 
1719272cc70bSAndy Fleming 	if (err)
1720272cc70bSAndy Fleming 		return err;
1721272cc70bSAndy Fleming 
1722998be3ddSRabin Vincent 	mmc->csd[0] = cmd.response[0];
1723998be3ddSRabin Vincent 	mmc->csd[1] = cmd.response[1];
1724998be3ddSRabin Vincent 	mmc->csd[2] = cmd.response[2];
1725998be3ddSRabin Vincent 	mmc->csd[3] = cmd.response[3];
1726272cc70bSAndy Fleming 
1727272cc70bSAndy Fleming 	if (mmc->version == MMC_VERSION_UNKNOWN) {
17280b453ffeSRabin Vincent 		int version = (cmd.response[0] >> 26) & 0xf;
1729272cc70bSAndy Fleming 
1730272cc70bSAndy Fleming 		switch (version) {
1731272cc70bSAndy Fleming 		case 0:
1732272cc70bSAndy Fleming 			mmc->version = MMC_VERSION_1_2;
1733272cc70bSAndy Fleming 			break;
1734272cc70bSAndy Fleming 		case 1:
1735272cc70bSAndy Fleming 			mmc->version = MMC_VERSION_1_4;
1736272cc70bSAndy Fleming 			break;
1737272cc70bSAndy Fleming 		case 2:
1738272cc70bSAndy Fleming 			mmc->version = MMC_VERSION_2_2;
1739272cc70bSAndy Fleming 			break;
1740272cc70bSAndy Fleming 		case 3:
1741272cc70bSAndy Fleming 			mmc->version = MMC_VERSION_3;
1742272cc70bSAndy Fleming 			break;
1743272cc70bSAndy Fleming 		case 4:
1744272cc70bSAndy Fleming 			mmc->version = MMC_VERSION_4;
1745272cc70bSAndy Fleming 			break;
1746272cc70bSAndy Fleming 		default:
1747272cc70bSAndy Fleming 			mmc->version = MMC_VERSION_1_2;
1748272cc70bSAndy Fleming 			break;
1749272cc70bSAndy Fleming 		}
1750272cc70bSAndy Fleming 	}
1751272cc70bSAndy Fleming 
1752272cc70bSAndy Fleming 	/* divide frequency by 10, since the mults are 10x bigger */
17530b453ffeSRabin Vincent 	freq = fbase[(cmd.response[0] & 0x7)];
17540b453ffeSRabin Vincent 	mult = multipliers[((cmd.response[0] >> 3) & 0xf)];
1755272cc70bSAndy Fleming 
17563e3ff0acSZiyuan Xu 	tran_speed = freq * mult;
1757272cc70bSAndy Fleming 
1758ab71188cSMarkus Niebel 	mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1);
1759998be3ddSRabin Vincent 	mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf);
1760272cc70bSAndy Fleming 
1761272cc70bSAndy Fleming 	if (IS_SD(mmc))
1762272cc70bSAndy Fleming 		mmc->write_bl_len = mmc->read_bl_len;
1763272cc70bSAndy Fleming 	else
1764998be3ddSRabin Vincent 		mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf);
1765272cc70bSAndy Fleming 
1766272cc70bSAndy Fleming 	if (mmc->high_capacity) {
1767272cc70bSAndy Fleming 		csize = (mmc->csd[1] & 0x3f) << 16
1768272cc70bSAndy Fleming 			| (mmc->csd[2] & 0xffff0000) >> 16;
1769272cc70bSAndy Fleming 		cmult = 8;
1770272cc70bSAndy Fleming 	} else {
1771272cc70bSAndy Fleming 		csize = (mmc->csd[1] & 0x3ff) << 2
1772272cc70bSAndy Fleming 			| (mmc->csd[2] & 0xc0000000) >> 30;
1773272cc70bSAndy Fleming 		cmult = (mmc->csd[2] & 0x00038000) >> 15;
1774272cc70bSAndy Fleming 	}
1775272cc70bSAndy Fleming 
1776f866a46dSStephen Warren 	mmc->capacity_user = (csize + 1) << (cmult + 2);
1777f866a46dSStephen Warren 	mmc->capacity_user *= mmc->read_bl_len;
1778f866a46dSStephen Warren 	mmc->capacity_boot = 0;
1779f866a46dSStephen Warren 	mmc->capacity_rpmb = 0;
1780f866a46dSStephen Warren 	for (i = 0; i < 4; i++)
1781f866a46dSStephen Warren 		mmc->capacity_gp[i] = 0;
1782272cc70bSAndy Fleming 
17838bfa195eSSimon Glass 	if (mmc->read_bl_len > MMC_MAX_BLOCK_LEN)
17848bfa195eSSimon Glass 		mmc->read_bl_len = MMC_MAX_BLOCK_LEN;
1785272cc70bSAndy Fleming 
17868bfa195eSSimon Glass 	if (mmc->write_bl_len > MMC_MAX_BLOCK_LEN)
17878bfa195eSSimon Glass 		mmc->write_bl_len = MMC_MAX_BLOCK_LEN;
1788272cc70bSAndy Fleming 
1789ab71188cSMarkus Niebel 	if ((mmc->dsr_imp) && (0xffffffff != mmc->dsr)) {
1790ab71188cSMarkus Niebel 		cmd.cmdidx = MMC_CMD_SET_DSR;
1791ab71188cSMarkus Niebel 		cmd.cmdarg = (mmc->dsr & 0xffff) << 16;
1792ab71188cSMarkus Niebel 		cmd.resp_type = MMC_RSP_NONE;
1793ab71188cSMarkus Niebel 		if (mmc_send_cmd(mmc, &cmd, NULL))
1794ab71188cSMarkus Niebel 			printf("MMC: SET_DSR failed\n");
1795ab71188cSMarkus Niebel 	}
1796ab71188cSMarkus Niebel 
1797272cc70bSAndy Fleming 	/* Select the card, and put it into Transfer Mode */
1798d52ebf10SThomas Chou 	if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
1799272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_SELECT_CARD;
1800fe8f7066SAjay Bhargav 		cmd.resp_type = MMC_RSP_R1;
1801272cc70bSAndy Fleming 		cmd.cmdarg = mmc->rca << 16;
1802272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
1803272cc70bSAndy Fleming 
1804272cc70bSAndy Fleming 		if (err)
1805272cc70bSAndy Fleming 			return err;
1806d52ebf10SThomas Chou 	}
1807272cc70bSAndy Fleming 
1808e6f99a56SLei Wen 	/*
1809e6f99a56SLei Wen 	 * For SD, its erase group is always one sector
1810e6f99a56SLei Wen 	 */
1811e6f99a56SLei Wen 	mmc->erase_grp_size = 1;
1812bc897b1dSLei Wen 	mmc->part_config = MMCPART_NOAVAILABLE;
1813d23e2c09SSukumar Ghorai 	if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) {
1814d23e2c09SSukumar Ghorai 		/* check  ext_csd version and capacity */
1815d23e2c09SSukumar Ghorai 		err = mmc_send_ext_csd(mmc, ext_csd);
18169cf199ebSDiego Santa Cruz 		if (err)
18179cf199ebSDiego Santa Cruz 			return err;
18189cf199ebSDiego Santa Cruz 		if (ext_csd[EXT_CSD_REV] >= 2) {
1819639b7827SYoshihiro Shimoda 			/*
1820639b7827SYoshihiro Shimoda 			 * According to the JEDEC Standard, the value of
1821639b7827SYoshihiro Shimoda 			 * ext_csd's capacity is valid if the value is more
1822639b7827SYoshihiro Shimoda 			 * than 2GB
1823639b7827SYoshihiro Shimoda 			 */
18240560db18SLei Wen 			capacity = ext_csd[EXT_CSD_SEC_CNT] << 0
18250560db18SLei Wen 					| ext_csd[EXT_CSD_SEC_CNT + 1] << 8
18260560db18SLei Wen 					| ext_csd[EXT_CSD_SEC_CNT + 2] << 16
18270560db18SLei Wen 					| ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
18288bfa195eSSimon Glass 			capacity *= MMC_MAX_BLOCK_LEN;
1829b1f1e821SŁukasz Majewski 			if ((capacity >> 20) > 2 * 1024)
1830f866a46dSStephen Warren 				mmc->capacity_user = capacity;
1831d23e2c09SSukumar Ghorai 		}
1832bc897b1dSLei Wen 
183364f4a619SJaehoon Chung 		switch (ext_csd[EXT_CSD_REV]) {
183464f4a619SJaehoon Chung 		case 1:
183564f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_1;
183664f4a619SJaehoon Chung 			break;
183764f4a619SJaehoon Chung 		case 2:
183864f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_2;
183964f4a619SJaehoon Chung 			break;
184064f4a619SJaehoon Chung 		case 3:
184164f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_3;
184264f4a619SJaehoon Chung 			break;
184364f4a619SJaehoon Chung 		case 5:
184464f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_41;
184564f4a619SJaehoon Chung 			break;
184664f4a619SJaehoon Chung 		case 6:
184764f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_5;
184864f4a619SJaehoon Chung 			break;
1849edab723bSMarkus Niebel 		case 7:
1850edab723bSMarkus Niebel 			mmc->version = MMC_VERSION_5_0;
1851edab723bSMarkus Niebel 			break;
18521a3619cfSStefan Wahren 		case 8:
18531a3619cfSStefan Wahren 			mmc->version = MMC_VERSION_5_1;
18541a3619cfSStefan Wahren 			break;
185564f4a619SJaehoon Chung 		}
185664f4a619SJaehoon Chung 
18578a0cf490SDiego Santa Cruz 		/* The partition data may be non-zero but it is only
18588a0cf490SDiego Santa Cruz 		 * effective if PARTITION_SETTING_COMPLETED is set in
18598a0cf490SDiego Santa Cruz 		 * EXT_CSD, so ignore any data if this bit is not set,
18608a0cf490SDiego Santa Cruz 		 * except for enabling the high-capacity group size
18618a0cf490SDiego Santa Cruz 		 * definition (see below). */
18628a0cf490SDiego Santa Cruz 		part_completed = !!(ext_csd[EXT_CSD_PARTITION_SETTING] &
18638a0cf490SDiego Santa Cruz 				    EXT_CSD_PARTITION_SETTING_COMPLETED);
18648a0cf490SDiego Santa Cruz 
18650c453bb7SDiego Santa Cruz 		/* store the partition info of emmc */
18660c453bb7SDiego Santa Cruz 		mmc->part_support = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
18670c453bb7SDiego Santa Cruz 		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) ||
18680c453bb7SDiego Santa Cruz 		    ext_csd[EXT_CSD_BOOT_MULT])
18690c453bb7SDiego Santa Cruz 			mmc->part_config = ext_csd[EXT_CSD_PART_CONF];
18708a0cf490SDiego Santa Cruz 		if (part_completed &&
18718a0cf490SDiego Santa Cruz 		    (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & ENHNCD_SUPPORT))
18720c453bb7SDiego Santa Cruz 			mmc->part_attr = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
1873a6a1f5f8SJason Zhu 		if (ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] & EXT_CSD_SEC_GB_CL_EN)
1874a6a1f5f8SJason Zhu 			mmc->esr.mmc_can_trim = 1;
18750c453bb7SDiego Santa Cruz 
18760c453bb7SDiego Santa Cruz 		mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17;
18770c453bb7SDiego Santa Cruz 
18780c453bb7SDiego Santa Cruz 		mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17;
18790c453bb7SDiego Santa Cruz 
18800c453bb7SDiego Santa Cruz 		for (i = 0; i < 4; i++) {
18810c453bb7SDiego Santa Cruz 			int idx = EXT_CSD_GP_SIZE_MULT + i * 3;
18828a0cf490SDiego Santa Cruz 			uint mult = (ext_csd[idx + 2] << 16) +
18830c453bb7SDiego Santa Cruz 				(ext_csd[idx + 1] << 8) + ext_csd[idx];
18848a0cf490SDiego Santa Cruz 			if (mult)
18858a0cf490SDiego Santa Cruz 				has_parts = true;
18868a0cf490SDiego Santa Cruz 			if (!part_completed)
18878a0cf490SDiego Santa Cruz 				continue;
18888a0cf490SDiego Santa Cruz 			mmc->capacity_gp[i] = mult;
18890c453bb7SDiego Santa Cruz 			mmc->capacity_gp[i] *=
18900c453bb7SDiego Santa Cruz 				ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
18910c453bb7SDiego Santa Cruz 			mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
1892f8e89d67SDiego Santa Cruz 			mmc->capacity_gp[i] <<= 19;
18930c453bb7SDiego Santa Cruz 		}
18940c453bb7SDiego Santa Cruz 
18958a0cf490SDiego Santa Cruz 		if (part_completed) {
1896a7f852b6SDiego Santa Cruz 			mmc->enh_user_size =
1897a7f852b6SDiego Santa Cruz 				(ext_csd[EXT_CSD_ENH_SIZE_MULT+2] << 16) +
1898a7f852b6SDiego Santa Cruz 				(ext_csd[EXT_CSD_ENH_SIZE_MULT+1] << 8) +
1899a7f852b6SDiego Santa Cruz 				ext_csd[EXT_CSD_ENH_SIZE_MULT];
1900a7f852b6SDiego Santa Cruz 			mmc->enh_user_size *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
1901a7f852b6SDiego Santa Cruz 			mmc->enh_user_size *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
1902a7f852b6SDiego Santa Cruz 			mmc->enh_user_size <<= 19;
1903a7f852b6SDiego Santa Cruz 			mmc->enh_user_start =
1904a7f852b6SDiego Santa Cruz 				(ext_csd[EXT_CSD_ENH_START_ADDR+3] << 24) +
1905a7f852b6SDiego Santa Cruz 				(ext_csd[EXT_CSD_ENH_START_ADDR+2] << 16) +
1906a7f852b6SDiego Santa Cruz 				(ext_csd[EXT_CSD_ENH_START_ADDR+1] << 8) +
1907a7f852b6SDiego Santa Cruz 				ext_csd[EXT_CSD_ENH_START_ADDR];
1908a7f852b6SDiego Santa Cruz 			if (mmc->high_capacity)
1909a7f852b6SDiego Santa Cruz 				mmc->enh_user_start <<= 9;
19108a0cf490SDiego Santa Cruz 		}
1911a7f852b6SDiego Santa Cruz 
1912e6f99a56SLei Wen 		/*
19131937e5aaSOliver Metz 		 * Host needs to enable ERASE_GRP_DEF bit if device is
19141937e5aaSOliver Metz 		 * partitioned. This bit will be lost every time after a reset
19151937e5aaSOliver Metz 		 * or power off. This will affect erase size.
1916e6f99a56SLei Wen 		 */
19178a0cf490SDiego Santa Cruz 		if (part_completed)
19180c453bb7SDiego Santa Cruz 			has_parts = true;
19191937e5aaSOliver Metz 		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) &&
19200c453bb7SDiego Santa Cruz 		    (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB))
19210c453bb7SDiego Santa Cruz 			has_parts = true;
19220c453bb7SDiego Santa Cruz 		if (has_parts) {
19231937e5aaSOliver Metz 			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
19241937e5aaSOliver Metz 				EXT_CSD_ERASE_GROUP_DEF, 1);
19251937e5aaSOliver Metz 
19261937e5aaSOliver Metz 			if (err)
19271937e5aaSOliver Metz 				return err;
1928021a8055SHannes Petermaier 			else
1929021a8055SHannes Petermaier 				ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1;
1930037dc0abSDiego Santa Cruz 		}
19311937e5aaSOliver Metz 
1932037dc0abSDiego Santa Cruz 		if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01) {
19331937e5aaSOliver Metz 			/* Read out group size from ext_csd */
19340560db18SLei Wen 			mmc->erase_grp_size =
1935a4ff9f83SDiego Santa Cruz 				ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024;
1936d7b29129SMarkus Niebel 			/*
1937d7b29129SMarkus Niebel 			 * if high capacity and partition setting completed
1938d7b29129SMarkus Niebel 			 * SEC_COUNT is valid even if it is smaller than 2 GiB
1939d7b29129SMarkus Niebel 			 * JEDEC Standard JESD84-B45, 6.2.4
1940d7b29129SMarkus Niebel 			 */
19418a0cf490SDiego Santa Cruz 			if (mmc->high_capacity && part_completed) {
1942d7b29129SMarkus Niebel 				capacity = (ext_csd[EXT_CSD_SEC_CNT]) |
1943d7b29129SMarkus Niebel 					(ext_csd[EXT_CSD_SEC_CNT + 1] << 8) |
1944d7b29129SMarkus Niebel 					(ext_csd[EXT_CSD_SEC_CNT + 2] << 16) |
1945d7b29129SMarkus Niebel 					(ext_csd[EXT_CSD_SEC_CNT + 3] << 24);
1946d7b29129SMarkus Niebel 				capacity *= MMC_MAX_BLOCK_LEN;
1947d7b29129SMarkus Niebel 				mmc->capacity_user = capacity;
1948d7b29129SMarkus Niebel 			}
19498bfa195eSSimon Glass 		} else {
19501937e5aaSOliver Metz 			/* Calculate the group size from the csd value. */
1951e6f99a56SLei Wen 			int erase_gsz, erase_gmul;
1952e6f99a56SLei Wen 			erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10;
1953e6f99a56SLei Wen 			erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5;
1954e6f99a56SLei Wen 			mmc->erase_grp_size = (erase_gsz + 1)
1955e6f99a56SLei Wen 				* (erase_gmul + 1);
1956e6f99a56SLei Wen 		}
1957037dc0abSDiego Santa Cruz 
1958037dc0abSDiego Santa Cruz 		mmc->hc_wp_grp_size = 1024
1959037dc0abSDiego Santa Cruz 			* ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]
1960037dc0abSDiego Santa Cruz 			* ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
19619e41a00bSDiego Santa Cruz 
19629e41a00bSDiego Santa Cruz 		mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET];
1963f866a46dSStephen Warren 	}
1964f866a46dSStephen Warren 
1965c40fdca6SSimon Glass 	err = mmc_set_capacity(mmc, mmc_get_blk_desc(mmc)->hwpart);
1966f866a46dSStephen Warren 	if (err)
1967f866a46dSStephen Warren 		return err;
1968d23e2c09SSukumar Ghorai 
1969272cc70bSAndy Fleming 	if (IS_SD(mmc))
1970272cc70bSAndy Fleming 		err = sd_change_freq(mmc);
1971272cc70bSAndy Fleming 	else
1972272cc70bSAndy Fleming 		err = mmc_change_freq(mmc);
1973272cc70bSAndy Fleming 
1974272cc70bSAndy Fleming 	if (err)
1975272cc70bSAndy Fleming 		return err;
1976272cc70bSAndy Fleming 
1977272cc70bSAndy Fleming 	/* Restrict card's capabilities by what the host can do */
197893bfd616SPantelis Antoniou 	mmc->card_caps &= mmc->cfg->host_caps;
1979272cc70bSAndy Fleming 
1980272cc70bSAndy Fleming 	if (IS_SD(mmc)) {
1981272cc70bSAndy Fleming 		if (mmc->card_caps & MMC_MODE_4BIT) {
1982272cc70bSAndy Fleming 			cmd.cmdidx = MMC_CMD_APP_CMD;
1983272cc70bSAndy Fleming 			cmd.resp_type = MMC_RSP_R1;
1984272cc70bSAndy Fleming 			cmd.cmdarg = mmc->rca << 16;
1985272cc70bSAndy Fleming 
1986272cc70bSAndy Fleming 			err = mmc_send_cmd(mmc, &cmd, NULL);
1987272cc70bSAndy Fleming 			if (err)
1988272cc70bSAndy Fleming 				return err;
1989272cc70bSAndy Fleming 
1990272cc70bSAndy Fleming 			cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH;
1991272cc70bSAndy Fleming 			cmd.resp_type = MMC_RSP_R1;
1992272cc70bSAndy Fleming 			cmd.cmdarg = 2;
1993272cc70bSAndy Fleming 			err = mmc_send_cmd(mmc, &cmd, NULL);
1994272cc70bSAndy Fleming 			if (err)
1995272cc70bSAndy Fleming 				return err;
1996272cc70bSAndy Fleming 
1997272cc70bSAndy Fleming 			mmc_set_bus_width(mmc, 4);
1998272cc70bSAndy Fleming 		}
1999272cc70bSAndy Fleming 
20003697e599SPeng Fan 		err = sd_read_ssr(mmc);
20013697e599SPeng Fan 		if (err)
20023697e599SPeng Fan 			return err;
20033697e599SPeng Fan 
2004272cc70bSAndy Fleming 		if (mmc->card_caps & MMC_MODE_HS)
2005fe414819SJason Zhu 			tran_speed = MMC_HIGH_52_MAX_DTR;
2006272cc70bSAndy Fleming 		else
2007fe414819SJason Zhu 			tran_speed = MMC_HIGH_26_MAX_DTR;
2008ad5fd922SJaehoon Chung 
20093e3ff0acSZiyuan Xu 		mmc_set_clock(mmc, tran_speed);
201049dba033SZiyuan Xu 	}
2011272cc70bSAndy Fleming 
20125af8f45cSAndrew Gabbasov 	/* Fix the block length for DDR mode */
201349dba033SZiyuan Xu 	if (mmc_card_ddr(mmc)) {
20145af8f45cSAndrew Gabbasov 		mmc->read_bl_len = MMC_MAX_BLOCK_LEN;
20155af8f45cSAndrew Gabbasov 		mmc->write_bl_len = MMC_MAX_BLOCK_LEN;
20165af8f45cSAndrew Gabbasov 	}
20175af8f45cSAndrew Gabbasov 
2018272cc70bSAndy Fleming 	/* fill in device description */
2019c40fdca6SSimon Glass 	bdesc = mmc_get_blk_desc(mmc);
2020c40fdca6SSimon Glass 	bdesc->lun = 0;
2021c40fdca6SSimon Glass 	bdesc->hwpart = 0;
2022c40fdca6SSimon Glass 	bdesc->type = 0;
2023c40fdca6SSimon Glass 	bdesc->blksz = mmc->read_bl_len;
2024c40fdca6SSimon Glass 	bdesc->log2blksz = LOG2(bdesc->blksz);
2025c40fdca6SSimon Glass 	bdesc->lba = lldiv(mmc->capacity, mmc->read_bl_len);
2026fc011f64SSjoerd Simons #if !defined(CONFIG_SPL_BUILD) || \
2027fc011f64SSjoerd Simons 		(defined(CONFIG_SPL_LIBCOMMON_SUPPORT) && \
2028fc011f64SSjoerd Simons 		!defined(CONFIG_USE_TINY_PRINTF))
2029c40fdca6SSimon Glass 	sprintf(bdesc->vendor, "Man %06x Snr %04x%04x",
2030babce5f6STaylor Hutt 		mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff),
2031babce5f6STaylor Hutt 		(mmc->cid[3] >> 16) & 0xffff);
2032c40fdca6SSimon Glass 	sprintf(bdesc->product, "%c%c%c%c%c%c", mmc->cid[0] & 0xff,
20330b453ffeSRabin Vincent 		(mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
2034babce5f6STaylor Hutt 		(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff,
2035babce5f6STaylor Hutt 		(mmc->cid[2] >> 24) & 0xff);
2036c40fdca6SSimon Glass 	sprintf(bdesc->revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf,
2037babce5f6STaylor Hutt 		(mmc->cid[2] >> 16) & 0xf);
203856196826SPaul Burton #else
2039c40fdca6SSimon Glass 	bdesc->vendor[0] = 0;
2040c40fdca6SSimon Glass 	bdesc->product[0] = 0;
2041c40fdca6SSimon Glass 	bdesc->revision[0] = 0;
204256196826SPaul Burton #endif
2043122efd43SMikhail Kshevetskiy #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT)
2044c40fdca6SSimon Glass 	part_init(bdesc);
2045122efd43SMikhail Kshevetskiy #endif
2046272cc70bSAndy Fleming 
2047272cc70bSAndy Fleming 	return 0;
2048272cc70bSAndy Fleming }
2049272cc70bSAndy Fleming 
2050479fbf72SJason Zhu #ifndef CONFIG_MMC_USE_PRE_CONFIG
2051fdbb873eSKim Phillips static int mmc_send_if_cond(struct mmc *mmc)
2052272cc70bSAndy Fleming {
2053272cc70bSAndy Fleming 	struct mmc_cmd cmd;
2054272cc70bSAndy Fleming 	int err;
2055272cc70bSAndy Fleming 
2056272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_SEND_IF_COND;
2057272cc70bSAndy Fleming 	/* We set the bit if the host supports voltages between 2.7 and 3.6 V */
205893bfd616SPantelis Antoniou 	cmd.cmdarg = ((mmc->cfg->voltages & 0xff8000) != 0) << 8 | 0xaa;
2059272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R7;
2060272cc70bSAndy Fleming 
2061272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
2062272cc70bSAndy Fleming 
2063272cc70bSAndy Fleming 	if (err)
2064272cc70bSAndy Fleming 		return err;
2065272cc70bSAndy Fleming 
2066998be3ddSRabin Vincent 	if ((cmd.response[0] & 0xff) != 0xaa)
2067915ffa52SJaehoon Chung 		return -EOPNOTSUPP;
2068272cc70bSAndy Fleming 	else
2069272cc70bSAndy Fleming 		mmc->version = SD_VERSION_2;
2070272cc70bSAndy Fleming 
2071272cc70bSAndy Fleming 	return 0;
2072272cc70bSAndy Fleming }
2073479fbf72SJason Zhu #endif
2074272cc70bSAndy Fleming 
2075c4d660d4SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC)
207695de9ab2SPaul Kocialkowski /* board-specific MMC power initializations. */
207795de9ab2SPaul Kocialkowski __weak void board_mmc_power_init(void)
207895de9ab2SPaul Kocialkowski {
207995de9ab2SPaul Kocialkowski }
208005cbeb7cSSimon Glass #endif
208195de9ab2SPaul Kocialkowski 
2082479fbf72SJason Zhu #ifndef CONFIG_MMC_USE_PRE_CONFIG
20832051aefeSPeng Fan static int mmc_power_init(struct mmc *mmc)
20842051aefeSPeng Fan {
2085c4d660d4SSimon Glass #if CONFIG_IS_ENABLED(DM_MMC)
208605cbeb7cSSimon Glass #if defined(CONFIG_DM_REGULATOR) && !defined(CONFIG_SPL_BUILD)
20872051aefeSPeng Fan 	struct udevice *vmmc_supply;
20882051aefeSPeng Fan 	int ret;
20892051aefeSPeng Fan 
20902051aefeSPeng Fan 	ret = device_get_supply_regulator(mmc->dev, "vmmc-supply",
20912051aefeSPeng Fan 					  &vmmc_supply);
20922051aefeSPeng Fan 	if (ret) {
2093288db7c7SJaehoon Chung 		debug("%s: No vmmc supply\n", mmc->dev->name);
20942051aefeSPeng Fan 		return 0;
20952051aefeSPeng Fan 	}
20962051aefeSPeng Fan 
20972051aefeSPeng Fan 	ret = regulator_set_enable(vmmc_supply, true);
20982051aefeSPeng Fan 	if (ret) {
20992051aefeSPeng Fan 		puts("Error enabling VMMC supply\n");
21002051aefeSPeng Fan 		return ret;
21012051aefeSPeng Fan 	}
21022051aefeSPeng Fan #endif
210305cbeb7cSSimon Glass #else /* !CONFIG_DM_MMC */
210405cbeb7cSSimon Glass 	/*
210505cbeb7cSSimon Glass 	 * Driver model should use a regulator, as above, rather than calling
210605cbeb7cSSimon Glass 	 * out to board code.
210705cbeb7cSSimon Glass 	 */
210805cbeb7cSSimon Glass 	board_mmc_power_init();
210905cbeb7cSSimon Glass #endif
21102051aefeSPeng Fan 	return 0;
21112051aefeSPeng Fan }
2112479fbf72SJason Zhu #endif
2113479fbf72SJason Zhu #ifdef CONFIG_MMC_USE_PRE_CONFIG
2114479fbf72SJason Zhu static int mmc_select_card(struct mmc *mmc, int n)
2115479fbf72SJason Zhu {
2116479fbf72SJason Zhu 	struct mmc_cmd cmd;
2117479fbf72SJason Zhu 	int err = 0;
21182051aefeSPeng Fan 
2119479fbf72SJason Zhu 	memset(&cmd, 0, sizeof(struct mmc_cmd));
2120479fbf72SJason Zhu 	if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
2121479fbf72SJason Zhu 		mmc->rca = n;
2122479fbf72SJason Zhu 		cmd.cmdidx = MMC_CMD_SELECT_CARD;
2123479fbf72SJason Zhu 		cmd.resp_type = MMC_RSP_R1;
2124479fbf72SJason Zhu 		cmd.cmdarg = mmc->rca << 16;
2125479fbf72SJason Zhu 		err = mmc_send_cmd(mmc, &cmd, NULL);
2126479fbf72SJason Zhu 	}
2127479fbf72SJason Zhu 
2128479fbf72SJason Zhu 	return err;
2129479fbf72SJason Zhu }
2130479fbf72SJason Zhu 
2131479fbf72SJason Zhu int mmc_start_init(struct mmc *mmc)
2132479fbf72SJason Zhu {
2133479fbf72SJason Zhu 	/*
2134479fbf72SJason Zhu 	 * We use the MMC config set by the bootrom.
2135479fbf72SJason Zhu 	 * So it is no need to reset the eMMC device.
2136479fbf72SJason Zhu 	 */
2137479fbf72SJason Zhu 	mmc_set_bus_width(mmc, 8);
2138479fbf72SJason Zhu 	mmc_set_clock(mmc, 1);
2139479fbf72SJason Zhu 	mmc_set_timing(mmc, MMC_TIMING_LEGACY);
2140479fbf72SJason Zhu 	/* Send cmd7 to return stand-by state*/
2141479fbf72SJason Zhu 	mmc_select_card(mmc, 0);
2142479fbf72SJason Zhu 	mmc->version = MMC_VERSION_UNKNOWN;
2143479fbf72SJason Zhu 	mmc->high_capacity = 1;
2144479fbf72SJason Zhu 	/*
2145479fbf72SJason Zhu 	 * The RCA is set to 2 by rockchip bootrom, use the default
2146479fbf72SJason Zhu 	 * value here.
2147479fbf72SJason Zhu 	 */
2148479fbf72SJason Zhu #ifdef CONFIG_ARCH_ROCKCHIP
2149479fbf72SJason Zhu 	mmc->rca = 2;
2150479fbf72SJason Zhu #else
2151479fbf72SJason Zhu 	mmc->rca = 1;
2152479fbf72SJason Zhu #endif
2153479fbf72SJason Zhu 	return 0;
2154479fbf72SJason Zhu }
2155479fbf72SJason Zhu #else
2156e9550449SChe-Liang Chiou int mmc_start_init(struct mmc *mmc)
2157272cc70bSAndy Fleming {
21588ca51e51SSimon Glass 	bool no_card;
2159afd5932bSMacpaul Lin 	int err;
2160272cc70bSAndy Fleming 
2161ab769f22SPantelis Antoniou 	/* we pretend there's no card when init is NULL */
21628ca51e51SSimon Glass 	no_card = mmc_getcd(mmc) == 0;
2163e7881d85SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC)
21648ca51e51SSimon Glass 	no_card = no_card || (mmc->cfg->ops->init == NULL);
21658ca51e51SSimon Glass #endif
21668ca51e51SSimon Glass 	if (no_card) {
216748972d90SThierry Reding 		mmc->has_init = 0;
216856196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
216948972d90SThierry Reding 		printf("MMC: no card present\n");
217056196826SPaul Burton #endif
2171915ffa52SJaehoon Chung 		return -ENOMEDIUM;
217248972d90SThierry Reding 	}
217348972d90SThierry Reding 
2174bc897b1dSLei Wen 	if (mmc->has_init)
2175bc897b1dSLei Wen 		return 0;
2176bc897b1dSLei Wen 
21775a8dbdc6SYangbo Lu #ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT
21785a8dbdc6SYangbo Lu 	mmc_adapter_card_type_ident();
21795a8dbdc6SYangbo Lu #endif
21802051aefeSPeng Fan 	err = mmc_power_init(mmc);
21812051aefeSPeng Fan 	if (err)
21822051aefeSPeng Fan 		return err;
218395de9ab2SPaul Kocialkowski 
2184e7881d85SSimon Glass #if CONFIG_IS_ENABLED(DM_MMC)
21858ca51e51SSimon Glass 	/* The device has already been probed ready for use */
21868ca51e51SSimon Glass #else
2187ab769f22SPantelis Antoniou 	/* made sure it's not NULL earlier */
218893bfd616SPantelis Antoniou 	err = mmc->cfg->ops->init(mmc);
2189272cc70bSAndy Fleming 	if (err)
2190272cc70bSAndy Fleming 		return err;
21918ca51e51SSimon Glass #endif
2192b86b85e2SIlya Yanok 	mmc_set_bus_width(mmc, 1);
2193b86b85e2SIlya Yanok 	mmc_set_clock(mmc, 1);
219481db2d36SZiyuan Xu 	mmc_set_timing(mmc, MMC_TIMING_LEGACY);
2195b86b85e2SIlya Yanok 
2196272cc70bSAndy Fleming 	/* Reset the Card */
2197272cc70bSAndy Fleming 	err = mmc_go_idle(mmc);
2198272cc70bSAndy Fleming 
2199272cc70bSAndy Fleming 	if (err)
2200272cc70bSAndy Fleming 		return err;
2201272cc70bSAndy Fleming 
2202bc897b1dSLei Wen 	/* The internal partition reset to user partition(0) at every CMD0*/
2203c40fdca6SSimon Glass 	mmc_get_blk_desc(mmc)->hwpart = 0;
2204bc897b1dSLei Wen 
2205272cc70bSAndy Fleming 	/* Test for SD version 2 */
2206272cc70bSAndy Fleming 	err = mmc_send_if_cond(mmc);
2207272cc70bSAndy Fleming 
2208272cc70bSAndy Fleming 	/* Now try to get the SD card's operating condition */
2209272cc70bSAndy Fleming 	err = sd_send_op_cond(mmc);
2210272cc70bSAndy Fleming 
2211272cc70bSAndy Fleming 	/* If the command timed out, we check for an MMC card */
2212915ffa52SJaehoon Chung 	if (err == -ETIMEDOUT) {
2213272cc70bSAndy Fleming 		err = mmc_send_op_cond(mmc);
2214272cc70bSAndy Fleming 
2215bd47c135SAndrew Gabbasov 		if (err) {
221656196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
2217272cc70bSAndy Fleming 			printf("Card did not respond to voltage select!\n");
221856196826SPaul Burton #endif
2219915ffa52SJaehoon Chung 			return -EOPNOTSUPP;
2220272cc70bSAndy Fleming 		}
2221272cc70bSAndy Fleming 	}
2222272cc70bSAndy Fleming 
2223bd47c135SAndrew Gabbasov 	if (!err)
2224e9550449SChe-Liang Chiou 		mmc->init_in_progress = 1;
2225e9550449SChe-Liang Chiou 
2226e9550449SChe-Liang Chiou 	return err;
2227e9550449SChe-Liang Chiou }
2228479fbf72SJason Zhu #endif
2229e9550449SChe-Liang Chiou 
2230e9550449SChe-Liang Chiou static int mmc_complete_init(struct mmc *mmc)
2231e9550449SChe-Liang Chiou {
2232e9550449SChe-Liang Chiou 	int err = 0;
2233e9550449SChe-Liang Chiou 
2234bd47c135SAndrew Gabbasov 	mmc->init_in_progress = 0;
2235e9550449SChe-Liang Chiou 	if (mmc->op_cond_pending)
2236e9550449SChe-Liang Chiou 		err = mmc_complete_op_cond(mmc);
2237e9550449SChe-Liang Chiou 
2238e9550449SChe-Liang Chiou 	if (!err)
2239bc897b1dSLei Wen 		err = mmc_startup(mmc);
2240bc897b1dSLei Wen 	if (err)
2241bc897b1dSLei Wen 		mmc->has_init = 0;
2242bc897b1dSLei Wen 	else
2243bc897b1dSLei Wen 		mmc->has_init = 1;
2244e9550449SChe-Liang Chiou 	return err;
2245e9550449SChe-Liang Chiou }
2246e9550449SChe-Liang Chiou 
2247e9550449SChe-Liang Chiou int mmc_init(struct mmc *mmc)
2248e9550449SChe-Liang Chiou {
2249bd47c135SAndrew Gabbasov 	int err = 0;
2250ce9eca94SMarek Vasut 	__maybe_unused unsigned start;
2251c4d660d4SSimon Glass #if CONFIG_IS_ENABLED(DM_MMC)
225233fb211dSSimon Glass 	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(mmc->dev);
2253e9550449SChe-Liang Chiou 
225433fb211dSSimon Glass 	upriv->mmc = mmc;
225533fb211dSSimon Glass #endif
2256e9550449SChe-Liang Chiou 	if (mmc->has_init)
2257e9550449SChe-Liang Chiou 		return 0;
2258d803fea5SMateusz Zalega 
2259d803fea5SMateusz Zalega 	start = get_timer(0);
2260d803fea5SMateusz Zalega 
2261e9550449SChe-Liang Chiou 	if (!mmc->init_in_progress)
2262e9550449SChe-Liang Chiou 		err = mmc_start_init(mmc);
2263e9550449SChe-Liang Chiou 
2264bd47c135SAndrew Gabbasov 	if (!err)
2265e9550449SChe-Liang Chiou 		err = mmc_complete_init(mmc);
2266919b4858SJagan Teki 	if (err)
2267919b4858SJagan Teki 		printf("%s: %d, time %lu\n", __func__, err, get_timer(start));
2268919b4858SJagan Teki 
2269bc897b1dSLei Wen 	return err;
2270272cc70bSAndy Fleming }
2271272cc70bSAndy Fleming 
2272ab71188cSMarkus Niebel int mmc_set_dsr(struct mmc *mmc, u16 val)
2273ab71188cSMarkus Niebel {
2274ab71188cSMarkus Niebel 	mmc->dsr = val;
2275ab71188cSMarkus Niebel 	return 0;
2276ab71188cSMarkus Niebel }
2277ab71188cSMarkus Niebel 
2278cee9ab7cSJeroen Hofstee /* CPU-specific MMC initializations */
2279cee9ab7cSJeroen Hofstee __weak int cpu_mmc_init(bd_t *bis)
2280272cc70bSAndy Fleming {
2281272cc70bSAndy Fleming 	return -1;
2282272cc70bSAndy Fleming }
2283272cc70bSAndy Fleming 
2284cee9ab7cSJeroen Hofstee /* board-specific MMC initializations. */
2285cee9ab7cSJeroen Hofstee __weak int board_mmc_init(bd_t *bis)
2286cee9ab7cSJeroen Hofstee {
2287cee9ab7cSJeroen Hofstee 	return -1;
2288cee9ab7cSJeroen Hofstee }
2289272cc70bSAndy Fleming 
2290e9550449SChe-Liang Chiou void mmc_set_preinit(struct mmc *mmc, int preinit)
2291e9550449SChe-Liang Chiou {
2292e9550449SChe-Liang Chiou 	mmc->preinit = preinit;
2293e9550449SChe-Liang Chiou }
2294e9550449SChe-Liang Chiou 
2295c4d660d4SSimon Glass #if CONFIG_IS_ENABLED(DM_MMC) && defined(CONFIG_SPL_BUILD)
22968e3332e2SSjoerd Simons static int mmc_probe(bd_t *bis)
22978e3332e2SSjoerd Simons {
22988e3332e2SSjoerd Simons 	return 0;
22998e3332e2SSjoerd Simons }
2300c4d660d4SSimon Glass #elif CONFIG_IS_ENABLED(DM_MMC)
23018e3332e2SSjoerd Simons static int mmc_probe(bd_t *bis)
23028e3332e2SSjoerd Simons {
23034a1db6d8SSimon Glass 	int ret, i;
23048e3332e2SSjoerd Simons 	struct uclass *uc;
23054a1db6d8SSimon Glass 	struct udevice *dev;
23068e3332e2SSjoerd Simons 
23078e3332e2SSjoerd Simons 	ret = uclass_get(UCLASS_MMC, &uc);
23088e3332e2SSjoerd Simons 	if (ret)
23098e3332e2SSjoerd Simons 		return ret;
23108e3332e2SSjoerd Simons 
23114a1db6d8SSimon Glass 	/*
23124a1db6d8SSimon Glass 	 * Try to add them in sequence order. Really with driver model we
23134a1db6d8SSimon Glass 	 * should allow holes, but the current MMC list does not allow that.
23144a1db6d8SSimon Glass 	 * So if we request 0, 1, 3 we will get 0, 1, 2.
23154a1db6d8SSimon Glass 	 */
23164a1db6d8SSimon Glass 	for (i = 0; ; i++) {
23174a1db6d8SSimon Glass 		ret = uclass_get_device_by_seq(UCLASS_MMC, i, &dev);
23184a1db6d8SSimon Glass 		if (ret == -ENODEV)
23194a1db6d8SSimon Glass 			break;
23204a1db6d8SSimon Glass 	}
23214a1db6d8SSimon Glass 	uclass_foreach_dev(dev, uc) {
23224a1db6d8SSimon Glass 		ret = device_probe(dev);
23238e3332e2SSjoerd Simons 		if (ret)
23244a1db6d8SSimon Glass 			printf("%s - probe failed: %d\n", dev->name, ret);
23258e3332e2SSjoerd Simons 	}
23268e3332e2SSjoerd Simons 
23278e3332e2SSjoerd Simons 	return 0;
23288e3332e2SSjoerd Simons }
23298e3332e2SSjoerd Simons #else
23308e3332e2SSjoerd Simons static int mmc_probe(bd_t *bis)
23318e3332e2SSjoerd Simons {
23328e3332e2SSjoerd Simons 	if (board_mmc_init(bis) < 0)
23338e3332e2SSjoerd Simons 		cpu_mmc_init(bis);
23348e3332e2SSjoerd Simons 
23358e3332e2SSjoerd Simons 	return 0;
23368e3332e2SSjoerd Simons }
23378e3332e2SSjoerd Simons #endif
2338e9550449SChe-Liang Chiou 
2339272cc70bSAndy Fleming int mmc_initialize(bd_t *bis)
2340272cc70bSAndy Fleming {
23411b26bab1SDaniel Kochmański 	static int initialized = 0;
23428e3332e2SSjoerd Simons 	int ret;
23431b26bab1SDaniel Kochmański 	if (initialized)	/* Avoid initializing mmc multiple times */
23441b26bab1SDaniel Kochmański 		return 0;
23451b26bab1SDaniel Kochmański 	initialized = 1;
23461b26bab1SDaniel Kochmański 
2347c4d660d4SSimon Glass #if !CONFIG_IS_ENABLED(BLK)
2348b5b838f1SMarek Vasut #if !CONFIG_IS_ENABLED(MMC_TINY)
2349c40fdca6SSimon Glass 	mmc_list_init();
2350c40fdca6SSimon Glass #endif
2351b5b838f1SMarek Vasut #endif
23528e3332e2SSjoerd Simons 	ret = mmc_probe(bis);
23538e3332e2SSjoerd Simons 	if (ret)
23548e3332e2SSjoerd Simons 		return ret;
2355272cc70bSAndy Fleming 
2356bb0dc108SYing Zhang #ifndef CONFIG_SPL_BUILD
2357272cc70bSAndy Fleming 	print_mmc_devices(',');
2358bb0dc108SYing Zhang #endif
2359272cc70bSAndy Fleming 
2360c40fdca6SSimon Glass 	mmc_do_preinit();
2361272cc70bSAndy Fleming 	return 0;
2362272cc70bSAndy Fleming }
2363cd3d4880STomas Melin 
2364cd3d4880STomas Melin #ifdef CONFIG_CMD_BKOPS_ENABLE
2365cd3d4880STomas Melin int mmc_set_bkops_enable(struct mmc *mmc)
2366cd3d4880STomas Melin {
2367cd3d4880STomas Melin 	int err;
2368cd3d4880STomas Melin 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
2369cd3d4880STomas Melin 
2370cd3d4880STomas Melin 	err = mmc_send_ext_csd(mmc, ext_csd);
2371cd3d4880STomas Melin 	if (err) {
2372cd3d4880STomas Melin 		puts("Could not get ext_csd register values\n");
2373cd3d4880STomas Melin 		return err;
2374cd3d4880STomas Melin 	}
2375cd3d4880STomas Melin 
2376cd3d4880STomas Melin 	if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) {
2377cd3d4880STomas Melin 		puts("Background operations not supported on device\n");
2378cd3d4880STomas Melin 		return -EMEDIUMTYPE;
2379cd3d4880STomas Melin 	}
2380cd3d4880STomas Melin 
2381cd3d4880STomas Melin 	if (ext_csd[EXT_CSD_BKOPS_EN] & 0x1) {
2382cd3d4880STomas Melin 		puts("Background operations already enabled\n");
2383cd3d4880STomas Melin 		return 0;
2384cd3d4880STomas Melin 	}
2385cd3d4880STomas Melin 
2386cd3d4880STomas Melin 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BKOPS_EN, 1);
2387cd3d4880STomas Melin 	if (err) {
2388cd3d4880STomas Melin 		puts("Failed to enable manual background operations\n");
2389cd3d4880STomas Melin 		return err;
2390cd3d4880STomas Melin 	}
2391cd3d4880STomas Melin 
2392cd3d4880STomas Melin 	puts("Enabled manual background operations\n");
2393cd3d4880STomas Melin 
2394cd3d4880STomas Melin 	return 0;
2395cd3d4880STomas Melin }
2396cd3d4880STomas Melin #endif
2397