xref: /rk3399_rockchip-uboot/drivers/mmc/mmc.c (revision ea6a84b6834a830883a492f3991ab4afefd038af)
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;
find_mmc_device(int dev_num)37b5b838f1SMarek Vasut struct mmc *find_mmc_device(int dev_num)
38b5b838f1SMarek Vasut {
39b5b838f1SMarek Vasut 	return &mmc_static;
40b5b838f1SMarek Vasut }
41b5b838f1SMarek Vasut 
mmc_do_preinit(void)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 
mmc_get_blk_desc(struct mmc * mmc)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)
board_mmc_getwp(struct mmc * mmc)59750121c3SJeroen Hofstee __weak int board_mmc_getwp(struct mmc *mmc)
60d23d8d7eSNikita Kiryanov {
61d23d8d7eSNikita Kiryanov 	return -1;
62d23d8d7eSNikita Kiryanov }
63d23d8d7eSNikita Kiryanov 
mmc_getwp(struct mmc * mmc)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 
board_mmc_getcd(struct mmc * mmc)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
mmmc_trace_before_send(struct mmc * mmc,struct mmc_cmd * cmd)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 
mmmc_trace_after_send(struct mmc * mmc,struct mmc_cmd * cmd,int ret)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 
mmc_trace_state(struct mmc * mmc,struct mmc_cmd * cmd)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)
mmc_send_cmd(struct mmc * mmc,struct mmc_cmd * cmd,struct mmc_data * data)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 
mmc_send_status(struct mmc * mmc,int timeout)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 
mmc_set_blocklen(struct mmc * mmc,int len)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 
mmc_set_blockcount(struct mmc * mmc,unsigned int blkcnt,bool is_rel_write)225*ea6a84b6SZiyuan Xu int mmc_set_blockcount(struct mmc *mmc, unsigned int blkcnt, bool is_rel_write)
226*ea6a84b6SZiyuan Xu {
227*ea6a84b6SZiyuan Xu 	struct mmc_cmd cmd = {0};
228*ea6a84b6SZiyuan Xu 
229*ea6a84b6SZiyuan Xu 	cmd.cmdidx = MMC_CMD_SET_BLOCK_COUNT;
230*ea6a84b6SZiyuan Xu 	cmd.cmdarg = blkcnt & 0x0000FFFF;
231*ea6a84b6SZiyuan Xu 	if (is_rel_write)
232*ea6a84b6SZiyuan Xu 		cmd.cmdarg |= 1 << 31;
233*ea6a84b6SZiyuan Xu 	cmd.resp_type = MMC_RSP_R1;
234*ea6a84b6SZiyuan Xu 
235*ea6a84b6SZiyuan Xu 	return mmc_send_cmd(mmc, &cmd, NULL);
236*ea6a84b6SZiyuan Xu }
237*ea6a84b6SZiyuan Xu 
mmc_read_blocks(struct mmc * mmc,void * dst,lbaint_t start,lbaint_t blkcnt)238ff8fef56SSascha Silbe static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start,
239fdbb873eSKim Phillips 			   lbaint_t blkcnt)
240272cc70bSAndy Fleming {
241272cc70bSAndy Fleming 	struct mmc_cmd cmd;
242272cc70bSAndy Fleming 	struct mmc_data data;
243272cc70bSAndy Fleming 
2444a1a06bcSAlagu Sankar 	if (blkcnt > 1)
2454a1a06bcSAlagu Sankar 		cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
2464a1a06bcSAlagu Sankar 	else
247272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
248272cc70bSAndy Fleming 
249272cc70bSAndy Fleming 	if (mmc->high_capacity)
2504a1a06bcSAlagu Sankar 		cmd.cmdarg = start;
251272cc70bSAndy Fleming 	else
2524a1a06bcSAlagu Sankar 		cmd.cmdarg = start * mmc->read_bl_len;
253272cc70bSAndy Fleming 
254272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
255272cc70bSAndy Fleming 
256272cc70bSAndy Fleming 	data.dest = dst;
2574a1a06bcSAlagu Sankar 	data.blocks = blkcnt;
258272cc70bSAndy Fleming 	data.blocksize = mmc->read_bl_len;
259272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
260272cc70bSAndy Fleming 
2614a1a06bcSAlagu Sankar 	if (mmc_send_cmd(mmc, &cmd, &data))
2624a1a06bcSAlagu Sankar 		return 0;
2634a1a06bcSAlagu Sankar 
2644a1a06bcSAlagu Sankar 	if (blkcnt > 1) {
2654a1a06bcSAlagu Sankar 		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
2664a1a06bcSAlagu Sankar 		cmd.cmdarg = 0;
2674a1a06bcSAlagu Sankar 		cmd.resp_type = MMC_RSP_R1b;
2684a1a06bcSAlagu Sankar 		if (mmc_send_cmd(mmc, &cmd, NULL)) {
26956196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
2704a1a06bcSAlagu Sankar 			printf("mmc fail to send stop cmd\n");
27156196826SPaul Burton #endif
2724a1a06bcSAlagu Sankar 			return 0;
2734a1a06bcSAlagu Sankar 		}
274272cc70bSAndy Fleming 	}
275272cc70bSAndy Fleming 
2764a1a06bcSAlagu Sankar 	return blkcnt;
277272cc70bSAndy Fleming }
278272cc70bSAndy Fleming 
27947f7fd3aSJason Zhu #ifdef CONFIG_SPL_BLK_READ_PREPARE
mmc_read_blocks_prepare(struct mmc * mmc,void * dst,lbaint_t start,lbaint_t blkcnt)28047f7fd3aSJason Zhu static int mmc_read_blocks_prepare(struct mmc *mmc, void *dst, lbaint_t start,
28147f7fd3aSJason Zhu 				   lbaint_t blkcnt)
28247f7fd3aSJason Zhu {
28347f7fd3aSJason Zhu 	struct mmc_cmd cmd;
28447f7fd3aSJason Zhu 	struct mmc_data data;
28547f7fd3aSJason Zhu 
286*ea6a84b6SZiyuan Xu 	if (blkcnt > 1) {
287*ea6a84b6SZiyuan Xu 		mmc_set_blockcount(mmc, blkcnt, false);
28847f7fd3aSJason Zhu 		cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
289*ea6a84b6SZiyuan Xu 	} else {
29047f7fd3aSJason Zhu 		cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
291*ea6a84b6SZiyuan Xu 	}
29247f7fd3aSJason Zhu 
29347f7fd3aSJason Zhu 	if (mmc->high_capacity)
29447f7fd3aSJason Zhu 		cmd.cmdarg = start;
29547f7fd3aSJason Zhu 	else
29647f7fd3aSJason Zhu 		cmd.cmdarg = start * mmc->read_bl_len;
29747f7fd3aSJason Zhu 
29847f7fd3aSJason Zhu 	cmd.resp_type = MMC_RSP_R1;
29947f7fd3aSJason Zhu 
30047f7fd3aSJason Zhu 	data.dest = dst;
30147f7fd3aSJason Zhu 	data.blocks = blkcnt;
30247f7fd3aSJason Zhu 	data.blocksize = mmc->read_bl_len;
30347f7fd3aSJason Zhu 	data.flags = MMC_DATA_READ;
30447f7fd3aSJason Zhu 
30547f7fd3aSJason Zhu 	if (mmc_send_cmd_prepare(mmc, &cmd, &data))
30647f7fd3aSJason Zhu 		return 0;
30747f7fd3aSJason Zhu 
30847f7fd3aSJason Zhu 	return blkcnt;
30947f7fd3aSJason Zhu }
31047f7fd3aSJason Zhu #endif
31147f7fd3aSJason Zhu 
31247f7fd3aSJason Zhu #ifdef CONFIG_SPL_BLK_READ_PREPARE
31347f7fd3aSJason Zhu #if CONFIG_IS_ENABLED(BLK)
mmc_bread_prepare(struct udevice * dev,lbaint_t start,lbaint_t blkcnt,void * dst)31447f7fd3aSJason Zhu ulong mmc_bread_prepare(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, void *dst)
31547f7fd3aSJason Zhu #else
31647f7fd3aSJason Zhu ulong mmc_bread_prepare(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
31747f7fd3aSJason Zhu 			void *dst)
31847f7fd3aSJason Zhu #endif
31947f7fd3aSJason Zhu {
32047f7fd3aSJason Zhu #if CONFIG_IS_ENABLED(BLK)
32147f7fd3aSJason Zhu 	struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
32247f7fd3aSJason Zhu #endif
32347f7fd3aSJason Zhu 	int dev_num = block_dev->devnum;
32447f7fd3aSJason Zhu 	int timeout = 0;
32547f7fd3aSJason Zhu 	int err;
32647f7fd3aSJason Zhu 
32747f7fd3aSJason Zhu 	if (blkcnt == 0)
32847f7fd3aSJason Zhu 		return 0;
32947f7fd3aSJason Zhu 
33047f7fd3aSJason Zhu 	struct mmc *mmc = find_mmc_device(dev_num);
33147f7fd3aSJason Zhu 
33247f7fd3aSJason Zhu 	if (!mmc)
33347f7fd3aSJason Zhu 		return 0;
33447f7fd3aSJason Zhu 
33547f7fd3aSJason Zhu 	if (CONFIG_IS_ENABLED(MMC_TINY))
33647f7fd3aSJason Zhu 		err = mmc_switch_part(mmc, block_dev->hwpart);
33747f7fd3aSJason Zhu 	else
33847f7fd3aSJason Zhu 		err = blk_dselect_hwpart(block_dev, block_dev->hwpart);
33947f7fd3aSJason Zhu 
34047f7fd3aSJason Zhu 	if (err < 0)
34147f7fd3aSJason Zhu 		return 0;
34247f7fd3aSJason Zhu 
34347f7fd3aSJason Zhu 	if ((start + blkcnt) > block_dev->lba) {
34447f7fd3aSJason Zhu #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
34547f7fd3aSJason Zhu 		printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
34647f7fd3aSJason Zhu 		       start + blkcnt, block_dev->lba);
34747f7fd3aSJason Zhu #endif
34847f7fd3aSJason Zhu 		return 0;
34947f7fd3aSJason Zhu 	}
35047f7fd3aSJason Zhu 
35147f7fd3aSJason Zhu 	if (mmc_set_blocklen(mmc, mmc->read_bl_len)) {
35247f7fd3aSJason Zhu 		debug("%s: Failed to set blocklen\n", __func__);
35347f7fd3aSJason Zhu 		return 0;
35447f7fd3aSJason Zhu 	}
35547f7fd3aSJason Zhu 
35647f7fd3aSJason Zhu 	if (mmc_read_blocks_prepare(mmc, dst, start, blkcnt) != blkcnt) {
35747f7fd3aSJason Zhu 		debug("%s: Failed to read blocks\n", __func__);
35847f7fd3aSJason Zhu re_init_retry:
35947f7fd3aSJason Zhu 		timeout++;
36047f7fd3aSJason Zhu 		/*
36147f7fd3aSJason Zhu 		 * Try re-init seven times.
36247f7fd3aSJason Zhu 		 */
36347f7fd3aSJason Zhu 		if (timeout > 7) {
36447f7fd3aSJason Zhu 			printf("Re-init retry timeout\n");
36547f7fd3aSJason Zhu 			return 0;
36647f7fd3aSJason Zhu 		}
36747f7fd3aSJason Zhu 
36847f7fd3aSJason Zhu 		mmc->has_init = 0;
36947f7fd3aSJason Zhu 		if (mmc_init(mmc))
37047f7fd3aSJason Zhu 			return 0;
37147f7fd3aSJason Zhu 
37247f7fd3aSJason Zhu 		if (mmc_read_blocks_prepare(mmc, dst, start, blkcnt) != blkcnt) {
37347f7fd3aSJason Zhu 			printf("%s: Re-init mmc_read_blocks_prepare error\n",
37447f7fd3aSJason Zhu 			       __func__);
37547f7fd3aSJason Zhu 			goto re_init_retry;
37647f7fd3aSJason Zhu 		}
37747f7fd3aSJason Zhu 	}
37847f7fd3aSJason Zhu 
37947f7fd3aSJason Zhu 	return blkcnt;
38047f7fd3aSJason Zhu }
38147f7fd3aSJason Zhu #endif
38247f7fd3aSJason Zhu 
3837863dac1SJason Zhu #if CONFIG_IS_ENABLED(BLK)
mmc_bread(struct udevice * dev,lbaint_t start,lbaint_t blkcnt,void * dst)3847863dac1SJason Zhu ulong mmc_bread(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, void *dst)
3857863dac1SJason Zhu #else
3867863dac1SJason Zhu ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
3877863dac1SJason Zhu 		void *dst)
3887863dac1SJason Zhu #endif
3897863dac1SJason Zhu {
3907863dac1SJason Zhu #if CONFIG_IS_ENABLED(BLK)
3917863dac1SJason Zhu 	struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
3927863dac1SJason Zhu #endif
3937863dac1SJason Zhu 	int dev_num = block_dev->devnum;
3947863dac1SJason Zhu 	int err;
3957863dac1SJason Zhu 	lbaint_t cur, blocks_todo = blkcnt;
3967863dac1SJason Zhu 
3977863dac1SJason Zhu #ifdef CONFIG_SPL_BLK_READ_PREPARE
3987863dac1SJason Zhu 	if (block_dev->op_flag == BLK_PRE_RW)
3997863dac1SJason Zhu #if CONFIG_IS_ENABLED(BLK)
4007863dac1SJason Zhu 		return mmc_bread_prepare(dev, start, blkcnt, dst);
4017863dac1SJason Zhu #else
4027863dac1SJason Zhu 		return mmc_bread_prepare(block_dev, start, blkcnt, dst);
4037863dac1SJason Zhu #endif
4047863dac1SJason Zhu #endif
4057863dac1SJason Zhu 	if (blkcnt == 0)
4067863dac1SJason Zhu 		return 0;
4077863dac1SJason Zhu 
4087863dac1SJason Zhu 	struct mmc *mmc = find_mmc_device(dev_num);
4097863dac1SJason Zhu 	if (!mmc)
4107863dac1SJason Zhu 		return 0;
4117863dac1SJason Zhu 
4127863dac1SJason Zhu 	if (CONFIG_IS_ENABLED(MMC_TINY))
4137863dac1SJason Zhu 		err = mmc_switch_part(mmc, block_dev->hwpart);
4147863dac1SJason Zhu 	else
4157863dac1SJason Zhu 		err = blk_dselect_hwpart(block_dev, block_dev->hwpart);
4167863dac1SJason Zhu 
4177863dac1SJason Zhu 	if (err < 0)
4187863dac1SJason Zhu 		return 0;
4197863dac1SJason Zhu 
4207863dac1SJason Zhu 	if ((start + blkcnt) > block_dev->lba) {
4217863dac1SJason Zhu #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
4227863dac1SJason Zhu 		printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
4237863dac1SJason Zhu 			start + blkcnt, block_dev->lba);
4247863dac1SJason Zhu #endif
4257863dac1SJason Zhu 		return 0;
4267863dac1SJason Zhu 	}
4277863dac1SJason Zhu 
4287863dac1SJason Zhu 	if (mmc_set_blocklen(mmc, mmc->read_bl_len)) {
4297863dac1SJason Zhu 		debug("%s: Failed to set blocklen\n", __func__);
4307863dac1SJason Zhu 		return 0;
4317863dac1SJason Zhu 	}
4327863dac1SJason Zhu 
4337863dac1SJason Zhu 	do {
4347863dac1SJason Zhu 		cur = (blocks_todo > mmc->cfg->b_max) ?
4357863dac1SJason Zhu 			mmc->cfg->b_max : blocks_todo;
4367863dac1SJason Zhu 		if (mmc_read_blocks(mmc, dst, start, cur) != cur) {
4377863dac1SJason Zhu 			debug("%s: Failed to read blocks\n", __func__);
4387863dac1SJason Zhu 			int timeout = 0;
4397863dac1SJason Zhu re_init_retry:
4407863dac1SJason Zhu 			timeout++;
4417863dac1SJason Zhu 			/*
4427863dac1SJason Zhu 			 * Try re-init seven times.
4437863dac1SJason Zhu 			 */
4447863dac1SJason Zhu 			if (timeout > 7) {
4457863dac1SJason Zhu 				printf("Re-init retry timeout\n");
4467863dac1SJason Zhu 				return 0;
4477863dac1SJason Zhu 			}
4487863dac1SJason Zhu 
4497863dac1SJason Zhu 			mmc->has_init = 0;
4507863dac1SJason Zhu 			if (mmc_init(mmc))
4517863dac1SJason Zhu 				return 0;
4527863dac1SJason Zhu 
4537863dac1SJason Zhu 			if (mmc_read_blocks(mmc, dst, start, cur) != cur) {
4547863dac1SJason Zhu 				printf("%s: Re-init mmc_read_blocks error\n",
4557863dac1SJason Zhu 				       __func__);
4567863dac1SJason Zhu 				goto re_init_retry;
4577863dac1SJason Zhu 			}
4587863dac1SJason Zhu 		}
4597863dac1SJason Zhu 		blocks_todo -= cur;
4607863dac1SJason Zhu 		start += cur;
4617863dac1SJason Zhu 		dst += cur * mmc->read_bl_len;
4627863dac1SJason Zhu 	} while (blocks_todo > 0);
4637863dac1SJason Zhu 
4647863dac1SJason Zhu 	return blkcnt;
4657863dac1SJason Zhu }
4667863dac1SJason Zhu 
mmc_set_clock(struct mmc * mmc,uint clock)46749dba033SZiyuan Xu void mmc_set_clock(struct mmc *mmc, uint clock)
46849dba033SZiyuan Xu {
46949dba033SZiyuan Xu 	if (clock > mmc->cfg->f_max)
47049dba033SZiyuan Xu 		clock = mmc->cfg->f_max;
47149dba033SZiyuan Xu 
47249dba033SZiyuan Xu 	if (clock < mmc->cfg->f_min)
47349dba033SZiyuan Xu 		clock = mmc->cfg->f_min;
47449dba033SZiyuan Xu 
47549dba033SZiyuan Xu 	mmc->clock = clock;
47649dba033SZiyuan Xu 
47749dba033SZiyuan Xu 	mmc_set_ios(mmc);
47849dba033SZiyuan Xu }
47949dba033SZiyuan Xu 
mmc_set_bus_width(struct mmc * mmc,uint width)48049dba033SZiyuan Xu static void mmc_set_bus_width(struct mmc *mmc, uint width)
48149dba033SZiyuan Xu {
48249dba033SZiyuan Xu 	mmc->bus_width = width;
48349dba033SZiyuan Xu 
48449dba033SZiyuan Xu 	mmc_set_ios(mmc);
48549dba033SZiyuan Xu }
48649dba033SZiyuan Xu 
mmc_set_timing(struct mmc * mmc,uint timing)48781db2d36SZiyuan Xu static void mmc_set_timing(struct mmc *mmc, uint timing)
48881db2d36SZiyuan Xu {
48981db2d36SZiyuan Xu 	mmc->timing = timing;
49081db2d36SZiyuan Xu 	mmc_set_ios(mmc);
49181db2d36SZiyuan Xu }
49281db2d36SZiyuan Xu 
mmc_go_idle(struct mmc * mmc)493fdbb873eSKim Phillips static int mmc_go_idle(struct mmc *mmc)
494272cc70bSAndy Fleming {
495272cc70bSAndy Fleming 	struct mmc_cmd cmd;
496272cc70bSAndy Fleming 	int err;
497272cc70bSAndy Fleming 
498272cc70bSAndy Fleming 	udelay(1000);
499272cc70bSAndy Fleming 
500272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_GO_IDLE_STATE;
501272cc70bSAndy Fleming 	cmd.cmdarg = 0;
502272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_NONE;
503272cc70bSAndy Fleming 
504272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
505272cc70bSAndy Fleming 
506272cc70bSAndy Fleming 	if (err)
507272cc70bSAndy Fleming 		return err;
508272cc70bSAndy Fleming 
509272cc70bSAndy Fleming 	udelay(2000);
510272cc70bSAndy Fleming 
511272cc70bSAndy Fleming 	return 0;
512272cc70bSAndy Fleming }
513272cc70bSAndy Fleming 
514479fbf72SJason Zhu #ifndef CONFIG_MMC_USE_PRE_CONFIG
sd_send_op_cond(struct mmc * mmc)515fdbb873eSKim Phillips static int sd_send_op_cond(struct mmc *mmc)
516272cc70bSAndy Fleming {
517272cc70bSAndy Fleming 	int timeout = 1000;
518272cc70bSAndy Fleming 	int err;
519272cc70bSAndy Fleming 	struct mmc_cmd cmd;
520272cc70bSAndy Fleming 
5211677eef4SAndrew Gabbasov 	while (1) {
522272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_APP_CMD;
523272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R1;
524272cc70bSAndy Fleming 		cmd.cmdarg = 0;
525272cc70bSAndy Fleming 
526272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
527272cc70bSAndy Fleming 
528272cc70bSAndy Fleming 		if (err)
529272cc70bSAndy Fleming 			return err;
530272cc70bSAndy Fleming 
531272cc70bSAndy Fleming 		cmd.cmdidx = SD_CMD_APP_SEND_OP_COND;
532272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R3;
533250de12bSStefano Babic 
534250de12bSStefano Babic 		/*
535250de12bSStefano Babic 		 * Most cards do not answer if some reserved bits
536250de12bSStefano Babic 		 * in the ocr are set. However, Some controller
537250de12bSStefano Babic 		 * can set bit 7 (reserved for low voltages), but
538250de12bSStefano Babic 		 * how to manage low voltages SD card is not yet
539250de12bSStefano Babic 		 * specified.
540250de12bSStefano Babic 		 */
541d52ebf10SThomas Chou 		cmd.cmdarg = mmc_host_is_spi(mmc) ? 0 :
54293bfd616SPantelis Antoniou 			(mmc->cfg->voltages & 0xff8000);
543272cc70bSAndy Fleming 
544272cc70bSAndy Fleming 		if (mmc->version == SD_VERSION_2)
545272cc70bSAndy Fleming 			cmd.cmdarg |= OCR_HCS;
546272cc70bSAndy Fleming 
547272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
548272cc70bSAndy Fleming 
549272cc70bSAndy Fleming 		if (err)
550272cc70bSAndy Fleming 			return err;
551272cc70bSAndy Fleming 
5521677eef4SAndrew Gabbasov 		if (cmd.response[0] & OCR_BUSY)
5531677eef4SAndrew Gabbasov 			break;
554272cc70bSAndy Fleming 
5551677eef4SAndrew Gabbasov 		if (timeout-- <= 0)
556915ffa52SJaehoon Chung 			return -EOPNOTSUPP;
557272cc70bSAndy Fleming 
5581677eef4SAndrew Gabbasov 		udelay(1000);
5591677eef4SAndrew Gabbasov 	}
5601677eef4SAndrew Gabbasov 
561272cc70bSAndy Fleming 	if (mmc->version != SD_VERSION_2)
562272cc70bSAndy Fleming 		mmc->version = SD_VERSION_1_0;
563272cc70bSAndy Fleming 
564d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc)) { /* read OCR for spi */
565d52ebf10SThomas Chou 		cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
566d52ebf10SThomas Chou 		cmd.resp_type = MMC_RSP_R3;
567d52ebf10SThomas Chou 		cmd.cmdarg = 0;
568d52ebf10SThomas Chou 
569d52ebf10SThomas Chou 		err = mmc_send_cmd(mmc, &cmd, NULL);
570d52ebf10SThomas Chou 
571d52ebf10SThomas Chou 		if (err)
572d52ebf10SThomas Chou 			return err;
573d52ebf10SThomas Chou 	}
574d52ebf10SThomas Chou 
575998be3ddSRabin Vincent 	mmc->ocr = cmd.response[0];
576272cc70bSAndy Fleming 
577272cc70bSAndy Fleming 	mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
578272cc70bSAndy Fleming 	mmc->rca = 0;
579272cc70bSAndy Fleming 
580272cc70bSAndy Fleming 	return 0;
581272cc70bSAndy Fleming }
582479fbf72SJason Zhu #endif
583272cc70bSAndy Fleming 
mmc_send_op_cond_iter(struct mmc * mmc,int use_arg)5845289b535SAndrew Gabbasov static int mmc_send_op_cond_iter(struct mmc *mmc, int use_arg)
585272cc70bSAndy Fleming {
5865289b535SAndrew Gabbasov 	struct mmc_cmd cmd;
587272cc70bSAndy Fleming 	int err;
588272cc70bSAndy Fleming 
5895289b535SAndrew Gabbasov 	cmd.cmdidx = MMC_CMD_SEND_OP_COND;
5905289b535SAndrew Gabbasov 	cmd.resp_type = MMC_RSP_R3;
5915289b535SAndrew Gabbasov 	cmd.cmdarg = 0;
5925a20397bSRob Herring 	if (use_arg && !mmc_host_is_spi(mmc))
5935a20397bSRob Herring 		cmd.cmdarg = OCR_HCS |
59493bfd616SPantelis Antoniou 			(mmc->cfg->voltages &
595a626c8d4SAndrew Gabbasov 			(mmc->ocr & OCR_VOLTAGE_MASK)) |
596a626c8d4SAndrew Gabbasov 			(mmc->ocr & OCR_ACCESS_MODE);
597e9550449SChe-Liang Chiou 
5985289b535SAndrew Gabbasov 	err = mmc_send_cmd(mmc, &cmd, NULL);
599e9550449SChe-Liang Chiou 	if (err)
600e9550449SChe-Liang Chiou 		return err;
6015289b535SAndrew Gabbasov 	mmc->ocr = cmd.response[0];
602e9550449SChe-Liang Chiou 	return 0;
603e9550449SChe-Liang Chiou }
604e9550449SChe-Liang Chiou 
605479fbf72SJason Zhu #ifndef CONFIG_MMC_USE_PRE_CONFIG
mmc_send_op_cond(struct mmc * mmc)606750121c3SJeroen Hofstee static int mmc_send_op_cond(struct mmc *mmc)
607e9550449SChe-Liang Chiou {
608e9550449SChe-Liang Chiou 	int err, i;
609e9550449SChe-Liang Chiou 
610272cc70bSAndy Fleming 	/* Some cards seem to need this */
611272cc70bSAndy Fleming 	mmc_go_idle(mmc);
612272cc70bSAndy Fleming 
61331cacbabSRaffaele Recalcati  	/* Asking to the card its capabilities */
614e9550449SChe-Liang Chiou 	for (i = 0; i < 2; i++) {
6155289b535SAndrew Gabbasov 		err = mmc_send_op_cond_iter(mmc, i != 0);
61631cacbabSRaffaele Recalcati 		if (err)
61731cacbabSRaffaele Recalcati 			return err;
61831cacbabSRaffaele Recalcati 
619e9550449SChe-Liang Chiou 		/* exit if not busy (flag seems to be inverted) */
620a626c8d4SAndrew Gabbasov 		if (mmc->ocr & OCR_BUSY)
621bd47c135SAndrew Gabbasov 			break;
622e9550449SChe-Liang Chiou 	}
623bd47c135SAndrew Gabbasov 	mmc->op_cond_pending = 1;
624bd47c135SAndrew Gabbasov 	return 0;
625e9550449SChe-Liang Chiou }
626479fbf72SJason Zhu #endif
mmc_complete_op_cond(struct mmc * mmc)627750121c3SJeroen Hofstee static int mmc_complete_op_cond(struct mmc *mmc)
628e9550449SChe-Liang Chiou {
629e9550449SChe-Liang Chiou 	struct mmc_cmd cmd;
630e9550449SChe-Liang Chiou 	int timeout = 1000;
631e9550449SChe-Liang Chiou 	uint start;
632e9550449SChe-Liang Chiou 	int err;
633e9550449SChe-Liang Chiou 
634e9550449SChe-Liang Chiou 	mmc->op_cond_pending = 0;
635cc17c01fSAndrew Gabbasov 	if (!(mmc->ocr & OCR_BUSY)) {
636d188b113SYangbo Lu 		/* Some cards seem to need this */
637d188b113SYangbo Lu 		mmc_go_idle(mmc);
638d188b113SYangbo Lu 
639e9550449SChe-Liang Chiou 		start = get_timer(0);
6401677eef4SAndrew Gabbasov 		while (1) {
6415289b535SAndrew Gabbasov 			err = mmc_send_op_cond_iter(mmc, 1);
642272cc70bSAndy Fleming 			if (err)
643272cc70bSAndy Fleming 				return err;
6441677eef4SAndrew Gabbasov 			if (mmc->ocr & OCR_BUSY)
6451677eef4SAndrew Gabbasov 				break;
646e9550449SChe-Liang Chiou 			if (get_timer(start) > timeout)
647915ffa52SJaehoon Chung 				return -EOPNOTSUPP;
648e9550449SChe-Liang Chiou 			udelay(100);
6491677eef4SAndrew Gabbasov 		}
650cc17c01fSAndrew Gabbasov 	}
651272cc70bSAndy Fleming 
652d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc)) { /* read OCR for spi */
653d52ebf10SThomas Chou 		cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
654d52ebf10SThomas Chou 		cmd.resp_type = MMC_RSP_R3;
655d52ebf10SThomas Chou 		cmd.cmdarg = 0;
656d52ebf10SThomas Chou 
657d52ebf10SThomas Chou 		err = mmc_send_cmd(mmc, &cmd, NULL);
658d52ebf10SThomas Chou 
659d52ebf10SThomas Chou 		if (err)
660d52ebf10SThomas Chou 			return err;
661a626c8d4SAndrew Gabbasov 
662a626c8d4SAndrew Gabbasov 		mmc->ocr = cmd.response[0];
663d52ebf10SThomas Chou 	}
664d52ebf10SThomas Chou 
665272cc70bSAndy Fleming 	mmc->version = MMC_VERSION_UNKNOWN;
666272cc70bSAndy Fleming 
667272cc70bSAndy Fleming 	mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
668def816a2SStephen Warren 	mmc->rca = 1;
669272cc70bSAndy Fleming 
670272cc70bSAndy Fleming 	return 0;
671272cc70bSAndy Fleming }
672272cc70bSAndy Fleming 
673272cc70bSAndy Fleming 
mmc_send_ext_csd(struct mmc * mmc,u8 * ext_csd)674fdbb873eSKim Phillips static int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd)
675272cc70bSAndy Fleming {
676272cc70bSAndy Fleming 	struct mmc_cmd cmd;
677272cc70bSAndy Fleming 	struct mmc_data data;
678272cc70bSAndy Fleming 	int err;
679272cc70bSAndy Fleming 
680a95f7caaSJason Zhu #ifdef CONFIG_MMC_USE_PRE_CONFIG
681a95f7caaSJason Zhu 	static int initialized;
682e531136eSJason Zhu 	if (initialized) {
683e531136eSJason Zhu 		memcpy(ext_csd, mmc_ext_csd, 512);
684e531136eSJason Zhu 		return 0;
685e531136eSJason Zhu 	}
686e531136eSJason Zhu 
687e531136eSJason Zhu 	initialized = 1;
688a95f7caaSJason Zhu #endif
689272cc70bSAndy Fleming 	/* Get the Card Status Register */
690272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SEND_EXT_CSD;
691272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
692272cc70bSAndy Fleming 	cmd.cmdarg = 0;
693272cc70bSAndy Fleming 
694cdfd1ac6SYoshihiro Shimoda 	data.dest = (char *)ext_csd;
695272cc70bSAndy Fleming 	data.blocks = 1;
6968bfa195eSSimon Glass 	data.blocksize = MMC_MAX_BLOCK_LEN;
697272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
698272cc70bSAndy Fleming 
699272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, &data);
700e531136eSJason Zhu 	memcpy(mmc_ext_csd, ext_csd, 512);
7012056aa9fSJason Zhu #if defined(CONFIG_MMC_USE_PRE_CONFIG) && defined(CONFIG_SPL_BUILD)
7022056aa9fSJason Zhu 	char *mmc_ecsd_base = NULL;
7032056aa9fSJason Zhu 	ulong mmc_ecsd;
704272cc70bSAndy Fleming 
7052056aa9fSJason Zhu 	mmc_ecsd = dev_read_u32_default(mmc->dev, "mmc-ecsd", 0);
7062056aa9fSJason Zhu 	mmc_ecsd_base = (char *)mmc_ecsd;
7072056aa9fSJason Zhu 	if (mmc_ecsd_base) {
7082056aa9fSJason Zhu 		memcpy(mmc_ecsd_base, ext_csd, 512);
7092056aa9fSJason Zhu 		*(unsigned int *)(mmc_ecsd_base + 512) = 0x55aa55aa;
7102056aa9fSJason Zhu 	}
7112056aa9fSJason Zhu #endif
712272cc70bSAndy Fleming 	return err;
713272cc70bSAndy Fleming }
714272cc70bSAndy Fleming 
mmc_poll_for_busy(struct mmc * mmc,u8 send_status)7159e8ce816SZiyuan Xu static int mmc_poll_for_busy(struct mmc *mmc, u8 send_status)
716272cc70bSAndy Fleming {
717272cc70bSAndy Fleming 	struct mmc_cmd cmd;
71855e5defdSZiyuan Xu 	u8 busy = true;
71955e5defdSZiyuan Xu 	uint start;
72055e5defdSZiyuan Xu 	int ret;
7215d4fc8d9SRaffaele Recalcati 	int timeout = 1000;
72255e5defdSZiyuan Xu 
72355e5defdSZiyuan Xu 	cmd.cmdidx = MMC_CMD_SEND_STATUS;
72455e5defdSZiyuan Xu 	cmd.resp_type = MMC_RSP_R1;
72555e5defdSZiyuan Xu 	cmd.cmdarg = mmc->rca << 16;
72655e5defdSZiyuan Xu 
72755e5defdSZiyuan Xu 	start = get_timer(0);
72855e5defdSZiyuan Xu 
7299e8ce816SZiyuan Xu 	if (!send_status && !mmc_can_card_busy(mmc)) {
7309e8ce816SZiyuan Xu 		mdelay(timeout);
7319e8ce816SZiyuan Xu 		return 0;
7329e8ce816SZiyuan Xu 	}
7339e8ce816SZiyuan Xu 
73455e5defdSZiyuan Xu 	do {
7359e8ce816SZiyuan Xu 		if (!send_status) {
73655e5defdSZiyuan Xu 			busy = mmc_card_busy(mmc);
73755e5defdSZiyuan Xu 		} else {
73855e5defdSZiyuan Xu 			ret = mmc_send_cmd(mmc, &cmd, NULL);
73955e5defdSZiyuan Xu 
74055e5defdSZiyuan Xu 			if (ret)
74155e5defdSZiyuan Xu 				return ret;
74255e5defdSZiyuan Xu 
74355e5defdSZiyuan Xu 			if (cmd.response[0] & MMC_STATUS_SWITCH_ERROR)
74455e5defdSZiyuan Xu 				return -EBADMSG;
74555e5defdSZiyuan Xu 			busy = (cmd.response[0] & MMC_STATUS_CURR_STATE) ==
74655e5defdSZiyuan Xu 				MMC_STATE_PRG;
74755e5defdSZiyuan Xu 		}
74855e5defdSZiyuan Xu 
74955e5defdSZiyuan Xu 		if (get_timer(start) > timeout && busy)
75055e5defdSZiyuan Xu 			return -ETIMEDOUT;
75155e5defdSZiyuan Xu 	} while (busy);
75255e5defdSZiyuan Xu 
75355e5defdSZiyuan Xu 	return 0;
75455e5defdSZiyuan Xu }
75555e5defdSZiyuan Xu 
__mmc_switch(struct mmc * mmc,u8 set,u8 index,u8 value,u8 send_status)75655e5defdSZiyuan Xu static int __mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value,
75755e5defdSZiyuan Xu 			u8 send_status)
75855e5defdSZiyuan Xu {
75955e5defdSZiyuan Xu 	struct mmc_cmd cmd;
760a9003dc6SMaxime Ripard 	int retries = 3;
7615d4fc8d9SRaffaele Recalcati 	int ret;
762272cc70bSAndy Fleming 
763272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SWITCH;
764272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1b;
765272cc70bSAndy Fleming 	cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
766272cc70bSAndy Fleming 				 (index << 16) |
767272cc70bSAndy Fleming 				 (value << 8);
768272cc70bSAndy Fleming 
76955e5defdSZiyuan Xu 	do {
7705d4fc8d9SRaffaele Recalcati 		ret = mmc_send_cmd(mmc, &cmd, NULL);
7715d4fc8d9SRaffaele Recalcati 
7729e8ce816SZiyuan Xu 		if (!ret)
7739e8ce816SZiyuan Xu 			return mmc_poll_for_busy(mmc, send_status);
77455e5defdSZiyuan Xu 	} while (--retries > 0 && ret);
77555e5defdSZiyuan Xu 
776a9003dc6SMaxime Ripard 	return ret;
777a9003dc6SMaxime Ripard }
778a9003dc6SMaxime Ripard 
mmc_switch(struct mmc * mmc,u8 set,u8 index,u8 value)77955e5defdSZiyuan Xu int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)
78055e5defdSZiyuan Xu {
78155e5defdSZiyuan Xu 	return __mmc_switch(mmc, set, index, value, true);
782272cc70bSAndy Fleming }
783272cc70bSAndy Fleming 
mmc_select_bus_width(struct mmc * mmc)78449dba033SZiyuan Xu static int mmc_select_bus_width(struct mmc *mmc)
78549dba033SZiyuan Xu {
78649dba033SZiyuan Xu 	u32 ext_csd_bits[] = {
78749dba033SZiyuan Xu 		EXT_CSD_BUS_WIDTH_8,
78849dba033SZiyuan Xu 		EXT_CSD_BUS_WIDTH_4,
78949dba033SZiyuan Xu 	};
79049dba033SZiyuan Xu 	u32 bus_widths[] = {
79149dba033SZiyuan Xu 		MMC_BUS_WIDTH_8BIT,
79249dba033SZiyuan Xu 		MMC_BUS_WIDTH_4BIT,
79349dba033SZiyuan Xu 	};
79449dba033SZiyuan Xu 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
79549dba033SZiyuan Xu 	ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN);
79649dba033SZiyuan Xu 	u32 idx, bus_width = 0;
79749dba033SZiyuan Xu 	int err = 0;
79849dba033SZiyuan Xu 
79949dba033SZiyuan Xu 	if (mmc->version < MMC_VERSION_4 ||
80049dba033SZiyuan Xu 	    !(mmc->cfg->host_caps & (MMC_MODE_4BIT | MMC_MODE_8BIT)))
80149dba033SZiyuan Xu 		return 0;
80249dba033SZiyuan Xu 
80349dba033SZiyuan Xu 	err = mmc_send_ext_csd(mmc, ext_csd);
80449dba033SZiyuan Xu 
80549dba033SZiyuan Xu 	if (err)
80649dba033SZiyuan Xu 		return err;
80749dba033SZiyuan Xu 
80849dba033SZiyuan Xu 	idx = (mmc->cfg->host_caps & MMC_MODE_8BIT) ? 0 : 1;
80949dba033SZiyuan Xu 
81049dba033SZiyuan Xu 	/*
81149dba033SZiyuan Xu 	 * Unlike SD, MMC cards dont have a configuration register to notify
81249dba033SZiyuan Xu 	 * supported bus width. So bus test command should be run to identify
81349dba033SZiyuan Xu 	 * the supported bus width or compare the ext csd values of current
81449dba033SZiyuan Xu 	 * bus width and ext csd values of 1 bit mode read earlier.
81549dba033SZiyuan Xu 	 */
81649dba033SZiyuan Xu 	for (; idx < ARRAY_SIZE(bus_widths); idx++) {
81749dba033SZiyuan Xu 		/*
81849dba033SZiyuan Xu 		 * Host is capable of 8bit transfer, then switch
81949dba033SZiyuan Xu 		 * the device to work in 8bit transfer mode. If the
82049dba033SZiyuan Xu 		 * mmc switch command returns error then switch to
82149dba033SZiyuan Xu 		 * 4bit transfer mode. On success set the corresponding
82249dba033SZiyuan Xu 		 * bus width on the host.
82349dba033SZiyuan Xu 		 */
82449dba033SZiyuan Xu 		err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
82549dba033SZiyuan Xu 				 EXT_CSD_BUS_WIDTH, ext_csd_bits[idx]);
82649dba033SZiyuan Xu 		if (err)
82749dba033SZiyuan Xu 			continue;
82849dba033SZiyuan Xu 
82949dba033SZiyuan Xu 		bus_width = bus_widths[idx];
83049dba033SZiyuan Xu 		mmc_set_bus_width(mmc, bus_width);
83149dba033SZiyuan Xu 
83249dba033SZiyuan Xu 		err = mmc_send_ext_csd(mmc, test_csd);
83349dba033SZiyuan Xu 
83449dba033SZiyuan Xu 		if (err)
83549dba033SZiyuan Xu 			continue;
83649dba033SZiyuan Xu 
83749dba033SZiyuan Xu 		/* Only compare read only fields */
83849dba033SZiyuan Xu 		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] ==
83949dba033SZiyuan Xu 			test_csd[EXT_CSD_PARTITIONING_SUPPORT]) &&
84049dba033SZiyuan Xu 		    (ext_csd[EXT_CSD_HC_WP_GRP_SIZE] ==
84149dba033SZiyuan Xu 			test_csd[EXT_CSD_HC_WP_GRP_SIZE]) &&
84249dba033SZiyuan Xu 		    (ext_csd[EXT_CSD_REV] == test_csd[EXT_CSD_REV]) &&
84349dba033SZiyuan Xu 			(ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] ==
84449dba033SZiyuan Xu 			test_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) &&
84549dba033SZiyuan Xu 		    !memcmp(&ext_csd[EXT_CSD_SEC_CNT],
84649dba033SZiyuan Xu 			&test_csd[EXT_CSD_SEC_CNT], 4)) {
84749dba033SZiyuan Xu 			err = bus_width;
84849dba033SZiyuan Xu 			break;
84949dba033SZiyuan Xu 		} else {
85049dba033SZiyuan Xu 			err = -EBADMSG;
85149dba033SZiyuan Xu 		}
85249dba033SZiyuan Xu 	}
85349dba033SZiyuan Xu 
85449dba033SZiyuan Xu 	return err;
85549dba033SZiyuan Xu }
85649dba033SZiyuan Xu 
857699945cbSJason Zhu #ifndef CONFIG_MMC_SIMPLE
85849dba033SZiyuan Xu static const u8 tuning_blk_pattern_4bit[] = {
85949dba033SZiyuan Xu 	0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
86049dba033SZiyuan Xu 	0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
86149dba033SZiyuan Xu 	0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
86249dba033SZiyuan Xu 	0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
86349dba033SZiyuan Xu 	0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
86449dba033SZiyuan Xu 	0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
86549dba033SZiyuan Xu 	0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
86649dba033SZiyuan Xu 	0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
86749dba033SZiyuan Xu };
86849dba033SZiyuan Xu 
86949dba033SZiyuan Xu static const u8 tuning_blk_pattern_8bit[] = {
87049dba033SZiyuan Xu 	0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
87149dba033SZiyuan Xu 	0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
87249dba033SZiyuan Xu 	0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
87349dba033SZiyuan Xu 	0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
87449dba033SZiyuan Xu 	0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
87549dba033SZiyuan Xu 	0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
87649dba033SZiyuan Xu 	0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
87749dba033SZiyuan Xu 	0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
87849dba033SZiyuan Xu 	0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
87949dba033SZiyuan Xu 	0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
88049dba033SZiyuan Xu 	0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
88149dba033SZiyuan Xu 	0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
88249dba033SZiyuan Xu 	0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
88349dba033SZiyuan Xu 	0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
88449dba033SZiyuan Xu 	0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
88549dba033SZiyuan Xu 	0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
88649dba033SZiyuan Xu };
88749dba033SZiyuan Xu 
mmc_send_tuning(struct mmc * mmc,u32 opcode)88849dba033SZiyuan Xu int mmc_send_tuning(struct mmc *mmc, u32 opcode)
88949dba033SZiyuan Xu {
89049dba033SZiyuan Xu 	struct mmc_cmd cmd;
89149dba033SZiyuan Xu 	struct mmc_data data;
89249dba033SZiyuan Xu 	const u8 *tuning_block_pattern;
89349dba033SZiyuan Xu 	int size, err = 0;
89449dba033SZiyuan Xu 	u8 *data_buf;
89549dba033SZiyuan Xu 
89649dba033SZiyuan Xu 	if (mmc->bus_width == MMC_BUS_WIDTH_8BIT) {
89749dba033SZiyuan Xu 		tuning_block_pattern = tuning_blk_pattern_8bit;
89849dba033SZiyuan Xu 		size = sizeof(tuning_blk_pattern_8bit);
89949dba033SZiyuan Xu 	} else if (mmc->bus_width == MMC_BUS_WIDTH_4BIT) {
90049dba033SZiyuan Xu 		tuning_block_pattern = tuning_blk_pattern_4bit;
90149dba033SZiyuan Xu 		size = sizeof(tuning_blk_pattern_4bit);
90249dba033SZiyuan Xu 	} else {
90349dba033SZiyuan Xu 		return -EINVAL;
90449dba033SZiyuan Xu 	}
90549dba033SZiyuan Xu 
90649dba033SZiyuan Xu 	data_buf = calloc(1, size);
90749dba033SZiyuan Xu 	if (!data_buf)
90849dba033SZiyuan Xu 		return -ENOMEM;
90949dba033SZiyuan Xu 
91049dba033SZiyuan Xu 	cmd.cmdidx = opcode;
91149dba033SZiyuan Xu 	cmd.resp_type = MMC_RSP_R1;
91249dba033SZiyuan Xu 	cmd.cmdarg = 0;
91349dba033SZiyuan Xu 
91449dba033SZiyuan Xu 	data.dest = (char *)data_buf;
91549dba033SZiyuan Xu 	data.blocksize = size;
91649dba033SZiyuan Xu 	data.blocks = 1;
91749dba033SZiyuan Xu 	data.flags = MMC_DATA_READ;
91849dba033SZiyuan Xu 
91949dba033SZiyuan Xu 	err = mmc_send_cmd(mmc, &cmd, &data);
920e09ef32eSYifeng Zhao 	if (err) {
921e09ef32eSYifeng Zhao 		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
922e09ef32eSYifeng Zhao 		cmd.cmdarg = 0;
923e09ef32eSYifeng Zhao 		cmd.resp_type = MMC_RSP_R1b;
924e09ef32eSYifeng Zhao 		mmc_send_cmd(mmc, &cmd, NULL);
92549dba033SZiyuan Xu 		goto out;
926e09ef32eSYifeng Zhao 	}
92749dba033SZiyuan Xu 	if (memcmp(data_buf, tuning_block_pattern, size))
92849dba033SZiyuan Xu 		err = -EIO;
92949dba033SZiyuan Xu out:
93049dba033SZiyuan Xu 	free(data_buf);
93149dba033SZiyuan Xu 	return err;
93249dba033SZiyuan Xu }
93349dba033SZiyuan Xu 
mmc_execute_tuning(struct mmc * mmc)93449dba033SZiyuan Xu static int mmc_execute_tuning(struct mmc *mmc)
93549dba033SZiyuan Xu {
93649dba033SZiyuan Xu #ifdef CONFIG_DM_MMC
93749dba033SZiyuan Xu 	struct dm_mmc_ops *ops = mmc_get_ops(mmc->dev);
93849dba033SZiyuan Xu #endif
93949dba033SZiyuan Xu 	u32 opcode;
94049dba033SZiyuan Xu 
94149dba033SZiyuan Xu 	if (IS_SD(mmc))
94249dba033SZiyuan Xu 		opcode = MMC_SEND_TUNING_BLOCK;
94349dba033SZiyuan Xu 	else
94449dba033SZiyuan Xu 		opcode = MMC_SEND_TUNING_BLOCK_HS200;
94549dba033SZiyuan Xu 
94649dba033SZiyuan Xu #ifndef CONFIG_DM_MMC
94749dba033SZiyuan Xu 	if (mmc->cfg->ops->execute_tuning) {
94849dba033SZiyuan Xu 		return mmc->cfg->ops->execute_tuning(mmc, opcode);
94949dba033SZiyuan Xu #else
95049dba033SZiyuan Xu 	if (ops->execute_tuning) {
95149dba033SZiyuan Xu 		return ops->execute_tuning(mmc->dev, opcode);
95249dba033SZiyuan Xu #endif
95349dba033SZiyuan Xu 	} else {
95449dba033SZiyuan Xu 		debug("Tuning feature required for HS200 mode.\n");
95549dba033SZiyuan Xu 		return -EIO;
95649dba033SZiyuan Xu 	}
95749dba033SZiyuan Xu }
95849dba033SZiyuan Xu 
95949dba033SZiyuan Xu static int mmc_hs200_tuning(struct mmc *mmc)
96049dba033SZiyuan Xu {
96149dba033SZiyuan Xu 	return mmc_execute_tuning(mmc);
96249dba033SZiyuan Xu }
96349dba033SZiyuan Xu 
964699945cbSJason Zhu #else
965699945cbSJason Zhu int mmc_send_tuning(struct mmc *mmc, u32 opcode) { return 0; }
966699945cbSJason Zhu int mmc_execute_tuning(struct mmc *mmc) { return 0; }
967699945cbSJason Zhu static int mmc_hs200_tuning(struct mmc *mmc) { return 0; }
968699945cbSJason Zhu #endif
969699945cbSJason Zhu 
970e61cd3d7SZiyuan Xu static int mmc_select_hs(struct mmc *mmc)
971e61cd3d7SZiyuan Xu {
972e61cd3d7SZiyuan Xu 	int ret;
973e61cd3d7SZiyuan Xu 
974e61cd3d7SZiyuan Xu 	ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
975e61cd3d7SZiyuan Xu 			 EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS);
976e61cd3d7SZiyuan Xu 
977e61cd3d7SZiyuan Xu 	if (!ret)
978e61cd3d7SZiyuan Xu 		mmc_set_timing(mmc, MMC_TIMING_MMC_HS);
979e61cd3d7SZiyuan Xu 
980e61cd3d7SZiyuan Xu 	return ret;
981e61cd3d7SZiyuan Xu }
982e61cd3d7SZiyuan Xu 
9835545757fSZiyuan Xu static int mmc_select_hs_ddr(struct mmc *mmc)
9845545757fSZiyuan Xu {
9855545757fSZiyuan Xu 	u32 ext_csd_bits;
9865545757fSZiyuan Xu 	int err = 0;
9875545757fSZiyuan Xu 
9885545757fSZiyuan Xu 	if (mmc->bus_width == MMC_BUS_WIDTH_1BIT)
9895545757fSZiyuan Xu 		return 0;
9905545757fSZiyuan Xu 
9915545757fSZiyuan Xu 	ext_csd_bits = (mmc->bus_width == MMC_BUS_WIDTH_8BIT) ?
9925545757fSZiyuan Xu 			EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4;
9935545757fSZiyuan Xu 
9945545757fSZiyuan Xu 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
9955545757fSZiyuan Xu 			 EXT_CSD_BUS_WIDTH, ext_csd_bits);
9965545757fSZiyuan Xu 	if (err)
9975545757fSZiyuan Xu 		return err;
9985545757fSZiyuan Xu 
9995545757fSZiyuan Xu 	mmc_set_timing(mmc, MMC_TIMING_MMC_DDR52);
10005545757fSZiyuan Xu 
10015545757fSZiyuan Xu 	return 0;
10025545757fSZiyuan Xu }
10035545757fSZiyuan Xu 
1004699945cbSJason Zhu #ifndef CONFIG_MMC_SIMPLE
100549dba033SZiyuan Xu static int mmc_select_hs200(struct mmc *mmc)
100649dba033SZiyuan Xu {
100749dba033SZiyuan Xu 	int ret;
100849dba033SZiyuan Xu 
100949dba033SZiyuan Xu 	/*
101049dba033SZiyuan Xu 	 * Set the bus width(4 or 8) with host's support and
101149dba033SZiyuan Xu 	 * switch to HS200 mode if bus width is set successfully.
101249dba033SZiyuan Xu 	 */
101349dba033SZiyuan Xu 	ret = mmc_select_bus_width(mmc);
101449dba033SZiyuan Xu 
101549dba033SZiyuan Xu 	if (ret > 0) {
101649dba033SZiyuan Xu 		ret = __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
101749dba033SZiyuan Xu 				   EXT_CSD_HS_TIMING,
101849dba033SZiyuan Xu 				   EXT_CSD_TIMING_HS200, false);
101949dba033SZiyuan Xu 
102049dba033SZiyuan Xu 		if (ret)
102149dba033SZiyuan Xu 			return ret;
102249dba033SZiyuan Xu 
102349dba033SZiyuan Xu 		mmc_set_timing(mmc, MMC_TIMING_MMC_HS200);
102449dba033SZiyuan Xu 	}
102549dba033SZiyuan Xu 
102649dba033SZiyuan Xu 	return ret;
102749dba033SZiyuan Xu }
102849dba033SZiyuan Xu 
102999685543SYifeng Zhao static int mmc_switch_to_hs400(struct mmc *mmc)
103099685543SYifeng Zhao {
103199685543SYifeng Zhao 	u8 val, fixed_drv_type, card_drv_type, drive_strength;
103299685543SYifeng Zhao 
103399685543SYifeng Zhao 	fixed_drv_type = mmc->cfg->fixed_drv_type;
103499685543SYifeng Zhao 	card_drv_type = mmc->raw_driver_strength | mmc_driver_type_mask(0);
103599685543SYifeng Zhao 	drive_strength = (card_drv_type & mmc_driver_type_mask(fixed_drv_type))
103699685543SYifeng Zhao 				 ? fixed_drv_type : 0;
103799685543SYifeng Zhao 	val = EXT_CSD_TIMING_HS400 | drive_strength << EXT_CSD_DRV_STR_SHIFT;
103899685543SYifeng Zhao 
103999685543SYifeng Zhao 	return __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, val, false);
104099685543SYifeng Zhao }
104199685543SYifeng Zhao 
1042b673f29aSZiyuan Xu static int mmc_select_hs400(struct mmc *mmc)
1043b673f29aSZiyuan Xu {
1044b673f29aSZiyuan Xu 	int ret;
1045b673f29aSZiyuan Xu 
1046b673f29aSZiyuan Xu 	/* Switch card to HS mode */
1047b673f29aSZiyuan Xu 	ret = __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1048b673f29aSZiyuan Xu 			   EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, false);
1049b673f29aSZiyuan Xu 	if (ret)
1050b673f29aSZiyuan Xu 		return ret;
1051b673f29aSZiyuan Xu 
1052b673f29aSZiyuan Xu 	/* Set host controller to HS timing */
1053b673f29aSZiyuan Xu 	mmc_set_timing(mmc, MMC_TIMING_MMC_HS);
1054b673f29aSZiyuan Xu 
105513669fc5SYifeng Zhao 	/* Reduce frequency to HS frequency */
105613669fc5SYifeng Zhao 	mmc_set_clock(mmc, MMC_HIGH_52_MAX_DTR);
105713669fc5SYifeng Zhao 
1058b673f29aSZiyuan Xu 	ret = mmc_send_status(mmc, 1000);
1059b673f29aSZiyuan Xu 	if (ret)
1060b673f29aSZiyuan Xu 		return ret;
1061b673f29aSZiyuan Xu 
1062b673f29aSZiyuan Xu 	/* Switch card to DDR */
1063b673f29aSZiyuan Xu 	ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1064b673f29aSZiyuan Xu 			 EXT_CSD_BUS_WIDTH,
1065b673f29aSZiyuan Xu 			 EXT_CSD_DDR_BUS_WIDTH_8);
1066b673f29aSZiyuan Xu 	if (ret)
1067b673f29aSZiyuan Xu 		return ret;
1068b673f29aSZiyuan Xu 
1069b673f29aSZiyuan Xu 	/* Switch card to HS400 */
107099685543SYifeng Zhao 	ret = mmc_switch_to_hs400(mmc);
1071b673f29aSZiyuan Xu 	if (ret)
1072b673f29aSZiyuan Xu 		return ret;
1073b673f29aSZiyuan Xu 
1074b673f29aSZiyuan Xu 	/* Set host controller to HS400 timing and frequency */
1075b673f29aSZiyuan Xu 	mmc_set_timing(mmc, MMC_TIMING_MMC_HS400);
1076b673f29aSZiyuan Xu 
1077b673f29aSZiyuan Xu 	return ret;
1078b673f29aSZiyuan Xu }
107913669fc5SYifeng Zhao 
108013669fc5SYifeng Zhao static int mmc_select_hs400es(struct mmc *mmc)
108113669fc5SYifeng Zhao {
108213669fc5SYifeng Zhao 	int err;
108313669fc5SYifeng Zhao 
108413669fc5SYifeng Zhao 	/* Switch card to HS mode */
108513669fc5SYifeng Zhao 	err = __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
108613669fc5SYifeng Zhao 			   EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, false);
108713669fc5SYifeng Zhao 	if (err)
108813669fc5SYifeng Zhao 		return err;
108913669fc5SYifeng Zhao 
109013669fc5SYifeng Zhao 	/* Set host controller to HS timing */
109113669fc5SYifeng Zhao 	mmc_set_timing(mmc, MMC_TIMING_MMC_HS);
109213669fc5SYifeng Zhao 
109313669fc5SYifeng Zhao 	err = mmc_send_status(mmc, 1000);
109413669fc5SYifeng Zhao 	if (err)
109513669fc5SYifeng Zhao 		return err;
109613669fc5SYifeng Zhao 
109713669fc5SYifeng Zhao 	mmc_set_clock(mmc, MMC_HIGH_52_MAX_DTR);
109813669fc5SYifeng Zhao 
109913669fc5SYifeng Zhao 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
110013669fc5SYifeng Zhao 			 EXT_CSD_DDR_BUS_WIDTH_8 |
110113669fc5SYifeng Zhao 			 EXT_CSD_BUS_WIDTH_STROBE);
110213669fc5SYifeng Zhao 	if (err) {
110313669fc5SYifeng Zhao 		printf("switch to bus width for hs400 failed\n");
110413669fc5SYifeng Zhao 		return err;
110513669fc5SYifeng Zhao 	}
110613669fc5SYifeng Zhao 
110713669fc5SYifeng Zhao 	/* Switch card to HS400 */
110899685543SYifeng Zhao 	err = mmc_switch_to_hs400(mmc);
110913669fc5SYifeng Zhao 	if (err)
111013669fc5SYifeng Zhao 		return err;
111113669fc5SYifeng Zhao 
111213669fc5SYifeng Zhao 	/* Set host controller to HS400 timing and frequency */
111313669fc5SYifeng Zhao 	mmc_set_timing(mmc, MMC_TIMING_MMC_HS400ES);
111413669fc5SYifeng Zhao 
111513669fc5SYifeng Zhao 	return mmc_set_enhanced_strobe(mmc);
111613669fc5SYifeng Zhao }
1117699945cbSJason Zhu #else
1118699945cbSJason Zhu static int mmc_select_hs200(struct mmc *mmc) { return 0; }
1119699945cbSJason Zhu static int mmc_select_hs400(struct mmc *mmc) { return 0; }
112013669fc5SYifeng Zhao static int mmc_select_hs400es(struct mmc *mmc) { return 0; }
1121699945cbSJason Zhu #endif
1122b673f29aSZiyuan Xu 
1123227f658eSZiyuan Xu static u32 mmc_select_card_type(struct mmc *mmc, u8 *ext_csd)
1124227f658eSZiyuan Xu {
1125227f658eSZiyuan Xu 	u8 card_type;
1126227f658eSZiyuan Xu 	u32 host_caps, avail_type = 0;
1127227f658eSZiyuan Xu 
1128227f658eSZiyuan Xu 	card_type = ext_csd[EXT_CSD_CARD_TYPE];
1129227f658eSZiyuan Xu 	host_caps = mmc->cfg->host_caps;
1130227f658eSZiyuan Xu 
1131227f658eSZiyuan Xu 	if ((host_caps & MMC_MODE_HS) &&
1132227f658eSZiyuan Xu 	    (card_type & EXT_CSD_CARD_TYPE_26))
1133227f658eSZiyuan Xu 		avail_type |= EXT_CSD_CARD_TYPE_26;
1134227f658eSZiyuan Xu 
1135227f658eSZiyuan Xu 	if ((host_caps & MMC_MODE_HS) &&
1136227f658eSZiyuan Xu 	    (card_type & EXT_CSD_CARD_TYPE_52))
1137227f658eSZiyuan Xu 		avail_type |= EXT_CSD_CARD_TYPE_52;
1138227f658eSZiyuan Xu 
1139227f658eSZiyuan Xu 	/*
1140227f658eSZiyuan Xu 	 * For the moment, u-boot doesn't support signal voltage
1141227f658eSZiyuan Xu 	 * switch, therefor we assume that host support ddr52
1142227f658eSZiyuan Xu 	 * at 1.8v or 3.3v I/O(1.2v I/O not supported, hs200 and
1143227f658eSZiyuan Xu 	 * hs400 are the same).
1144227f658eSZiyuan Xu 	 */
1145227f658eSZiyuan Xu 	if ((host_caps & MMC_MODE_DDR_52MHz) &&
1146227f658eSZiyuan Xu 	    (card_type & EXT_CSD_CARD_TYPE_DDR_1_8V))
1147227f658eSZiyuan Xu 		avail_type |= EXT_CSD_CARD_TYPE_DDR_1_8V;
1148227f658eSZiyuan Xu 
1149227f658eSZiyuan Xu 	if ((host_caps & MMC_MODE_HS200) &&
1150227f658eSZiyuan Xu 	    (card_type & EXT_CSD_CARD_TYPE_HS200_1_8V))
1151227f658eSZiyuan Xu 		avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V;
1152227f658eSZiyuan Xu 
1153227f658eSZiyuan Xu 	/*
1154227f658eSZiyuan Xu 	 * If host can support HS400, it means that host can also
1155227f658eSZiyuan Xu 	 * support HS200.
1156227f658eSZiyuan Xu 	 */
1157227f658eSZiyuan Xu 	if ((host_caps & MMC_MODE_HS400) &&
1158227f658eSZiyuan Xu 	    (host_caps & MMC_MODE_8BIT) &&
1159227f658eSZiyuan Xu 	    (card_type & EXT_CSD_CARD_TYPE_HS400_1_8V))
1160227f658eSZiyuan Xu 		avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V |
1161227f658eSZiyuan Xu 				EXT_CSD_CARD_TYPE_HS400_1_8V;
1162227f658eSZiyuan Xu 
1163227f658eSZiyuan Xu 	if ((host_caps & MMC_MODE_HS400ES) &&
1164227f658eSZiyuan Xu 	    (host_caps & MMC_MODE_8BIT) &&
1165227f658eSZiyuan Xu 	    ext_csd[EXT_CSD_STROBE_SUPPORT] &&
1166227f658eSZiyuan Xu 	    (avail_type & EXT_CSD_CARD_TYPE_HS400_1_8V))
1167227f658eSZiyuan Xu 		avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V |
1168227f658eSZiyuan Xu 				EXT_CSD_CARD_TYPE_HS400_1_8V |
1169227f658eSZiyuan Xu 				EXT_CSD_CARD_TYPE_HS400ES;
1170227f658eSZiyuan Xu 
1171227f658eSZiyuan Xu 	return avail_type;
1172227f658eSZiyuan Xu }
1173227f658eSZiyuan Xu 
117449dba033SZiyuan Xu static void mmc_set_bus_speed(struct mmc *mmc, u8 avail_type)
117549dba033SZiyuan Xu {
117649dba033SZiyuan Xu 	int clock = 0;
117749dba033SZiyuan Xu 
117849dba033SZiyuan Xu 	if (mmc_card_hs(mmc))
117949dba033SZiyuan Xu 		clock = (avail_type & EXT_CSD_CARD_TYPE_52) ?
118049dba033SZiyuan Xu 			MMC_HIGH_52_MAX_DTR : MMC_HIGH_26_MAX_DTR;
118149dba033SZiyuan Xu 	else if (mmc_card_hs200(mmc) ||
118249dba033SZiyuan Xu 		 mmc_card_hs400(mmc) ||
118349dba033SZiyuan Xu 		 mmc_card_hs400es(mmc))
118449dba033SZiyuan Xu 		clock = MMC_HS200_MAX_DTR;
118549dba033SZiyuan Xu 
118649dba033SZiyuan Xu 	mmc_set_clock(mmc, clock);
118749dba033SZiyuan Xu }
118849dba033SZiyuan Xu 
1189fdbb873eSKim Phillips static int mmc_change_freq(struct mmc *mmc)
1190272cc70bSAndy Fleming {
11918bfa195eSSimon Glass 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
1192227f658eSZiyuan Xu 	u32 avail_type;
1193272cc70bSAndy Fleming 	int err;
1194272cc70bSAndy Fleming 
1195fc5b32fbSAndrew Gabbasov 	mmc->card_caps = 0;
1196272cc70bSAndy Fleming 
1197d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc))
1198d52ebf10SThomas Chou 		return 0;
1199d52ebf10SThomas Chou 
1200272cc70bSAndy Fleming 	/* Only version 4 supports high-speed */
1201272cc70bSAndy Fleming 	if (mmc->version < MMC_VERSION_4)
1202272cc70bSAndy Fleming 		return 0;
1203272cc70bSAndy Fleming 
1204fc5b32fbSAndrew Gabbasov 	mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT;
1205fc5b32fbSAndrew Gabbasov 
1206272cc70bSAndy Fleming 	err = mmc_send_ext_csd(mmc, ext_csd);
1207272cc70bSAndy Fleming 
1208272cc70bSAndy Fleming 	if (err)
1209272cc70bSAndy Fleming 		return err;
1210272cc70bSAndy Fleming 
1211227f658eSZiyuan Xu 	avail_type = mmc_select_card_type(mmc, ext_csd);
1212272cc70bSAndy Fleming 
121313669fc5SYifeng Zhao 	if (avail_type & EXT_CSD_CARD_TYPE_HS400ES) {
121413669fc5SYifeng Zhao 		err = mmc_select_bus_width(mmc);
121513669fc5SYifeng Zhao 		if (err > 0 && mmc->bus_width == MMC_BUS_WIDTH_8BIT) {
121613669fc5SYifeng Zhao 			err = mmc_select_hs400es(mmc);
121713669fc5SYifeng Zhao 			mmc_set_bus_speed(mmc, avail_type);
121813669fc5SYifeng Zhao 			if (!err)
121913669fc5SYifeng Zhao 				return err;
122013669fc5SYifeng Zhao 		}
122113669fc5SYifeng Zhao 	}
122213669fc5SYifeng Zhao 
122349dba033SZiyuan Xu 	if (avail_type & EXT_CSD_CARD_TYPE_HS200)
122449dba033SZiyuan Xu 		err = mmc_select_hs200(mmc);
12251f250d0aSJason Zhu 	else if (avail_type & EXT_CSD_CARD_TYPE_HS)
1226e61cd3d7SZiyuan Xu 		err = mmc_select_hs(mmc);
1227227f658eSZiyuan Xu 	else
1228227f658eSZiyuan Xu 		err = -EINVAL;
1229272cc70bSAndy Fleming 
1230272cc70bSAndy Fleming 	if (err)
1231a5e27b41SHeiko Schocher 		return err;
1232272cc70bSAndy Fleming 
123349dba033SZiyuan Xu 	mmc_set_bus_speed(mmc, avail_type);
1234272cc70bSAndy Fleming 
1235b673f29aSZiyuan Xu 	if (mmc_card_hs200(mmc)) {
123649dba033SZiyuan Xu 		err = mmc_hs200_tuning(mmc);
1237b673f29aSZiyuan Xu 		if (avail_type & EXT_CSD_CARD_TYPE_HS400 &&
1238b673f29aSZiyuan Xu 		    mmc->bus_width == MMC_BUS_WIDTH_8BIT) {
1239b673f29aSZiyuan Xu 			err = mmc_select_hs400(mmc);
1240b673f29aSZiyuan Xu 			mmc_set_bus_speed(mmc, avail_type);
1241b673f29aSZiyuan Xu 		}
1242b673f29aSZiyuan Xu 	} else if (!mmc_card_hs400es(mmc)) {
124349dba033SZiyuan Xu 		err = mmc_select_bus_width(mmc) > 0 ? 0 : err;
12445545757fSZiyuan Xu 		if (!err && avail_type & EXT_CSD_CARD_TYPE_DDR_52)
12455545757fSZiyuan Xu 			err = mmc_select_hs_ddr(mmc);
12465545757fSZiyuan Xu 	}
124749dba033SZiyuan Xu 
1248272cc70bSAndy Fleming 	return err;
1249272cc70bSAndy Fleming }
1250272cc70bSAndy Fleming 
1251f866a46dSStephen Warren static int mmc_set_capacity(struct mmc *mmc, int part_num)
1252f866a46dSStephen Warren {
1253f866a46dSStephen Warren 	switch (part_num) {
1254f866a46dSStephen Warren 	case 0:
1255f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_user;
1256f866a46dSStephen Warren 		break;
1257f866a46dSStephen Warren 	case 1:
1258f866a46dSStephen Warren 	case 2:
1259f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_boot;
1260f866a46dSStephen Warren 		break;
1261f866a46dSStephen Warren 	case 3:
1262f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_rpmb;
1263f866a46dSStephen Warren 		break;
1264f866a46dSStephen Warren 	case 4:
1265f866a46dSStephen Warren 	case 5:
1266f866a46dSStephen Warren 	case 6:
1267f866a46dSStephen Warren 	case 7:
1268f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_gp[part_num - 4];
1269f866a46dSStephen Warren 		break;
1270f866a46dSStephen Warren 	default:
1271f866a46dSStephen Warren 		return -1;
1272f866a46dSStephen Warren 	}
1273f866a46dSStephen Warren 
1274c40fdca6SSimon Glass 	mmc_get_blk_desc(mmc)->lba = lldiv(mmc->capacity, mmc->read_bl_len);
1275f866a46dSStephen Warren 
1276f866a46dSStephen Warren 	return 0;
1277f866a46dSStephen Warren }
1278f866a46dSStephen Warren 
12797dba0b93SSimon Glass int mmc_switch_part(struct mmc *mmc, unsigned int part_num)
1280bc897b1dSLei Wen {
1281f866a46dSStephen Warren 	int ret;
1282bc897b1dSLei Wen 
1283f866a46dSStephen Warren 	ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
1284bc897b1dSLei Wen 			 (mmc->part_config & ~PART_ACCESS_MASK)
1285bc897b1dSLei Wen 			 | (part_num & PART_ACCESS_MASK));
1286f866a46dSStephen Warren 
12876dc93e70SPeter Bigot 	/*
12886dc93e70SPeter Bigot 	 * Set the capacity if the switch succeeded or was intended
12896dc93e70SPeter Bigot 	 * to return to representing the raw device.
12906dc93e70SPeter Bigot 	 */
1291873cc1d7SStephen Warren 	if ((ret == 0) || ((ret == -ENODEV) && (part_num == 0))) {
12926dc93e70SPeter Bigot 		ret = mmc_set_capacity(mmc, part_num);
1293fdbb139fSSimon Glass 		mmc_get_blk_desc(mmc)->hwpart = part_num;
1294873cc1d7SStephen Warren 	}
12956dc93e70SPeter Bigot 
12966dc93e70SPeter Bigot 	return ret;
1297bc897b1dSLei Wen }
1298bc897b1dSLei Wen 
1299ac9da0e0SDiego Santa Cruz int mmc_hwpart_config(struct mmc *mmc,
1300ac9da0e0SDiego Santa Cruz 		      const struct mmc_hwpart_conf *conf,
1301ac9da0e0SDiego Santa Cruz 		      enum mmc_hwpart_conf_mode mode)
1302ac9da0e0SDiego Santa Cruz {
1303ac9da0e0SDiego Santa Cruz 	u8 part_attrs = 0;
1304ac9da0e0SDiego Santa Cruz 	u32 enh_size_mult;
1305ac9da0e0SDiego Santa Cruz 	u32 enh_start_addr;
1306ac9da0e0SDiego Santa Cruz 	u32 gp_size_mult[4];
1307ac9da0e0SDiego Santa Cruz 	u32 max_enh_size_mult;
1308ac9da0e0SDiego Santa Cruz 	u32 tot_enh_size_mult = 0;
13098dda5b0eSDiego Santa Cruz 	u8 wr_rel_set;
1310ac9da0e0SDiego Santa Cruz 	int i, pidx, err;
1311ac9da0e0SDiego Santa Cruz 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
1312ac9da0e0SDiego Santa Cruz 
1313ac9da0e0SDiego Santa Cruz 	if (mode < MMC_HWPART_CONF_CHECK || mode > MMC_HWPART_CONF_COMPLETE)
1314ac9da0e0SDiego Santa Cruz 		return -EINVAL;
1315ac9da0e0SDiego Santa Cruz 
1316ac9da0e0SDiego Santa Cruz 	if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4_41)) {
1317ac9da0e0SDiego Santa Cruz 		printf("eMMC >= 4.4 required for enhanced user data area\n");
1318ac9da0e0SDiego Santa Cruz 		return -EMEDIUMTYPE;
1319ac9da0e0SDiego Santa Cruz 	}
1320ac9da0e0SDiego Santa Cruz 
1321ac9da0e0SDiego Santa Cruz 	if (!(mmc->part_support & PART_SUPPORT)) {
1322ac9da0e0SDiego Santa Cruz 		printf("Card does not support partitioning\n");
1323ac9da0e0SDiego Santa Cruz 		return -EMEDIUMTYPE;
1324ac9da0e0SDiego Santa Cruz 	}
1325ac9da0e0SDiego Santa Cruz 
1326ac9da0e0SDiego Santa Cruz 	if (!mmc->hc_wp_grp_size) {
1327ac9da0e0SDiego Santa Cruz 		printf("Card does not define HC WP group size\n");
1328ac9da0e0SDiego Santa Cruz 		return -EMEDIUMTYPE;
1329ac9da0e0SDiego Santa Cruz 	}
1330ac9da0e0SDiego Santa Cruz 
1331ac9da0e0SDiego Santa Cruz 	/* check partition alignment and total enhanced size */
1332ac9da0e0SDiego Santa Cruz 	if (conf->user.enh_size) {
1333ac9da0e0SDiego Santa Cruz 		if (conf->user.enh_size % mmc->hc_wp_grp_size ||
1334ac9da0e0SDiego Santa Cruz 		    conf->user.enh_start % mmc->hc_wp_grp_size) {
1335ac9da0e0SDiego Santa Cruz 			printf("User data enhanced area not HC WP group "
1336ac9da0e0SDiego Santa Cruz 			       "size aligned\n");
1337ac9da0e0SDiego Santa Cruz 			return -EINVAL;
1338ac9da0e0SDiego Santa Cruz 		}
1339ac9da0e0SDiego Santa Cruz 		part_attrs |= EXT_CSD_ENH_USR;
1340ac9da0e0SDiego Santa Cruz 		enh_size_mult = conf->user.enh_size / mmc->hc_wp_grp_size;
1341ac9da0e0SDiego Santa Cruz 		if (mmc->high_capacity) {
1342ac9da0e0SDiego Santa Cruz 			enh_start_addr = conf->user.enh_start;
1343ac9da0e0SDiego Santa Cruz 		} else {
1344ac9da0e0SDiego Santa Cruz 			enh_start_addr = (conf->user.enh_start << 9);
1345ac9da0e0SDiego Santa Cruz 		}
1346ac9da0e0SDiego Santa Cruz 	} else {
1347ac9da0e0SDiego Santa Cruz 		enh_size_mult = 0;
1348ac9da0e0SDiego Santa Cruz 		enh_start_addr = 0;
1349ac9da0e0SDiego Santa Cruz 	}
1350ac9da0e0SDiego Santa Cruz 	tot_enh_size_mult += enh_size_mult;
1351ac9da0e0SDiego Santa Cruz 
1352ac9da0e0SDiego Santa Cruz 	for (pidx = 0; pidx < 4; pidx++) {
1353ac9da0e0SDiego Santa Cruz 		if (conf->gp_part[pidx].size % mmc->hc_wp_grp_size) {
1354ac9da0e0SDiego Santa Cruz 			printf("GP%i partition not HC WP group size "
1355ac9da0e0SDiego Santa Cruz 			       "aligned\n", pidx+1);
1356ac9da0e0SDiego Santa Cruz 			return -EINVAL;
1357ac9da0e0SDiego Santa Cruz 		}
1358ac9da0e0SDiego Santa Cruz 		gp_size_mult[pidx] = conf->gp_part[pidx].size / mmc->hc_wp_grp_size;
1359ac9da0e0SDiego Santa Cruz 		if (conf->gp_part[pidx].size && conf->gp_part[pidx].enhanced) {
1360ac9da0e0SDiego Santa Cruz 			part_attrs |= EXT_CSD_ENH_GP(pidx);
1361ac9da0e0SDiego Santa Cruz 			tot_enh_size_mult += gp_size_mult[pidx];
1362ac9da0e0SDiego Santa Cruz 		}
1363ac9da0e0SDiego Santa Cruz 	}
1364ac9da0e0SDiego Santa Cruz 
1365ac9da0e0SDiego Santa Cruz 	if (part_attrs && ! (mmc->part_support & ENHNCD_SUPPORT)) {
1366ac9da0e0SDiego Santa Cruz 		printf("Card does not support enhanced attribute\n");
1367ac9da0e0SDiego Santa Cruz 		return -EMEDIUMTYPE;
1368ac9da0e0SDiego Santa Cruz 	}
1369ac9da0e0SDiego Santa Cruz 
1370ac9da0e0SDiego Santa Cruz 	err = mmc_send_ext_csd(mmc, ext_csd);
1371ac9da0e0SDiego Santa Cruz 	if (err)
1372ac9da0e0SDiego Santa Cruz 		return err;
1373ac9da0e0SDiego Santa Cruz 
1374ac9da0e0SDiego Santa Cruz 	max_enh_size_mult =
1375ac9da0e0SDiego Santa Cruz 		(ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+2] << 16) +
1376ac9da0e0SDiego Santa Cruz 		(ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+1] << 8) +
1377ac9da0e0SDiego Santa Cruz 		ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT];
1378ac9da0e0SDiego Santa Cruz 	if (tot_enh_size_mult > max_enh_size_mult) {
1379ac9da0e0SDiego Santa Cruz 		printf("Total enhanced size exceeds maximum (%u > %u)\n",
1380ac9da0e0SDiego Santa Cruz 		       tot_enh_size_mult, max_enh_size_mult);
1381ac9da0e0SDiego Santa Cruz 		return -EMEDIUMTYPE;
1382ac9da0e0SDiego Santa Cruz 	}
1383ac9da0e0SDiego Santa Cruz 
13848dda5b0eSDiego Santa Cruz 	/* The default value of EXT_CSD_WR_REL_SET is device
13858dda5b0eSDiego Santa Cruz 	 * dependent, the values can only be changed if the
13868dda5b0eSDiego Santa Cruz 	 * EXT_CSD_HS_CTRL_REL bit is set. The values can be
13878dda5b0eSDiego Santa Cruz 	 * changed only once and before partitioning is completed. */
13888dda5b0eSDiego Santa Cruz 	wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET];
13898dda5b0eSDiego Santa Cruz 	if (conf->user.wr_rel_change) {
13908dda5b0eSDiego Santa Cruz 		if (conf->user.wr_rel_set)
13918dda5b0eSDiego Santa Cruz 			wr_rel_set |= EXT_CSD_WR_DATA_REL_USR;
13928dda5b0eSDiego Santa Cruz 		else
13938dda5b0eSDiego Santa Cruz 			wr_rel_set &= ~EXT_CSD_WR_DATA_REL_USR;
13948dda5b0eSDiego Santa Cruz 	}
13958dda5b0eSDiego Santa Cruz 	for (pidx = 0; pidx < 4; pidx++) {
13968dda5b0eSDiego Santa Cruz 		if (conf->gp_part[pidx].wr_rel_change) {
13978dda5b0eSDiego Santa Cruz 			if (conf->gp_part[pidx].wr_rel_set)
13988dda5b0eSDiego Santa Cruz 				wr_rel_set |= EXT_CSD_WR_DATA_REL_GP(pidx);
13998dda5b0eSDiego Santa Cruz 			else
14008dda5b0eSDiego Santa Cruz 				wr_rel_set &= ~EXT_CSD_WR_DATA_REL_GP(pidx);
14018dda5b0eSDiego Santa Cruz 		}
14028dda5b0eSDiego Santa Cruz 	}
14038dda5b0eSDiego Santa Cruz 
14048dda5b0eSDiego Santa Cruz 	if (wr_rel_set != ext_csd[EXT_CSD_WR_REL_SET] &&
14058dda5b0eSDiego Santa Cruz 	    !(ext_csd[EXT_CSD_WR_REL_PARAM] & EXT_CSD_HS_CTRL_REL)) {
14068dda5b0eSDiego Santa Cruz 		puts("Card does not support host controlled partition write "
14078dda5b0eSDiego Santa Cruz 		     "reliability settings\n");
14088dda5b0eSDiego Santa Cruz 		return -EMEDIUMTYPE;
14098dda5b0eSDiego Santa Cruz 	}
14108dda5b0eSDiego Santa Cruz 
1411ac9da0e0SDiego Santa Cruz 	if (ext_csd[EXT_CSD_PARTITION_SETTING] &
1412ac9da0e0SDiego Santa Cruz 	    EXT_CSD_PARTITION_SETTING_COMPLETED) {
1413ac9da0e0SDiego Santa Cruz 		printf("Card already partitioned\n");
1414ac9da0e0SDiego Santa Cruz 		return -EPERM;
1415ac9da0e0SDiego Santa Cruz 	}
1416ac9da0e0SDiego Santa Cruz 
1417ac9da0e0SDiego Santa Cruz 	if (mode == MMC_HWPART_CONF_CHECK)
1418ac9da0e0SDiego Santa Cruz 		return 0;
1419ac9da0e0SDiego Santa Cruz 
1420ac9da0e0SDiego Santa Cruz 	/* Partitioning requires high-capacity size definitions */
1421ac9da0e0SDiego Santa Cruz 	if (!(ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01)) {
1422ac9da0e0SDiego Santa Cruz 		err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1423ac9da0e0SDiego Santa Cruz 				 EXT_CSD_ERASE_GROUP_DEF, 1);
1424ac9da0e0SDiego Santa Cruz 
1425ac9da0e0SDiego Santa Cruz 		if (err)
1426ac9da0e0SDiego Santa Cruz 			return err;
1427ac9da0e0SDiego Santa Cruz 
1428ac9da0e0SDiego Santa Cruz 		ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1;
1429ac9da0e0SDiego Santa Cruz 
1430ac9da0e0SDiego Santa Cruz 		/* update erase group size to be high-capacity */
1431ac9da0e0SDiego Santa Cruz 		mmc->erase_grp_size =
1432ac9da0e0SDiego Santa Cruz 			ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024;
1433ac9da0e0SDiego Santa Cruz 
1434ac9da0e0SDiego Santa Cruz 	}
1435ac9da0e0SDiego Santa Cruz 
1436ac9da0e0SDiego Santa Cruz 	/* all OK, write the configuration */
1437ac9da0e0SDiego Santa Cruz 	for (i = 0; i < 4; i++) {
1438ac9da0e0SDiego Santa Cruz 		err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1439ac9da0e0SDiego Santa Cruz 				 EXT_CSD_ENH_START_ADDR+i,
1440ac9da0e0SDiego Santa Cruz 				 (enh_start_addr >> (i*8)) & 0xFF);
1441ac9da0e0SDiego Santa Cruz 		if (err)
1442ac9da0e0SDiego Santa Cruz 			return err;
1443ac9da0e0SDiego Santa Cruz 	}
1444ac9da0e0SDiego Santa Cruz 	for (i = 0; i < 3; i++) {
1445ac9da0e0SDiego Santa Cruz 		err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1446ac9da0e0SDiego Santa Cruz 				 EXT_CSD_ENH_SIZE_MULT+i,
1447ac9da0e0SDiego Santa Cruz 				 (enh_size_mult >> (i*8)) & 0xFF);
1448ac9da0e0SDiego Santa Cruz 		if (err)
1449ac9da0e0SDiego Santa Cruz 			return err;
1450ac9da0e0SDiego Santa Cruz 	}
1451ac9da0e0SDiego Santa Cruz 	for (pidx = 0; pidx < 4; pidx++) {
1452ac9da0e0SDiego Santa Cruz 		for (i = 0; i < 3; i++) {
1453ac9da0e0SDiego Santa Cruz 			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1454ac9da0e0SDiego Santa Cruz 					 EXT_CSD_GP_SIZE_MULT+pidx*3+i,
1455ac9da0e0SDiego Santa Cruz 					 (gp_size_mult[pidx] >> (i*8)) & 0xFF);
1456ac9da0e0SDiego Santa Cruz 			if (err)
1457ac9da0e0SDiego Santa Cruz 				return err;
1458ac9da0e0SDiego Santa Cruz 		}
1459ac9da0e0SDiego Santa Cruz 	}
1460ac9da0e0SDiego Santa Cruz 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1461ac9da0e0SDiego Santa Cruz 			 EXT_CSD_PARTITIONS_ATTRIBUTE, part_attrs);
1462ac9da0e0SDiego Santa Cruz 	if (err)
1463ac9da0e0SDiego Santa Cruz 		return err;
1464ac9da0e0SDiego Santa Cruz 
1465ac9da0e0SDiego Santa Cruz 	if (mode == MMC_HWPART_CONF_SET)
1466ac9da0e0SDiego Santa Cruz 		return 0;
1467ac9da0e0SDiego Santa Cruz 
14688dda5b0eSDiego Santa Cruz 	/* The WR_REL_SET is a write-once register but shall be
14698dda5b0eSDiego Santa Cruz 	 * written before setting PART_SETTING_COMPLETED. As it is
14708dda5b0eSDiego Santa Cruz 	 * write-once we can only write it when completing the
14718dda5b0eSDiego Santa Cruz 	 * partitioning. */
14728dda5b0eSDiego Santa Cruz 	if (wr_rel_set != ext_csd[EXT_CSD_WR_REL_SET]) {
14738dda5b0eSDiego Santa Cruz 		err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
14748dda5b0eSDiego Santa Cruz 				 EXT_CSD_WR_REL_SET, wr_rel_set);
14758dda5b0eSDiego Santa Cruz 		if (err)
14768dda5b0eSDiego Santa Cruz 			return err;
14778dda5b0eSDiego Santa Cruz 	}
14788dda5b0eSDiego Santa Cruz 
1479ac9da0e0SDiego Santa Cruz 	/* Setting PART_SETTING_COMPLETED confirms the partition
1480ac9da0e0SDiego Santa Cruz 	 * configuration but it only becomes effective after power
1481ac9da0e0SDiego Santa Cruz 	 * cycle, so we do not adjust the partition related settings
1482ac9da0e0SDiego Santa Cruz 	 * in the mmc struct. */
1483ac9da0e0SDiego Santa Cruz 
1484ac9da0e0SDiego Santa Cruz 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
1485ac9da0e0SDiego Santa Cruz 			 EXT_CSD_PARTITION_SETTING,
1486ac9da0e0SDiego Santa Cruz 			 EXT_CSD_PARTITION_SETTING_COMPLETED);
1487ac9da0e0SDiego Santa Cruz 	if (err)
1488ac9da0e0SDiego Santa Cruz 		return err;
1489ac9da0e0SDiego Santa Cruz 
1490ac9da0e0SDiego Santa Cruz 	return 0;
1491ac9da0e0SDiego Santa Cruz }
1492ac9da0e0SDiego Santa Cruz 
1493e7881d85SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC)
149448972d90SThierry Reding int mmc_getcd(struct mmc *mmc)
149548972d90SThierry Reding {
149648972d90SThierry Reding 	int cd;
149748972d90SThierry Reding 
149848972d90SThierry Reding 	cd = board_mmc_getcd(mmc);
149948972d90SThierry Reding 
1500d4e1da4eSPeter Korsgaard 	if (cd < 0) {
150193bfd616SPantelis Antoniou 		if (mmc->cfg->ops->getcd)
150293bfd616SPantelis Antoniou 			cd = mmc->cfg->ops->getcd(mmc);
1503d4e1da4eSPeter Korsgaard 		else
1504d4e1da4eSPeter Korsgaard 			cd = 1;
1505d4e1da4eSPeter Korsgaard 	}
150648972d90SThierry Reding 
150748972d90SThierry Reding 	return cd;
150848972d90SThierry Reding }
15098ca51e51SSimon Glass #endif
151048972d90SThierry Reding 
1511fdbb873eSKim Phillips static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)
1512272cc70bSAndy Fleming {
1513272cc70bSAndy Fleming 	struct mmc_cmd cmd;
1514272cc70bSAndy Fleming 	struct mmc_data data;
1515272cc70bSAndy Fleming 
1516272cc70bSAndy Fleming 	/* Switch the frequency */
1517272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_SWITCH_FUNC;
1518272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
1519272cc70bSAndy Fleming 	cmd.cmdarg = (mode << 31) | 0xffffff;
1520272cc70bSAndy Fleming 	cmd.cmdarg &= ~(0xf << (group * 4));
1521272cc70bSAndy Fleming 	cmd.cmdarg |= value << (group * 4);
1522272cc70bSAndy Fleming 
1523272cc70bSAndy Fleming 	data.dest = (char *)resp;
1524272cc70bSAndy Fleming 	data.blocksize = 64;
1525272cc70bSAndy Fleming 	data.blocks = 1;
1526272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
1527272cc70bSAndy Fleming 
1528272cc70bSAndy Fleming 	return mmc_send_cmd(mmc, &cmd, &data);
1529272cc70bSAndy Fleming }
1530272cc70bSAndy Fleming 
1531272cc70bSAndy Fleming 
1532fdbb873eSKim Phillips static int sd_change_freq(struct mmc *mmc)
1533272cc70bSAndy Fleming {
1534272cc70bSAndy Fleming 	int err;
1535272cc70bSAndy Fleming 	struct mmc_cmd cmd;
1536f781dd38SAnton staaf 	ALLOC_CACHE_ALIGN_BUFFER(uint, scr, 2);
1537f781dd38SAnton staaf 	ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16);
1538272cc70bSAndy Fleming 	struct mmc_data data;
1539272cc70bSAndy Fleming 	int timeout;
1540272cc70bSAndy Fleming 
1541272cc70bSAndy Fleming 	mmc->card_caps = 0;
1542272cc70bSAndy Fleming 
1543d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc))
1544d52ebf10SThomas Chou 		return 0;
1545d52ebf10SThomas Chou 
1546272cc70bSAndy Fleming 	/* Read the SCR to find out if this card supports higher speeds */
1547272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_APP_CMD;
1548272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
1549272cc70bSAndy Fleming 	cmd.cmdarg = mmc->rca << 16;
1550272cc70bSAndy Fleming 
1551272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
1552272cc70bSAndy Fleming 
1553272cc70bSAndy Fleming 	if (err)
1554272cc70bSAndy Fleming 		return err;
1555272cc70bSAndy Fleming 
1556272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_APP_SEND_SCR;
1557272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
1558272cc70bSAndy Fleming 	cmd.cmdarg = 0;
1559272cc70bSAndy Fleming 
1560272cc70bSAndy Fleming 	timeout = 3;
1561272cc70bSAndy Fleming 
1562272cc70bSAndy Fleming retry_scr:
1563f781dd38SAnton staaf 	data.dest = (char *)scr;
1564272cc70bSAndy Fleming 	data.blocksize = 8;
1565272cc70bSAndy Fleming 	data.blocks = 1;
1566272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
1567272cc70bSAndy Fleming 
1568272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, &data);
1569272cc70bSAndy Fleming 
1570272cc70bSAndy Fleming 	if (err) {
1571272cc70bSAndy Fleming 		if (timeout--)
1572272cc70bSAndy Fleming 			goto retry_scr;
1573272cc70bSAndy Fleming 
1574272cc70bSAndy Fleming 		return err;
1575272cc70bSAndy Fleming 	}
1576272cc70bSAndy Fleming 
15774e3d89baSYauhen Kharuzhy 	mmc->scr[0] = __be32_to_cpu(scr[0]);
15784e3d89baSYauhen Kharuzhy 	mmc->scr[1] = __be32_to_cpu(scr[1]);
1579272cc70bSAndy Fleming 
1580272cc70bSAndy Fleming 	switch ((mmc->scr[0] >> 24) & 0xf) {
1581272cc70bSAndy Fleming 	case 0:
1582272cc70bSAndy Fleming 		mmc->version = SD_VERSION_1_0;
1583272cc70bSAndy Fleming 		break;
1584272cc70bSAndy Fleming 	case 1:
1585272cc70bSAndy Fleming 		mmc->version = SD_VERSION_1_10;
1586272cc70bSAndy Fleming 		break;
1587272cc70bSAndy Fleming 	case 2:
1588272cc70bSAndy Fleming 		mmc->version = SD_VERSION_2;
15891741c64dSJaehoon Chung 		if ((mmc->scr[0] >> 15) & 0x1)
15901741c64dSJaehoon Chung 			mmc->version = SD_VERSION_3;
1591272cc70bSAndy Fleming 		break;
1592272cc70bSAndy Fleming 	default:
1593272cc70bSAndy Fleming 		mmc->version = SD_VERSION_1_0;
1594272cc70bSAndy Fleming 		break;
1595272cc70bSAndy Fleming 	}
1596272cc70bSAndy Fleming 
1597b44c7083SAlagu Sankar 	if (mmc->scr[0] & SD_DATA_4BIT)
1598b44c7083SAlagu Sankar 		mmc->card_caps |= MMC_MODE_4BIT;
1599b44c7083SAlagu Sankar 
1600272cc70bSAndy Fleming 	/* Version 1.0 doesn't support switching */
1601272cc70bSAndy Fleming 	if (mmc->version == SD_VERSION_1_0)
1602272cc70bSAndy Fleming 		return 0;
1603272cc70bSAndy Fleming 
1604272cc70bSAndy Fleming 	timeout = 4;
1605272cc70bSAndy Fleming 	while (timeout--) {
1606272cc70bSAndy Fleming 		err = sd_switch(mmc, SD_SWITCH_CHECK, 0, 1,
1607f781dd38SAnton staaf 				(u8 *)switch_status);
1608272cc70bSAndy Fleming 
1609272cc70bSAndy Fleming 		if (err)
1610272cc70bSAndy Fleming 			return err;
1611272cc70bSAndy Fleming 
1612272cc70bSAndy Fleming 		/* The high-speed function is busy.  Try again */
16134e3d89baSYauhen Kharuzhy 		if (!(__be32_to_cpu(switch_status[7]) & SD_HIGHSPEED_BUSY))
1614272cc70bSAndy Fleming 			break;
1615272cc70bSAndy Fleming 	}
1616272cc70bSAndy Fleming 
1617272cc70bSAndy Fleming 	/* If high-speed isn't supported, we return */
16184e3d89baSYauhen Kharuzhy 	if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED))
1619272cc70bSAndy Fleming 		return 0;
1620272cc70bSAndy Fleming 
16212c3fbf4cSMacpaul Lin 	/*
16222c3fbf4cSMacpaul Lin 	 * If the host doesn't support SD_HIGHSPEED, do not switch card to
16232c3fbf4cSMacpaul Lin 	 * HIGHSPEED mode even if the card support SD_HIGHSPPED.
16242c3fbf4cSMacpaul Lin 	 * This can avoid furthur problem when the card runs in different
16252c3fbf4cSMacpaul Lin 	 * mode between the host.
16262c3fbf4cSMacpaul Lin 	 */
162793bfd616SPantelis Antoniou 	if (!((mmc->cfg->host_caps & MMC_MODE_HS_52MHz) &&
162893bfd616SPantelis Antoniou 		(mmc->cfg->host_caps & MMC_MODE_HS)))
16292c3fbf4cSMacpaul Lin 		return 0;
16302c3fbf4cSMacpaul Lin 
1631f781dd38SAnton staaf 	err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status);
1632272cc70bSAndy Fleming 
1633272cc70bSAndy Fleming 	if (err)
1634272cc70bSAndy Fleming 		return err;
1635272cc70bSAndy Fleming 
16364e3d89baSYauhen Kharuzhy 	if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) == 0x01000000)
1637272cc70bSAndy Fleming 		mmc->card_caps |= MMC_MODE_HS;
1638272cc70bSAndy Fleming 
1639272cc70bSAndy Fleming 	return 0;
1640272cc70bSAndy Fleming }
1641272cc70bSAndy Fleming 
16423697e599SPeng Fan static int sd_read_ssr(struct mmc *mmc)
16433697e599SPeng Fan {
16443697e599SPeng Fan 	int err, i;
16453697e599SPeng Fan 	struct mmc_cmd cmd;
16463697e599SPeng Fan 	ALLOC_CACHE_ALIGN_BUFFER(uint, ssr, 16);
16473697e599SPeng Fan 	struct mmc_data data;
16483697e599SPeng Fan 	int timeout = 3;
16493697e599SPeng Fan 	unsigned int au, eo, et, es;
16503697e599SPeng Fan 
16513697e599SPeng Fan 	cmd.cmdidx = MMC_CMD_APP_CMD;
16523697e599SPeng Fan 	cmd.resp_type = MMC_RSP_R1;
16533697e599SPeng Fan 	cmd.cmdarg = mmc->rca << 16;
16543697e599SPeng Fan 
16553697e599SPeng Fan 	err = mmc_send_cmd(mmc, &cmd, NULL);
16563697e599SPeng Fan 	if (err)
16573697e599SPeng Fan 		return err;
16583697e599SPeng Fan 
16593697e599SPeng Fan 	cmd.cmdidx = SD_CMD_APP_SD_STATUS;
16603697e599SPeng Fan 	cmd.resp_type = MMC_RSP_R1;
16613697e599SPeng Fan 	cmd.cmdarg = 0;
16623697e599SPeng Fan 
16633697e599SPeng Fan retry_ssr:
16643697e599SPeng Fan 	data.dest = (char *)ssr;
16653697e599SPeng Fan 	data.blocksize = 64;
16663697e599SPeng Fan 	data.blocks = 1;
16673697e599SPeng Fan 	data.flags = MMC_DATA_READ;
16683697e599SPeng Fan 
16693697e599SPeng Fan 	err = mmc_send_cmd(mmc, &cmd, &data);
16703697e599SPeng Fan 	if (err) {
16713697e599SPeng Fan 		if (timeout--)
16723697e599SPeng Fan 			goto retry_ssr;
16733697e599SPeng Fan 
16743697e599SPeng Fan 		return err;
16753697e599SPeng Fan 	}
16763697e599SPeng Fan 
16773697e599SPeng Fan 	for (i = 0; i < 16; i++)
16783697e599SPeng Fan 		ssr[i] = be32_to_cpu(ssr[i]);
16793697e599SPeng Fan 
16803697e599SPeng Fan 	au = (ssr[2] >> 12) & 0xF;
16813697e599SPeng Fan 	if ((au <= 9) || (mmc->version == SD_VERSION_3)) {
16823697e599SPeng Fan 		mmc->ssr.au = sd_au_size[au];
16833697e599SPeng Fan 		es = (ssr[3] >> 24) & 0xFF;
16843697e599SPeng Fan 		es |= (ssr[2] & 0xFF) << 8;
16853697e599SPeng Fan 		et = (ssr[3] >> 18) & 0x3F;
16863697e599SPeng Fan 		if (es && et) {
16873697e599SPeng Fan 			eo = (ssr[3] >> 16) & 0x3;
16883697e599SPeng Fan 			mmc->ssr.erase_timeout = (et * 1000) / es;
16893697e599SPeng Fan 			mmc->ssr.erase_offset = eo * 1000;
16903697e599SPeng Fan 		}
16913697e599SPeng Fan 	} else {
16923697e599SPeng Fan 		debug("Invalid Allocation Unit Size.\n");
16933697e599SPeng Fan 	}
16943697e599SPeng Fan 
16953697e599SPeng Fan 	return 0;
16963697e599SPeng Fan }
16973697e599SPeng Fan 
1698272cc70bSAndy Fleming /* frequency bases */
1699272cc70bSAndy Fleming /* divided by 10 to be nice to platforms without floating point */
17005f837c2cSMike Frysinger static const int fbase[] = {
1701272cc70bSAndy Fleming 	10000,
1702272cc70bSAndy Fleming 	100000,
1703272cc70bSAndy Fleming 	1000000,
1704272cc70bSAndy Fleming 	10000000,
1705272cc70bSAndy Fleming };
1706272cc70bSAndy Fleming 
1707272cc70bSAndy Fleming /* Multiplier values for TRAN_SPEED.  Multiplied by 10 to be nice
1708272cc70bSAndy Fleming  * to platforms without floating point.
1709272cc70bSAndy Fleming  */
171061fe076fSSimon Glass static const u8 multipliers[] = {
1711272cc70bSAndy Fleming 	0,	/* reserved */
1712272cc70bSAndy Fleming 	10,
1713272cc70bSAndy Fleming 	12,
1714272cc70bSAndy Fleming 	13,
1715272cc70bSAndy Fleming 	15,
1716272cc70bSAndy Fleming 	20,
1717272cc70bSAndy Fleming 	25,
1718272cc70bSAndy Fleming 	30,
1719272cc70bSAndy Fleming 	35,
1720272cc70bSAndy Fleming 	40,
1721272cc70bSAndy Fleming 	45,
1722272cc70bSAndy Fleming 	50,
1723272cc70bSAndy Fleming 	55,
1724272cc70bSAndy Fleming 	60,
1725272cc70bSAndy Fleming 	70,
1726272cc70bSAndy Fleming 	80,
1727272cc70bSAndy Fleming };
1728272cc70bSAndy Fleming 
1729e7881d85SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC)
1730fdbb873eSKim Phillips static void mmc_set_ios(struct mmc *mmc)
1731272cc70bSAndy Fleming {
173293bfd616SPantelis Antoniou 	if (mmc->cfg->ops->set_ios)
173393bfd616SPantelis Antoniou 		mmc->cfg->ops->set_ios(mmc);
1734272cc70bSAndy Fleming }
1735ad77484aSZiyuan Xu 
1736ad77484aSZiyuan Xu static bool mmc_card_busy(struct mmc *mmc)
1737ad77484aSZiyuan Xu {
1738ad77484aSZiyuan Xu 	if (!mmc->cfg->ops->card_busy)
1739ad77484aSZiyuan Xu 		return -ENOSYS;
1740ad77484aSZiyuan Xu 
1741ad77484aSZiyuan Xu 	return mmc->cfg->ops->card_busy(mmc);
1742ad77484aSZiyuan Xu }
1743ad77484aSZiyuan Xu 
1744ad77484aSZiyuan Xu static bool mmc_can_card_busy(struct mmc *)
1745ad77484aSZiyuan Xu {
1746ad77484aSZiyuan Xu 	return !!mmc->cfg->ops->card_busy;
1747ad77484aSZiyuan Xu }
17488ca51e51SSimon Glass #endif
1749272cc70bSAndy Fleming 
1750fdbb873eSKim Phillips static int mmc_startup(struct mmc *mmc)
1751272cc70bSAndy Fleming {
1752f866a46dSStephen Warren 	int err, i;
17533e3ff0acSZiyuan Xu 	uint mult, freq, tran_speed;
1754639b7827SYoshihiro Shimoda 	u64 cmult, csize, capacity;
1755272cc70bSAndy Fleming 	struct mmc_cmd cmd;
17568bfa195eSSimon Glass 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
17570c453bb7SDiego Santa Cruz 	bool has_parts = false;
17588a0cf490SDiego Santa Cruz 	bool part_completed;
1759c40fdca6SSimon Glass 	struct blk_desc *bdesc;
1760272cc70bSAndy Fleming 
1761d52ebf10SThomas Chou #ifdef CONFIG_MMC_SPI_CRC_ON
1762d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */
1763d52ebf10SThomas Chou 		cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF;
1764d52ebf10SThomas Chou 		cmd.resp_type = MMC_RSP_R1;
1765d52ebf10SThomas Chou 		cmd.cmdarg = 1;
1766d52ebf10SThomas Chou 		err = mmc_send_cmd(mmc, &cmd, NULL);
1767d52ebf10SThomas Chou 
1768d52ebf10SThomas Chou 		if (err)
1769d52ebf10SThomas Chou 			return err;
1770d52ebf10SThomas Chou 	}
1771d52ebf10SThomas Chou #endif
1772479fbf72SJason Zhu #ifndef CONFIG_MMC_USE_PRE_CONFIG
1773272cc70bSAndy Fleming 	/* Put the Card in Identify Mode */
1774d52ebf10SThomas Chou 	cmd.cmdidx = mmc_host_is_spi(mmc) ? MMC_CMD_SEND_CID :
1775d52ebf10SThomas Chou 		MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */
1776272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R2;
1777272cc70bSAndy Fleming 	cmd.cmdarg = 0;
1778272cc70bSAndy Fleming 
1779272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
1780272cc70bSAndy Fleming 
1781272cc70bSAndy Fleming 	if (err)
1782272cc70bSAndy Fleming 		return err;
1783272cc70bSAndy Fleming 
1784272cc70bSAndy Fleming 	memcpy(mmc->cid, cmd.response, 16);
1785272cc70bSAndy Fleming 
1786272cc70bSAndy Fleming 	/*
1787272cc70bSAndy Fleming 	 * For MMC cards, set the Relative Address.
1788272cc70bSAndy Fleming 	 * For SD cards, get the Relatvie Address.
1789272cc70bSAndy Fleming 	 * This also puts the cards into Standby State
1790272cc70bSAndy Fleming 	 */
1791d52ebf10SThomas Chou 	if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
1792272cc70bSAndy Fleming 		cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;
1793272cc70bSAndy Fleming 		cmd.cmdarg = mmc->rca << 16;
1794272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R6;
1795272cc70bSAndy Fleming 
1796272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
1797272cc70bSAndy Fleming 
1798272cc70bSAndy Fleming 		if (err)
1799272cc70bSAndy Fleming 			return err;
1800272cc70bSAndy Fleming 
1801272cc70bSAndy Fleming 		if (IS_SD(mmc))
1802998be3ddSRabin Vincent 			mmc->rca = (cmd.response[0] >> 16) & 0xffff;
1803d52ebf10SThomas Chou 	}
1804479fbf72SJason Zhu #endif
1805272cc70bSAndy Fleming 	/* Get the Card-Specific Data */
1806272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SEND_CSD;
1807272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R2;
1808272cc70bSAndy Fleming 	cmd.cmdarg = mmc->rca << 16;
1809272cc70bSAndy Fleming 
1810272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
1811272cc70bSAndy Fleming 
1812272cc70bSAndy Fleming 	if (err)
1813272cc70bSAndy Fleming 		return err;
1814272cc70bSAndy Fleming 
1815998be3ddSRabin Vincent 	mmc->csd[0] = cmd.response[0];
1816998be3ddSRabin Vincent 	mmc->csd[1] = cmd.response[1];
1817998be3ddSRabin Vincent 	mmc->csd[2] = cmd.response[2];
1818998be3ddSRabin Vincent 	mmc->csd[3] = cmd.response[3];
1819272cc70bSAndy Fleming 
1820272cc70bSAndy Fleming 	if (mmc->version == MMC_VERSION_UNKNOWN) {
18210b453ffeSRabin Vincent 		int version = (cmd.response[0] >> 26) & 0xf;
1822272cc70bSAndy Fleming 
1823272cc70bSAndy Fleming 		switch (version) {
1824272cc70bSAndy Fleming 		case 0:
1825272cc70bSAndy Fleming 			mmc->version = MMC_VERSION_1_2;
1826272cc70bSAndy Fleming 			break;
1827272cc70bSAndy Fleming 		case 1:
1828272cc70bSAndy Fleming 			mmc->version = MMC_VERSION_1_4;
1829272cc70bSAndy Fleming 			break;
1830272cc70bSAndy Fleming 		case 2:
1831272cc70bSAndy Fleming 			mmc->version = MMC_VERSION_2_2;
1832272cc70bSAndy Fleming 			break;
1833272cc70bSAndy Fleming 		case 3:
1834272cc70bSAndy Fleming 			mmc->version = MMC_VERSION_3;
1835272cc70bSAndy Fleming 			break;
1836272cc70bSAndy Fleming 		case 4:
1837272cc70bSAndy Fleming 			mmc->version = MMC_VERSION_4;
1838272cc70bSAndy Fleming 			break;
1839272cc70bSAndy Fleming 		default:
1840272cc70bSAndy Fleming 			mmc->version = MMC_VERSION_1_2;
1841272cc70bSAndy Fleming 			break;
1842272cc70bSAndy Fleming 		}
1843272cc70bSAndy Fleming 	}
1844272cc70bSAndy Fleming 
1845272cc70bSAndy Fleming 	/* divide frequency by 10, since the mults are 10x bigger */
18460b453ffeSRabin Vincent 	freq = fbase[(cmd.response[0] & 0x7)];
18470b453ffeSRabin Vincent 	mult = multipliers[((cmd.response[0] >> 3) & 0xf)];
1848272cc70bSAndy Fleming 
18493e3ff0acSZiyuan Xu 	tran_speed = freq * mult;
1850272cc70bSAndy Fleming 
1851ab71188cSMarkus Niebel 	mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1);
1852998be3ddSRabin Vincent 	mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf);
1853272cc70bSAndy Fleming 
1854272cc70bSAndy Fleming 	if (IS_SD(mmc))
1855272cc70bSAndy Fleming 		mmc->write_bl_len = mmc->read_bl_len;
1856272cc70bSAndy Fleming 	else
1857998be3ddSRabin Vincent 		mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf);
1858272cc70bSAndy Fleming 
1859272cc70bSAndy Fleming 	if (mmc->high_capacity) {
1860272cc70bSAndy Fleming 		csize = (mmc->csd[1] & 0x3f) << 16
1861272cc70bSAndy Fleming 			| (mmc->csd[2] & 0xffff0000) >> 16;
1862272cc70bSAndy Fleming 		cmult = 8;
1863272cc70bSAndy Fleming 	} else {
1864272cc70bSAndy Fleming 		csize = (mmc->csd[1] & 0x3ff) << 2
1865272cc70bSAndy Fleming 			| (mmc->csd[2] & 0xc0000000) >> 30;
1866272cc70bSAndy Fleming 		cmult = (mmc->csd[2] & 0x00038000) >> 15;
1867272cc70bSAndy Fleming 	}
1868272cc70bSAndy Fleming 
1869f866a46dSStephen Warren 	mmc->capacity_user = (csize + 1) << (cmult + 2);
1870f866a46dSStephen Warren 	mmc->capacity_user *= mmc->read_bl_len;
1871f866a46dSStephen Warren 	mmc->capacity_boot = 0;
1872f866a46dSStephen Warren 	mmc->capacity_rpmb = 0;
1873f866a46dSStephen Warren 	for (i = 0; i < 4; i++)
1874f866a46dSStephen Warren 		mmc->capacity_gp[i] = 0;
1875272cc70bSAndy Fleming 
18768bfa195eSSimon Glass 	if (mmc->read_bl_len > MMC_MAX_BLOCK_LEN)
18778bfa195eSSimon Glass 		mmc->read_bl_len = MMC_MAX_BLOCK_LEN;
1878272cc70bSAndy Fleming 
18798bfa195eSSimon Glass 	if (mmc->write_bl_len > MMC_MAX_BLOCK_LEN)
18808bfa195eSSimon Glass 		mmc->write_bl_len = MMC_MAX_BLOCK_LEN;
1881272cc70bSAndy Fleming 
1882ab71188cSMarkus Niebel 	if ((mmc->dsr_imp) && (0xffffffff != mmc->dsr)) {
1883ab71188cSMarkus Niebel 		cmd.cmdidx = MMC_CMD_SET_DSR;
1884ab71188cSMarkus Niebel 		cmd.cmdarg = (mmc->dsr & 0xffff) << 16;
1885ab71188cSMarkus Niebel 		cmd.resp_type = MMC_RSP_NONE;
1886ab71188cSMarkus Niebel 		if (mmc_send_cmd(mmc, &cmd, NULL))
1887ab71188cSMarkus Niebel 			printf("MMC: SET_DSR failed\n");
1888ab71188cSMarkus Niebel 	}
1889ab71188cSMarkus Niebel 
1890272cc70bSAndy Fleming 	/* Select the card, and put it into Transfer Mode */
1891d52ebf10SThomas Chou 	if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
1892272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_SELECT_CARD;
1893fe8f7066SAjay Bhargav 		cmd.resp_type = MMC_RSP_R1;
1894272cc70bSAndy Fleming 		cmd.cmdarg = mmc->rca << 16;
1895272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
1896272cc70bSAndy Fleming 
1897272cc70bSAndy Fleming 		if (err)
1898272cc70bSAndy Fleming 			return err;
1899d52ebf10SThomas Chou 	}
1900272cc70bSAndy Fleming 
1901e6f99a56SLei Wen 	/*
1902e6f99a56SLei Wen 	 * For SD, its erase group is always one sector
1903e6f99a56SLei Wen 	 */
1904e6f99a56SLei Wen 	mmc->erase_grp_size = 1;
1905bc897b1dSLei Wen 	mmc->part_config = MMCPART_NOAVAILABLE;
1906d23e2c09SSukumar Ghorai 	if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) {
1907983bba80SYifeng Zhao 		/* select high speed to reduce initialization time */
1908983bba80SYifeng Zhao 		mmc_select_hs(mmc);
1909983bba80SYifeng Zhao 		mmc_set_clock(mmc, MMC_HIGH_52_MAX_DTR);
1910983bba80SYifeng Zhao 
1911d23e2c09SSukumar Ghorai 		/* check  ext_csd version and capacity */
1912d23e2c09SSukumar Ghorai 		err = mmc_send_ext_csd(mmc, ext_csd);
19139cf199ebSDiego Santa Cruz 		if (err)
19149cf199ebSDiego Santa Cruz 			return err;
19159cf199ebSDiego Santa Cruz 		if (ext_csd[EXT_CSD_REV] >= 2) {
1916639b7827SYoshihiro Shimoda 			/*
1917639b7827SYoshihiro Shimoda 			 * According to the JEDEC Standard, the value of
1918639b7827SYoshihiro Shimoda 			 * ext_csd's capacity is valid if the value is more
1919639b7827SYoshihiro Shimoda 			 * than 2GB
1920639b7827SYoshihiro Shimoda 			 */
19210560db18SLei Wen 			capacity = ext_csd[EXT_CSD_SEC_CNT] << 0
19220560db18SLei Wen 					| ext_csd[EXT_CSD_SEC_CNT + 1] << 8
19230560db18SLei Wen 					| ext_csd[EXT_CSD_SEC_CNT + 2] << 16
19240560db18SLei Wen 					| ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
19258bfa195eSSimon Glass 			capacity *= MMC_MAX_BLOCK_LEN;
1926b1f1e821SŁukasz Majewski 			if ((capacity >> 20) > 2 * 1024)
1927f866a46dSStephen Warren 				mmc->capacity_user = capacity;
1928d23e2c09SSukumar Ghorai 		}
1929bc897b1dSLei Wen 
193064f4a619SJaehoon Chung 		switch (ext_csd[EXT_CSD_REV]) {
193164f4a619SJaehoon Chung 		case 1:
193264f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_1;
193364f4a619SJaehoon Chung 			break;
193464f4a619SJaehoon Chung 		case 2:
193564f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_2;
193664f4a619SJaehoon Chung 			break;
193764f4a619SJaehoon Chung 		case 3:
193864f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_3;
193964f4a619SJaehoon Chung 			break;
194064f4a619SJaehoon Chung 		case 5:
194164f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_41;
194264f4a619SJaehoon Chung 			break;
194364f4a619SJaehoon Chung 		case 6:
194464f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_5;
194564f4a619SJaehoon Chung 			break;
1946edab723bSMarkus Niebel 		case 7:
1947edab723bSMarkus Niebel 			mmc->version = MMC_VERSION_5_0;
1948edab723bSMarkus Niebel 			break;
19491a3619cfSStefan Wahren 		case 8:
19501a3619cfSStefan Wahren 			mmc->version = MMC_VERSION_5_1;
19511a3619cfSStefan Wahren 			break;
195264f4a619SJaehoon Chung 		}
195364f4a619SJaehoon Chung 
19548a0cf490SDiego Santa Cruz 		/* The partition data may be non-zero but it is only
19558a0cf490SDiego Santa Cruz 		 * effective if PARTITION_SETTING_COMPLETED is set in
19568a0cf490SDiego Santa Cruz 		 * EXT_CSD, so ignore any data if this bit is not set,
19578a0cf490SDiego Santa Cruz 		 * except for enabling the high-capacity group size
19588a0cf490SDiego Santa Cruz 		 * definition (see below). */
19598a0cf490SDiego Santa Cruz 		part_completed = !!(ext_csd[EXT_CSD_PARTITION_SETTING] &
19608a0cf490SDiego Santa Cruz 				    EXT_CSD_PARTITION_SETTING_COMPLETED);
19618a0cf490SDiego Santa Cruz 
19620c453bb7SDiego Santa Cruz 		/* store the partition info of emmc */
19630c453bb7SDiego Santa Cruz 		mmc->part_support = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
19640c453bb7SDiego Santa Cruz 		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) ||
19650c453bb7SDiego Santa Cruz 		    ext_csd[EXT_CSD_BOOT_MULT])
19660c453bb7SDiego Santa Cruz 			mmc->part_config = ext_csd[EXT_CSD_PART_CONF];
19678a0cf490SDiego Santa Cruz 		if (part_completed &&
19688a0cf490SDiego Santa Cruz 		    (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & ENHNCD_SUPPORT))
19690c453bb7SDiego Santa Cruz 			mmc->part_attr = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
1970a6a1f5f8SJason Zhu 		if (ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] & EXT_CSD_SEC_GB_CL_EN)
1971a6a1f5f8SJason Zhu 			mmc->esr.mmc_can_trim = 1;
19720c453bb7SDiego Santa Cruz 
19730c453bb7SDiego Santa Cruz 		mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17;
19740c453bb7SDiego Santa Cruz 
19750c453bb7SDiego Santa Cruz 		mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17;
19760c453bb7SDiego Santa Cruz 
19770c453bb7SDiego Santa Cruz 		for (i = 0; i < 4; i++) {
19780c453bb7SDiego Santa Cruz 			int idx = EXT_CSD_GP_SIZE_MULT + i * 3;
19798a0cf490SDiego Santa Cruz 			uint mult = (ext_csd[idx + 2] << 16) +
19800c453bb7SDiego Santa Cruz 				(ext_csd[idx + 1] << 8) + ext_csd[idx];
19818a0cf490SDiego Santa Cruz 			if (mult)
19828a0cf490SDiego Santa Cruz 				has_parts = true;
19838a0cf490SDiego Santa Cruz 			if (!part_completed)
19848a0cf490SDiego Santa Cruz 				continue;
19858a0cf490SDiego Santa Cruz 			mmc->capacity_gp[i] = mult;
19860c453bb7SDiego Santa Cruz 			mmc->capacity_gp[i] *=
19870c453bb7SDiego Santa Cruz 				ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
19880c453bb7SDiego Santa Cruz 			mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
1989f8e89d67SDiego Santa Cruz 			mmc->capacity_gp[i] <<= 19;
19900c453bb7SDiego Santa Cruz 		}
19910c453bb7SDiego Santa Cruz 
19928a0cf490SDiego Santa Cruz 		if (part_completed) {
1993a7f852b6SDiego Santa Cruz 			mmc->enh_user_size =
1994a7f852b6SDiego Santa Cruz 				(ext_csd[EXT_CSD_ENH_SIZE_MULT+2] << 16) +
1995a7f852b6SDiego Santa Cruz 				(ext_csd[EXT_CSD_ENH_SIZE_MULT+1] << 8) +
1996a7f852b6SDiego Santa Cruz 				ext_csd[EXT_CSD_ENH_SIZE_MULT];
1997a7f852b6SDiego Santa Cruz 			mmc->enh_user_size *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
1998a7f852b6SDiego Santa Cruz 			mmc->enh_user_size *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
1999a7f852b6SDiego Santa Cruz 			mmc->enh_user_size <<= 19;
2000a7f852b6SDiego Santa Cruz 			mmc->enh_user_start =
2001a7f852b6SDiego Santa Cruz 				(ext_csd[EXT_CSD_ENH_START_ADDR+3] << 24) +
2002a7f852b6SDiego Santa Cruz 				(ext_csd[EXT_CSD_ENH_START_ADDR+2] << 16) +
2003a7f852b6SDiego Santa Cruz 				(ext_csd[EXT_CSD_ENH_START_ADDR+1] << 8) +
2004a7f852b6SDiego Santa Cruz 				ext_csd[EXT_CSD_ENH_START_ADDR];
2005a7f852b6SDiego Santa Cruz 			if (mmc->high_capacity)
2006a7f852b6SDiego Santa Cruz 				mmc->enh_user_start <<= 9;
20078a0cf490SDiego Santa Cruz 		}
2008a7f852b6SDiego Santa Cruz 
2009e6f99a56SLei Wen 		/*
20101937e5aaSOliver Metz 		 * Host needs to enable ERASE_GRP_DEF bit if device is
20111937e5aaSOliver Metz 		 * partitioned. This bit will be lost every time after a reset
20121937e5aaSOliver Metz 		 * or power off. This will affect erase size.
2013e6f99a56SLei Wen 		 */
20148a0cf490SDiego Santa Cruz 		if (part_completed)
20150c453bb7SDiego Santa Cruz 			has_parts = true;
20161937e5aaSOliver Metz 		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) &&
20170c453bb7SDiego Santa Cruz 		    (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB))
20180c453bb7SDiego Santa Cruz 			has_parts = true;
20190c453bb7SDiego Santa Cruz 		if (has_parts) {
20201937e5aaSOliver Metz 			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
20211937e5aaSOliver Metz 				EXT_CSD_ERASE_GROUP_DEF, 1);
20221937e5aaSOliver Metz 
20231937e5aaSOliver Metz 			if (err)
20241937e5aaSOliver Metz 				return err;
2025021a8055SHannes Petermaier 			else
2026021a8055SHannes Petermaier 				ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1;
2027037dc0abSDiego Santa Cruz 		}
20281937e5aaSOliver Metz 
2029037dc0abSDiego Santa Cruz 		if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01) {
20301937e5aaSOliver Metz 			/* Read out group size from ext_csd */
20310560db18SLei Wen 			mmc->erase_grp_size =
2032a4ff9f83SDiego Santa Cruz 				ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024;
2033d7b29129SMarkus Niebel 			/*
2034d7b29129SMarkus Niebel 			 * if high capacity and partition setting completed
2035d7b29129SMarkus Niebel 			 * SEC_COUNT is valid even if it is smaller than 2 GiB
2036d7b29129SMarkus Niebel 			 * JEDEC Standard JESD84-B45, 6.2.4
2037d7b29129SMarkus Niebel 			 */
20388a0cf490SDiego Santa Cruz 			if (mmc->high_capacity && part_completed) {
2039d7b29129SMarkus Niebel 				capacity = (ext_csd[EXT_CSD_SEC_CNT]) |
2040d7b29129SMarkus Niebel 					(ext_csd[EXT_CSD_SEC_CNT + 1] << 8) |
2041d7b29129SMarkus Niebel 					(ext_csd[EXT_CSD_SEC_CNT + 2] << 16) |
2042d7b29129SMarkus Niebel 					(ext_csd[EXT_CSD_SEC_CNT + 3] << 24);
2043d7b29129SMarkus Niebel 				capacity *= MMC_MAX_BLOCK_LEN;
2044d7b29129SMarkus Niebel 				mmc->capacity_user = capacity;
2045d7b29129SMarkus Niebel 			}
20468bfa195eSSimon Glass 		} else {
20471937e5aaSOliver Metz 			/* Calculate the group size from the csd value. */
2048e6f99a56SLei Wen 			int erase_gsz, erase_gmul;
2049e6f99a56SLei Wen 			erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10;
2050e6f99a56SLei Wen 			erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5;
2051e6f99a56SLei Wen 			mmc->erase_grp_size = (erase_gsz + 1)
2052e6f99a56SLei Wen 				* (erase_gmul + 1);
2053e6f99a56SLei Wen 		}
2054037dc0abSDiego Santa Cruz 
2055037dc0abSDiego Santa Cruz 		mmc->hc_wp_grp_size = 1024
2056037dc0abSDiego Santa Cruz 			* ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]
2057037dc0abSDiego Santa Cruz 			* ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
20589e41a00bSDiego Santa Cruz 
20599e41a00bSDiego Santa Cruz 		mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET];
20607a9b5d70SYifeng Zhao 
20617a9b5d70SYifeng Zhao 		mmc->raw_driver_strength = ext_csd[EXT_CSD_DRIVER_STRENGTH];
2062f866a46dSStephen Warren 	}
2063f866a46dSStephen Warren 
2064c40fdca6SSimon Glass 	err = mmc_set_capacity(mmc, mmc_get_blk_desc(mmc)->hwpart);
2065f866a46dSStephen Warren 	if (err)
2066f866a46dSStephen Warren 		return err;
2067d23e2c09SSukumar Ghorai 
2068272cc70bSAndy Fleming 	if (IS_SD(mmc))
2069272cc70bSAndy Fleming 		err = sd_change_freq(mmc);
2070272cc70bSAndy Fleming 	else
2071272cc70bSAndy Fleming 		err = mmc_change_freq(mmc);
2072272cc70bSAndy Fleming 
2073272cc70bSAndy Fleming 	if (err)
2074272cc70bSAndy Fleming 		return err;
2075272cc70bSAndy Fleming 
2076272cc70bSAndy Fleming 	/* Restrict card's capabilities by what the host can do */
207793bfd616SPantelis Antoniou 	mmc->card_caps &= mmc->cfg->host_caps;
2078272cc70bSAndy Fleming 
2079272cc70bSAndy Fleming 	if (IS_SD(mmc)) {
2080272cc70bSAndy Fleming 		if (mmc->card_caps & MMC_MODE_4BIT) {
2081272cc70bSAndy Fleming 			cmd.cmdidx = MMC_CMD_APP_CMD;
2082272cc70bSAndy Fleming 			cmd.resp_type = MMC_RSP_R1;
2083272cc70bSAndy Fleming 			cmd.cmdarg = mmc->rca << 16;
2084272cc70bSAndy Fleming 
2085272cc70bSAndy Fleming 			err = mmc_send_cmd(mmc, &cmd, NULL);
2086272cc70bSAndy Fleming 			if (err)
2087272cc70bSAndy Fleming 				return err;
2088272cc70bSAndy Fleming 
2089272cc70bSAndy Fleming 			cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH;
2090272cc70bSAndy Fleming 			cmd.resp_type = MMC_RSP_R1;
2091272cc70bSAndy Fleming 			cmd.cmdarg = 2;
2092272cc70bSAndy Fleming 			err = mmc_send_cmd(mmc, &cmd, NULL);
2093272cc70bSAndy Fleming 			if (err)
2094272cc70bSAndy Fleming 				return err;
2095272cc70bSAndy Fleming 
2096272cc70bSAndy Fleming 			mmc_set_bus_width(mmc, 4);
2097272cc70bSAndy Fleming 		}
2098272cc70bSAndy Fleming 
20993697e599SPeng Fan 		err = sd_read_ssr(mmc);
21003697e599SPeng Fan 		if (err)
21013697e599SPeng Fan 			return err;
21023697e599SPeng Fan 
2103272cc70bSAndy Fleming 		if (mmc->card_caps & MMC_MODE_HS)
2104fe414819SJason Zhu 			tran_speed = MMC_HIGH_52_MAX_DTR;
2105272cc70bSAndy Fleming 		else
2106fe414819SJason Zhu 			tran_speed = MMC_HIGH_26_MAX_DTR;
2107ad5fd922SJaehoon Chung 
21083e3ff0acSZiyuan Xu 		mmc_set_clock(mmc, tran_speed);
210949dba033SZiyuan Xu 	}
2110272cc70bSAndy Fleming 
21115af8f45cSAndrew Gabbasov 	/* Fix the block length for DDR mode */
211249dba033SZiyuan Xu 	if (mmc_card_ddr(mmc)) {
21135af8f45cSAndrew Gabbasov 		mmc->read_bl_len = MMC_MAX_BLOCK_LEN;
21145af8f45cSAndrew Gabbasov 		mmc->write_bl_len = MMC_MAX_BLOCK_LEN;
21155af8f45cSAndrew Gabbasov 	}
21165af8f45cSAndrew Gabbasov 
2117272cc70bSAndy Fleming 	/* fill in device description */
2118c40fdca6SSimon Glass 	bdesc = mmc_get_blk_desc(mmc);
2119c40fdca6SSimon Glass 	bdesc->lun = 0;
2120c40fdca6SSimon Glass 	bdesc->hwpart = 0;
2121c40fdca6SSimon Glass 	bdesc->type = 0;
2122c40fdca6SSimon Glass 	bdesc->blksz = mmc->read_bl_len;
2123c40fdca6SSimon Glass 	bdesc->log2blksz = LOG2(bdesc->blksz);
2124c40fdca6SSimon Glass 	bdesc->lba = lldiv(mmc->capacity, mmc->read_bl_len);
2125fc011f64SSjoerd Simons #if !defined(CONFIG_SPL_BUILD) || \
2126fc011f64SSjoerd Simons 		(defined(CONFIG_SPL_LIBCOMMON_SUPPORT) && \
2127fc011f64SSjoerd Simons 		!defined(CONFIG_USE_TINY_PRINTF))
2128c40fdca6SSimon Glass 	sprintf(bdesc->vendor, "Man %06x Snr %04x%04x",
2129babce5f6STaylor Hutt 		mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff),
2130babce5f6STaylor Hutt 		(mmc->cid[3] >> 16) & 0xffff);
2131c40fdca6SSimon Glass 	sprintf(bdesc->product, "%c%c%c%c%c%c", mmc->cid[0] & 0xff,
21320b453ffeSRabin Vincent 		(mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
2133babce5f6STaylor Hutt 		(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff,
2134babce5f6STaylor Hutt 		(mmc->cid[2] >> 24) & 0xff);
2135c40fdca6SSimon Glass 	sprintf(bdesc->revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf,
2136babce5f6STaylor Hutt 		(mmc->cid[2] >> 16) & 0xf);
213756196826SPaul Burton #else
2138c40fdca6SSimon Glass 	bdesc->vendor[0] = 0;
2139c40fdca6SSimon Glass 	bdesc->product[0] = 0;
2140c40fdca6SSimon Glass 	bdesc->revision[0] = 0;
214156196826SPaul Burton #endif
2142122efd43SMikhail Kshevetskiy #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT)
2143c40fdca6SSimon Glass 	part_init(bdesc);
2144122efd43SMikhail Kshevetskiy #endif
2145272cc70bSAndy Fleming 
2146272cc70bSAndy Fleming 	return 0;
2147272cc70bSAndy Fleming }
2148272cc70bSAndy Fleming 
2149479fbf72SJason Zhu #ifndef CONFIG_MMC_USE_PRE_CONFIG
2150fdbb873eSKim Phillips static int mmc_send_if_cond(struct mmc *mmc)
2151272cc70bSAndy Fleming {
2152272cc70bSAndy Fleming 	struct mmc_cmd cmd;
2153272cc70bSAndy Fleming 	int err;
2154272cc70bSAndy Fleming 
2155272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_SEND_IF_COND;
2156272cc70bSAndy Fleming 	/* We set the bit if the host supports voltages between 2.7 and 3.6 V */
215793bfd616SPantelis Antoniou 	cmd.cmdarg = ((mmc->cfg->voltages & 0xff8000) != 0) << 8 | 0xaa;
2158272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R7;
2159272cc70bSAndy Fleming 
2160272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
2161272cc70bSAndy Fleming 
2162272cc70bSAndy Fleming 	if (err)
2163272cc70bSAndy Fleming 		return err;
2164272cc70bSAndy Fleming 
2165998be3ddSRabin Vincent 	if ((cmd.response[0] & 0xff) != 0xaa)
2166915ffa52SJaehoon Chung 		return -EOPNOTSUPP;
2167272cc70bSAndy Fleming 	else
2168272cc70bSAndy Fleming 		mmc->version = SD_VERSION_2;
2169272cc70bSAndy Fleming 
2170272cc70bSAndy Fleming 	return 0;
2171272cc70bSAndy Fleming }
2172479fbf72SJason Zhu #endif
2173272cc70bSAndy Fleming 
2174c4d660d4SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC)
217595de9ab2SPaul Kocialkowski /* board-specific MMC power initializations. */
217695de9ab2SPaul Kocialkowski __weak void board_mmc_power_init(void)
217795de9ab2SPaul Kocialkowski {
217895de9ab2SPaul Kocialkowski }
217905cbeb7cSSimon Glass #endif
218095de9ab2SPaul Kocialkowski 
2181479fbf72SJason Zhu #ifndef CONFIG_MMC_USE_PRE_CONFIG
21822051aefeSPeng Fan static int mmc_power_init(struct mmc *mmc)
21832051aefeSPeng Fan {
2184c4d660d4SSimon Glass #if CONFIG_IS_ENABLED(DM_MMC)
218505cbeb7cSSimon Glass #if defined(CONFIG_DM_REGULATOR) && !defined(CONFIG_SPL_BUILD)
21862051aefeSPeng Fan 	struct udevice *vmmc_supply;
21872051aefeSPeng Fan 	int ret;
21882051aefeSPeng Fan 
21892051aefeSPeng Fan 	ret = device_get_supply_regulator(mmc->dev, "vmmc-supply",
21902051aefeSPeng Fan 					  &vmmc_supply);
21912051aefeSPeng Fan 	if (ret) {
2192288db7c7SJaehoon Chung 		debug("%s: No vmmc supply\n", mmc->dev->name);
21932051aefeSPeng Fan 		return 0;
21942051aefeSPeng Fan 	}
21952051aefeSPeng Fan 
21962051aefeSPeng Fan 	ret = regulator_set_enable(vmmc_supply, true);
21972051aefeSPeng Fan 	if (ret) {
21982051aefeSPeng Fan 		puts("Error enabling VMMC supply\n");
21992051aefeSPeng Fan 		return ret;
22002051aefeSPeng Fan 	}
22012051aefeSPeng Fan #endif
220205cbeb7cSSimon Glass #else /* !CONFIG_DM_MMC */
220305cbeb7cSSimon Glass 	/*
220405cbeb7cSSimon Glass 	 * Driver model should use a regulator, as above, rather than calling
220505cbeb7cSSimon Glass 	 * out to board code.
220605cbeb7cSSimon Glass 	 */
220705cbeb7cSSimon Glass 	board_mmc_power_init();
220805cbeb7cSSimon Glass #endif
22092051aefeSPeng Fan 	return 0;
22102051aefeSPeng Fan }
2211479fbf72SJason Zhu #endif
2212479fbf72SJason Zhu #ifdef CONFIG_MMC_USE_PRE_CONFIG
2213479fbf72SJason Zhu static int mmc_select_card(struct mmc *mmc, int n)
2214479fbf72SJason Zhu {
2215479fbf72SJason Zhu 	struct mmc_cmd cmd;
2216479fbf72SJason Zhu 	int err = 0;
22172051aefeSPeng Fan 
2218479fbf72SJason Zhu 	memset(&cmd, 0, sizeof(struct mmc_cmd));
2219479fbf72SJason Zhu 	if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
2220479fbf72SJason Zhu 		mmc->rca = n;
2221479fbf72SJason Zhu 		cmd.cmdidx = MMC_CMD_SELECT_CARD;
2222479fbf72SJason Zhu 		cmd.resp_type = MMC_RSP_R1;
2223479fbf72SJason Zhu 		cmd.cmdarg = mmc->rca << 16;
2224479fbf72SJason Zhu 		err = mmc_send_cmd(mmc, &cmd, NULL);
2225479fbf72SJason Zhu 	}
2226479fbf72SJason Zhu 
2227479fbf72SJason Zhu 	return err;
2228479fbf72SJason Zhu }
2229479fbf72SJason Zhu 
2230479fbf72SJason Zhu int mmc_start_init(struct mmc *mmc)
2231479fbf72SJason Zhu {
2232388345acSZiyuan Xu 	int bus_width = 1;
2233479fbf72SJason Zhu 	/*
2234479fbf72SJason Zhu 	 * We use the MMC config set by the bootrom.
2235479fbf72SJason Zhu 	 * So it is no need to reset the eMMC device.
2236479fbf72SJason Zhu 	 */
2237388345acSZiyuan Xu 	if (mmc->cfg->host_caps & MMC_MODE_8BIT)
2238388345acSZiyuan Xu 		bus_width = 8;
2239388345acSZiyuan Xu 	else if (mmc->cfg->host_caps & MMC_MODE_4BIT)
2240388345acSZiyuan Xu 		bus_width = 4;
2241388345acSZiyuan Xu 	mmc_set_bus_width(mmc, bus_width);
2242388345acSZiyuan Xu 
2243479fbf72SJason Zhu 	mmc_set_clock(mmc, 1);
2244479fbf72SJason Zhu 	mmc_set_timing(mmc, MMC_TIMING_LEGACY);
2245479fbf72SJason Zhu 	/* Send cmd7 to return stand-by state*/
2246479fbf72SJason Zhu 	mmc_select_card(mmc, 0);
2247479fbf72SJason Zhu 	mmc->version = MMC_VERSION_UNKNOWN;
2248479fbf72SJason Zhu 	mmc->high_capacity = 1;
2249479fbf72SJason Zhu 	/*
2250479fbf72SJason Zhu 	 * The RCA is set to 2 by rockchip bootrom, use the default
2251479fbf72SJason Zhu 	 * value here.
2252479fbf72SJason Zhu 	 */
2253479fbf72SJason Zhu #ifdef CONFIG_ARCH_ROCKCHIP
2254479fbf72SJason Zhu 	mmc->rca = 2;
2255479fbf72SJason Zhu #else
2256479fbf72SJason Zhu 	mmc->rca = 1;
2257479fbf72SJason Zhu #endif
2258479fbf72SJason Zhu 	return 0;
2259479fbf72SJason Zhu }
2260479fbf72SJason Zhu #else
2261e9550449SChe-Liang Chiou int mmc_start_init(struct mmc *mmc)
2262272cc70bSAndy Fleming {
22638ca51e51SSimon Glass 	bool no_card;
2264afd5932bSMacpaul Lin 	int err;
2265272cc70bSAndy Fleming 
2266ab769f22SPantelis Antoniou 	/* we pretend there's no card when init is NULL */
22678ca51e51SSimon Glass 	no_card = mmc_getcd(mmc) == 0;
2268e7881d85SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC)
22698ca51e51SSimon Glass 	no_card = no_card || (mmc->cfg->ops->init == NULL);
22708ca51e51SSimon Glass #endif
22718ca51e51SSimon Glass 	if (no_card) {
227248972d90SThierry Reding 		mmc->has_init = 0;
227356196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
227448972d90SThierry Reding 		printf("MMC: no card present\n");
227556196826SPaul Burton #endif
2276915ffa52SJaehoon Chung 		return -ENOMEDIUM;
227748972d90SThierry Reding 	}
227848972d90SThierry Reding 
2279bc897b1dSLei Wen 	if (mmc->has_init)
2280bc897b1dSLei Wen 		return 0;
2281bc897b1dSLei Wen 
22825a8dbdc6SYangbo Lu #ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT
22835a8dbdc6SYangbo Lu 	mmc_adapter_card_type_ident();
22845a8dbdc6SYangbo Lu #endif
22852051aefeSPeng Fan 	err = mmc_power_init(mmc);
22862051aefeSPeng Fan 	if (err)
22872051aefeSPeng Fan 		return err;
228895de9ab2SPaul Kocialkowski 
2289e7881d85SSimon Glass #if CONFIG_IS_ENABLED(DM_MMC)
22908ca51e51SSimon Glass 	/* The device has already been probed ready for use */
22918ca51e51SSimon Glass #else
2292ab769f22SPantelis Antoniou 	/* made sure it's not NULL earlier */
229393bfd616SPantelis Antoniou 	err = mmc->cfg->ops->init(mmc);
2294272cc70bSAndy Fleming 	if (err)
2295272cc70bSAndy Fleming 		return err;
22968ca51e51SSimon Glass #endif
2297b86b85e2SIlya Yanok 	mmc_set_bus_width(mmc, 1);
2298b86b85e2SIlya Yanok 	mmc_set_clock(mmc, 1);
229981db2d36SZiyuan Xu 	mmc_set_timing(mmc, MMC_TIMING_LEGACY);
2300b86b85e2SIlya Yanok 
2301272cc70bSAndy Fleming 	/* Reset the Card */
2302272cc70bSAndy Fleming 	err = mmc_go_idle(mmc);
2303272cc70bSAndy Fleming 
2304272cc70bSAndy Fleming 	if (err)
2305272cc70bSAndy Fleming 		return err;
2306272cc70bSAndy Fleming 
2307bc897b1dSLei Wen 	/* The internal partition reset to user partition(0) at every CMD0*/
2308c40fdca6SSimon Glass 	mmc_get_blk_desc(mmc)->hwpart = 0;
2309bc897b1dSLei Wen 
2310272cc70bSAndy Fleming 	/* Test for SD version 2 */
2311272cc70bSAndy Fleming 	err = mmc_send_if_cond(mmc);
2312272cc70bSAndy Fleming 
2313272cc70bSAndy Fleming 	/* Now try to get the SD card's operating condition */
2314272cc70bSAndy Fleming 	err = sd_send_op_cond(mmc);
2315272cc70bSAndy Fleming 
2316272cc70bSAndy Fleming 	/* If the command timed out, we check for an MMC card */
2317915ffa52SJaehoon Chung 	if (err == -ETIMEDOUT) {
2318272cc70bSAndy Fleming 		err = mmc_send_op_cond(mmc);
2319272cc70bSAndy Fleming 
2320bd47c135SAndrew Gabbasov 		if (err) {
232156196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
2322272cc70bSAndy Fleming 			printf("Card did not respond to voltage select!\n");
232356196826SPaul Burton #endif
2324915ffa52SJaehoon Chung 			return -EOPNOTSUPP;
2325272cc70bSAndy Fleming 		}
2326272cc70bSAndy Fleming 	}
2327272cc70bSAndy Fleming 
2328bd47c135SAndrew Gabbasov 	if (!err)
2329e9550449SChe-Liang Chiou 		mmc->init_in_progress = 1;
2330e9550449SChe-Liang Chiou 
2331e9550449SChe-Liang Chiou 	return err;
2332e9550449SChe-Liang Chiou }
2333479fbf72SJason Zhu #endif
2334e9550449SChe-Liang Chiou 
2335e9550449SChe-Liang Chiou static int mmc_complete_init(struct mmc *mmc)
2336e9550449SChe-Liang Chiou {
2337e9550449SChe-Liang Chiou 	int err = 0;
2338e9550449SChe-Liang Chiou 
2339bd47c135SAndrew Gabbasov 	mmc->init_in_progress = 0;
2340e9550449SChe-Liang Chiou 	if (mmc->op_cond_pending)
2341e9550449SChe-Liang Chiou 		err = mmc_complete_op_cond(mmc);
2342e9550449SChe-Liang Chiou 
2343e9550449SChe-Liang Chiou 	if (!err)
2344bc897b1dSLei Wen 		err = mmc_startup(mmc);
2345bc897b1dSLei Wen 	if (err)
2346bc897b1dSLei Wen 		mmc->has_init = 0;
2347bc897b1dSLei Wen 	else
2348bc897b1dSLei Wen 		mmc->has_init = 1;
2349e9550449SChe-Liang Chiou 	return err;
2350e9550449SChe-Liang Chiou }
2351e9550449SChe-Liang Chiou 
2352e9550449SChe-Liang Chiou int mmc_init(struct mmc *mmc)
2353e9550449SChe-Liang Chiou {
2354bd47c135SAndrew Gabbasov 	int err = 0;
2355ce9eca94SMarek Vasut 	__maybe_unused unsigned start;
2356c4d660d4SSimon Glass #if CONFIG_IS_ENABLED(DM_MMC)
235733fb211dSSimon Glass 	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(mmc->dev);
2358e9550449SChe-Liang Chiou 
235933fb211dSSimon Glass 	upriv->mmc = mmc;
236033fb211dSSimon Glass #endif
2361e9550449SChe-Liang Chiou 	if (mmc->has_init)
2362e9550449SChe-Liang Chiou 		return 0;
2363d803fea5SMateusz Zalega 
2364d803fea5SMateusz Zalega 	start = get_timer(0);
2365d803fea5SMateusz Zalega 
2366e9550449SChe-Liang Chiou 	if (!mmc->init_in_progress)
2367e9550449SChe-Liang Chiou 		err = mmc_start_init(mmc);
2368e9550449SChe-Liang Chiou 
2369bd47c135SAndrew Gabbasov 	if (!err)
2370e9550449SChe-Liang Chiou 		err = mmc_complete_init(mmc);
2371919b4858SJagan Teki 	if (err)
2372919b4858SJagan Teki 		printf("%s: %d, time %lu\n", __func__, err, get_timer(start));
2373919b4858SJagan Teki 
2374bc897b1dSLei Wen 	return err;
2375272cc70bSAndy Fleming }
2376272cc70bSAndy Fleming 
2377ab71188cSMarkus Niebel int mmc_set_dsr(struct mmc *mmc, u16 val)
2378ab71188cSMarkus Niebel {
2379ab71188cSMarkus Niebel 	mmc->dsr = val;
2380ab71188cSMarkus Niebel 	return 0;
2381ab71188cSMarkus Niebel }
2382ab71188cSMarkus Niebel 
2383cee9ab7cSJeroen Hofstee /* CPU-specific MMC initializations */
2384cee9ab7cSJeroen Hofstee __weak int cpu_mmc_init(bd_t *bis)
2385272cc70bSAndy Fleming {
2386272cc70bSAndy Fleming 	return -1;
2387272cc70bSAndy Fleming }
2388272cc70bSAndy Fleming 
2389cee9ab7cSJeroen Hofstee /* board-specific MMC initializations. */
2390cee9ab7cSJeroen Hofstee __weak int board_mmc_init(bd_t *bis)
2391cee9ab7cSJeroen Hofstee {
2392cee9ab7cSJeroen Hofstee 	return -1;
2393cee9ab7cSJeroen Hofstee }
2394272cc70bSAndy Fleming 
2395e9550449SChe-Liang Chiou void mmc_set_preinit(struct mmc *mmc, int preinit)
2396e9550449SChe-Liang Chiou {
2397e9550449SChe-Liang Chiou 	mmc->preinit = preinit;
2398e9550449SChe-Liang Chiou }
2399e9550449SChe-Liang Chiou 
2400c4d660d4SSimon Glass #if CONFIG_IS_ENABLED(DM_MMC) && defined(CONFIG_SPL_BUILD)
24018e3332e2SSjoerd Simons static int mmc_probe(bd_t *bis)
24028e3332e2SSjoerd Simons {
24038e3332e2SSjoerd Simons 	return 0;
24048e3332e2SSjoerd Simons }
2405c4d660d4SSimon Glass #elif CONFIG_IS_ENABLED(DM_MMC)
24068e3332e2SSjoerd Simons static int mmc_probe(bd_t *bis)
24078e3332e2SSjoerd Simons {
24084a1db6d8SSimon Glass 	int ret, i;
24098e3332e2SSjoerd Simons 	struct uclass *uc;
24104a1db6d8SSimon Glass 	struct udevice *dev;
24118e3332e2SSjoerd Simons 
24128e3332e2SSjoerd Simons 	ret = uclass_get(UCLASS_MMC, &uc);
24138e3332e2SSjoerd Simons 	if (ret)
24148e3332e2SSjoerd Simons 		return ret;
24158e3332e2SSjoerd Simons 
24164a1db6d8SSimon Glass 	/*
24174a1db6d8SSimon Glass 	 * Try to add them in sequence order. Really with driver model we
24184a1db6d8SSimon Glass 	 * should allow holes, but the current MMC list does not allow that.
24194a1db6d8SSimon Glass 	 * So if we request 0, 1, 3 we will get 0, 1, 2.
24204a1db6d8SSimon Glass 	 */
24214a1db6d8SSimon Glass 	for (i = 0; ; i++) {
24224a1db6d8SSimon Glass 		ret = uclass_get_device_by_seq(UCLASS_MMC, i, &dev);
24234a1db6d8SSimon Glass 		if (ret == -ENODEV)
24244a1db6d8SSimon Glass 			break;
24254a1db6d8SSimon Glass 	}
24264a1db6d8SSimon Glass 	uclass_foreach_dev(dev, uc) {
24274a1db6d8SSimon Glass 		ret = device_probe(dev);
24288e3332e2SSjoerd Simons 		if (ret)
24294a1db6d8SSimon Glass 			printf("%s - probe failed: %d\n", dev->name, ret);
24308e3332e2SSjoerd Simons 	}
24318e3332e2SSjoerd Simons 
24328e3332e2SSjoerd Simons 	return 0;
24338e3332e2SSjoerd Simons }
24348e3332e2SSjoerd Simons #else
24358e3332e2SSjoerd Simons static int mmc_probe(bd_t *bis)
24368e3332e2SSjoerd Simons {
24378e3332e2SSjoerd Simons 	if (board_mmc_init(bis) < 0)
24388e3332e2SSjoerd Simons 		cpu_mmc_init(bis);
24398e3332e2SSjoerd Simons 
24408e3332e2SSjoerd Simons 	return 0;
24418e3332e2SSjoerd Simons }
24428e3332e2SSjoerd Simons #endif
2443e9550449SChe-Liang Chiou 
2444272cc70bSAndy Fleming int mmc_initialize(bd_t *bis)
2445272cc70bSAndy Fleming {
24461b26bab1SDaniel Kochmański 	static int initialized = 0;
24478e3332e2SSjoerd Simons 	int ret;
24481b26bab1SDaniel Kochmański 	if (initialized)	/* Avoid initializing mmc multiple times */
24491b26bab1SDaniel Kochmański 		return 0;
24501b26bab1SDaniel Kochmański 	initialized = 1;
24511b26bab1SDaniel Kochmański 
2452c4d660d4SSimon Glass #if !CONFIG_IS_ENABLED(BLK)
2453b5b838f1SMarek Vasut #if !CONFIG_IS_ENABLED(MMC_TINY)
2454c40fdca6SSimon Glass 	mmc_list_init();
2455c40fdca6SSimon Glass #endif
2456b5b838f1SMarek Vasut #endif
24578e3332e2SSjoerd Simons 	ret = mmc_probe(bis);
24588e3332e2SSjoerd Simons 	if (ret)
24598e3332e2SSjoerd Simons 		return ret;
2460272cc70bSAndy Fleming 
2461bb0dc108SYing Zhang #ifndef CONFIG_SPL_BUILD
2462272cc70bSAndy Fleming 	print_mmc_devices(',');
2463bb0dc108SYing Zhang #endif
2464272cc70bSAndy Fleming 
2465c40fdca6SSimon Glass 	mmc_do_preinit();
2466272cc70bSAndy Fleming 	return 0;
2467272cc70bSAndy Fleming }
2468cd3d4880STomas Melin 
2469cd3d4880STomas Melin #ifdef CONFIG_CMD_BKOPS_ENABLE
2470cd3d4880STomas Melin int mmc_set_bkops_enable(struct mmc *mmc)
2471cd3d4880STomas Melin {
2472cd3d4880STomas Melin 	int err;
2473cd3d4880STomas Melin 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
2474cd3d4880STomas Melin 
2475cd3d4880STomas Melin 	err = mmc_send_ext_csd(mmc, ext_csd);
2476cd3d4880STomas Melin 	if (err) {
2477cd3d4880STomas Melin 		puts("Could not get ext_csd register values\n");
2478cd3d4880STomas Melin 		return err;
2479cd3d4880STomas Melin 	}
2480cd3d4880STomas Melin 
2481cd3d4880STomas Melin 	if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) {
2482cd3d4880STomas Melin 		puts("Background operations not supported on device\n");
2483cd3d4880STomas Melin 		return -EMEDIUMTYPE;
2484cd3d4880STomas Melin 	}
2485cd3d4880STomas Melin 
2486cd3d4880STomas Melin 	if (ext_csd[EXT_CSD_BKOPS_EN] & 0x1) {
2487cd3d4880STomas Melin 		puts("Background operations already enabled\n");
2488cd3d4880STomas Melin 		return 0;
2489cd3d4880STomas Melin 	}
2490cd3d4880STomas Melin 
2491cd3d4880STomas Melin 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BKOPS_EN, 1);
2492cd3d4880STomas Melin 	if (err) {
2493cd3d4880STomas Melin 		puts("Failed to enable manual background operations\n");
2494cd3d4880STomas Melin 		return err;
2495cd3d4880STomas Melin 	}
2496cd3d4880STomas Melin 
2497cd3d4880STomas Melin 	puts("Enabled manual background operations\n");
2498cd3d4880STomas Melin 
2499cd3d4880STomas Melin 	return 0;
2500cd3d4880STomas Melin }
2501cd3d4880STomas Melin #endif
2502