xref: /rk3399_rockchip-uboot/drivers/mmc/mmc.c (revision a95f7caa96edf1291c1d6d8b3b324f47083f144e)
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 {
661272cc70bSAndy Fleming 	struct mmc_cmd cmd;
662272cc70bSAndy Fleming 	struct mmc_data data;
663272cc70bSAndy Fleming 	int err;
664272cc70bSAndy Fleming 
665*a95f7caaSJason Zhu #ifdef CONFIG_MMC_USE_PRE_CONFIG
666*a95f7caaSJason Zhu 	static int initialized;
667e531136eSJason Zhu 	if (initialized) {
668e531136eSJason Zhu 		memcpy(ext_csd, mmc_ext_csd, 512);
669e531136eSJason Zhu 		return 0;
670e531136eSJason Zhu 	}
671e531136eSJason Zhu 
672e531136eSJason Zhu 	initialized = 1;
673*a95f7caaSJason Zhu #endif
674272cc70bSAndy Fleming 	/* Get the Card Status Register */
675272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SEND_EXT_CSD;
676272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
677272cc70bSAndy Fleming 	cmd.cmdarg = 0;
678272cc70bSAndy Fleming 
679cdfd1ac6SYoshihiro Shimoda 	data.dest = (char *)ext_csd;
680272cc70bSAndy Fleming 	data.blocks = 1;
6818bfa195eSSimon Glass 	data.blocksize = MMC_MAX_BLOCK_LEN;
682272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
683272cc70bSAndy Fleming 
684272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, &data);
685e531136eSJason Zhu 	memcpy(mmc_ext_csd, ext_csd, 512);
6862056aa9fSJason Zhu #if defined(CONFIG_MMC_USE_PRE_CONFIG) && defined(CONFIG_SPL_BUILD)
6872056aa9fSJason Zhu 	char *mmc_ecsd_base = NULL;
6882056aa9fSJason Zhu 	ulong mmc_ecsd;
689272cc70bSAndy Fleming 
6902056aa9fSJason Zhu 	mmc_ecsd = dev_read_u32_default(mmc->dev, "mmc-ecsd", 0);
6912056aa9fSJason Zhu 	mmc_ecsd_base = (char *)mmc_ecsd;
6922056aa9fSJason Zhu 	if (mmc_ecsd_base) {
6932056aa9fSJason Zhu 		memcpy(mmc_ecsd_base, ext_csd, 512);
6942056aa9fSJason Zhu 		*(unsigned int *)(mmc_ecsd_base + 512) = 0x55aa55aa;
6952056aa9fSJason Zhu 	}
6962056aa9fSJason Zhu #endif
697272cc70bSAndy Fleming 	return err;
698272cc70bSAndy Fleming }
699272cc70bSAndy Fleming 
7009e8ce816SZiyuan Xu static int mmc_poll_for_busy(struct mmc *mmc, u8 send_status)
701272cc70bSAndy Fleming {
702272cc70bSAndy Fleming 	struct mmc_cmd cmd;
70355e5defdSZiyuan Xu 	u8 busy = true;
70455e5defdSZiyuan Xu 	uint start;
70555e5defdSZiyuan Xu 	int ret;
7065d4fc8d9SRaffaele Recalcati 	int timeout = 1000;
70755e5defdSZiyuan Xu 
70855e5defdSZiyuan Xu 	cmd.cmdidx = MMC_CMD_SEND_STATUS;
70955e5defdSZiyuan Xu 	cmd.resp_type = MMC_RSP_R1;
71055e5defdSZiyuan Xu 	cmd.cmdarg = mmc->rca << 16;
71155e5defdSZiyuan Xu 
71255e5defdSZiyuan Xu 	start = get_timer(0);
71355e5defdSZiyuan Xu 
7149e8ce816SZiyuan Xu 	if (!send_status && !mmc_can_card_busy(mmc)) {
7159e8ce816SZiyuan Xu 		mdelay(timeout);
7169e8ce816SZiyuan Xu 		return 0;
7179e8ce816SZiyuan Xu 	}
7189e8ce816SZiyuan Xu 
71955e5defdSZiyuan Xu 	do {
7209e8ce816SZiyuan Xu 		if (!send_status) {
72155e5defdSZiyuan Xu 			busy = mmc_card_busy(mmc);
72255e5defdSZiyuan Xu 		} else {
72355e5defdSZiyuan Xu 			ret = mmc_send_cmd(mmc, &cmd, NULL);
72455e5defdSZiyuan Xu 
72555e5defdSZiyuan Xu 			if (ret)
72655e5defdSZiyuan Xu 				return ret;
72755e5defdSZiyuan Xu 
72855e5defdSZiyuan Xu 			if (cmd.response[0] & MMC_STATUS_SWITCH_ERROR)
72955e5defdSZiyuan Xu 				return -EBADMSG;
73055e5defdSZiyuan Xu 			busy = (cmd.response[0] & MMC_STATUS_CURR_STATE) ==
73155e5defdSZiyuan Xu 				MMC_STATE_PRG;
73255e5defdSZiyuan Xu 		}
73355e5defdSZiyuan Xu 
73455e5defdSZiyuan Xu 		if (get_timer(start) > timeout && busy)
73555e5defdSZiyuan Xu 			return -ETIMEDOUT;
73655e5defdSZiyuan Xu 	} while (busy);
73755e5defdSZiyuan Xu 
73855e5defdSZiyuan Xu 	return 0;
73955e5defdSZiyuan Xu }
74055e5defdSZiyuan Xu 
74155e5defdSZiyuan Xu static int __mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value,
74255e5defdSZiyuan Xu 			u8 send_status)
74355e5defdSZiyuan Xu {
74455e5defdSZiyuan Xu 	struct mmc_cmd cmd;
745a9003dc6SMaxime Ripard 	int retries = 3;
7465d4fc8d9SRaffaele Recalcati 	int ret;
747272cc70bSAndy Fleming 
748272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SWITCH;
749272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1b;
750272cc70bSAndy Fleming 	cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
751272cc70bSAndy Fleming 				 (index << 16) |
752272cc70bSAndy Fleming 				 (value << 8);
753272cc70bSAndy Fleming 
75455e5defdSZiyuan Xu 	do {
7555d4fc8d9SRaffaele Recalcati 		ret = mmc_send_cmd(mmc, &cmd, NULL);
7565d4fc8d9SRaffaele Recalcati 
7579e8ce816SZiyuan Xu 		if (!ret)
7589e8ce816SZiyuan Xu 			return mmc_poll_for_busy(mmc, send_status);
75955e5defdSZiyuan Xu 	} while (--retries > 0 && ret);
76055e5defdSZiyuan Xu 
761a9003dc6SMaxime Ripard 	return ret;
762a9003dc6SMaxime Ripard }
763a9003dc6SMaxime Ripard 
76455e5defdSZiyuan Xu int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)
76555e5defdSZiyuan Xu {
76655e5defdSZiyuan Xu 	return __mmc_switch(mmc, set, index, value, true);
767272cc70bSAndy Fleming }
768272cc70bSAndy Fleming 
76949dba033SZiyuan Xu static int mmc_select_bus_width(struct mmc *mmc)
77049dba033SZiyuan Xu {
77149dba033SZiyuan Xu 	u32 ext_csd_bits[] = {
77249dba033SZiyuan Xu 		EXT_CSD_BUS_WIDTH_8,
77349dba033SZiyuan Xu 		EXT_CSD_BUS_WIDTH_4,
77449dba033SZiyuan Xu 	};
77549dba033SZiyuan Xu 	u32 bus_widths[] = {
77649dba033SZiyuan Xu 		MMC_BUS_WIDTH_8BIT,
77749dba033SZiyuan Xu 		MMC_BUS_WIDTH_4BIT,
77849dba033SZiyuan Xu 	};
77949dba033SZiyuan Xu 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
78049dba033SZiyuan Xu 	ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN);
78149dba033SZiyuan Xu 	u32 idx, bus_width = 0;
78249dba033SZiyuan Xu 	int err = 0;
78349dba033SZiyuan Xu 
78449dba033SZiyuan Xu 	if (mmc->version < MMC_VERSION_4 ||
78549dba033SZiyuan Xu 	    !(mmc->cfg->host_caps & (MMC_MODE_4BIT | MMC_MODE_8BIT)))
78649dba033SZiyuan Xu 		return 0;
78749dba033SZiyuan Xu 
78849dba033SZiyuan Xu 	err = mmc_send_ext_csd(mmc, ext_csd);
78949dba033SZiyuan Xu 
79049dba033SZiyuan Xu 	if (err)
79149dba033SZiyuan Xu 		return err;
79249dba033SZiyuan Xu 
79349dba033SZiyuan Xu 	idx = (mmc->cfg->host_caps & MMC_MODE_8BIT) ? 0 : 1;
79449dba033SZiyuan Xu 
79549dba033SZiyuan Xu 	/*
79649dba033SZiyuan Xu 	 * Unlike SD, MMC cards dont have a configuration register to notify
79749dba033SZiyuan Xu 	 * supported bus width. So bus test command should be run to identify
79849dba033SZiyuan Xu 	 * the supported bus width or compare the ext csd values of current
79949dba033SZiyuan Xu 	 * bus width and ext csd values of 1 bit mode read earlier.
80049dba033SZiyuan Xu 	 */
80149dba033SZiyuan Xu 	for (; idx < ARRAY_SIZE(bus_widths); idx++) {
80249dba033SZiyuan Xu 		/*
80349dba033SZiyuan Xu 		 * Host is capable of 8bit transfer, then switch
80449dba033SZiyuan Xu 		 * the device to work in 8bit transfer mode. If the
80549dba033SZiyuan Xu 		 * mmc switch command returns error then switch to
80649dba033SZiyuan Xu 		 * 4bit transfer mode. On success set the corresponding
80749dba033SZiyuan Xu 		 * bus width on the host.
80849dba033SZiyuan Xu 		 */
80949dba033SZiyuan Xu 		err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
81049dba033SZiyuan Xu 				 EXT_CSD_BUS_WIDTH, ext_csd_bits[idx]);
81149dba033SZiyuan Xu 		if (err)
81249dba033SZiyuan Xu 			continue;
81349dba033SZiyuan Xu 
81449dba033SZiyuan Xu 		bus_width = bus_widths[idx];
81549dba033SZiyuan Xu 		mmc_set_bus_width(mmc, bus_width);
81649dba033SZiyuan Xu 
81749dba033SZiyuan Xu 		err = mmc_send_ext_csd(mmc, test_csd);
81849dba033SZiyuan Xu 
81949dba033SZiyuan Xu 		if (err)
82049dba033SZiyuan Xu 			continue;
82149dba033SZiyuan Xu 
82249dba033SZiyuan Xu 		/* Only compare read only fields */
82349dba033SZiyuan Xu 		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] ==
82449dba033SZiyuan Xu 			test_csd[EXT_CSD_PARTITIONING_SUPPORT]) &&
82549dba033SZiyuan Xu 		    (ext_csd[EXT_CSD_HC_WP_GRP_SIZE] ==
82649dba033SZiyuan Xu 			test_csd[EXT_CSD_HC_WP_GRP_SIZE]) &&
82749dba033SZiyuan Xu 		    (ext_csd[EXT_CSD_REV] == test_csd[EXT_CSD_REV]) &&
82849dba033SZiyuan Xu 			(ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] ==
82949dba033SZiyuan Xu 			test_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) &&
83049dba033SZiyuan Xu 		    !memcmp(&ext_csd[EXT_CSD_SEC_CNT],
83149dba033SZiyuan Xu 			&test_csd[EXT_CSD_SEC_CNT], 4)) {
83249dba033SZiyuan Xu 			err = bus_width;
83349dba033SZiyuan Xu 			break;
83449dba033SZiyuan Xu 		} else {
83549dba033SZiyuan Xu 			err = -EBADMSG;
83649dba033SZiyuan Xu 		}
83749dba033SZiyuan Xu 	}
83849dba033SZiyuan Xu 
83949dba033SZiyuan Xu 	return err;
84049dba033SZiyuan Xu }
84149dba033SZiyuan Xu 
842699945cbSJason Zhu #ifndef CONFIG_MMC_SIMPLE
84349dba033SZiyuan Xu static const u8 tuning_blk_pattern_4bit[] = {
84449dba033SZiyuan Xu 	0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
84549dba033SZiyuan Xu 	0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
84649dba033SZiyuan Xu 	0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
84749dba033SZiyuan Xu 	0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
84849dba033SZiyuan Xu 	0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
84949dba033SZiyuan Xu 	0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
85049dba033SZiyuan Xu 	0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
85149dba033SZiyuan Xu 	0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
85249dba033SZiyuan Xu };
85349dba033SZiyuan Xu 
85449dba033SZiyuan Xu static const u8 tuning_blk_pattern_8bit[] = {
85549dba033SZiyuan Xu 	0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
85649dba033SZiyuan Xu 	0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
85749dba033SZiyuan Xu 	0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
85849dba033SZiyuan Xu 	0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
85949dba033SZiyuan Xu 	0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
86049dba033SZiyuan Xu 	0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
86149dba033SZiyuan Xu 	0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
86249dba033SZiyuan Xu 	0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
86349dba033SZiyuan Xu 	0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
86449dba033SZiyuan Xu 	0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
86549dba033SZiyuan Xu 	0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
86649dba033SZiyuan Xu 	0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
86749dba033SZiyuan Xu 	0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
86849dba033SZiyuan Xu 	0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
86949dba033SZiyuan Xu 	0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
87049dba033SZiyuan Xu 	0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
87149dba033SZiyuan Xu };
87249dba033SZiyuan Xu 
87349dba033SZiyuan Xu int mmc_send_tuning(struct mmc *mmc, u32 opcode)
87449dba033SZiyuan Xu {
87549dba033SZiyuan Xu 	struct mmc_cmd cmd;
87649dba033SZiyuan Xu 	struct mmc_data data;
87749dba033SZiyuan Xu 	const u8 *tuning_block_pattern;
87849dba033SZiyuan Xu 	int size, err = 0;
87949dba033SZiyuan Xu 	u8 *data_buf;
88049dba033SZiyuan Xu 
88149dba033SZiyuan Xu 	if (mmc->bus_width == MMC_BUS_WIDTH_8BIT) {
88249dba033SZiyuan Xu 		tuning_block_pattern = tuning_blk_pattern_8bit;
88349dba033SZiyuan Xu 		size = sizeof(tuning_blk_pattern_8bit);
88449dba033SZiyuan Xu 	} else if (mmc->bus_width == MMC_BUS_WIDTH_4BIT) {
88549dba033SZiyuan Xu 		tuning_block_pattern = tuning_blk_pattern_4bit;
88649dba033SZiyuan Xu 		size = sizeof(tuning_blk_pattern_4bit);
88749dba033SZiyuan Xu 	} else {
88849dba033SZiyuan Xu 		return -EINVAL;
88949dba033SZiyuan Xu 	}
89049dba033SZiyuan Xu 
89149dba033SZiyuan Xu 	data_buf = calloc(1, size);
89249dba033SZiyuan Xu 	if (!data_buf)
89349dba033SZiyuan Xu 		return -ENOMEM;
89449dba033SZiyuan Xu 
89549dba033SZiyuan Xu 	cmd.cmdidx = opcode;
89649dba033SZiyuan Xu 	cmd.resp_type = MMC_RSP_R1;
89749dba033SZiyuan Xu 	cmd.cmdarg = 0;
89849dba033SZiyuan Xu 
89949dba033SZiyuan Xu 	data.dest = (char *)data_buf;
90049dba033SZiyuan Xu 	data.blocksize = size;
90149dba033SZiyuan Xu 	data.blocks = 1;
90249dba033SZiyuan Xu 	data.flags = MMC_DATA_READ;
90349dba033SZiyuan Xu 
90449dba033SZiyuan Xu 	err = mmc_send_cmd(mmc, &cmd, &data);
90549dba033SZiyuan Xu 	if (err)
90649dba033SZiyuan Xu 		goto out;
90749dba033SZiyuan Xu 
90849dba033SZiyuan Xu 	if (memcmp(data_buf, tuning_block_pattern, size))
90949dba033SZiyuan Xu 		err = -EIO;
91049dba033SZiyuan Xu out:
91149dba033SZiyuan Xu 	free(data_buf);
91249dba033SZiyuan Xu 	return err;
91349dba033SZiyuan Xu }
91449dba033SZiyuan Xu 
91549dba033SZiyuan Xu static int mmc_execute_tuning(struct mmc *mmc)
91649dba033SZiyuan Xu {
91749dba033SZiyuan Xu #ifdef CONFIG_DM_MMC
91849dba033SZiyuan Xu 	struct dm_mmc_ops *ops = mmc_get_ops(mmc->dev);
91949dba033SZiyuan Xu #endif
92049dba033SZiyuan Xu 	u32 opcode;
92149dba033SZiyuan Xu 
92249dba033SZiyuan Xu 	if (IS_SD(mmc))
92349dba033SZiyuan Xu 		opcode = MMC_SEND_TUNING_BLOCK;
92449dba033SZiyuan Xu 	else
92549dba033SZiyuan Xu 		opcode = MMC_SEND_TUNING_BLOCK_HS200;
92649dba033SZiyuan Xu 
92749dba033SZiyuan Xu #ifndef CONFIG_DM_MMC
92849dba033SZiyuan Xu 	if (mmc->cfg->ops->execute_tuning) {
92949dba033SZiyuan Xu 		return mmc->cfg->ops->execute_tuning(mmc, opcode);
93049dba033SZiyuan Xu #else
93149dba033SZiyuan Xu 	if (ops->execute_tuning) {
93249dba033SZiyuan Xu 		return ops->execute_tuning(mmc->dev, opcode);
93349dba033SZiyuan Xu #endif
93449dba033SZiyuan Xu 	} else {
93549dba033SZiyuan Xu 		debug("Tuning feature required for HS200 mode.\n");
93649dba033SZiyuan Xu 		return -EIO;
93749dba033SZiyuan Xu 	}
93849dba033SZiyuan Xu }
93949dba033SZiyuan Xu 
94049dba033SZiyuan Xu static int mmc_hs200_tuning(struct mmc *mmc)
94149dba033SZiyuan Xu {
94249dba033SZiyuan Xu 	return mmc_execute_tuning(mmc);
94349dba033SZiyuan Xu }
94449dba033SZiyuan Xu 
945699945cbSJason Zhu #else
946699945cbSJason Zhu int mmc_send_tuning(struct mmc *mmc, u32 opcode) { return 0; }
947699945cbSJason Zhu int mmc_execute_tuning(struct mmc *mmc) { return 0; }
948699945cbSJason Zhu static int mmc_hs200_tuning(struct mmc *mmc) { return 0; }
949699945cbSJason Zhu #endif
950699945cbSJason Zhu 
951e61cd3d7SZiyuan Xu static int mmc_select_hs(struct mmc *mmc)
952e61cd3d7SZiyuan Xu {
953e61cd3d7SZiyuan Xu 	int ret;
954e61cd3d7SZiyuan Xu 
955e61cd3d7SZiyuan Xu 	ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
956e61cd3d7SZiyuan Xu 			 EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS);
957e61cd3d7SZiyuan Xu 
958e61cd3d7SZiyuan Xu 	if (!ret)
959e61cd3d7SZiyuan Xu 		mmc_set_timing(mmc, MMC_TIMING_MMC_HS);
960e61cd3d7SZiyuan Xu 
961e61cd3d7SZiyuan Xu 	return ret;
962e61cd3d7SZiyuan Xu }
963e61cd3d7SZiyuan Xu 
9645545757fSZiyuan Xu static int mmc_select_hs_ddr(struct mmc *mmc)
9655545757fSZiyuan Xu {
9665545757fSZiyuan Xu 	u32 ext_csd_bits;
9675545757fSZiyuan Xu 	int err = 0;
9685545757fSZiyuan Xu 
9695545757fSZiyuan Xu 	if (mmc->bus_width == MMC_BUS_WIDTH_1BIT)
9705545757fSZiyuan Xu 		return 0;
9715545757fSZiyuan Xu 
9725545757fSZiyuan Xu 	ext_csd_bits = (mmc->bus_width == MMC_BUS_WIDTH_8BIT) ?
9735545757fSZiyuan Xu 			EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4;
9745545757fSZiyuan Xu 
9755545757fSZiyuan Xu 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
9765545757fSZiyuan Xu 			 EXT_CSD_BUS_WIDTH, ext_csd_bits);
9775545757fSZiyuan Xu 	if (err)
9785545757fSZiyuan Xu 		return err;
9795545757fSZiyuan Xu 
9805545757fSZiyuan Xu 	mmc_set_timing(mmc, MMC_TIMING_MMC_DDR52);
9815545757fSZiyuan Xu 
9825545757fSZiyuan Xu 	return 0;
9835545757fSZiyuan Xu }
9845545757fSZiyuan Xu 
985699945cbSJason Zhu #ifndef CONFIG_MMC_SIMPLE
98649dba033SZiyuan Xu static int mmc_select_hs200(struct mmc *mmc)
98749dba033SZiyuan Xu {
98849dba033SZiyuan Xu 	int ret;
98949dba033SZiyuan Xu 
99049dba033SZiyuan Xu 	/*
99149dba033SZiyuan Xu 	 * Set the bus width(4 or 8) with host's support and
99249dba033SZiyuan Xu 	 * switch to HS200 mode if bus width is set successfully.
99349dba033SZiyuan Xu 	 */
99449dba033SZiyuan Xu 	ret = mmc_select_bus_width(mmc);
99549dba033SZiyuan Xu 
99649dba033SZiyuan Xu 	if (ret > 0) {
99749dba033SZiyuan Xu 		ret = __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
99849dba033SZiyuan Xu 				   EXT_CSD_HS_TIMING,
99949dba033SZiyuan Xu 				   EXT_CSD_TIMING_HS200, false);
100049dba033SZiyuan Xu 
100149dba033SZiyuan Xu 		if (ret)
100249dba033SZiyuan Xu 			return ret;
100349dba033SZiyuan Xu 
100449dba033SZiyuan Xu 		mmc_set_timing(mmc, MMC_TIMING_MMC_HS200);
100549dba033SZiyuan Xu 	}
100649dba033SZiyuan Xu 
100749dba033SZiyuan Xu 	return ret;
100849dba033SZiyuan Xu }
100949dba033SZiyuan Xu 
1010b673f29aSZiyuan Xu static int mmc_select_hs400(struct mmc *mmc)
1011b673f29aSZiyuan Xu {
1012b673f29aSZiyuan Xu 	int ret;
1013b673f29aSZiyuan Xu 
1014e56bff5bSJason Zhu 	/* Reduce frequency to HS frequency */
1015e56bff5bSJason Zhu 	mmc_set_clock(mmc, MMC_HIGH_52_MAX_DTR);
1016e56bff5bSJason Zhu 
1017b673f29aSZiyuan Xu 	/* Switch card to HS mode */
1018b673f29aSZiyuan Xu 	ret = __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1019b673f29aSZiyuan Xu 			   EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, false);
1020b673f29aSZiyuan Xu 	if (ret)
1021b673f29aSZiyuan Xu 		return ret;
1022b673f29aSZiyuan Xu 
1023b673f29aSZiyuan Xu 	/* Set host controller to HS timing */
1024b673f29aSZiyuan Xu 	mmc_set_timing(mmc, MMC_TIMING_MMC_HS);
1025b673f29aSZiyuan Xu 
1026b673f29aSZiyuan Xu 	ret = mmc_send_status(mmc, 1000);
1027b673f29aSZiyuan Xu 	if (ret)
1028b673f29aSZiyuan Xu 		return ret;
1029b673f29aSZiyuan Xu 
1030b673f29aSZiyuan Xu 	/* Switch card to DDR */
1031b673f29aSZiyuan Xu 	ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1032b673f29aSZiyuan Xu 			 EXT_CSD_BUS_WIDTH,
1033b673f29aSZiyuan Xu 			 EXT_CSD_DDR_BUS_WIDTH_8);
1034b673f29aSZiyuan Xu 	if (ret)
1035b673f29aSZiyuan Xu 		return ret;
1036b673f29aSZiyuan Xu 
1037b673f29aSZiyuan Xu 	/* Switch card to HS400 */
1038b673f29aSZiyuan Xu 	ret = __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1039b673f29aSZiyuan Xu 			   EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400, false);
1040b673f29aSZiyuan Xu 	if (ret)
1041b673f29aSZiyuan Xu 		return ret;
1042b673f29aSZiyuan Xu 
1043b673f29aSZiyuan Xu 	/* Set host controller to HS400 timing and frequency */
1044b673f29aSZiyuan Xu 	mmc_set_timing(mmc, MMC_TIMING_MMC_HS400);
1045b673f29aSZiyuan Xu 
1046b673f29aSZiyuan Xu 	return ret;
1047b673f29aSZiyuan Xu }
1048699945cbSJason Zhu #else
1049699945cbSJason Zhu static int mmc_select_hs200(struct mmc *mmc) { return 0; }
1050699945cbSJason Zhu static int mmc_select_hs400(struct mmc *mmc) { return 0; }
1051699945cbSJason Zhu #endif
1052b673f29aSZiyuan Xu 
1053227f658eSZiyuan Xu static u32 mmc_select_card_type(struct mmc *mmc, u8 *ext_csd)
1054227f658eSZiyuan Xu {
1055227f658eSZiyuan Xu 	u8 card_type;
1056227f658eSZiyuan Xu 	u32 host_caps, avail_type = 0;
1057227f658eSZiyuan Xu 
1058227f658eSZiyuan Xu 	card_type = ext_csd[EXT_CSD_CARD_TYPE];
1059227f658eSZiyuan Xu 	host_caps = mmc->cfg->host_caps;
1060227f658eSZiyuan Xu 
1061227f658eSZiyuan Xu 	if ((host_caps & MMC_MODE_HS) &&
1062227f658eSZiyuan Xu 	    (card_type & EXT_CSD_CARD_TYPE_26))
1063227f658eSZiyuan Xu 		avail_type |= EXT_CSD_CARD_TYPE_26;
1064227f658eSZiyuan Xu 
1065227f658eSZiyuan Xu 	if ((host_caps & MMC_MODE_HS) &&
1066227f658eSZiyuan Xu 	    (card_type & EXT_CSD_CARD_TYPE_52))
1067227f658eSZiyuan Xu 		avail_type |= EXT_CSD_CARD_TYPE_52;
1068227f658eSZiyuan Xu 
1069227f658eSZiyuan Xu 	/*
1070227f658eSZiyuan Xu 	 * For the moment, u-boot doesn't support signal voltage
1071227f658eSZiyuan Xu 	 * switch, therefor we assume that host support ddr52
1072227f658eSZiyuan Xu 	 * at 1.8v or 3.3v I/O(1.2v I/O not supported, hs200 and
1073227f658eSZiyuan Xu 	 * hs400 are the same).
1074227f658eSZiyuan Xu 	 */
1075227f658eSZiyuan Xu 	if ((host_caps & MMC_MODE_DDR_52MHz) &&
1076227f658eSZiyuan Xu 	    (card_type & EXT_CSD_CARD_TYPE_DDR_1_8V))
1077227f658eSZiyuan Xu 		avail_type |= EXT_CSD_CARD_TYPE_DDR_1_8V;
1078227f658eSZiyuan Xu 
1079227f658eSZiyuan Xu 	if ((host_caps & MMC_MODE_HS200) &&
1080227f658eSZiyuan Xu 	    (card_type & EXT_CSD_CARD_TYPE_HS200_1_8V))
1081227f658eSZiyuan Xu 		avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V;
1082227f658eSZiyuan Xu 
1083227f658eSZiyuan Xu 	/*
1084227f658eSZiyuan Xu 	 * If host can support HS400, it means that host can also
1085227f658eSZiyuan Xu 	 * support HS200.
1086227f658eSZiyuan Xu 	 */
1087227f658eSZiyuan Xu 	if ((host_caps & MMC_MODE_HS400) &&
1088227f658eSZiyuan Xu 	    (host_caps & MMC_MODE_8BIT) &&
1089227f658eSZiyuan Xu 	    (card_type & EXT_CSD_CARD_TYPE_HS400_1_8V))
1090227f658eSZiyuan Xu 		avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V |
1091227f658eSZiyuan Xu 				EXT_CSD_CARD_TYPE_HS400_1_8V;
1092227f658eSZiyuan Xu 
1093227f658eSZiyuan Xu 	if ((host_caps & MMC_MODE_HS400ES) &&
1094227f658eSZiyuan Xu 	    (host_caps & MMC_MODE_8BIT) &&
1095227f658eSZiyuan Xu 	    ext_csd[EXT_CSD_STROBE_SUPPORT] &&
1096227f658eSZiyuan Xu 	    (avail_type & EXT_CSD_CARD_TYPE_HS400_1_8V))
1097227f658eSZiyuan Xu 		avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V |
1098227f658eSZiyuan Xu 				EXT_CSD_CARD_TYPE_HS400_1_8V |
1099227f658eSZiyuan Xu 				EXT_CSD_CARD_TYPE_HS400ES;
1100227f658eSZiyuan Xu 
1101227f658eSZiyuan Xu 	return avail_type;
1102227f658eSZiyuan Xu }
1103227f658eSZiyuan Xu 
110449dba033SZiyuan Xu static void mmc_set_bus_speed(struct mmc *mmc, u8 avail_type)
110549dba033SZiyuan Xu {
110649dba033SZiyuan Xu 	int clock = 0;
110749dba033SZiyuan Xu 
110849dba033SZiyuan Xu 	if (mmc_card_hs(mmc))
110949dba033SZiyuan Xu 		clock = (avail_type & EXT_CSD_CARD_TYPE_52) ?
111049dba033SZiyuan Xu 			MMC_HIGH_52_MAX_DTR : MMC_HIGH_26_MAX_DTR;
111149dba033SZiyuan Xu 	else if (mmc_card_hs200(mmc) ||
111249dba033SZiyuan Xu 		 mmc_card_hs400(mmc) ||
111349dba033SZiyuan Xu 		 mmc_card_hs400es(mmc))
111449dba033SZiyuan Xu 		clock = MMC_HS200_MAX_DTR;
111549dba033SZiyuan Xu 
111649dba033SZiyuan Xu 	mmc_set_clock(mmc, clock);
111749dba033SZiyuan Xu }
111849dba033SZiyuan Xu 
1119fdbb873eSKim Phillips static int mmc_change_freq(struct mmc *mmc)
1120272cc70bSAndy Fleming {
11218bfa195eSSimon Glass 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
1122227f658eSZiyuan Xu 	u32 avail_type;
1123272cc70bSAndy Fleming 	int err;
1124272cc70bSAndy Fleming 
1125fc5b32fbSAndrew Gabbasov 	mmc->card_caps = 0;
1126272cc70bSAndy Fleming 
1127d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc))
1128d52ebf10SThomas Chou 		return 0;
1129d52ebf10SThomas Chou 
1130272cc70bSAndy Fleming 	/* Only version 4 supports high-speed */
1131272cc70bSAndy Fleming 	if (mmc->version < MMC_VERSION_4)
1132272cc70bSAndy Fleming 		return 0;
1133272cc70bSAndy Fleming 
1134fc5b32fbSAndrew Gabbasov 	mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT;
1135fc5b32fbSAndrew Gabbasov 
1136272cc70bSAndy Fleming 	err = mmc_send_ext_csd(mmc, ext_csd);
1137272cc70bSAndy Fleming 
1138272cc70bSAndy Fleming 	if (err)
1139272cc70bSAndy Fleming 		return err;
1140272cc70bSAndy Fleming 
1141227f658eSZiyuan Xu 	avail_type = mmc_select_card_type(mmc, ext_csd);
1142272cc70bSAndy Fleming 
114349dba033SZiyuan Xu 	if (avail_type & EXT_CSD_CARD_TYPE_HS200)
114449dba033SZiyuan Xu 		err = mmc_select_hs200(mmc);
11451f250d0aSJason Zhu 	else if (avail_type & EXT_CSD_CARD_TYPE_HS)
1146e61cd3d7SZiyuan Xu 		err = mmc_select_hs(mmc);
1147227f658eSZiyuan Xu 	else
1148227f658eSZiyuan Xu 		err = -EINVAL;
1149272cc70bSAndy Fleming 
1150272cc70bSAndy Fleming 	if (err)
1151a5e27b41SHeiko Schocher 		return err;
1152272cc70bSAndy Fleming 
115349dba033SZiyuan Xu 	mmc_set_bus_speed(mmc, avail_type);
1154272cc70bSAndy Fleming 
1155b673f29aSZiyuan Xu 	if (mmc_card_hs200(mmc)) {
115649dba033SZiyuan Xu 		err = mmc_hs200_tuning(mmc);
1157b673f29aSZiyuan Xu 		if (avail_type & EXT_CSD_CARD_TYPE_HS400 &&
1158b673f29aSZiyuan Xu 		    mmc->bus_width == MMC_BUS_WIDTH_8BIT) {
1159b673f29aSZiyuan Xu 			err = mmc_select_hs400(mmc);
1160b673f29aSZiyuan Xu 			mmc_set_bus_speed(mmc, avail_type);
1161b673f29aSZiyuan Xu 		}
1162b673f29aSZiyuan Xu 	} else if (!mmc_card_hs400es(mmc)) {
116349dba033SZiyuan Xu 		err = mmc_select_bus_width(mmc) > 0 ? 0 : err;
11645545757fSZiyuan Xu 		if (!err && avail_type & EXT_CSD_CARD_TYPE_DDR_52)
11655545757fSZiyuan Xu 			err = mmc_select_hs_ddr(mmc);
11665545757fSZiyuan Xu 	}
116749dba033SZiyuan Xu 
1168272cc70bSAndy Fleming 	return err;
1169272cc70bSAndy Fleming }
1170272cc70bSAndy Fleming 
1171f866a46dSStephen Warren static int mmc_set_capacity(struct mmc *mmc, int part_num)
1172f866a46dSStephen Warren {
1173f866a46dSStephen Warren 	switch (part_num) {
1174f866a46dSStephen Warren 	case 0:
1175f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_user;
1176f866a46dSStephen Warren 		break;
1177f866a46dSStephen Warren 	case 1:
1178f866a46dSStephen Warren 	case 2:
1179f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_boot;
1180f866a46dSStephen Warren 		break;
1181f866a46dSStephen Warren 	case 3:
1182f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_rpmb;
1183f866a46dSStephen Warren 		break;
1184f866a46dSStephen Warren 	case 4:
1185f866a46dSStephen Warren 	case 5:
1186f866a46dSStephen Warren 	case 6:
1187f866a46dSStephen Warren 	case 7:
1188f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_gp[part_num - 4];
1189f866a46dSStephen Warren 		break;
1190f866a46dSStephen Warren 	default:
1191f866a46dSStephen Warren 		return -1;
1192f866a46dSStephen Warren 	}
1193f866a46dSStephen Warren 
1194c40fdca6SSimon Glass 	mmc_get_blk_desc(mmc)->lba = lldiv(mmc->capacity, mmc->read_bl_len);
1195f866a46dSStephen Warren 
1196f866a46dSStephen Warren 	return 0;
1197f866a46dSStephen Warren }
1198f866a46dSStephen Warren 
11997dba0b93SSimon Glass int mmc_switch_part(struct mmc *mmc, unsigned int part_num)
1200bc897b1dSLei Wen {
1201f866a46dSStephen Warren 	int ret;
1202bc897b1dSLei Wen 
1203f866a46dSStephen Warren 	ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
1204bc897b1dSLei Wen 			 (mmc->part_config & ~PART_ACCESS_MASK)
1205bc897b1dSLei Wen 			 | (part_num & PART_ACCESS_MASK));
1206f866a46dSStephen Warren 
12076dc93e70SPeter Bigot 	/*
12086dc93e70SPeter Bigot 	 * Set the capacity if the switch succeeded or was intended
12096dc93e70SPeter Bigot 	 * to return to representing the raw device.
12106dc93e70SPeter Bigot 	 */
1211873cc1d7SStephen Warren 	if ((ret == 0) || ((ret == -ENODEV) && (part_num == 0))) {
12126dc93e70SPeter Bigot 		ret = mmc_set_capacity(mmc, part_num);
1213fdbb139fSSimon Glass 		mmc_get_blk_desc(mmc)->hwpart = part_num;
1214873cc1d7SStephen Warren 	}
12156dc93e70SPeter Bigot 
12166dc93e70SPeter Bigot 	return ret;
1217bc897b1dSLei Wen }
1218bc897b1dSLei Wen 
1219ac9da0e0SDiego Santa Cruz int mmc_hwpart_config(struct mmc *mmc,
1220ac9da0e0SDiego Santa Cruz 		      const struct mmc_hwpart_conf *conf,
1221ac9da0e0SDiego Santa Cruz 		      enum mmc_hwpart_conf_mode mode)
1222ac9da0e0SDiego Santa Cruz {
1223ac9da0e0SDiego Santa Cruz 	u8 part_attrs = 0;
1224ac9da0e0SDiego Santa Cruz 	u32 enh_size_mult;
1225ac9da0e0SDiego Santa Cruz 	u32 enh_start_addr;
1226ac9da0e0SDiego Santa Cruz 	u32 gp_size_mult[4];
1227ac9da0e0SDiego Santa Cruz 	u32 max_enh_size_mult;
1228ac9da0e0SDiego Santa Cruz 	u32 tot_enh_size_mult = 0;
12298dda5b0eSDiego Santa Cruz 	u8 wr_rel_set;
1230ac9da0e0SDiego Santa Cruz 	int i, pidx, err;
1231ac9da0e0SDiego Santa Cruz 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
1232ac9da0e0SDiego Santa Cruz 
1233ac9da0e0SDiego Santa Cruz 	if (mode < MMC_HWPART_CONF_CHECK || mode > MMC_HWPART_CONF_COMPLETE)
1234ac9da0e0SDiego Santa Cruz 		return -EINVAL;
1235ac9da0e0SDiego Santa Cruz 
1236ac9da0e0SDiego Santa Cruz 	if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4_41)) {
1237ac9da0e0SDiego Santa Cruz 		printf("eMMC >= 4.4 required for enhanced user data area\n");
1238ac9da0e0SDiego Santa Cruz 		return -EMEDIUMTYPE;
1239ac9da0e0SDiego Santa Cruz 	}
1240ac9da0e0SDiego Santa Cruz 
1241ac9da0e0SDiego Santa Cruz 	if (!(mmc->part_support & PART_SUPPORT)) {
1242ac9da0e0SDiego Santa Cruz 		printf("Card does not support partitioning\n");
1243ac9da0e0SDiego Santa Cruz 		return -EMEDIUMTYPE;
1244ac9da0e0SDiego Santa Cruz 	}
1245ac9da0e0SDiego Santa Cruz 
1246ac9da0e0SDiego Santa Cruz 	if (!mmc->hc_wp_grp_size) {
1247ac9da0e0SDiego Santa Cruz 		printf("Card does not define HC WP group size\n");
1248ac9da0e0SDiego Santa Cruz 		return -EMEDIUMTYPE;
1249ac9da0e0SDiego Santa Cruz 	}
1250ac9da0e0SDiego Santa Cruz 
1251ac9da0e0SDiego Santa Cruz 	/* check partition alignment and total enhanced size */
1252ac9da0e0SDiego Santa Cruz 	if (conf->user.enh_size) {
1253ac9da0e0SDiego Santa Cruz 		if (conf->user.enh_size % mmc->hc_wp_grp_size ||
1254ac9da0e0SDiego Santa Cruz 		    conf->user.enh_start % mmc->hc_wp_grp_size) {
1255ac9da0e0SDiego Santa Cruz 			printf("User data enhanced area not HC WP group "
1256ac9da0e0SDiego Santa Cruz 			       "size aligned\n");
1257ac9da0e0SDiego Santa Cruz 			return -EINVAL;
1258ac9da0e0SDiego Santa Cruz 		}
1259ac9da0e0SDiego Santa Cruz 		part_attrs |= EXT_CSD_ENH_USR;
1260ac9da0e0SDiego Santa Cruz 		enh_size_mult = conf->user.enh_size / mmc->hc_wp_grp_size;
1261ac9da0e0SDiego Santa Cruz 		if (mmc->high_capacity) {
1262ac9da0e0SDiego Santa Cruz 			enh_start_addr = conf->user.enh_start;
1263ac9da0e0SDiego Santa Cruz 		} else {
1264ac9da0e0SDiego Santa Cruz 			enh_start_addr = (conf->user.enh_start << 9);
1265ac9da0e0SDiego Santa Cruz 		}
1266ac9da0e0SDiego Santa Cruz 	} else {
1267ac9da0e0SDiego Santa Cruz 		enh_size_mult = 0;
1268ac9da0e0SDiego Santa Cruz 		enh_start_addr = 0;
1269ac9da0e0SDiego Santa Cruz 	}
1270ac9da0e0SDiego Santa Cruz 	tot_enh_size_mult += enh_size_mult;
1271ac9da0e0SDiego Santa Cruz 
1272ac9da0e0SDiego Santa Cruz 	for (pidx = 0; pidx < 4; pidx++) {
1273ac9da0e0SDiego Santa Cruz 		if (conf->gp_part[pidx].size % mmc->hc_wp_grp_size) {
1274ac9da0e0SDiego Santa Cruz 			printf("GP%i partition not HC WP group size "
1275ac9da0e0SDiego Santa Cruz 			       "aligned\n", pidx+1);
1276ac9da0e0SDiego Santa Cruz 			return -EINVAL;
1277ac9da0e0SDiego Santa Cruz 		}
1278ac9da0e0SDiego Santa Cruz 		gp_size_mult[pidx] = conf->gp_part[pidx].size / mmc->hc_wp_grp_size;
1279ac9da0e0SDiego Santa Cruz 		if (conf->gp_part[pidx].size && conf->gp_part[pidx].enhanced) {
1280ac9da0e0SDiego Santa Cruz 			part_attrs |= EXT_CSD_ENH_GP(pidx);
1281ac9da0e0SDiego Santa Cruz 			tot_enh_size_mult += gp_size_mult[pidx];
1282ac9da0e0SDiego Santa Cruz 		}
1283ac9da0e0SDiego Santa Cruz 	}
1284ac9da0e0SDiego Santa Cruz 
1285ac9da0e0SDiego Santa Cruz 	if (part_attrs && ! (mmc->part_support & ENHNCD_SUPPORT)) {
1286ac9da0e0SDiego Santa Cruz 		printf("Card does not support enhanced attribute\n");
1287ac9da0e0SDiego Santa Cruz 		return -EMEDIUMTYPE;
1288ac9da0e0SDiego Santa Cruz 	}
1289ac9da0e0SDiego Santa Cruz 
1290ac9da0e0SDiego Santa Cruz 	err = mmc_send_ext_csd(mmc, ext_csd);
1291ac9da0e0SDiego Santa Cruz 	if (err)
1292ac9da0e0SDiego Santa Cruz 		return err;
1293ac9da0e0SDiego Santa Cruz 
1294ac9da0e0SDiego Santa Cruz 	max_enh_size_mult =
1295ac9da0e0SDiego Santa Cruz 		(ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+2] << 16) +
1296ac9da0e0SDiego Santa Cruz 		(ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+1] << 8) +
1297ac9da0e0SDiego Santa Cruz 		ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT];
1298ac9da0e0SDiego Santa Cruz 	if (tot_enh_size_mult > max_enh_size_mult) {
1299ac9da0e0SDiego Santa Cruz 		printf("Total enhanced size exceeds maximum (%u > %u)\n",
1300ac9da0e0SDiego Santa Cruz 		       tot_enh_size_mult, max_enh_size_mult);
1301ac9da0e0SDiego Santa Cruz 		return -EMEDIUMTYPE;
1302ac9da0e0SDiego Santa Cruz 	}
1303ac9da0e0SDiego Santa Cruz 
13048dda5b0eSDiego Santa Cruz 	/* The default value of EXT_CSD_WR_REL_SET is device
13058dda5b0eSDiego Santa Cruz 	 * dependent, the values can only be changed if the
13068dda5b0eSDiego Santa Cruz 	 * EXT_CSD_HS_CTRL_REL bit is set. The values can be
13078dda5b0eSDiego Santa Cruz 	 * changed only once and before partitioning is completed. */
13088dda5b0eSDiego Santa Cruz 	wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET];
13098dda5b0eSDiego Santa Cruz 	if (conf->user.wr_rel_change) {
13108dda5b0eSDiego Santa Cruz 		if (conf->user.wr_rel_set)
13118dda5b0eSDiego Santa Cruz 			wr_rel_set |= EXT_CSD_WR_DATA_REL_USR;
13128dda5b0eSDiego Santa Cruz 		else
13138dda5b0eSDiego Santa Cruz 			wr_rel_set &= ~EXT_CSD_WR_DATA_REL_USR;
13148dda5b0eSDiego Santa Cruz 	}
13158dda5b0eSDiego Santa Cruz 	for (pidx = 0; pidx < 4; pidx++) {
13168dda5b0eSDiego Santa Cruz 		if (conf->gp_part[pidx].wr_rel_change) {
13178dda5b0eSDiego Santa Cruz 			if (conf->gp_part[pidx].wr_rel_set)
13188dda5b0eSDiego Santa Cruz 				wr_rel_set |= EXT_CSD_WR_DATA_REL_GP(pidx);
13198dda5b0eSDiego Santa Cruz 			else
13208dda5b0eSDiego Santa Cruz 				wr_rel_set &= ~EXT_CSD_WR_DATA_REL_GP(pidx);
13218dda5b0eSDiego Santa Cruz 		}
13228dda5b0eSDiego Santa Cruz 	}
13238dda5b0eSDiego Santa Cruz 
13248dda5b0eSDiego Santa Cruz 	if (wr_rel_set != ext_csd[EXT_CSD_WR_REL_SET] &&
13258dda5b0eSDiego Santa Cruz 	    !(ext_csd[EXT_CSD_WR_REL_PARAM] & EXT_CSD_HS_CTRL_REL)) {
13268dda5b0eSDiego Santa Cruz 		puts("Card does not support host controlled partition write "
13278dda5b0eSDiego Santa Cruz 		     "reliability settings\n");
13288dda5b0eSDiego Santa Cruz 		return -EMEDIUMTYPE;
13298dda5b0eSDiego Santa Cruz 	}
13308dda5b0eSDiego Santa Cruz 
1331ac9da0e0SDiego Santa Cruz 	if (ext_csd[EXT_CSD_PARTITION_SETTING] &
1332ac9da0e0SDiego Santa Cruz 	    EXT_CSD_PARTITION_SETTING_COMPLETED) {
1333ac9da0e0SDiego Santa Cruz 		printf("Card already partitioned\n");
1334ac9da0e0SDiego Santa Cruz 		return -EPERM;
1335ac9da0e0SDiego Santa Cruz 	}
1336ac9da0e0SDiego Santa Cruz 
1337ac9da0e0SDiego Santa Cruz 	if (mode == MMC_HWPART_CONF_CHECK)
1338ac9da0e0SDiego Santa Cruz 		return 0;
1339ac9da0e0SDiego Santa Cruz 
1340ac9da0e0SDiego Santa Cruz 	/* Partitioning requires high-capacity size definitions */
1341ac9da0e0SDiego Santa Cruz 	if (!(ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01)) {
1342ac9da0e0SDiego Santa Cruz 		err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1343ac9da0e0SDiego Santa Cruz 				 EXT_CSD_ERASE_GROUP_DEF, 1);
1344ac9da0e0SDiego Santa Cruz 
1345ac9da0e0SDiego Santa Cruz 		if (err)
1346ac9da0e0SDiego Santa Cruz 			return err;
1347ac9da0e0SDiego Santa Cruz 
1348ac9da0e0SDiego Santa Cruz 		ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1;
1349ac9da0e0SDiego Santa Cruz 
1350ac9da0e0SDiego Santa Cruz 		/* update erase group size to be high-capacity */
1351ac9da0e0SDiego Santa Cruz 		mmc->erase_grp_size =
1352ac9da0e0SDiego Santa Cruz 			ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024;
1353ac9da0e0SDiego Santa Cruz 
1354ac9da0e0SDiego Santa Cruz 	}
1355ac9da0e0SDiego Santa Cruz 
1356ac9da0e0SDiego Santa Cruz 	/* all OK, write the configuration */
1357ac9da0e0SDiego Santa Cruz 	for (i = 0; i < 4; i++) {
1358ac9da0e0SDiego Santa Cruz 		err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1359ac9da0e0SDiego Santa Cruz 				 EXT_CSD_ENH_START_ADDR+i,
1360ac9da0e0SDiego Santa Cruz 				 (enh_start_addr >> (i*8)) & 0xFF);
1361ac9da0e0SDiego Santa Cruz 		if (err)
1362ac9da0e0SDiego Santa Cruz 			return err;
1363ac9da0e0SDiego Santa Cruz 	}
1364ac9da0e0SDiego Santa Cruz 	for (i = 0; i < 3; i++) {
1365ac9da0e0SDiego Santa Cruz 		err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1366ac9da0e0SDiego Santa Cruz 				 EXT_CSD_ENH_SIZE_MULT+i,
1367ac9da0e0SDiego Santa Cruz 				 (enh_size_mult >> (i*8)) & 0xFF);
1368ac9da0e0SDiego Santa Cruz 		if (err)
1369ac9da0e0SDiego Santa Cruz 			return err;
1370ac9da0e0SDiego Santa Cruz 	}
1371ac9da0e0SDiego Santa Cruz 	for (pidx = 0; pidx < 4; pidx++) {
1372ac9da0e0SDiego Santa Cruz 		for (i = 0; i < 3; i++) {
1373ac9da0e0SDiego Santa Cruz 			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1374ac9da0e0SDiego Santa Cruz 					 EXT_CSD_GP_SIZE_MULT+pidx*3+i,
1375ac9da0e0SDiego Santa Cruz 					 (gp_size_mult[pidx] >> (i*8)) & 0xFF);
1376ac9da0e0SDiego Santa Cruz 			if (err)
1377ac9da0e0SDiego Santa Cruz 				return err;
1378ac9da0e0SDiego Santa Cruz 		}
1379ac9da0e0SDiego Santa Cruz 	}
1380ac9da0e0SDiego Santa Cruz 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1381ac9da0e0SDiego Santa Cruz 			 EXT_CSD_PARTITIONS_ATTRIBUTE, part_attrs);
1382ac9da0e0SDiego Santa Cruz 	if (err)
1383ac9da0e0SDiego Santa Cruz 		return err;
1384ac9da0e0SDiego Santa Cruz 
1385ac9da0e0SDiego Santa Cruz 	if (mode == MMC_HWPART_CONF_SET)
1386ac9da0e0SDiego Santa Cruz 		return 0;
1387ac9da0e0SDiego Santa Cruz 
13888dda5b0eSDiego Santa Cruz 	/* The WR_REL_SET is a write-once register but shall be
13898dda5b0eSDiego Santa Cruz 	 * written before setting PART_SETTING_COMPLETED. As it is
13908dda5b0eSDiego Santa Cruz 	 * write-once we can only write it when completing the
13918dda5b0eSDiego Santa Cruz 	 * partitioning. */
13928dda5b0eSDiego Santa Cruz 	if (wr_rel_set != ext_csd[EXT_CSD_WR_REL_SET]) {
13938dda5b0eSDiego Santa Cruz 		err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
13948dda5b0eSDiego Santa Cruz 				 EXT_CSD_WR_REL_SET, wr_rel_set);
13958dda5b0eSDiego Santa Cruz 		if (err)
13968dda5b0eSDiego Santa Cruz 			return err;
13978dda5b0eSDiego Santa Cruz 	}
13988dda5b0eSDiego Santa Cruz 
1399ac9da0e0SDiego Santa Cruz 	/* Setting PART_SETTING_COMPLETED confirms the partition
1400ac9da0e0SDiego Santa Cruz 	 * configuration but it only becomes effective after power
1401ac9da0e0SDiego Santa Cruz 	 * cycle, so we do not adjust the partition related settings
1402ac9da0e0SDiego Santa Cruz 	 * in the mmc struct. */
1403ac9da0e0SDiego Santa Cruz 
1404ac9da0e0SDiego Santa Cruz 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1405ac9da0e0SDiego Santa Cruz 			 EXT_CSD_PARTITION_SETTING,
1406ac9da0e0SDiego Santa Cruz 			 EXT_CSD_PARTITION_SETTING_COMPLETED);
1407ac9da0e0SDiego Santa Cruz 	if (err)
1408ac9da0e0SDiego Santa Cruz 		return err;
1409ac9da0e0SDiego Santa Cruz 
1410ac9da0e0SDiego Santa Cruz 	return 0;
1411ac9da0e0SDiego Santa Cruz }
1412ac9da0e0SDiego Santa Cruz 
1413e7881d85SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC)
141448972d90SThierry Reding int mmc_getcd(struct mmc *mmc)
141548972d90SThierry Reding {
141648972d90SThierry Reding 	int cd;
141748972d90SThierry Reding 
141848972d90SThierry Reding 	cd = board_mmc_getcd(mmc);
141948972d90SThierry Reding 
1420d4e1da4eSPeter Korsgaard 	if (cd < 0) {
142193bfd616SPantelis Antoniou 		if (mmc->cfg->ops->getcd)
142293bfd616SPantelis Antoniou 			cd = mmc->cfg->ops->getcd(mmc);
1423d4e1da4eSPeter Korsgaard 		else
1424d4e1da4eSPeter Korsgaard 			cd = 1;
1425d4e1da4eSPeter Korsgaard 	}
142648972d90SThierry Reding 
142748972d90SThierry Reding 	return cd;
142848972d90SThierry Reding }
14298ca51e51SSimon Glass #endif
143048972d90SThierry Reding 
1431fdbb873eSKim Phillips static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)
1432272cc70bSAndy Fleming {
1433272cc70bSAndy Fleming 	struct mmc_cmd cmd;
1434272cc70bSAndy Fleming 	struct mmc_data data;
1435272cc70bSAndy Fleming 
1436272cc70bSAndy Fleming 	/* Switch the frequency */
1437272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_SWITCH_FUNC;
1438272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
1439272cc70bSAndy Fleming 	cmd.cmdarg = (mode << 31) | 0xffffff;
1440272cc70bSAndy Fleming 	cmd.cmdarg &= ~(0xf << (group * 4));
1441272cc70bSAndy Fleming 	cmd.cmdarg |= value << (group * 4);
1442272cc70bSAndy Fleming 
1443272cc70bSAndy Fleming 	data.dest = (char *)resp;
1444272cc70bSAndy Fleming 	data.blocksize = 64;
1445272cc70bSAndy Fleming 	data.blocks = 1;
1446272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
1447272cc70bSAndy Fleming 
1448272cc70bSAndy Fleming 	return mmc_send_cmd(mmc, &cmd, &data);
1449272cc70bSAndy Fleming }
1450272cc70bSAndy Fleming 
1451272cc70bSAndy Fleming 
1452fdbb873eSKim Phillips static int sd_change_freq(struct mmc *mmc)
1453272cc70bSAndy Fleming {
1454272cc70bSAndy Fleming 	int err;
1455272cc70bSAndy Fleming 	struct mmc_cmd cmd;
1456f781dd38SAnton staaf 	ALLOC_CACHE_ALIGN_BUFFER(uint, scr, 2);
1457f781dd38SAnton staaf 	ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16);
1458272cc70bSAndy Fleming 	struct mmc_data data;
1459272cc70bSAndy Fleming 	int timeout;
1460272cc70bSAndy Fleming 
1461272cc70bSAndy Fleming 	mmc->card_caps = 0;
1462272cc70bSAndy Fleming 
1463d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc))
1464d52ebf10SThomas Chou 		return 0;
1465d52ebf10SThomas Chou 
1466272cc70bSAndy Fleming 	/* Read the SCR to find out if this card supports higher speeds */
1467272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_APP_CMD;
1468272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
1469272cc70bSAndy Fleming 	cmd.cmdarg = mmc->rca << 16;
1470272cc70bSAndy Fleming 
1471272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
1472272cc70bSAndy Fleming 
1473272cc70bSAndy Fleming 	if (err)
1474272cc70bSAndy Fleming 		return err;
1475272cc70bSAndy Fleming 
1476272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_APP_SEND_SCR;
1477272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
1478272cc70bSAndy Fleming 	cmd.cmdarg = 0;
1479272cc70bSAndy Fleming 
1480272cc70bSAndy Fleming 	timeout = 3;
1481272cc70bSAndy Fleming 
1482272cc70bSAndy Fleming retry_scr:
1483f781dd38SAnton staaf 	data.dest = (char *)scr;
1484272cc70bSAndy Fleming 	data.blocksize = 8;
1485272cc70bSAndy Fleming 	data.blocks = 1;
1486272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
1487272cc70bSAndy Fleming 
1488272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, &data);
1489272cc70bSAndy Fleming 
1490272cc70bSAndy Fleming 	if (err) {
1491272cc70bSAndy Fleming 		if (timeout--)
1492272cc70bSAndy Fleming 			goto retry_scr;
1493272cc70bSAndy Fleming 
1494272cc70bSAndy Fleming 		return err;
1495272cc70bSAndy Fleming 	}
1496272cc70bSAndy Fleming 
14974e3d89baSYauhen Kharuzhy 	mmc->scr[0] = __be32_to_cpu(scr[0]);
14984e3d89baSYauhen Kharuzhy 	mmc->scr[1] = __be32_to_cpu(scr[1]);
1499272cc70bSAndy Fleming 
1500272cc70bSAndy Fleming 	switch ((mmc->scr[0] >> 24) & 0xf) {
1501272cc70bSAndy Fleming 	case 0:
1502272cc70bSAndy Fleming 		mmc->version = SD_VERSION_1_0;
1503272cc70bSAndy Fleming 		break;
1504272cc70bSAndy Fleming 	case 1:
1505272cc70bSAndy Fleming 		mmc->version = SD_VERSION_1_10;
1506272cc70bSAndy Fleming 		break;
1507272cc70bSAndy Fleming 	case 2:
1508272cc70bSAndy Fleming 		mmc->version = SD_VERSION_2;
15091741c64dSJaehoon Chung 		if ((mmc->scr[0] >> 15) & 0x1)
15101741c64dSJaehoon Chung 			mmc->version = SD_VERSION_3;
1511272cc70bSAndy Fleming 		break;
1512272cc70bSAndy Fleming 	default:
1513272cc70bSAndy Fleming 		mmc->version = SD_VERSION_1_0;
1514272cc70bSAndy Fleming 		break;
1515272cc70bSAndy Fleming 	}
1516272cc70bSAndy Fleming 
1517b44c7083SAlagu Sankar 	if (mmc->scr[0] & SD_DATA_4BIT)
1518b44c7083SAlagu Sankar 		mmc->card_caps |= MMC_MODE_4BIT;
1519b44c7083SAlagu Sankar 
1520272cc70bSAndy Fleming 	/* Version 1.0 doesn't support switching */
1521272cc70bSAndy Fleming 	if (mmc->version == SD_VERSION_1_0)
1522272cc70bSAndy Fleming 		return 0;
1523272cc70bSAndy Fleming 
1524272cc70bSAndy Fleming 	timeout = 4;
1525272cc70bSAndy Fleming 	while (timeout--) {
1526272cc70bSAndy Fleming 		err = sd_switch(mmc, SD_SWITCH_CHECK, 0, 1,
1527f781dd38SAnton staaf 				(u8 *)switch_status);
1528272cc70bSAndy Fleming 
1529272cc70bSAndy Fleming 		if (err)
1530272cc70bSAndy Fleming 			return err;
1531272cc70bSAndy Fleming 
1532272cc70bSAndy Fleming 		/* The high-speed function is busy.  Try again */
15334e3d89baSYauhen Kharuzhy 		if (!(__be32_to_cpu(switch_status[7]) & SD_HIGHSPEED_BUSY))
1534272cc70bSAndy Fleming 			break;
1535272cc70bSAndy Fleming 	}
1536272cc70bSAndy Fleming 
1537272cc70bSAndy Fleming 	/* If high-speed isn't supported, we return */
15384e3d89baSYauhen Kharuzhy 	if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED))
1539272cc70bSAndy Fleming 		return 0;
1540272cc70bSAndy Fleming 
15412c3fbf4cSMacpaul Lin 	/*
15422c3fbf4cSMacpaul Lin 	 * If the host doesn't support SD_HIGHSPEED, do not switch card to
15432c3fbf4cSMacpaul Lin 	 * HIGHSPEED mode even if the card support SD_HIGHSPPED.
15442c3fbf4cSMacpaul Lin 	 * This can avoid furthur problem when the card runs in different
15452c3fbf4cSMacpaul Lin 	 * mode between the host.
15462c3fbf4cSMacpaul Lin 	 */
154793bfd616SPantelis Antoniou 	if (!((mmc->cfg->host_caps & MMC_MODE_HS_52MHz) &&
154893bfd616SPantelis Antoniou 		(mmc->cfg->host_caps & MMC_MODE_HS)))
15492c3fbf4cSMacpaul Lin 		return 0;
15502c3fbf4cSMacpaul Lin 
1551f781dd38SAnton staaf 	err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status);
1552272cc70bSAndy Fleming 
1553272cc70bSAndy Fleming 	if (err)
1554272cc70bSAndy Fleming 		return err;
1555272cc70bSAndy Fleming 
15564e3d89baSYauhen Kharuzhy 	if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) == 0x01000000)
1557272cc70bSAndy Fleming 		mmc->card_caps |= MMC_MODE_HS;
1558272cc70bSAndy Fleming 
1559272cc70bSAndy Fleming 	return 0;
1560272cc70bSAndy Fleming }
1561272cc70bSAndy Fleming 
15623697e599SPeng Fan static int sd_read_ssr(struct mmc *mmc)
15633697e599SPeng Fan {
15643697e599SPeng Fan 	int err, i;
15653697e599SPeng Fan 	struct mmc_cmd cmd;
15663697e599SPeng Fan 	ALLOC_CACHE_ALIGN_BUFFER(uint, ssr, 16);
15673697e599SPeng Fan 	struct mmc_data data;
15683697e599SPeng Fan 	int timeout = 3;
15693697e599SPeng Fan 	unsigned int au, eo, et, es;
15703697e599SPeng Fan 
15713697e599SPeng Fan 	cmd.cmdidx = MMC_CMD_APP_CMD;
15723697e599SPeng Fan 	cmd.resp_type = MMC_RSP_R1;
15733697e599SPeng Fan 	cmd.cmdarg = mmc->rca << 16;
15743697e599SPeng Fan 
15753697e599SPeng Fan 	err = mmc_send_cmd(mmc, &cmd, NULL);
15763697e599SPeng Fan 	if (err)
15773697e599SPeng Fan 		return err;
15783697e599SPeng Fan 
15793697e599SPeng Fan 	cmd.cmdidx = SD_CMD_APP_SD_STATUS;
15803697e599SPeng Fan 	cmd.resp_type = MMC_RSP_R1;
15813697e599SPeng Fan 	cmd.cmdarg = 0;
15823697e599SPeng Fan 
15833697e599SPeng Fan retry_ssr:
15843697e599SPeng Fan 	data.dest = (char *)ssr;
15853697e599SPeng Fan 	data.blocksize = 64;
15863697e599SPeng Fan 	data.blocks = 1;
15873697e599SPeng Fan 	data.flags = MMC_DATA_READ;
15883697e599SPeng Fan 
15893697e599SPeng Fan 	err = mmc_send_cmd(mmc, &cmd, &data);
15903697e599SPeng Fan 	if (err) {
15913697e599SPeng Fan 		if (timeout--)
15923697e599SPeng Fan 			goto retry_ssr;
15933697e599SPeng Fan 
15943697e599SPeng Fan 		return err;
15953697e599SPeng Fan 	}
15963697e599SPeng Fan 
15973697e599SPeng Fan 	for (i = 0; i < 16; i++)
15983697e599SPeng Fan 		ssr[i] = be32_to_cpu(ssr[i]);
15993697e599SPeng Fan 
16003697e599SPeng Fan 	au = (ssr[2] >> 12) & 0xF;
16013697e599SPeng Fan 	if ((au <= 9) || (mmc->version == SD_VERSION_3)) {
16023697e599SPeng Fan 		mmc->ssr.au = sd_au_size[au];
16033697e599SPeng Fan 		es = (ssr[3] >> 24) & 0xFF;
16043697e599SPeng Fan 		es |= (ssr[2] & 0xFF) << 8;
16053697e599SPeng Fan 		et = (ssr[3] >> 18) & 0x3F;
16063697e599SPeng Fan 		if (es && et) {
16073697e599SPeng Fan 			eo = (ssr[3] >> 16) & 0x3;
16083697e599SPeng Fan 			mmc->ssr.erase_timeout = (et * 1000) / es;
16093697e599SPeng Fan 			mmc->ssr.erase_offset = eo * 1000;
16103697e599SPeng Fan 		}
16113697e599SPeng Fan 	} else {
16123697e599SPeng Fan 		debug("Invalid Allocation Unit Size.\n");
16133697e599SPeng Fan 	}
16143697e599SPeng Fan 
16153697e599SPeng Fan 	return 0;
16163697e599SPeng Fan }
16173697e599SPeng Fan 
1618272cc70bSAndy Fleming /* frequency bases */
1619272cc70bSAndy Fleming /* divided by 10 to be nice to platforms without floating point */
16205f837c2cSMike Frysinger static const int fbase[] = {
1621272cc70bSAndy Fleming 	10000,
1622272cc70bSAndy Fleming 	100000,
1623272cc70bSAndy Fleming 	1000000,
1624272cc70bSAndy Fleming 	10000000,
1625272cc70bSAndy Fleming };
1626272cc70bSAndy Fleming 
1627272cc70bSAndy Fleming /* Multiplier values for TRAN_SPEED.  Multiplied by 10 to be nice
1628272cc70bSAndy Fleming  * to platforms without floating point.
1629272cc70bSAndy Fleming  */
163061fe076fSSimon Glass static const u8 multipliers[] = {
1631272cc70bSAndy Fleming 	0,	/* reserved */
1632272cc70bSAndy Fleming 	10,
1633272cc70bSAndy Fleming 	12,
1634272cc70bSAndy Fleming 	13,
1635272cc70bSAndy Fleming 	15,
1636272cc70bSAndy Fleming 	20,
1637272cc70bSAndy Fleming 	25,
1638272cc70bSAndy Fleming 	30,
1639272cc70bSAndy Fleming 	35,
1640272cc70bSAndy Fleming 	40,
1641272cc70bSAndy Fleming 	45,
1642272cc70bSAndy Fleming 	50,
1643272cc70bSAndy Fleming 	55,
1644272cc70bSAndy Fleming 	60,
1645272cc70bSAndy Fleming 	70,
1646272cc70bSAndy Fleming 	80,
1647272cc70bSAndy Fleming };
1648272cc70bSAndy Fleming 
1649e7881d85SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC)
1650fdbb873eSKim Phillips static void mmc_set_ios(struct mmc *mmc)
1651272cc70bSAndy Fleming {
165293bfd616SPantelis Antoniou 	if (mmc->cfg->ops->set_ios)
165393bfd616SPantelis Antoniou 		mmc->cfg->ops->set_ios(mmc);
1654272cc70bSAndy Fleming }
1655ad77484aSZiyuan Xu 
1656ad77484aSZiyuan Xu static bool mmc_card_busy(struct mmc *mmc)
1657ad77484aSZiyuan Xu {
1658ad77484aSZiyuan Xu 	if (!mmc->cfg->ops->card_busy)
1659ad77484aSZiyuan Xu 		return -ENOSYS;
1660ad77484aSZiyuan Xu 
1661ad77484aSZiyuan Xu 	return mmc->cfg->ops->card_busy(mmc);
1662ad77484aSZiyuan Xu }
1663ad77484aSZiyuan Xu 
1664ad77484aSZiyuan Xu static bool mmc_can_card_busy(struct mmc *)
1665ad77484aSZiyuan Xu {
1666ad77484aSZiyuan Xu 	return !!mmc->cfg->ops->card_busy;
1667ad77484aSZiyuan Xu }
16688ca51e51SSimon Glass #endif
1669272cc70bSAndy Fleming 
1670fdbb873eSKim Phillips static int mmc_startup(struct mmc *mmc)
1671272cc70bSAndy Fleming {
1672f866a46dSStephen Warren 	int err, i;
16733e3ff0acSZiyuan Xu 	uint mult, freq, tran_speed;
1674639b7827SYoshihiro Shimoda 	u64 cmult, csize, capacity;
1675272cc70bSAndy Fleming 	struct mmc_cmd cmd;
16768bfa195eSSimon Glass 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
16770c453bb7SDiego Santa Cruz 	bool has_parts = false;
16788a0cf490SDiego Santa Cruz 	bool part_completed;
1679c40fdca6SSimon Glass 	struct blk_desc *bdesc;
1680272cc70bSAndy Fleming 
1681d52ebf10SThomas Chou #ifdef CONFIG_MMC_SPI_CRC_ON
1682d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */
1683d52ebf10SThomas Chou 		cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF;
1684d52ebf10SThomas Chou 		cmd.resp_type = MMC_RSP_R1;
1685d52ebf10SThomas Chou 		cmd.cmdarg = 1;
1686d52ebf10SThomas Chou 		err = mmc_send_cmd(mmc, &cmd, NULL);
1687d52ebf10SThomas Chou 
1688d52ebf10SThomas Chou 		if (err)
1689d52ebf10SThomas Chou 			return err;
1690d52ebf10SThomas Chou 	}
1691d52ebf10SThomas Chou #endif
1692479fbf72SJason Zhu #ifndef CONFIG_MMC_USE_PRE_CONFIG
1693272cc70bSAndy Fleming 	/* Put the Card in Identify Mode */
1694d52ebf10SThomas Chou 	cmd.cmdidx = mmc_host_is_spi(mmc) ? MMC_CMD_SEND_CID :
1695d52ebf10SThomas Chou 		MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */
1696272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R2;
1697272cc70bSAndy Fleming 	cmd.cmdarg = 0;
1698272cc70bSAndy Fleming 
1699272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
1700272cc70bSAndy Fleming 
1701272cc70bSAndy Fleming 	if (err)
1702272cc70bSAndy Fleming 		return err;
1703272cc70bSAndy Fleming 
1704272cc70bSAndy Fleming 	memcpy(mmc->cid, cmd.response, 16);
1705272cc70bSAndy Fleming 
1706272cc70bSAndy Fleming 	/*
1707272cc70bSAndy Fleming 	 * For MMC cards, set the Relative Address.
1708272cc70bSAndy Fleming 	 * For SD cards, get the Relatvie Address.
1709272cc70bSAndy Fleming 	 * This also puts the cards into Standby State
1710272cc70bSAndy Fleming 	 */
1711d52ebf10SThomas Chou 	if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
1712272cc70bSAndy Fleming 		cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;
1713272cc70bSAndy Fleming 		cmd.cmdarg = mmc->rca << 16;
1714272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R6;
1715272cc70bSAndy Fleming 
1716272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
1717272cc70bSAndy Fleming 
1718272cc70bSAndy Fleming 		if (err)
1719272cc70bSAndy Fleming 			return err;
1720272cc70bSAndy Fleming 
1721272cc70bSAndy Fleming 		if (IS_SD(mmc))
1722998be3ddSRabin Vincent 			mmc->rca = (cmd.response[0] >> 16) & 0xffff;
1723d52ebf10SThomas Chou 	}
1724479fbf72SJason Zhu #endif
1725272cc70bSAndy Fleming 	/* Get the Card-Specific Data */
1726272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SEND_CSD;
1727272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R2;
1728272cc70bSAndy Fleming 	cmd.cmdarg = mmc->rca << 16;
1729272cc70bSAndy Fleming 
1730272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
1731272cc70bSAndy Fleming 
1732272cc70bSAndy Fleming 	if (err)
1733272cc70bSAndy Fleming 		return err;
1734272cc70bSAndy Fleming 
1735998be3ddSRabin Vincent 	mmc->csd[0] = cmd.response[0];
1736998be3ddSRabin Vincent 	mmc->csd[1] = cmd.response[1];
1737998be3ddSRabin Vincent 	mmc->csd[2] = cmd.response[2];
1738998be3ddSRabin Vincent 	mmc->csd[3] = cmd.response[3];
1739272cc70bSAndy Fleming 
1740272cc70bSAndy Fleming 	if (mmc->version == MMC_VERSION_UNKNOWN) {
17410b453ffeSRabin Vincent 		int version = (cmd.response[0] >> 26) & 0xf;
1742272cc70bSAndy Fleming 
1743272cc70bSAndy Fleming 		switch (version) {
1744272cc70bSAndy Fleming 		case 0:
1745272cc70bSAndy Fleming 			mmc->version = MMC_VERSION_1_2;
1746272cc70bSAndy Fleming 			break;
1747272cc70bSAndy Fleming 		case 1:
1748272cc70bSAndy Fleming 			mmc->version = MMC_VERSION_1_4;
1749272cc70bSAndy Fleming 			break;
1750272cc70bSAndy Fleming 		case 2:
1751272cc70bSAndy Fleming 			mmc->version = MMC_VERSION_2_2;
1752272cc70bSAndy Fleming 			break;
1753272cc70bSAndy Fleming 		case 3:
1754272cc70bSAndy Fleming 			mmc->version = MMC_VERSION_3;
1755272cc70bSAndy Fleming 			break;
1756272cc70bSAndy Fleming 		case 4:
1757272cc70bSAndy Fleming 			mmc->version = MMC_VERSION_4;
1758272cc70bSAndy Fleming 			break;
1759272cc70bSAndy Fleming 		default:
1760272cc70bSAndy Fleming 			mmc->version = MMC_VERSION_1_2;
1761272cc70bSAndy Fleming 			break;
1762272cc70bSAndy Fleming 		}
1763272cc70bSAndy Fleming 	}
1764272cc70bSAndy Fleming 
1765272cc70bSAndy Fleming 	/* divide frequency by 10, since the mults are 10x bigger */
17660b453ffeSRabin Vincent 	freq = fbase[(cmd.response[0] & 0x7)];
17670b453ffeSRabin Vincent 	mult = multipliers[((cmd.response[0] >> 3) & 0xf)];
1768272cc70bSAndy Fleming 
17693e3ff0acSZiyuan Xu 	tran_speed = freq * mult;
1770272cc70bSAndy Fleming 
1771ab71188cSMarkus Niebel 	mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1);
1772998be3ddSRabin Vincent 	mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf);
1773272cc70bSAndy Fleming 
1774272cc70bSAndy Fleming 	if (IS_SD(mmc))
1775272cc70bSAndy Fleming 		mmc->write_bl_len = mmc->read_bl_len;
1776272cc70bSAndy Fleming 	else
1777998be3ddSRabin Vincent 		mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf);
1778272cc70bSAndy Fleming 
1779272cc70bSAndy Fleming 	if (mmc->high_capacity) {
1780272cc70bSAndy Fleming 		csize = (mmc->csd[1] & 0x3f) << 16
1781272cc70bSAndy Fleming 			| (mmc->csd[2] & 0xffff0000) >> 16;
1782272cc70bSAndy Fleming 		cmult = 8;
1783272cc70bSAndy Fleming 	} else {
1784272cc70bSAndy Fleming 		csize = (mmc->csd[1] & 0x3ff) << 2
1785272cc70bSAndy Fleming 			| (mmc->csd[2] & 0xc0000000) >> 30;
1786272cc70bSAndy Fleming 		cmult = (mmc->csd[2] & 0x00038000) >> 15;
1787272cc70bSAndy Fleming 	}
1788272cc70bSAndy Fleming 
1789f866a46dSStephen Warren 	mmc->capacity_user = (csize + 1) << (cmult + 2);
1790f866a46dSStephen Warren 	mmc->capacity_user *= mmc->read_bl_len;
1791f866a46dSStephen Warren 	mmc->capacity_boot = 0;
1792f866a46dSStephen Warren 	mmc->capacity_rpmb = 0;
1793f866a46dSStephen Warren 	for (i = 0; i < 4; i++)
1794f866a46dSStephen Warren 		mmc->capacity_gp[i] = 0;
1795272cc70bSAndy Fleming 
17968bfa195eSSimon Glass 	if (mmc->read_bl_len > MMC_MAX_BLOCK_LEN)
17978bfa195eSSimon Glass 		mmc->read_bl_len = MMC_MAX_BLOCK_LEN;
1798272cc70bSAndy Fleming 
17998bfa195eSSimon Glass 	if (mmc->write_bl_len > MMC_MAX_BLOCK_LEN)
18008bfa195eSSimon Glass 		mmc->write_bl_len = MMC_MAX_BLOCK_LEN;
1801272cc70bSAndy Fleming 
1802ab71188cSMarkus Niebel 	if ((mmc->dsr_imp) && (0xffffffff != mmc->dsr)) {
1803ab71188cSMarkus Niebel 		cmd.cmdidx = MMC_CMD_SET_DSR;
1804ab71188cSMarkus Niebel 		cmd.cmdarg = (mmc->dsr & 0xffff) << 16;
1805ab71188cSMarkus Niebel 		cmd.resp_type = MMC_RSP_NONE;
1806ab71188cSMarkus Niebel 		if (mmc_send_cmd(mmc, &cmd, NULL))
1807ab71188cSMarkus Niebel 			printf("MMC: SET_DSR failed\n");
1808ab71188cSMarkus Niebel 	}
1809ab71188cSMarkus Niebel 
1810272cc70bSAndy Fleming 	/* Select the card, and put it into Transfer Mode */
1811d52ebf10SThomas Chou 	if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
1812272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_SELECT_CARD;
1813fe8f7066SAjay Bhargav 		cmd.resp_type = MMC_RSP_R1;
1814272cc70bSAndy Fleming 		cmd.cmdarg = mmc->rca << 16;
1815272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
1816272cc70bSAndy Fleming 
1817272cc70bSAndy Fleming 		if (err)
1818272cc70bSAndy Fleming 			return err;
1819d52ebf10SThomas Chou 	}
1820272cc70bSAndy Fleming 
1821e6f99a56SLei Wen 	/*
1822e6f99a56SLei Wen 	 * For SD, its erase group is always one sector
1823e6f99a56SLei Wen 	 */
1824e6f99a56SLei Wen 	mmc->erase_grp_size = 1;
1825bc897b1dSLei Wen 	mmc->part_config = MMCPART_NOAVAILABLE;
1826d23e2c09SSukumar Ghorai 	if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) {
1827d23e2c09SSukumar Ghorai 		/* check  ext_csd version and capacity */
1828d23e2c09SSukumar Ghorai 		err = mmc_send_ext_csd(mmc, ext_csd);
18299cf199ebSDiego Santa Cruz 		if (err)
18309cf199ebSDiego Santa Cruz 			return err;
18319cf199ebSDiego Santa Cruz 		if (ext_csd[EXT_CSD_REV] >= 2) {
1832639b7827SYoshihiro Shimoda 			/*
1833639b7827SYoshihiro Shimoda 			 * According to the JEDEC Standard, the value of
1834639b7827SYoshihiro Shimoda 			 * ext_csd's capacity is valid if the value is more
1835639b7827SYoshihiro Shimoda 			 * than 2GB
1836639b7827SYoshihiro Shimoda 			 */
18370560db18SLei Wen 			capacity = ext_csd[EXT_CSD_SEC_CNT] << 0
18380560db18SLei Wen 					| ext_csd[EXT_CSD_SEC_CNT + 1] << 8
18390560db18SLei Wen 					| ext_csd[EXT_CSD_SEC_CNT + 2] << 16
18400560db18SLei Wen 					| ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
18418bfa195eSSimon Glass 			capacity *= MMC_MAX_BLOCK_LEN;
1842b1f1e821SŁukasz Majewski 			if ((capacity >> 20) > 2 * 1024)
1843f866a46dSStephen Warren 				mmc->capacity_user = capacity;
1844d23e2c09SSukumar Ghorai 		}
1845bc897b1dSLei Wen 
184664f4a619SJaehoon Chung 		switch (ext_csd[EXT_CSD_REV]) {
184764f4a619SJaehoon Chung 		case 1:
184864f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_1;
184964f4a619SJaehoon Chung 			break;
185064f4a619SJaehoon Chung 		case 2:
185164f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_2;
185264f4a619SJaehoon Chung 			break;
185364f4a619SJaehoon Chung 		case 3:
185464f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_3;
185564f4a619SJaehoon Chung 			break;
185664f4a619SJaehoon Chung 		case 5:
185764f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_41;
185864f4a619SJaehoon Chung 			break;
185964f4a619SJaehoon Chung 		case 6:
186064f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_5;
186164f4a619SJaehoon Chung 			break;
1862edab723bSMarkus Niebel 		case 7:
1863edab723bSMarkus Niebel 			mmc->version = MMC_VERSION_5_0;
1864edab723bSMarkus Niebel 			break;
18651a3619cfSStefan Wahren 		case 8:
18661a3619cfSStefan Wahren 			mmc->version = MMC_VERSION_5_1;
18671a3619cfSStefan Wahren 			break;
186864f4a619SJaehoon Chung 		}
186964f4a619SJaehoon Chung 
18708a0cf490SDiego Santa Cruz 		/* The partition data may be non-zero but it is only
18718a0cf490SDiego Santa Cruz 		 * effective if PARTITION_SETTING_COMPLETED is set in
18728a0cf490SDiego Santa Cruz 		 * EXT_CSD, so ignore any data if this bit is not set,
18738a0cf490SDiego Santa Cruz 		 * except for enabling the high-capacity group size
18748a0cf490SDiego Santa Cruz 		 * definition (see below). */
18758a0cf490SDiego Santa Cruz 		part_completed = !!(ext_csd[EXT_CSD_PARTITION_SETTING] &
18768a0cf490SDiego Santa Cruz 				    EXT_CSD_PARTITION_SETTING_COMPLETED);
18778a0cf490SDiego Santa Cruz 
18780c453bb7SDiego Santa Cruz 		/* store the partition info of emmc */
18790c453bb7SDiego Santa Cruz 		mmc->part_support = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
18800c453bb7SDiego Santa Cruz 		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) ||
18810c453bb7SDiego Santa Cruz 		    ext_csd[EXT_CSD_BOOT_MULT])
18820c453bb7SDiego Santa Cruz 			mmc->part_config = ext_csd[EXT_CSD_PART_CONF];
18838a0cf490SDiego Santa Cruz 		if (part_completed &&
18848a0cf490SDiego Santa Cruz 		    (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & ENHNCD_SUPPORT))
18850c453bb7SDiego Santa Cruz 			mmc->part_attr = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
1886a6a1f5f8SJason Zhu 		if (ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] & EXT_CSD_SEC_GB_CL_EN)
1887a6a1f5f8SJason Zhu 			mmc->esr.mmc_can_trim = 1;
18880c453bb7SDiego Santa Cruz 
18890c453bb7SDiego Santa Cruz 		mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17;
18900c453bb7SDiego Santa Cruz 
18910c453bb7SDiego Santa Cruz 		mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17;
18920c453bb7SDiego Santa Cruz 
18930c453bb7SDiego Santa Cruz 		for (i = 0; i < 4; i++) {
18940c453bb7SDiego Santa Cruz 			int idx = EXT_CSD_GP_SIZE_MULT + i * 3;
18958a0cf490SDiego Santa Cruz 			uint mult = (ext_csd[idx + 2] << 16) +
18960c453bb7SDiego Santa Cruz 				(ext_csd[idx + 1] << 8) + ext_csd[idx];
18978a0cf490SDiego Santa Cruz 			if (mult)
18988a0cf490SDiego Santa Cruz 				has_parts = true;
18998a0cf490SDiego Santa Cruz 			if (!part_completed)
19008a0cf490SDiego Santa Cruz 				continue;
19018a0cf490SDiego Santa Cruz 			mmc->capacity_gp[i] = mult;
19020c453bb7SDiego Santa Cruz 			mmc->capacity_gp[i] *=
19030c453bb7SDiego Santa Cruz 				ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
19040c453bb7SDiego Santa Cruz 			mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
1905f8e89d67SDiego Santa Cruz 			mmc->capacity_gp[i] <<= 19;
19060c453bb7SDiego Santa Cruz 		}
19070c453bb7SDiego Santa Cruz 
19088a0cf490SDiego Santa Cruz 		if (part_completed) {
1909a7f852b6SDiego Santa Cruz 			mmc->enh_user_size =
1910a7f852b6SDiego Santa Cruz 				(ext_csd[EXT_CSD_ENH_SIZE_MULT+2] << 16) +
1911a7f852b6SDiego Santa Cruz 				(ext_csd[EXT_CSD_ENH_SIZE_MULT+1] << 8) +
1912a7f852b6SDiego Santa Cruz 				ext_csd[EXT_CSD_ENH_SIZE_MULT];
1913a7f852b6SDiego Santa Cruz 			mmc->enh_user_size *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
1914a7f852b6SDiego Santa Cruz 			mmc->enh_user_size *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
1915a7f852b6SDiego Santa Cruz 			mmc->enh_user_size <<= 19;
1916a7f852b6SDiego Santa Cruz 			mmc->enh_user_start =
1917a7f852b6SDiego Santa Cruz 				(ext_csd[EXT_CSD_ENH_START_ADDR+3] << 24) +
1918a7f852b6SDiego Santa Cruz 				(ext_csd[EXT_CSD_ENH_START_ADDR+2] << 16) +
1919a7f852b6SDiego Santa Cruz 				(ext_csd[EXT_CSD_ENH_START_ADDR+1] << 8) +
1920a7f852b6SDiego Santa Cruz 				ext_csd[EXT_CSD_ENH_START_ADDR];
1921a7f852b6SDiego Santa Cruz 			if (mmc->high_capacity)
1922a7f852b6SDiego Santa Cruz 				mmc->enh_user_start <<= 9;
19238a0cf490SDiego Santa Cruz 		}
1924a7f852b6SDiego Santa Cruz 
1925e6f99a56SLei Wen 		/*
19261937e5aaSOliver Metz 		 * Host needs to enable ERASE_GRP_DEF bit if device is
19271937e5aaSOliver Metz 		 * partitioned. This bit will be lost every time after a reset
19281937e5aaSOliver Metz 		 * or power off. This will affect erase size.
1929e6f99a56SLei Wen 		 */
19308a0cf490SDiego Santa Cruz 		if (part_completed)
19310c453bb7SDiego Santa Cruz 			has_parts = true;
19321937e5aaSOliver Metz 		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) &&
19330c453bb7SDiego Santa Cruz 		    (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB))
19340c453bb7SDiego Santa Cruz 			has_parts = true;
19350c453bb7SDiego Santa Cruz 		if (has_parts) {
19361937e5aaSOliver Metz 			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
19371937e5aaSOliver Metz 				EXT_CSD_ERASE_GROUP_DEF, 1);
19381937e5aaSOliver Metz 
19391937e5aaSOliver Metz 			if (err)
19401937e5aaSOliver Metz 				return err;
1941021a8055SHannes Petermaier 			else
1942021a8055SHannes Petermaier 				ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1;
1943037dc0abSDiego Santa Cruz 		}
19441937e5aaSOliver Metz 
1945037dc0abSDiego Santa Cruz 		if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01) {
19461937e5aaSOliver Metz 			/* Read out group size from ext_csd */
19470560db18SLei Wen 			mmc->erase_grp_size =
1948a4ff9f83SDiego Santa Cruz 				ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024;
1949d7b29129SMarkus Niebel 			/*
1950d7b29129SMarkus Niebel 			 * if high capacity and partition setting completed
1951d7b29129SMarkus Niebel 			 * SEC_COUNT is valid even if it is smaller than 2 GiB
1952d7b29129SMarkus Niebel 			 * JEDEC Standard JESD84-B45, 6.2.4
1953d7b29129SMarkus Niebel 			 */
19548a0cf490SDiego Santa Cruz 			if (mmc->high_capacity && part_completed) {
1955d7b29129SMarkus Niebel 				capacity = (ext_csd[EXT_CSD_SEC_CNT]) |
1956d7b29129SMarkus Niebel 					(ext_csd[EXT_CSD_SEC_CNT + 1] << 8) |
1957d7b29129SMarkus Niebel 					(ext_csd[EXT_CSD_SEC_CNT + 2] << 16) |
1958d7b29129SMarkus Niebel 					(ext_csd[EXT_CSD_SEC_CNT + 3] << 24);
1959d7b29129SMarkus Niebel 				capacity *= MMC_MAX_BLOCK_LEN;
1960d7b29129SMarkus Niebel 				mmc->capacity_user = capacity;
1961d7b29129SMarkus Niebel 			}
19628bfa195eSSimon Glass 		} else {
19631937e5aaSOliver Metz 			/* Calculate the group size from the csd value. */
1964e6f99a56SLei Wen 			int erase_gsz, erase_gmul;
1965e6f99a56SLei Wen 			erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10;
1966e6f99a56SLei Wen 			erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5;
1967e6f99a56SLei Wen 			mmc->erase_grp_size = (erase_gsz + 1)
1968e6f99a56SLei Wen 				* (erase_gmul + 1);
1969e6f99a56SLei Wen 		}
1970037dc0abSDiego Santa Cruz 
1971037dc0abSDiego Santa Cruz 		mmc->hc_wp_grp_size = 1024
1972037dc0abSDiego Santa Cruz 			* ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]
1973037dc0abSDiego Santa Cruz 			* ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
19749e41a00bSDiego Santa Cruz 
19759e41a00bSDiego Santa Cruz 		mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET];
1976f866a46dSStephen Warren 	}
1977f866a46dSStephen Warren 
1978c40fdca6SSimon Glass 	err = mmc_set_capacity(mmc, mmc_get_blk_desc(mmc)->hwpart);
1979f866a46dSStephen Warren 	if (err)
1980f866a46dSStephen Warren 		return err;
1981d23e2c09SSukumar Ghorai 
1982272cc70bSAndy Fleming 	if (IS_SD(mmc))
1983272cc70bSAndy Fleming 		err = sd_change_freq(mmc);
1984272cc70bSAndy Fleming 	else
1985272cc70bSAndy Fleming 		err = mmc_change_freq(mmc);
1986272cc70bSAndy Fleming 
1987272cc70bSAndy Fleming 	if (err)
1988272cc70bSAndy Fleming 		return err;
1989272cc70bSAndy Fleming 
1990272cc70bSAndy Fleming 	/* Restrict card's capabilities by what the host can do */
199193bfd616SPantelis Antoniou 	mmc->card_caps &= mmc->cfg->host_caps;
1992272cc70bSAndy Fleming 
1993272cc70bSAndy Fleming 	if (IS_SD(mmc)) {
1994272cc70bSAndy Fleming 		if (mmc->card_caps & MMC_MODE_4BIT) {
1995272cc70bSAndy Fleming 			cmd.cmdidx = MMC_CMD_APP_CMD;
1996272cc70bSAndy Fleming 			cmd.resp_type = MMC_RSP_R1;
1997272cc70bSAndy Fleming 			cmd.cmdarg = mmc->rca << 16;
1998272cc70bSAndy Fleming 
1999272cc70bSAndy Fleming 			err = mmc_send_cmd(mmc, &cmd, NULL);
2000272cc70bSAndy Fleming 			if (err)
2001272cc70bSAndy Fleming 				return err;
2002272cc70bSAndy Fleming 
2003272cc70bSAndy Fleming 			cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH;
2004272cc70bSAndy Fleming 			cmd.resp_type = MMC_RSP_R1;
2005272cc70bSAndy Fleming 			cmd.cmdarg = 2;
2006272cc70bSAndy Fleming 			err = mmc_send_cmd(mmc, &cmd, NULL);
2007272cc70bSAndy Fleming 			if (err)
2008272cc70bSAndy Fleming 				return err;
2009272cc70bSAndy Fleming 
2010272cc70bSAndy Fleming 			mmc_set_bus_width(mmc, 4);
2011272cc70bSAndy Fleming 		}
2012272cc70bSAndy Fleming 
20133697e599SPeng Fan 		err = sd_read_ssr(mmc);
20143697e599SPeng Fan 		if (err)
20153697e599SPeng Fan 			return err;
20163697e599SPeng Fan 
2017272cc70bSAndy Fleming 		if (mmc->card_caps & MMC_MODE_HS)
2018fe414819SJason Zhu 			tran_speed = MMC_HIGH_52_MAX_DTR;
2019272cc70bSAndy Fleming 		else
2020fe414819SJason Zhu 			tran_speed = MMC_HIGH_26_MAX_DTR;
2021ad5fd922SJaehoon Chung 
20223e3ff0acSZiyuan Xu 		mmc_set_clock(mmc, tran_speed);
202349dba033SZiyuan Xu 	}
2024272cc70bSAndy Fleming 
20255af8f45cSAndrew Gabbasov 	/* Fix the block length for DDR mode */
202649dba033SZiyuan Xu 	if (mmc_card_ddr(mmc)) {
20275af8f45cSAndrew Gabbasov 		mmc->read_bl_len = MMC_MAX_BLOCK_LEN;
20285af8f45cSAndrew Gabbasov 		mmc->write_bl_len = MMC_MAX_BLOCK_LEN;
20295af8f45cSAndrew Gabbasov 	}
20305af8f45cSAndrew Gabbasov 
2031272cc70bSAndy Fleming 	/* fill in device description */
2032c40fdca6SSimon Glass 	bdesc = mmc_get_blk_desc(mmc);
2033c40fdca6SSimon Glass 	bdesc->lun = 0;
2034c40fdca6SSimon Glass 	bdesc->hwpart = 0;
2035c40fdca6SSimon Glass 	bdesc->type = 0;
2036c40fdca6SSimon Glass 	bdesc->blksz = mmc->read_bl_len;
2037c40fdca6SSimon Glass 	bdesc->log2blksz = LOG2(bdesc->blksz);
2038c40fdca6SSimon Glass 	bdesc->lba = lldiv(mmc->capacity, mmc->read_bl_len);
2039fc011f64SSjoerd Simons #if !defined(CONFIG_SPL_BUILD) || \
2040fc011f64SSjoerd Simons 		(defined(CONFIG_SPL_LIBCOMMON_SUPPORT) && \
2041fc011f64SSjoerd Simons 		!defined(CONFIG_USE_TINY_PRINTF))
2042c40fdca6SSimon Glass 	sprintf(bdesc->vendor, "Man %06x Snr %04x%04x",
2043babce5f6STaylor Hutt 		mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff),
2044babce5f6STaylor Hutt 		(mmc->cid[3] >> 16) & 0xffff);
2045c40fdca6SSimon Glass 	sprintf(bdesc->product, "%c%c%c%c%c%c", mmc->cid[0] & 0xff,
20460b453ffeSRabin Vincent 		(mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
2047babce5f6STaylor Hutt 		(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff,
2048babce5f6STaylor Hutt 		(mmc->cid[2] >> 24) & 0xff);
2049c40fdca6SSimon Glass 	sprintf(bdesc->revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf,
2050babce5f6STaylor Hutt 		(mmc->cid[2] >> 16) & 0xf);
205156196826SPaul Burton #else
2052c40fdca6SSimon Glass 	bdesc->vendor[0] = 0;
2053c40fdca6SSimon Glass 	bdesc->product[0] = 0;
2054c40fdca6SSimon Glass 	bdesc->revision[0] = 0;
205556196826SPaul Burton #endif
2056122efd43SMikhail Kshevetskiy #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT)
2057c40fdca6SSimon Glass 	part_init(bdesc);
2058122efd43SMikhail Kshevetskiy #endif
2059272cc70bSAndy Fleming 
2060272cc70bSAndy Fleming 	return 0;
2061272cc70bSAndy Fleming }
2062272cc70bSAndy Fleming 
2063479fbf72SJason Zhu #ifndef CONFIG_MMC_USE_PRE_CONFIG
2064fdbb873eSKim Phillips static int mmc_send_if_cond(struct mmc *mmc)
2065272cc70bSAndy Fleming {
2066272cc70bSAndy Fleming 	struct mmc_cmd cmd;
2067272cc70bSAndy Fleming 	int err;
2068272cc70bSAndy Fleming 
2069272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_SEND_IF_COND;
2070272cc70bSAndy Fleming 	/* We set the bit if the host supports voltages between 2.7 and 3.6 V */
207193bfd616SPantelis Antoniou 	cmd.cmdarg = ((mmc->cfg->voltages & 0xff8000) != 0) << 8 | 0xaa;
2072272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R7;
2073272cc70bSAndy Fleming 
2074272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
2075272cc70bSAndy Fleming 
2076272cc70bSAndy Fleming 	if (err)
2077272cc70bSAndy Fleming 		return err;
2078272cc70bSAndy Fleming 
2079998be3ddSRabin Vincent 	if ((cmd.response[0] & 0xff) != 0xaa)
2080915ffa52SJaehoon Chung 		return -EOPNOTSUPP;
2081272cc70bSAndy Fleming 	else
2082272cc70bSAndy Fleming 		mmc->version = SD_VERSION_2;
2083272cc70bSAndy Fleming 
2084272cc70bSAndy Fleming 	return 0;
2085272cc70bSAndy Fleming }
2086479fbf72SJason Zhu #endif
2087272cc70bSAndy Fleming 
2088c4d660d4SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC)
208995de9ab2SPaul Kocialkowski /* board-specific MMC power initializations. */
209095de9ab2SPaul Kocialkowski __weak void board_mmc_power_init(void)
209195de9ab2SPaul Kocialkowski {
209295de9ab2SPaul Kocialkowski }
209305cbeb7cSSimon Glass #endif
209495de9ab2SPaul Kocialkowski 
2095479fbf72SJason Zhu #ifndef CONFIG_MMC_USE_PRE_CONFIG
20962051aefeSPeng Fan static int mmc_power_init(struct mmc *mmc)
20972051aefeSPeng Fan {
2098c4d660d4SSimon Glass #if CONFIG_IS_ENABLED(DM_MMC)
209905cbeb7cSSimon Glass #if defined(CONFIG_DM_REGULATOR) && !defined(CONFIG_SPL_BUILD)
21002051aefeSPeng Fan 	struct udevice *vmmc_supply;
21012051aefeSPeng Fan 	int ret;
21022051aefeSPeng Fan 
21032051aefeSPeng Fan 	ret = device_get_supply_regulator(mmc->dev, "vmmc-supply",
21042051aefeSPeng Fan 					  &vmmc_supply);
21052051aefeSPeng Fan 	if (ret) {
2106288db7c7SJaehoon Chung 		debug("%s: No vmmc supply\n", mmc->dev->name);
21072051aefeSPeng Fan 		return 0;
21082051aefeSPeng Fan 	}
21092051aefeSPeng Fan 
21102051aefeSPeng Fan 	ret = regulator_set_enable(vmmc_supply, true);
21112051aefeSPeng Fan 	if (ret) {
21122051aefeSPeng Fan 		puts("Error enabling VMMC supply\n");
21132051aefeSPeng Fan 		return ret;
21142051aefeSPeng Fan 	}
21152051aefeSPeng Fan #endif
211605cbeb7cSSimon Glass #else /* !CONFIG_DM_MMC */
211705cbeb7cSSimon Glass 	/*
211805cbeb7cSSimon Glass 	 * Driver model should use a regulator, as above, rather than calling
211905cbeb7cSSimon Glass 	 * out to board code.
212005cbeb7cSSimon Glass 	 */
212105cbeb7cSSimon Glass 	board_mmc_power_init();
212205cbeb7cSSimon Glass #endif
21232051aefeSPeng Fan 	return 0;
21242051aefeSPeng Fan }
2125479fbf72SJason Zhu #endif
2126479fbf72SJason Zhu #ifdef CONFIG_MMC_USE_PRE_CONFIG
2127479fbf72SJason Zhu static int mmc_select_card(struct mmc *mmc, int n)
2128479fbf72SJason Zhu {
2129479fbf72SJason Zhu 	struct mmc_cmd cmd;
2130479fbf72SJason Zhu 	int err = 0;
21312051aefeSPeng Fan 
2132479fbf72SJason Zhu 	memset(&cmd, 0, sizeof(struct mmc_cmd));
2133479fbf72SJason Zhu 	if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
2134479fbf72SJason Zhu 		mmc->rca = n;
2135479fbf72SJason Zhu 		cmd.cmdidx = MMC_CMD_SELECT_CARD;
2136479fbf72SJason Zhu 		cmd.resp_type = MMC_RSP_R1;
2137479fbf72SJason Zhu 		cmd.cmdarg = mmc->rca << 16;
2138479fbf72SJason Zhu 		err = mmc_send_cmd(mmc, &cmd, NULL);
2139479fbf72SJason Zhu 	}
2140479fbf72SJason Zhu 
2141479fbf72SJason Zhu 	return err;
2142479fbf72SJason Zhu }
2143479fbf72SJason Zhu 
2144479fbf72SJason Zhu int mmc_start_init(struct mmc *mmc)
2145479fbf72SJason Zhu {
2146479fbf72SJason Zhu 	/*
2147479fbf72SJason Zhu 	 * We use the MMC config set by the bootrom.
2148479fbf72SJason Zhu 	 * So it is no need to reset the eMMC device.
2149479fbf72SJason Zhu 	 */
2150479fbf72SJason Zhu 	mmc_set_bus_width(mmc, 8);
2151479fbf72SJason Zhu 	mmc_set_clock(mmc, 1);
2152479fbf72SJason Zhu 	mmc_set_timing(mmc, MMC_TIMING_LEGACY);
2153479fbf72SJason Zhu 	/* Send cmd7 to return stand-by state*/
2154479fbf72SJason Zhu 	mmc_select_card(mmc, 0);
2155479fbf72SJason Zhu 	mmc->version = MMC_VERSION_UNKNOWN;
2156479fbf72SJason Zhu 	mmc->high_capacity = 1;
2157479fbf72SJason Zhu 	/*
2158479fbf72SJason Zhu 	 * The RCA is set to 2 by rockchip bootrom, use the default
2159479fbf72SJason Zhu 	 * value here.
2160479fbf72SJason Zhu 	 */
2161479fbf72SJason Zhu #ifdef CONFIG_ARCH_ROCKCHIP
2162479fbf72SJason Zhu 	mmc->rca = 2;
2163479fbf72SJason Zhu #else
2164479fbf72SJason Zhu 	mmc->rca = 1;
2165479fbf72SJason Zhu #endif
2166479fbf72SJason Zhu 	return 0;
2167479fbf72SJason Zhu }
2168479fbf72SJason Zhu #else
2169e9550449SChe-Liang Chiou int mmc_start_init(struct mmc *mmc)
2170272cc70bSAndy Fleming {
21718ca51e51SSimon Glass 	bool no_card;
2172afd5932bSMacpaul Lin 	int err;
2173272cc70bSAndy Fleming 
2174ab769f22SPantelis Antoniou 	/* we pretend there's no card when init is NULL */
21758ca51e51SSimon Glass 	no_card = mmc_getcd(mmc) == 0;
2176e7881d85SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC)
21778ca51e51SSimon Glass 	no_card = no_card || (mmc->cfg->ops->init == NULL);
21788ca51e51SSimon Glass #endif
21798ca51e51SSimon Glass 	if (no_card) {
218048972d90SThierry Reding 		mmc->has_init = 0;
218156196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
218248972d90SThierry Reding 		printf("MMC: no card present\n");
218356196826SPaul Burton #endif
2184915ffa52SJaehoon Chung 		return -ENOMEDIUM;
218548972d90SThierry Reding 	}
218648972d90SThierry Reding 
2187bc897b1dSLei Wen 	if (mmc->has_init)
2188bc897b1dSLei Wen 		return 0;
2189bc897b1dSLei Wen 
21905a8dbdc6SYangbo Lu #ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT
21915a8dbdc6SYangbo Lu 	mmc_adapter_card_type_ident();
21925a8dbdc6SYangbo Lu #endif
21932051aefeSPeng Fan 	err = mmc_power_init(mmc);
21942051aefeSPeng Fan 	if (err)
21952051aefeSPeng Fan 		return err;
219695de9ab2SPaul Kocialkowski 
2197e7881d85SSimon Glass #if CONFIG_IS_ENABLED(DM_MMC)
21988ca51e51SSimon Glass 	/* The device has already been probed ready for use */
21998ca51e51SSimon Glass #else
2200ab769f22SPantelis Antoniou 	/* made sure it's not NULL earlier */
220193bfd616SPantelis Antoniou 	err = mmc->cfg->ops->init(mmc);
2202272cc70bSAndy Fleming 	if (err)
2203272cc70bSAndy Fleming 		return err;
22048ca51e51SSimon Glass #endif
2205b86b85e2SIlya Yanok 	mmc_set_bus_width(mmc, 1);
2206b86b85e2SIlya Yanok 	mmc_set_clock(mmc, 1);
220781db2d36SZiyuan Xu 	mmc_set_timing(mmc, MMC_TIMING_LEGACY);
2208b86b85e2SIlya Yanok 
2209272cc70bSAndy Fleming 	/* Reset the Card */
2210272cc70bSAndy Fleming 	err = mmc_go_idle(mmc);
2211272cc70bSAndy Fleming 
2212272cc70bSAndy Fleming 	if (err)
2213272cc70bSAndy Fleming 		return err;
2214272cc70bSAndy Fleming 
2215bc897b1dSLei Wen 	/* The internal partition reset to user partition(0) at every CMD0*/
2216c40fdca6SSimon Glass 	mmc_get_blk_desc(mmc)->hwpart = 0;
2217bc897b1dSLei Wen 
2218272cc70bSAndy Fleming 	/* Test for SD version 2 */
2219272cc70bSAndy Fleming 	err = mmc_send_if_cond(mmc);
2220272cc70bSAndy Fleming 
2221272cc70bSAndy Fleming 	/* Now try to get the SD card's operating condition */
2222272cc70bSAndy Fleming 	err = sd_send_op_cond(mmc);
2223272cc70bSAndy Fleming 
2224272cc70bSAndy Fleming 	/* If the command timed out, we check for an MMC card */
2225915ffa52SJaehoon Chung 	if (err == -ETIMEDOUT) {
2226272cc70bSAndy Fleming 		err = mmc_send_op_cond(mmc);
2227272cc70bSAndy Fleming 
2228bd47c135SAndrew Gabbasov 		if (err) {
222956196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
2230272cc70bSAndy Fleming 			printf("Card did not respond to voltage select!\n");
223156196826SPaul Burton #endif
2232915ffa52SJaehoon Chung 			return -EOPNOTSUPP;
2233272cc70bSAndy Fleming 		}
2234272cc70bSAndy Fleming 	}
2235272cc70bSAndy Fleming 
2236bd47c135SAndrew Gabbasov 	if (!err)
2237e9550449SChe-Liang Chiou 		mmc->init_in_progress = 1;
2238e9550449SChe-Liang Chiou 
2239e9550449SChe-Liang Chiou 	return err;
2240e9550449SChe-Liang Chiou }
2241479fbf72SJason Zhu #endif
2242e9550449SChe-Liang Chiou 
2243e9550449SChe-Liang Chiou static int mmc_complete_init(struct mmc *mmc)
2244e9550449SChe-Liang Chiou {
2245e9550449SChe-Liang Chiou 	int err = 0;
2246e9550449SChe-Liang Chiou 
2247bd47c135SAndrew Gabbasov 	mmc->init_in_progress = 0;
2248e9550449SChe-Liang Chiou 	if (mmc->op_cond_pending)
2249e9550449SChe-Liang Chiou 		err = mmc_complete_op_cond(mmc);
2250e9550449SChe-Liang Chiou 
2251e9550449SChe-Liang Chiou 	if (!err)
2252bc897b1dSLei Wen 		err = mmc_startup(mmc);
2253bc897b1dSLei Wen 	if (err)
2254bc897b1dSLei Wen 		mmc->has_init = 0;
2255bc897b1dSLei Wen 	else
2256bc897b1dSLei Wen 		mmc->has_init = 1;
2257e9550449SChe-Liang Chiou 	return err;
2258e9550449SChe-Liang Chiou }
2259e9550449SChe-Liang Chiou 
2260e9550449SChe-Liang Chiou int mmc_init(struct mmc *mmc)
2261e9550449SChe-Liang Chiou {
2262bd47c135SAndrew Gabbasov 	int err = 0;
2263ce9eca94SMarek Vasut 	__maybe_unused unsigned start;
2264c4d660d4SSimon Glass #if CONFIG_IS_ENABLED(DM_MMC)
226533fb211dSSimon Glass 	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(mmc->dev);
2266e9550449SChe-Liang Chiou 
226733fb211dSSimon Glass 	upriv->mmc = mmc;
226833fb211dSSimon Glass #endif
2269e9550449SChe-Liang Chiou 	if (mmc->has_init)
2270e9550449SChe-Liang Chiou 		return 0;
2271d803fea5SMateusz Zalega 
2272d803fea5SMateusz Zalega 	start = get_timer(0);
2273d803fea5SMateusz Zalega 
2274e9550449SChe-Liang Chiou 	if (!mmc->init_in_progress)
2275e9550449SChe-Liang Chiou 		err = mmc_start_init(mmc);
2276e9550449SChe-Liang Chiou 
2277bd47c135SAndrew Gabbasov 	if (!err)
2278e9550449SChe-Liang Chiou 		err = mmc_complete_init(mmc);
2279919b4858SJagan Teki 	if (err)
2280919b4858SJagan Teki 		printf("%s: %d, time %lu\n", __func__, err, get_timer(start));
2281919b4858SJagan Teki 
2282bc897b1dSLei Wen 	return err;
2283272cc70bSAndy Fleming }
2284272cc70bSAndy Fleming 
2285ab71188cSMarkus Niebel int mmc_set_dsr(struct mmc *mmc, u16 val)
2286ab71188cSMarkus Niebel {
2287ab71188cSMarkus Niebel 	mmc->dsr = val;
2288ab71188cSMarkus Niebel 	return 0;
2289ab71188cSMarkus Niebel }
2290ab71188cSMarkus Niebel 
2291cee9ab7cSJeroen Hofstee /* CPU-specific MMC initializations */
2292cee9ab7cSJeroen Hofstee __weak int cpu_mmc_init(bd_t *bis)
2293272cc70bSAndy Fleming {
2294272cc70bSAndy Fleming 	return -1;
2295272cc70bSAndy Fleming }
2296272cc70bSAndy Fleming 
2297cee9ab7cSJeroen Hofstee /* board-specific MMC initializations. */
2298cee9ab7cSJeroen Hofstee __weak int board_mmc_init(bd_t *bis)
2299cee9ab7cSJeroen Hofstee {
2300cee9ab7cSJeroen Hofstee 	return -1;
2301cee9ab7cSJeroen Hofstee }
2302272cc70bSAndy Fleming 
2303e9550449SChe-Liang Chiou void mmc_set_preinit(struct mmc *mmc, int preinit)
2304e9550449SChe-Liang Chiou {
2305e9550449SChe-Liang Chiou 	mmc->preinit = preinit;
2306e9550449SChe-Liang Chiou }
2307e9550449SChe-Liang Chiou 
2308c4d660d4SSimon Glass #if CONFIG_IS_ENABLED(DM_MMC) && defined(CONFIG_SPL_BUILD)
23098e3332e2SSjoerd Simons static int mmc_probe(bd_t *bis)
23108e3332e2SSjoerd Simons {
23118e3332e2SSjoerd Simons 	return 0;
23128e3332e2SSjoerd Simons }
2313c4d660d4SSimon Glass #elif CONFIG_IS_ENABLED(DM_MMC)
23148e3332e2SSjoerd Simons static int mmc_probe(bd_t *bis)
23158e3332e2SSjoerd Simons {
23164a1db6d8SSimon Glass 	int ret, i;
23178e3332e2SSjoerd Simons 	struct uclass *uc;
23184a1db6d8SSimon Glass 	struct udevice *dev;
23198e3332e2SSjoerd Simons 
23208e3332e2SSjoerd Simons 	ret = uclass_get(UCLASS_MMC, &uc);
23218e3332e2SSjoerd Simons 	if (ret)
23228e3332e2SSjoerd Simons 		return ret;
23238e3332e2SSjoerd Simons 
23244a1db6d8SSimon Glass 	/*
23254a1db6d8SSimon Glass 	 * Try to add them in sequence order. Really with driver model we
23264a1db6d8SSimon Glass 	 * should allow holes, but the current MMC list does not allow that.
23274a1db6d8SSimon Glass 	 * So if we request 0, 1, 3 we will get 0, 1, 2.
23284a1db6d8SSimon Glass 	 */
23294a1db6d8SSimon Glass 	for (i = 0; ; i++) {
23304a1db6d8SSimon Glass 		ret = uclass_get_device_by_seq(UCLASS_MMC, i, &dev);
23314a1db6d8SSimon Glass 		if (ret == -ENODEV)
23324a1db6d8SSimon Glass 			break;
23334a1db6d8SSimon Glass 	}
23344a1db6d8SSimon Glass 	uclass_foreach_dev(dev, uc) {
23354a1db6d8SSimon Glass 		ret = device_probe(dev);
23368e3332e2SSjoerd Simons 		if (ret)
23374a1db6d8SSimon Glass 			printf("%s - probe failed: %d\n", dev->name, ret);
23388e3332e2SSjoerd Simons 	}
23398e3332e2SSjoerd Simons 
23408e3332e2SSjoerd Simons 	return 0;
23418e3332e2SSjoerd Simons }
23428e3332e2SSjoerd Simons #else
23438e3332e2SSjoerd Simons static int mmc_probe(bd_t *bis)
23448e3332e2SSjoerd Simons {
23458e3332e2SSjoerd Simons 	if (board_mmc_init(bis) < 0)
23468e3332e2SSjoerd Simons 		cpu_mmc_init(bis);
23478e3332e2SSjoerd Simons 
23488e3332e2SSjoerd Simons 	return 0;
23498e3332e2SSjoerd Simons }
23508e3332e2SSjoerd Simons #endif
2351e9550449SChe-Liang Chiou 
2352272cc70bSAndy Fleming int mmc_initialize(bd_t *bis)
2353272cc70bSAndy Fleming {
23541b26bab1SDaniel Kochmański 	static int initialized = 0;
23558e3332e2SSjoerd Simons 	int ret;
23561b26bab1SDaniel Kochmański 	if (initialized)	/* Avoid initializing mmc multiple times */
23571b26bab1SDaniel Kochmański 		return 0;
23581b26bab1SDaniel Kochmański 	initialized = 1;
23591b26bab1SDaniel Kochmański 
2360c4d660d4SSimon Glass #if !CONFIG_IS_ENABLED(BLK)
2361b5b838f1SMarek Vasut #if !CONFIG_IS_ENABLED(MMC_TINY)
2362c40fdca6SSimon Glass 	mmc_list_init();
2363c40fdca6SSimon Glass #endif
2364b5b838f1SMarek Vasut #endif
23658e3332e2SSjoerd Simons 	ret = mmc_probe(bis);
23668e3332e2SSjoerd Simons 	if (ret)
23678e3332e2SSjoerd Simons 		return ret;
2368272cc70bSAndy Fleming 
2369bb0dc108SYing Zhang #ifndef CONFIG_SPL_BUILD
2370272cc70bSAndy Fleming 	print_mmc_devices(',');
2371bb0dc108SYing Zhang #endif
2372272cc70bSAndy Fleming 
2373c40fdca6SSimon Glass 	mmc_do_preinit();
2374272cc70bSAndy Fleming 	return 0;
2375272cc70bSAndy Fleming }
2376cd3d4880STomas Melin 
2377cd3d4880STomas Melin #ifdef CONFIG_CMD_BKOPS_ENABLE
2378cd3d4880STomas Melin int mmc_set_bkops_enable(struct mmc *mmc)
2379cd3d4880STomas Melin {
2380cd3d4880STomas Melin 	int err;
2381cd3d4880STomas Melin 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
2382cd3d4880STomas Melin 
2383cd3d4880STomas Melin 	err = mmc_send_ext_csd(mmc, ext_csd);
2384cd3d4880STomas Melin 	if (err) {
2385cd3d4880STomas Melin 		puts("Could not get ext_csd register values\n");
2386cd3d4880STomas Melin 		return err;
2387cd3d4880STomas Melin 	}
2388cd3d4880STomas Melin 
2389cd3d4880STomas Melin 	if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) {
2390cd3d4880STomas Melin 		puts("Background operations not supported on device\n");
2391cd3d4880STomas Melin 		return -EMEDIUMTYPE;
2392cd3d4880STomas Melin 	}
2393cd3d4880STomas Melin 
2394cd3d4880STomas Melin 	if (ext_csd[EXT_CSD_BKOPS_EN] & 0x1) {
2395cd3d4880STomas Melin 		puts("Background operations already enabled\n");
2396cd3d4880STomas Melin 		return 0;
2397cd3d4880STomas Melin 	}
2398cd3d4880STomas Melin 
2399cd3d4880STomas Melin 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BKOPS_EN, 1);
2400cd3d4880STomas Melin 	if (err) {
2401cd3d4880STomas Melin 		puts("Failed to enable manual background operations\n");
2402cd3d4880STomas Melin 		return err;
2403cd3d4880STomas Melin 	}
2404cd3d4880STomas Melin 
2405cd3d4880STomas Melin 	puts("Enabled manual background operations\n");
2406cd3d4880STomas Melin 
2407cd3d4880STomas Melin 	return 0;
2408cd3d4880STomas Melin }
2409cd3d4880STomas Melin #endif
2410