xref: /rk3399_rockchip-uboot/drivers/mmc/mmc.c (revision d22e3d46a918bde2e6d3bc3f5782548d5ed75358)
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>
13272cc70bSAndy Fleming #include <mmc.h>
14272cc70bSAndy Fleming #include <part.h>
15272cc70bSAndy Fleming #include <malloc.h>
16272cc70bSAndy Fleming #include <linux/list.h>
179b1f942cSRabin Vincent #include <div64.h>
18da61fa5fSPaul Burton #include "mmc_private.h"
19272cc70bSAndy Fleming 
20272cc70bSAndy Fleming static struct list_head mmc_devices;
21272cc70bSAndy Fleming static int cur_dev_num = -1;
22272cc70bSAndy Fleming 
23d23d8d7eSNikita Kiryanov int __weak board_mmc_getwp(struct mmc *mmc)
24d23d8d7eSNikita Kiryanov {
25d23d8d7eSNikita Kiryanov 	return -1;
26d23d8d7eSNikita Kiryanov }
27d23d8d7eSNikita Kiryanov 
28d23d8d7eSNikita Kiryanov int mmc_getwp(struct mmc *mmc)
29d23d8d7eSNikita Kiryanov {
30d23d8d7eSNikita Kiryanov 	int wp;
31d23d8d7eSNikita Kiryanov 
32d23d8d7eSNikita Kiryanov 	wp = board_mmc_getwp(mmc);
33d23d8d7eSNikita Kiryanov 
34d4e1da4eSPeter Korsgaard 	if (wp < 0) {
3593bfd616SPantelis Antoniou 		if (mmc->cfg->ops->getwp)
3693bfd616SPantelis Antoniou 			wp = mmc->cfg->ops->getwp(mmc);
37d4e1da4eSPeter Korsgaard 		else
38d4e1da4eSPeter Korsgaard 			wp = 0;
39d4e1da4eSPeter Korsgaard 	}
40d23d8d7eSNikita Kiryanov 
41d23d8d7eSNikita Kiryanov 	return wp;
42d23d8d7eSNikita Kiryanov }
43d23d8d7eSNikita Kiryanov 
44314284b1SThierry Reding int __board_mmc_getcd(struct mmc *mmc) {
4511fdade2SStefano Babic 	return -1;
4611fdade2SStefano Babic }
4711fdade2SStefano Babic 
48314284b1SThierry Reding int board_mmc_getcd(struct mmc *mmc)__attribute__((weak,
4911fdade2SStefano Babic 	alias("__board_mmc_getcd")));
5011fdade2SStefano Babic 
51da61fa5fSPaul Burton int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
52272cc70bSAndy Fleming {
535db2fe3aSRaffaele Recalcati 	int ret;
548635ff9eSMarek Vasut 
558635ff9eSMarek Vasut #ifdef CONFIG_MMC_TRACE
565db2fe3aSRaffaele Recalcati 	int i;
575db2fe3aSRaffaele Recalcati 	u8 *ptr;
585db2fe3aSRaffaele Recalcati 
595db2fe3aSRaffaele Recalcati 	printf("CMD_SEND:%d\n", cmd->cmdidx);
605db2fe3aSRaffaele Recalcati 	printf("\t\tARG\t\t\t 0x%08X\n", cmd->cmdarg);
6193bfd616SPantelis Antoniou 	ret = mmc->cfg->ops->send_cmd(mmc, cmd, data);
625db2fe3aSRaffaele Recalcati 	switch (cmd->resp_type) {
635db2fe3aSRaffaele Recalcati 		case MMC_RSP_NONE:
645db2fe3aSRaffaele Recalcati 			printf("\t\tMMC_RSP_NONE\n");
655db2fe3aSRaffaele Recalcati 			break;
665db2fe3aSRaffaele Recalcati 		case MMC_RSP_R1:
675db2fe3aSRaffaele Recalcati 			printf("\t\tMMC_RSP_R1,5,6,7 \t 0x%08X \n",
685db2fe3aSRaffaele Recalcati 				cmd->response[0]);
695db2fe3aSRaffaele Recalcati 			break;
705db2fe3aSRaffaele Recalcati 		case MMC_RSP_R1b:
715db2fe3aSRaffaele Recalcati 			printf("\t\tMMC_RSP_R1b\t\t 0x%08X \n",
725db2fe3aSRaffaele Recalcati 				cmd->response[0]);
735db2fe3aSRaffaele Recalcati 			break;
745db2fe3aSRaffaele Recalcati 		case MMC_RSP_R2:
755db2fe3aSRaffaele Recalcati 			printf("\t\tMMC_RSP_R2\t\t 0x%08X \n",
765db2fe3aSRaffaele Recalcati 				cmd->response[0]);
775db2fe3aSRaffaele Recalcati 			printf("\t\t          \t\t 0x%08X \n",
785db2fe3aSRaffaele Recalcati 				cmd->response[1]);
795db2fe3aSRaffaele Recalcati 			printf("\t\t          \t\t 0x%08X \n",
805db2fe3aSRaffaele Recalcati 				cmd->response[2]);
815db2fe3aSRaffaele Recalcati 			printf("\t\t          \t\t 0x%08X \n",
825db2fe3aSRaffaele Recalcati 				cmd->response[3]);
835db2fe3aSRaffaele Recalcati 			printf("\n");
845db2fe3aSRaffaele Recalcati 			printf("\t\t\t\t\tDUMPING DATA\n");
855db2fe3aSRaffaele Recalcati 			for (i = 0; i < 4; i++) {
865db2fe3aSRaffaele Recalcati 				int j;
875db2fe3aSRaffaele Recalcati 				printf("\t\t\t\t\t%03d - ", i*4);
88146bec79SDirk Behme 				ptr = (u8 *)&cmd->response[i];
895db2fe3aSRaffaele Recalcati 				ptr += 3;
905db2fe3aSRaffaele Recalcati 				for (j = 0; j < 4; j++)
915db2fe3aSRaffaele Recalcati 					printf("%02X ", *ptr--);
925db2fe3aSRaffaele Recalcati 				printf("\n");
935db2fe3aSRaffaele Recalcati 			}
945db2fe3aSRaffaele Recalcati 			break;
955db2fe3aSRaffaele Recalcati 		case MMC_RSP_R3:
965db2fe3aSRaffaele Recalcati 			printf("\t\tMMC_RSP_R3,4\t\t 0x%08X \n",
975db2fe3aSRaffaele Recalcati 				cmd->response[0]);
985db2fe3aSRaffaele Recalcati 			break;
995db2fe3aSRaffaele Recalcati 		default:
1005db2fe3aSRaffaele Recalcati 			printf("\t\tERROR MMC rsp not supported\n");
1015db2fe3aSRaffaele Recalcati 			break;
1025db2fe3aSRaffaele Recalcati 	}
1035db2fe3aSRaffaele Recalcati #else
10493bfd616SPantelis Antoniou 	ret = mmc->cfg->ops->send_cmd(mmc, cmd, data);
1055db2fe3aSRaffaele Recalcati #endif
1068635ff9eSMarek Vasut 	return ret;
107272cc70bSAndy Fleming }
108272cc70bSAndy Fleming 
109da61fa5fSPaul Burton int mmc_send_status(struct mmc *mmc, int timeout)
1105d4fc8d9SRaffaele Recalcati {
1115d4fc8d9SRaffaele Recalcati 	struct mmc_cmd cmd;
112d617c426SJan Kloetzke 	int err, retries = 5;
1135d4fc8d9SRaffaele Recalcati #ifdef CONFIG_MMC_TRACE
1145d4fc8d9SRaffaele Recalcati 	int status;
1155d4fc8d9SRaffaele Recalcati #endif
1165d4fc8d9SRaffaele Recalcati 
1175d4fc8d9SRaffaele Recalcati 	cmd.cmdidx = MMC_CMD_SEND_STATUS;
1185d4fc8d9SRaffaele Recalcati 	cmd.resp_type = MMC_RSP_R1;
119aaf3d41aSMarek Vasut 	if (!mmc_host_is_spi(mmc))
120aaf3d41aSMarek Vasut 		cmd.cmdarg = mmc->rca << 16;
1215d4fc8d9SRaffaele Recalcati 
1225d4fc8d9SRaffaele Recalcati 	do {
1235d4fc8d9SRaffaele Recalcati 		err = mmc_send_cmd(mmc, &cmd, NULL);
124d617c426SJan Kloetzke 		if (!err) {
125d617c426SJan Kloetzke 			if ((cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) &&
126d617c426SJan Kloetzke 			    (cmd.response[0] & MMC_STATUS_CURR_STATE) !=
127d617c426SJan Kloetzke 			     MMC_STATE_PRG)
1285d4fc8d9SRaffaele Recalcati 				break;
129d617c426SJan Kloetzke 			else if (cmd.response[0] & MMC_STATUS_MASK) {
13056196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
131d617c426SJan Kloetzke 				printf("Status Error: 0x%08X\n",
132d617c426SJan Kloetzke 					cmd.response[0]);
13356196826SPaul Burton #endif
134d617c426SJan Kloetzke 				return COMM_ERR;
135d617c426SJan Kloetzke 			}
136d617c426SJan Kloetzke 		} else if (--retries < 0)
137d617c426SJan Kloetzke 			return err;
1385d4fc8d9SRaffaele Recalcati 
1395d4fc8d9SRaffaele Recalcati 		udelay(1000);
1405d4fc8d9SRaffaele Recalcati 
1415d4fc8d9SRaffaele Recalcati 	} while (timeout--);
1425d4fc8d9SRaffaele Recalcati 
1435db2fe3aSRaffaele Recalcati #ifdef CONFIG_MMC_TRACE
1445db2fe3aSRaffaele Recalcati 	status = (cmd.response[0] & MMC_STATUS_CURR_STATE) >> 9;
1455db2fe3aSRaffaele Recalcati 	printf("CURR STATE:%d\n", status);
1465db2fe3aSRaffaele Recalcati #endif
1475b0c942fSJongman Heo 	if (timeout <= 0) {
14856196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
1495d4fc8d9SRaffaele Recalcati 		printf("Timeout waiting card ready\n");
15056196826SPaul Burton #endif
1515d4fc8d9SRaffaele Recalcati 		return TIMEOUT;
1525d4fc8d9SRaffaele Recalcati 	}
1535d4fc8d9SRaffaele Recalcati 
1545d4fc8d9SRaffaele Recalcati 	return 0;
1555d4fc8d9SRaffaele Recalcati }
1565d4fc8d9SRaffaele Recalcati 
157da61fa5fSPaul Burton int mmc_set_blocklen(struct mmc *mmc, int len)
158272cc70bSAndy Fleming {
159272cc70bSAndy Fleming 	struct mmc_cmd cmd;
160272cc70bSAndy Fleming 
161*d22e3d46SJaehoon Chung 	if (mmc->card_caps & MMC_MODE_DDR_52MHz)
162*d22e3d46SJaehoon Chung 		return 0;
163*d22e3d46SJaehoon Chung 
164272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SET_BLOCKLEN;
165272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
166272cc70bSAndy Fleming 	cmd.cmdarg = len;
167272cc70bSAndy Fleming 
168272cc70bSAndy Fleming 	return mmc_send_cmd(mmc, &cmd, NULL);
169272cc70bSAndy Fleming }
170272cc70bSAndy Fleming 
171272cc70bSAndy Fleming struct mmc *find_mmc_device(int dev_num)
172272cc70bSAndy Fleming {
173272cc70bSAndy Fleming 	struct mmc *m;
174272cc70bSAndy Fleming 	struct list_head *entry;
175272cc70bSAndy Fleming 
176272cc70bSAndy Fleming 	list_for_each(entry, &mmc_devices) {
177272cc70bSAndy Fleming 		m = list_entry(entry, struct mmc, link);
178272cc70bSAndy Fleming 
179272cc70bSAndy Fleming 		if (m->block_dev.dev == dev_num)
180272cc70bSAndy Fleming 			return m;
181272cc70bSAndy Fleming 	}
182272cc70bSAndy Fleming 
18356196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
184272cc70bSAndy Fleming 	printf("MMC Device %d not found\n", dev_num);
18556196826SPaul Burton #endif
186272cc70bSAndy Fleming 
187272cc70bSAndy Fleming 	return NULL;
188272cc70bSAndy Fleming }
189272cc70bSAndy Fleming 
190ff8fef56SSascha Silbe static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start,
191fdbb873eSKim Phillips 			   lbaint_t blkcnt)
192272cc70bSAndy Fleming {
193272cc70bSAndy Fleming 	struct mmc_cmd cmd;
194272cc70bSAndy Fleming 	struct mmc_data data;
195272cc70bSAndy Fleming 
1964a1a06bcSAlagu Sankar 	if (blkcnt > 1)
1974a1a06bcSAlagu Sankar 		cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
1984a1a06bcSAlagu Sankar 	else
199272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
200272cc70bSAndy Fleming 
201272cc70bSAndy Fleming 	if (mmc->high_capacity)
2024a1a06bcSAlagu Sankar 		cmd.cmdarg = start;
203272cc70bSAndy Fleming 	else
2044a1a06bcSAlagu Sankar 		cmd.cmdarg = start * mmc->read_bl_len;
205272cc70bSAndy Fleming 
206272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
207272cc70bSAndy Fleming 
208272cc70bSAndy Fleming 	data.dest = dst;
2094a1a06bcSAlagu Sankar 	data.blocks = blkcnt;
210272cc70bSAndy Fleming 	data.blocksize = mmc->read_bl_len;
211272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
212272cc70bSAndy Fleming 
2134a1a06bcSAlagu Sankar 	if (mmc_send_cmd(mmc, &cmd, &data))
2144a1a06bcSAlagu Sankar 		return 0;
2154a1a06bcSAlagu Sankar 
2164a1a06bcSAlagu Sankar 	if (blkcnt > 1) {
2174a1a06bcSAlagu Sankar 		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
2184a1a06bcSAlagu Sankar 		cmd.cmdarg = 0;
2194a1a06bcSAlagu Sankar 		cmd.resp_type = MMC_RSP_R1b;
2204a1a06bcSAlagu Sankar 		if (mmc_send_cmd(mmc, &cmd, NULL)) {
22156196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
2224a1a06bcSAlagu Sankar 			printf("mmc fail to send stop cmd\n");
22356196826SPaul Burton #endif
2244a1a06bcSAlagu Sankar 			return 0;
2254a1a06bcSAlagu Sankar 		}
226272cc70bSAndy Fleming 	}
227272cc70bSAndy Fleming 
2284a1a06bcSAlagu Sankar 	return blkcnt;
229272cc70bSAndy Fleming }
230272cc70bSAndy Fleming 
231ff8fef56SSascha Silbe static ulong mmc_bread(int dev_num, lbaint_t start, lbaint_t blkcnt, void *dst)
232272cc70bSAndy Fleming {
2334a1a06bcSAlagu Sankar 	lbaint_t cur, blocks_todo = blkcnt;
234272cc70bSAndy Fleming 
2354a1a06bcSAlagu Sankar 	if (blkcnt == 0)
2364a1a06bcSAlagu Sankar 		return 0;
2374a1a06bcSAlagu Sankar 
2384a1a06bcSAlagu Sankar 	struct mmc *mmc = find_mmc_device(dev_num);
239272cc70bSAndy Fleming 	if (!mmc)
240272cc70bSAndy Fleming 		return 0;
241272cc70bSAndy Fleming 
242d2bf29e3SLei Wen 	if ((start + blkcnt) > mmc->block_dev.lba) {
24356196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
244ff8fef56SSascha Silbe 		printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
245d2bf29e3SLei Wen 			start + blkcnt, mmc->block_dev.lba);
24656196826SPaul Burton #endif
247d2bf29e3SLei Wen 		return 0;
248d2bf29e3SLei Wen 	}
249272cc70bSAndy Fleming 
2504a1a06bcSAlagu Sankar 	if (mmc_set_blocklen(mmc, mmc->read_bl_len))
251272cc70bSAndy Fleming 		return 0;
252272cc70bSAndy Fleming 
2534a1a06bcSAlagu Sankar 	do {
25493bfd616SPantelis Antoniou 		cur = (blocks_todo > mmc->cfg->b_max) ?
25593bfd616SPantelis Antoniou 			mmc->cfg->b_max : blocks_todo;
2564a1a06bcSAlagu Sankar 		if(mmc_read_blocks(mmc, dst, start, cur) != cur)
2574a1a06bcSAlagu Sankar 			return 0;
2584a1a06bcSAlagu Sankar 		blocks_todo -= cur;
2594a1a06bcSAlagu Sankar 		start += cur;
2604a1a06bcSAlagu Sankar 		dst += cur * mmc->read_bl_len;
2614a1a06bcSAlagu Sankar 	} while (blocks_todo > 0);
262272cc70bSAndy Fleming 
263272cc70bSAndy Fleming 	return blkcnt;
264272cc70bSAndy Fleming }
265272cc70bSAndy Fleming 
266fdbb873eSKim Phillips static int mmc_go_idle(struct mmc *mmc)
267272cc70bSAndy Fleming {
268272cc70bSAndy Fleming 	struct mmc_cmd cmd;
269272cc70bSAndy Fleming 	int err;
270272cc70bSAndy Fleming 
271272cc70bSAndy Fleming 	udelay(1000);
272272cc70bSAndy Fleming 
273272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_GO_IDLE_STATE;
274272cc70bSAndy Fleming 	cmd.cmdarg = 0;
275272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_NONE;
276272cc70bSAndy Fleming 
277272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
278272cc70bSAndy Fleming 
279272cc70bSAndy Fleming 	if (err)
280272cc70bSAndy Fleming 		return err;
281272cc70bSAndy Fleming 
282272cc70bSAndy Fleming 	udelay(2000);
283272cc70bSAndy Fleming 
284272cc70bSAndy Fleming 	return 0;
285272cc70bSAndy Fleming }
286272cc70bSAndy Fleming 
287fdbb873eSKim Phillips static int sd_send_op_cond(struct mmc *mmc)
288272cc70bSAndy Fleming {
289272cc70bSAndy Fleming 	int timeout = 1000;
290272cc70bSAndy Fleming 	int err;
291272cc70bSAndy Fleming 	struct mmc_cmd cmd;
292272cc70bSAndy Fleming 
293272cc70bSAndy Fleming 	do {
294272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_APP_CMD;
295272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R1;
296272cc70bSAndy Fleming 		cmd.cmdarg = 0;
297272cc70bSAndy Fleming 
298272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
299272cc70bSAndy Fleming 
300272cc70bSAndy Fleming 		if (err)
301272cc70bSAndy Fleming 			return err;
302272cc70bSAndy Fleming 
303272cc70bSAndy Fleming 		cmd.cmdidx = SD_CMD_APP_SEND_OP_COND;
304272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R3;
305250de12bSStefano Babic 
306250de12bSStefano Babic 		/*
307250de12bSStefano Babic 		 * Most cards do not answer if some reserved bits
308250de12bSStefano Babic 		 * in the ocr are set. However, Some controller
309250de12bSStefano Babic 		 * can set bit 7 (reserved for low voltages), but
310250de12bSStefano Babic 		 * how to manage low voltages SD card is not yet
311250de12bSStefano Babic 		 * specified.
312250de12bSStefano Babic 		 */
313d52ebf10SThomas Chou 		cmd.cmdarg = mmc_host_is_spi(mmc) ? 0 :
31493bfd616SPantelis Antoniou 			(mmc->cfg->voltages & 0xff8000);
315272cc70bSAndy Fleming 
316272cc70bSAndy Fleming 		if (mmc->version == SD_VERSION_2)
317272cc70bSAndy Fleming 			cmd.cmdarg |= OCR_HCS;
318272cc70bSAndy Fleming 
319272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
320272cc70bSAndy Fleming 
321272cc70bSAndy Fleming 		if (err)
322272cc70bSAndy Fleming 			return err;
323272cc70bSAndy Fleming 
324272cc70bSAndy Fleming 		udelay(1000);
325272cc70bSAndy Fleming 	} while ((!(cmd.response[0] & OCR_BUSY)) && timeout--);
326272cc70bSAndy Fleming 
327272cc70bSAndy Fleming 	if (timeout <= 0)
328272cc70bSAndy Fleming 		return UNUSABLE_ERR;
329272cc70bSAndy Fleming 
330272cc70bSAndy Fleming 	if (mmc->version != SD_VERSION_2)
331272cc70bSAndy Fleming 		mmc->version = SD_VERSION_1_0;
332272cc70bSAndy Fleming 
333d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc)) { /* read OCR for spi */
334d52ebf10SThomas Chou 		cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
335d52ebf10SThomas Chou 		cmd.resp_type = MMC_RSP_R3;
336d52ebf10SThomas Chou 		cmd.cmdarg = 0;
337d52ebf10SThomas Chou 
338d52ebf10SThomas Chou 		err = mmc_send_cmd(mmc, &cmd, NULL);
339d52ebf10SThomas Chou 
340d52ebf10SThomas Chou 		if (err)
341d52ebf10SThomas Chou 			return err;
342d52ebf10SThomas Chou 	}
343d52ebf10SThomas Chou 
344998be3ddSRabin Vincent 	mmc->ocr = cmd.response[0];
345272cc70bSAndy Fleming 
346272cc70bSAndy Fleming 	mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
347272cc70bSAndy Fleming 	mmc->rca = 0;
348272cc70bSAndy Fleming 
349272cc70bSAndy Fleming 	return 0;
350272cc70bSAndy Fleming }
351272cc70bSAndy Fleming 
352e9550449SChe-Liang Chiou /* We pass in the cmd since otherwise the init seems to fail */
353e9550449SChe-Liang Chiou static int mmc_send_op_cond_iter(struct mmc *mmc, struct mmc_cmd *cmd,
354e9550449SChe-Liang Chiou 		int use_arg)
355272cc70bSAndy Fleming {
356272cc70bSAndy Fleming 	int err;
357272cc70bSAndy Fleming 
358e9550449SChe-Liang Chiou 	cmd->cmdidx = MMC_CMD_SEND_OP_COND;
359e9550449SChe-Liang Chiou 	cmd->resp_type = MMC_RSP_R3;
360e9550449SChe-Liang Chiou 	cmd->cmdarg = 0;
361e9550449SChe-Liang Chiou 	if (use_arg && !mmc_host_is_spi(mmc)) {
362e9550449SChe-Liang Chiou 		cmd->cmdarg =
36393bfd616SPantelis Antoniou 			(mmc->cfg->voltages &
364e9550449SChe-Liang Chiou 			(mmc->op_cond_response & OCR_VOLTAGE_MASK)) |
365e9550449SChe-Liang Chiou 			(mmc->op_cond_response & OCR_ACCESS_MODE);
366e9550449SChe-Liang Chiou 
36793bfd616SPantelis Antoniou 		if (mmc->cfg->host_caps & MMC_MODE_HC)
368e9550449SChe-Liang Chiou 			cmd->cmdarg |= OCR_HCS;
369e9550449SChe-Liang Chiou 	}
370e9550449SChe-Liang Chiou 	err = mmc_send_cmd(mmc, cmd, NULL);
371e9550449SChe-Liang Chiou 	if (err)
372e9550449SChe-Liang Chiou 		return err;
373e9550449SChe-Liang Chiou 	mmc->op_cond_response = cmd->response[0];
374e9550449SChe-Liang Chiou 	return 0;
375e9550449SChe-Liang Chiou }
376e9550449SChe-Liang Chiou 
377e9550449SChe-Liang Chiou int mmc_send_op_cond(struct mmc *mmc)
378e9550449SChe-Liang Chiou {
379e9550449SChe-Liang Chiou 	struct mmc_cmd cmd;
380e9550449SChe-Liang Chiou 	int err, i;
381e9550449SChe-Liang Chiou 
382272cc70bSAndy Fleming 	/* Some cards seem to need this */
383272cc70bSAndy Fleming 	mmc_go_idle(mmc);
384272cc70bSAndy Fleming 
38531cacbabSRaffaele Recalcati  	/* Asking to the card its capabilities */
386e9550449SChe-Liang Chiou 	mmc->op_cond_pending = 1;
387e9550449SChe-Liang Chiou 	for (i = 0; i < 2; i++) {
388e9550449SChe-Liang Chiou 		err = mmc_send_op_cond_iter(mmc, &cmd, i != 0);
38931cacbabSRaffaele Recalcati 		if (err)
39031cacbabSRaffaele Recalcati 			return err;
39131cacbabSRaffaele Recalcati 
392e9550449SChe-Liang Chiou 		/* exit if not busy (flag seems to be inverted) */
393e9550449SChe-Liang Chiou 		if (mmc->op_cond_response & OCR_BUSY)
394e9550449SChe-Liang Chiou 			return 0;
395e9550449SChe-Liang Chiou 	}
396e9550449SChe-Liang Chiou 	return IN_PROGRESS;
397e9550449SChe-Liang Chiou }
39831cacbabSRaffaele Recalcati 
399e9550449SChe-Liang Chiou int mmc_complete_op_cond(struct mmc *mmc)
400e9550449SChe-Liang Chiou {
401e9550449SChe-Liang Chiou 	struct mmc_cmd cmd;
402e9550449SChe-Liang Chiou 	int timeout = 1000;
403e9550449SChe-Liang Chiou 	uint start;
404e9550449SChe-Liang Chiou 	int err;
405e9550449SChe-Liang Chiou 
406e9550449SChe-Liang Chiou 	mmc->op_cond_pending = 0;
407e9550449SChe-Liang Chiou 	start = get_timer(0);
408272cc70bSAndy Fleming 	do {
409e9550449SChe-Liang Chiou 		err = mmc_send_op_cond_iter(mmc, &cmd, 1);
410272cc70bSAndy Fleming 		if (err)
411272cc70bSAndy Fleming 			return err;
412e9550449SChe-Liang Chiou 		if (get_timer(start) > timeout)
413272cc70bSAndy Fleming 			return UNUSABLE_ERR;
414e9550449SChe-Liang Chiou 		udelay(100);
415e9550449SChe-Liang Chiou 	} while (!(mmc->op_cond_response & OCR_BUSY));
416272cc70bSAndy Fleming 
417d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc)) { /* read OCR for spi */
418d52ebf10SThomas Chou 		cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
419d52ebf10SThomas Chou 		cmd.resp_type = MMC_RSP_R3;
420d52ebf10SThomas Chou 		cmd.cmdarg = 0;
421d52ebf10SThomas Chou 
422d52ebf10SThomas Chou 		err = mmc_send_cmd(mmc, &cmd, NULL);
423d52ebf10SThomas Chou 
424d52ebf10SThomas Chou 		if (err)
425d52ebf10SThomas Chou 			return err;
426d52ebf10SThomas Chou 	}
427d52ebf10SThomas Chou 
428272cc70bSAndy Fleming 	mmc->version = MMC_VERSION_UNKNOWN;
429998be3ddSRabin Vincent 	mmc->ocr = cmd.response[0];
430272cc70bSAndy Fleming 
431272cc70bSAndy Fleming 	mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
432def816a2SStephen Warren 	mmc->rca = 1;
433272cc70bSAndy Fleming 
434272cc70bSAndy Fleming 	return 0;
435272cc70bSAndy Fleming }
436272cc70bSAndy Fleming 
437272cc70bSAndy Fleming 
438fdbb873eSKim Phillips static int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd)
439272cc70bSAndy Fleming {
440272cc70bSAndy Fleming 	struct mmc_cmd cmd;
441272cc70bSAndy Fleming 	struct mmc_data data;
442272cc70bSAndy Fleming 	int err;
443272cc70bSAndy Fleming 
444272cc70bSAndy Fleming 	/* Get the Card Status Register */
445272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SEND_EXT_CSD;
446272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
447272cc70bSAndy Fleming 	cmd.cmdarg = 0;
448272cc70bSAndy Fleming 
449cdfd1ac6SYoshihiro Shimoda 	data.dest = (char *)ext_csd;
450272cc70bSAndy Fleming 	data.blocks = 1;
4518bfa195eSSimon Glass 	data.blocksize = MMC_MAX_BLOCK_LEN;
452272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
453272cc70bSAndy Fleming 
454272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, &data);
455272cc70bSAndy Fleming 
456272cc70bSAndy Fleming 	return err;
457272cc70bSAndy Fleming }
458272cc70bSAndy Fleming 
459272cc70bSAndy Fleming 
460fdbb873eSKim Phillips static int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)
461272cc70bSAndy Fleming {
462272cc70bSAndy Fleming 	struct mmc_cmd cmd;
4635d4fc8d9SRaffaele Recalcati 	int timeout = 1000;
4645d4fc8d9SRaffaele Recalcati 	int ret;
465272cc70bSAndy Fleming 
466272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SWITCH;
467272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1b;
468272cc70bSAndy Fleming 	cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
469272cc70bSAndy Fleming 				 (index << 16) |
470272cc70bSAndy Fleming 				 (value << 8);
471272cc70bSAndy Fleming 
4725d4fc8d9SRaffaele Recalcati 	ret = mmc_send_cmd(mmc, &cmd, NULL);
4735d4fc8d9SRaffaele Recalcati 
4745d4fc8d9SRaffaele Recalcati 	/* Waiting for the ready status */
47593ad0d18SJan Kloetzke 	if (!ret)
47693ad0d18SJan Kloetzke 		ret = mmc_send_status(mmc, timeout);
4775d4fc8d9SRaffaele Recalcati 
4785d4fc8d9SRaffaele Recalcati 	return ret;
4795d4fc8d9SRaffaele Recalcati 
480272cc70bSAndy Fleming }
481272cc70bSAndy Fleming 
482fdbb873eSKim Phillips static int mmc_change_freq(struct mmc *mmc)
483272cc70bSAndy Fleming {
4848bfa195eSSimon Glass 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
485272cc70bSAndy Fleming 	char cardtype;
486272cc70bSAndy Fleming 	int err;
487272cc70bSAndy Fleming 
488272cc70bSAndy Fleming 	mmc->card_caps = 0;
489272cc70bSAndy Fleming 
490d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc))
491d52ebf10SThomas Chou 		return 0;
492d52ebf10SThomas Chou 
493272cc70bSAndy Fleming 	/* Only version 4 supports high-speed */
494272cc70bSAndy Fleming 	if (mmc->version < MMC_VERSION_4)
495272cc70bSAndy Fleming 		return 0;
496272cc70bSAndy Fleming 
497272cc70bSAndy Fleming 	err = mmc_send_ext_csd(mmc, ext_csd);
498272cc70bSAndy Fleming 
499272cc70bSAndy Fleming 	if (err)
500272cc70bSAndy Fleming 		return err;
501272cc70bSAndy Fleming 
5020560db18SLei Wen 	cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf;
503272cc70bSAndy Fleming 
504272cc70bSAndy Fleming 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1);
505272cc70bSAndy Fleming 
506272cc70bSAndy Fleming 	if (err)
507272cc70bSAndy Fleming 		return err;
508272cc70bSAndy Fleming 
509272cc70bSAndy Fleming 	/* Now check to see that it worked */
510272cc70bSAndy Fleming 	err = mmc_send_ext_csd(mmc, ext_csd);
511272cc70bSAndy Fleming 
512272cc70bSAndy Fleming 	if (err)
513272cc70bSAndy Fleming 		return err;
514272cc70bSAndy Fleming 
515272cc70bSAndy Fleming 	/* No high-speed support */
5160560db18SLei Wen 	if (!ext_csd[EXT_CSD_HS_TIMING])
517272cc70bSAndy Fleming 		return 0;
518272cc70bSAndy Fleming 
519272cc70bSAndy Fleming 	/* High Speed is set, there are two types: 52MHz and 26MHz */
520*d22e3d46SJaehoon Chung 	if (cardtype & EXT_CSD_CARD_TYPE_52) {
521*d22e3d46SJaehoon Chung 		if (cardtype & EXT_CSD_CARD_TYPE_DDR_52)
522*d22e3d46SJaehoon Chung 			mmc->card_caps |= MMC_MODE_DDR_52MHz;
523272cc70bSAndy Fleming 		mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
524*d22e3d46SJaehoon Chung 	} else {
525272cc70bSAndy Fleming 		mmc->card_caps |= MMC_MODE_HS;
526*d22e3d46SJaehoon Chung 	}
527272cc70bSAndy Fleming 
528272cc70bSAndy Fleming 	return 0;
529272cc70bSAndy Fleming }
530272cc70bSAndy Fleming 
531f866a46dSStephen Warren static int mmc_set_capacity(struct mmc *mmc, int part_num)
532f866a46dSStephen Warren {
533f866a46dSStephen Warren 	switch (part_num) {
534f866a46dSStephen Warren 	case 0:
535f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_user;
536f866a46dSStephen Warren 		break;
537f866a46dSStephen Warren 	case 1:
538f866a46dSStephen Warren 	case 2:
539f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_boot;
540f866a46dSStephen Warren 		break;
541f866a46dSStephen Warren 	case 3:
542f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_rpmb;
543f866a46dSStephen Warren 		break;
544f866a46dSStephen Warren 	case 4:
545f866a46dSStephen Warren 	case 5:
546f866a46dSStephen Warren 	case 6:
547f866a46dSStephen Warren 	case 7:
548f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_gp[part_num - 4];
549f866a46dSStephen Warren 		break;
550f866a46dSStephen Warren 	default:
551f866a46dSStephen Warren 		return -1;
552f866a46dSStephen Warren 	}
553f866a46dSStephen Warren 
554f866a46dSStephen Warren 	mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len);
555f866a46dSStephen Warren 
556f866a46dSStephen Warren 	return 0;
557f866a46dSStephen Warren }
558f866a46dSStephen Warren 
559bc897b1dSLei Wen int mmc_switch_part(int dev_num, unsigned int part_num)
560bc897b1dSLei Wen {
561bc897b1dSLei Wen 	struct mmc *mmc = find_mmc_device(dev_num);
562f866a46dSStephen Warren 	int ret;
563bc897b1dSLei Wen 
564bc897b1dSLei Wen 	if (!mmc)
565bc897b1dSLei Wen 		return -1;
566bc897b1dSLei Wen 
567f866a46dSStephen Warren 	ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
568bc897b1dSLei Wen 			 (mmc->part_config & ~PART_ACCESS_MASK)
569bc897b1dSLei Wen 			 | (part_num & PART_ACCESS_MASK));
570f866a46dSStephen Warren 	if (ret)
571f866a46dSStephen Warren 		return ret;
572f866a46dSStephen Warren 
573f866a46dSStephen Warren 	return mmc_set_capacity(mmc, part_num);
574bc897b1dSLei Wen }
575bc897b1dSLei Wen 
57648972d90SThierry Reding int mmc_getcd(struct mmc *mmc)
57748972d90SThierry Reding {
57848972d90SThierry Reding 	int cd;
57948972d90SThierry Reding 
58048972d90SThierry Reding 	cd = board_mmc_getcd(mmc);
58148972d90SThierry Reding 
582d4e1da4eSPeter Korsgaard 	if (cd < 0) {
58393bfd616SPantelis Antoniou 		if (mmc->cfg->ops->getcd)
58493bfd616SPantelis Antoniou 			cd = mmc->cfg->ops->getcd(mmc);
585d4e1da4eSPeter Korsgaard 		else
586d4e1da4eSPeter Korsgaard 			cd = 1;
587d4e1da4eSPeter Korsgaard 	}
58848972d90SThierry Reding 
58948972d90SThierry Reding 	return cd;
59048972d90SThierry Reding }
59148972d90SThierry Reding 
592fdbb873eSKim Phillips static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)
593272cc70bSAndy Fleming {
594272cc70bSAndy Fleming 	struct mmc_cmd cmd;
595272cc70bSAndy Fleming 	struct mmc_data data;
596272cc70bSAndy Fleming 
597272cc70bSAndy Fleming 	/* Switch the frequency */
598272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_SWITCH_FUNC;
599272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
600272cc70bSAndy Fleming 	cmd.cmdarg = (mode << 31) | 0xffffff;
601272cc70bSAndy Fleming 	cmd.cmdarg &= ~(0xf << (group * 4));
602272cc70bSAndy Fleming 	cmd.cmdarg |= value << (group * 4);
603272cc70bSAndy Fleming 
604272cc70bSAndy Fleming 	data.dest = (char *)resp;
605272cc70bSAndy Fleming 	data.blocksize = 64;
606272cc70bSAndy Fleming 	data.blocks = 1;
607272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
608272cc70bSAndy Fleming 
609272cc70bSAndy Fleming 	return mmc_send_cmd(mmc, &cmd, &data);
610272cc70bSAndy Fleming }
611272cc70bSAndy Fleming 
612272cc70bSAndy Fleming 
613fdbb873eSKim Phillips static int sd_change_freq(struct mmc *mmc)
614272cc70bSAndy Fleming {
615272cc70bSAndy Fleming 	int err;
616272cc70bSAndy Fleming 	struct mmc_cmd cmd;
617f781dd38SAnton staaf 	ALLOC_CACHE_ALIGN_BUFFER(uint, scr, 2);
618f781dd38SAnton staaf 	ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16);
619272cc70bSAndy Fleming 	struct mmc_data data;
620272cc70bSAndy Fleming 	int timeout;
621272cc70bSAndy Fleming 
622272cc70bSAndy Fleming 	mmc->card_caps = 0;
623272cc70bSAndy Fleming 
624d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc))
625d52ebf10SThomas Chou 		return 0;
626d52ebf10SThomas Chou 
627272cc70bSAndy Fleming 	/* Read the SCR to find out if this card supports higher speeds */
628272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_APP_CMD;
629272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
630272cc70bSAndy Fleming 	cmd.cmdarg = mmc->rca << 16;
631272cc70bSAndy Fleming 
632272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
633272cc70bSAndy Fleming 
634272cc70bSAndy Fleming 	if (err)
635272cc70bSAndy Fleming 		return err;
636272cc70bSAndy Fleming 
637272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_APP_SEND_SCR;
638272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
639272cc70bSAndy Fleming 	cmd.cmdarg = 0;
640272cc70bSAndy Fleming 
641272cc70bSAndy Fleming 	timeout = 3;
642272cc70bSAndy Fleming 
643272cc70bSAndy Fleming retry_scr:
644f781dd38SAnton staaf 	data.dest = (char *)scr;
645272cc70bSAndy Fleming 	data.blocksize = 8;
646272cc70bSAndy Fleming 	data.blocks = 1;
647272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
648272cc70bSAndy Fleming 
649272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, &data);
650272cc70bSAndy Fleming 
651272cc70bSAndy Fleming 	if (err) {
652272cc70bSAndy Fleming 		if (timeout--)
653272cc70bSAndy Fleming 			goto retry_scr;
654272cc70bSAndy Fleming 
655272cc70bSAndy Fleming 		return err;
656272cc70bSAndy Fleming 	}
657272cc70bSAndy Fleming 
6584e3d89baSYauhen Kharuzhy 	mmc->scr[0] = __be32_to_cpu(scr[0]);
6594e3d89baSYauhen Kharuzhy 	mmc->scr[1] = __be32_to_cpu(scr[1]);
660272cc70bSAndy Fleming 
661272cc70bSAndy Fleming 	switch ((mmc->scr[0] >> 24) & 0xf) {
662272cc70bSAndy Fleming 		case 0:
663272cc70bSAndy Fleming 			mmc->version = SD_VERSION_1_0;
664272cc70bSAndy Fleming 			break;
665272cc70bSAndy Fleming 		case 1:
666272cc70bSAndy Fleming 			mmc->version = SD_VERSION_1_10;
667272cc70bSAndy Fleming 			break;
668272cc70bSAndy Fleming 		case 2:
669272cc70bSAndy Fleming 			mmc->version = SD_VERSION_2;
6701741c64dSJaehoon Chung 			if ((mmc->scr[0] >> 15) & 0x1)
6711741c64dSJaehoon Chung 				mmc->version = SD_VERSION_3;
672272cc70bSAndy Fleming 			break;
673272cc70bSAndy Fleming 		default:
674272cc70bSAndy Fleming 			mmc->version = SD_VERSION_1_0;
675272cc70bSAndy Fleming 			break;
676272cc70bSAndy Fleming 	}
677272cc70bSAndy Fleming 
678b44c7083SAlagu Sankar 	if (mmc->scr[0] & SD_DATA_4BIT)
679b44c7083SAlagu Sankar 		mmc->card_caps |= MMC_MODE_4BIT;
680b44c7083SAlagu Sankar 
681272cc70bSAndy Fleming 	/* Version 1.0 doesn't support switching */
682272cc70bSAndy Fleming 	if (mmc->version == SD_VERSION_1_0)
683272cc70bSAndy Fleming 		return 0;
684272cc70bSAndy Fleming 
685272cc70bSAndy Fleming 	timeout = 4;
686272cc70bSAndy Fleming 	while (timeout--) {
687272cc70bSAndy Fleming 		err = sd_switch(mmc, SD_SWITCH_CHECK, 0, 1,
688f781dd38SAnton staaf 				(u8 *)switch_status);
689272cc70bSAndy Fleming 
690272cc70bSAndy Fleming 		if (err)
691272cc70bSAndy Fleming 			return err;
692272cc70bSAndy Fleming 
693272cc70bSAndy Fleming 		/* The high-speed function is busy.  Try again */
6944e3d89baSYauhen Kharuzhy 		if (!(__be32_to_cpu(switch_status[7]) & SD_HIGHSPEED_BUSY))
695272cc70bSAndy Fleming 			break;
696272cc70bSAndy Fleming 	}
697272cc70bSAndy Fleming 
698272cc70bSAndy Fleming 	/* If high-speed isn't supported, we return */
6994e3d89baSYauhen Kharuzhy 	if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED))
700272cc70bSAndy Fleming 		return 0;
701272cc70bSAndy Fleming 
7022c3fbf4cSMacpaul Lin 	/*
7032c3fbf4cSMacpaul Lin 	 * If the host doesn't support SD_HIGHSPEED, do not switch card to
7042c3fbf4cSMacpaul Lin 	 * HIGHSPEED mode even if the card support SD_HIGHSPPED.
7052c3fbf4cSMacpaul Lin 	 * This can avoid furthur problem when the card runs in different
7062c3fbf4cSMacpaul Lin 	 * mode between the host.
7072c3fbf4cSMacpaul Lin 	 */
70893bfd616SPantelis Antoniou 	if (!((mmc->cfg->host_caps & MMC_MODE_HS_52MHz) &&
70993bfd616SPantelis Antoniou 		(mmc->cfg->host_caps & MMC_MODE_HS)))
7102c3fbf4cSMacpaul Lin 		return 0;
7112c3fbf4cSMacpaul Lin 
712f781dd38SAnton staaf 	err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status);
713272cc70bSAndy Fleming 
714272cc70bSAndy Fleming 	if (err)
715272cc70bSAndy Fleming 		return err;
716272cc70bSAndy Fleming 
7174e3d89baSYauhen Kharuzhy 	if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) == 0x01000000)
718272cc70bSAndy Fleming 		mmc->card_caps |= MMC_MODE_HS;
719272cc70bSAndy Fleming 
720272cc70bSAndy Fleming 	return 0;
721272cc70bSAndy Fleming }
722272cc70bSAndy Fleming 
723272cc70bSAndy Fleming /* frequency bases */
724272cc70bSAndy Fleming /* divided by 10 to be nice to platforms without floating point */
7255f837c2cSMike Frysinger static const int fbase[] = {
726272cc70bSAndy Fleming 	10000,
727272cc70bSAndy Fleming 	100000,
728272cc70bSAndy Fleming 	1000000,
729272cc70bSAndy Fleming 	10000000,
730272cc70bSAndy Fleming };
731272cc70bSAndy Fleming 
732272cc70bSAndy Fleming /* Multiplier values for TRAN_SPEED.  Multiplied by 10 to be nice
733272cc70bSAndy Fleming  * to platforms without floating point.
734272cc70bSAndy Fleming  */
7355f837c2cSMike Frysinger static const int multipliers[] = {
736272cc70bSAndy Fleming 	0,	/* reserved */
737272cc70bSAndy Fleming 	10,
738272cc70bSAndy Fleming 	12,
739272cc70bSAndy Fleming 	13,
740272cc70bSAndy Fleming 	15,
741272cc70bSAndy Fleming 	20,
742272cc70bSAndy Fleming 	25,
743272cc70bSAndy Fleming 	30,
744272cc70bSAndy Fleming 	35,
745272cc70bSAndy Fleming 	40,
746272cc70bSAndy Fleming 	45,
747272cc70bSAndy Fleming 	50,
748272cc70bSAndy Fleming 	55,
749272cc70bSAndy Fleming 	60,
750272cc70bSAndy Fleming 	70,
751272cc70bSAndy Fleming 	80,
752272cc70bSAndy Fleming };
753272cc70bSAndy Fleming 
754fdbb873eSKim Phillips static void mmc_set_ios(struct mmc *mmc)
755272cc70bSAndy Fleming {
75693bfd616SPantelis Antoniou 	if (mmc->cfg->ops->set_ios)
75793bfd616SPantelis Antoniou 		mmc->cfg->ops->set_ios(mmc);
758272cc70bSAndy Fleming }
759272cc70bSAndy Fleming 
760272cc70bSAndy Fleming void mmc_set_clock(struct mmc *mmc, uint clock)
761272cc70bSAndy Fleming {
76293bfd616SPantelis Antoniou 	if (clock > mmc->cfg->f_max)
76393bfd616SPantelis Antoniou 		clock = mmc->cfg->f_max;
764272cc70bSAndy Fleming 
76593bfd616SPantelis Antoniou 	if (clock < mmc->cfg->f_min)
76693bfd616SPantelis Antoniou 		clock = mmc->cfg->f_min;
767272cc70bSAndy Fleming 
768272cc70bSAndy Fleming 	mmc->clock = clock;
769272cc70bSAndy Fleming 
770272cc70bSAndy Fleming 	mmc_set_ios(mmc);
771272cc70bSAndy Fleming }
772272cc70bSAndy Fleming 
773fdbb873eSKim Phillips static void mmc_set_bus_width(struct mmc *mmc, uint width)
774272cc70bSAndy Fleming {
775272cc70bSAndy Fleming 	mmc->bus_width = width;
776272cc70bSAndy Fleming 
777272cc70bSAndy Fleming 	mmc_set_ios(mmc);
778272cc70bSAndy Fleming }
779272cc70bSAndy Fleming 
780fdbb873eSKim Phillips static int mmc_startup(struct mmc *mmc)
781272cc70bSAndy Fleming {
782f866a46dSStephen Warren 	int err, i;
783272cc70bSAndy Fleming 	uint mult, freq;
784639b7827SYoshihiro Shimoda 	u64 cmult, csize, capacity;
785272cc70bSAndy Fleming 	struct mmc_cmd cmd;
7868bfa195eSSimon Glass 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
7878bfa195eSSimon Glass 	ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN);
7885d4fc8d9SRaffaele Recalcati 	int timeout = 1000;
789272cc70bSAndy Fleming 
790d52ebf10SThomas Chou #ifdef CONFIG_MMC_SPI_CRC_ON
791d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */
792d52ebf10SThomas Chou 		cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF;
793d52ebf10SThomas Chou 		cmd.resp_type = MMC_RSP_R1;
794d52ebf10SThomas Chou 		cmd.cmdarg = 1;
795d52ebf10SThomas Chou 		err = mmc_send_cmd(mmc, &cmd, NULL);
796d52ebf10SThomas Chou 
797d52ebf10SThomas Chou 		if (err)
798d52ebf10SThomas Chou 			return err;
799d52ebf10SThomas Chou 	}
800d52ebf10SThomas Chou #endif
801d52ebf10SThomas Chou 
802272cc70bSAndy Fleming 	/* Put the Card in Identify Mode */
803d52ebf10SThomas Chou 	cmd.cmdidx = mmc_host_is_spi(mmc) ? MMC_CMD_SEND_CID :
804d52ebf10SThomas Chou 		MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */
805272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R2;
806272cc70bSAndy Fleming 	cmd.cmdarg = 0;
807272cc70bSAndy Fleming 
808272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
809272cc70bSAndy Fleming 
810272cc70bSAndy Fleming 	if (err)
811272cc70bSAndy Fleming 		return err;
812272cc70bSAndy Fleming 
813272cc70bSAndy Fleming 	memcpy(mmc->cid, cmd.response, 16);
814272cc70bSAndy Fleming 
815272cc70bSAndy Fleming 	/*
816272cc70bSAndy Fleming 	 * For MMC cards, set the Relative Address.
817272cc70bSAndy Fleming 	 * For SD cards, get the Relatvie Address.
818272cc70bSAndy Fleming 	 * This also puts the cards into Standby State
819272cc70bSAndy Fleming 	 */
820d52ebf10SThomas Chou 	if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
821272cc70bSAndy Fleming 		cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;
822272cc70bSAndy Fleming 		cmd.cmdarg = mmc->rca << 16;
823272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R6;
824272cc70bSAndy Fleming 
825272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
826272cc70bSAndy Fleming 
827272cc70bSAndy Fleming 		if (err)
828272cc70bSAndy Fleming 			return err;
829272cc70bSAndy Fleming 
830272cc70bSAndy Fleming 		if (IS_SD(mmc))
831998be3ddSRabin Vincent 			mmc->rca = (cmd.response[0] >> 16) & 0xffff;
832d52ebf10SThomas Chou 	}
833272cc70bSAndy Fleming 
834272cc70bSAndy Fleming 	/* Get the Card-Specific Data */
835272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SEND_CSD;
836272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R2;
837272cc70bSAndy Fleming 	cmd.cmdarg = mmc->rca << 16;
838272cc70bSAndy Fleming 
839272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
840272cc70bSAndy Fleming 
8415d4fc8d9SRaffaele Recalcati 	/* Waiting for the ready status */
8425d4fc8d9SRaffaele Recalcati 	mmc_send_status(mmc, timeout);
8435d4fc8d9SRaffaele Recalcati 
844272cc70bSAndy Fleming 	if (err)
845272cc70bSAndy Fleming 		return err;
846272cc70bSAndy Fleming 
847998be3ddSRabin Vincent 	mmc->csd[0] = cmd.response[0];
848998be3ddSRabin Vincent 	mmc->csd[1] = cmd.response[1];
849998be3ddSRabin Vincent 	mmc->csd[2] = cmd.response[2];
850998be3ddSRabin Vincent 	mmc->csd[3] = cmd.response[3];
851272cc70bSAndy Fleming 
852272cc70bSAndy Fleming 	if (mmc->version == MMC_VERSION_UNKNOWN) {
8530b453ffeSRabin Vincent 		int version = (cmd.response[0] >> 26) & 0xf;
854272cc70bSAndy Fleming 
855272cc70bSAndy Fleming 		switch (version) {
856272cc70bSAndy Fleming 			case 0:
857272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_1_2;
858272cc70bSAndy Fleming 				break;
859272cc70bSAndy Fleming 			case 1:
860272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_1_4;
861272cc70bSAndy Fleming 				break;
862272cc70bSAndy Fleming 			case 2:
863272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_2_2;
864272cc70bSAndy Fleming 				break;
865272cc70bSAndy Fleming 			case 3:
866272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_3;
867272cc70bSAndy Fleming 				break;
868272cc70bSAndy Fleming 			case 4:
869272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_4;
870272cc70bSAndy Fleming 				break;
871272cc70bSAndy Fleming 			default:
872272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_1_2;
873272cc70bSAndy Fleming 				break;
874272cc70bSAndy Fleming 		}
875272cc70bSAndy Fleming 	}
876272cc70bSAndy Fleming 
877272cc70bSAndy Fleming 	/* divide frequency by 10, since the mults are 10x bigger */
8780b453ffeSRabin Vincent 	freq = fbase[(cmd.response[0] & 0x7)];
8790b453ffeSRabin Vincent 	mult = multipliers[((cmd.response[0] >> 3) & 0xf)];
880272cc70bSAndy Fleming 
881272cc70bSAndy Fleming 	mmc->tran_speed = freq * mult;
882272cc70bSAndy Fleming 
883ab71188cSMarkus Niebel 	mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1);
884998be3ddSRabin Vincent 	mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf);
885272cc70bSAndy Fleming 
886272cc70bSAndy Fleming 	if (IS_SD(mmc))
887272cc70bSAndy Fleming 		mmc->write_bl_len = mmc->read_bl_len;
888272cc70bSAndy Fleming 	else
889998be3ddSRabin Vincent 		mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf);
890272cc70bSAndy Fleming 
891272cc70bSAndy Fleming 	if (mmc->high_capacity) {
892272cc70bSAndy Fleming 		csize = (mmc->csd[1] & 0x3f) << 16
893272cc70bSAndy Fleming 			| (mmc->csd[2] & 0xffff0000) >> 16;
894272cc70bSAndy Fleming 		cmult = 8;
895272cc70bSAndy Fleming 	} else {
896272cc70bSAndy Fleming 		csize = (mmc->csd[1] & 0x3ff) << 2
897272cc70bSAndy Fleming 			| (mmc->csd[2] & 0xc0000000) >> 30;
898272cc70bSAndy Fleming 		cmult = (mmc->csd[2] & 0x00038000) >> 15;
899272cc70bSAndy Fleming 	}
900272cc70bSAndy Fleming 
901f866a46dSStephen Warren 	mmc->capacity_user = (csize + 1) << (cmult + 2);
902f866a46dSStephen Warren 	mmc->capacity_user *= mmc->read_bl_len;
903f866a46dSStephen Warren 	mmc->capacity_boot = 0;
904f866a46dSStephen Warren 	mmc->capacity_rpmb = 0;
905f866a46dSStephen Warren 	for (i = 0; i < 4; i++)
906f866a46dSStephen Warren 		mmc->capacity_gp[i] = 0;
907272cc70bSAndy Fleming 
9088bfa195eSSimon Glass 	if (mmc->read_bl_len > MMC_MAX_BLOCK_LEN)
9098bfa195eSSimon Glass 		mmc->read_bl_len = MMC_MAX_BLOCK_LEN;
910272cc70bSAndy Fleming 
9118bfa195eSSimon Glass 	if (mmc->write_bl_len > MMC_MAX_BLOCK_LEN)
9128bfa195eSSimon Glass 		mmc->write_bl_len = MMC_MAX_BLOCK_LEN;
913272cc70bSAndy Fleming 
914ab71188cSMarkus Niebel 	if ((mmc->dsr_imp) && (0xffffffff != mmc->dsr)) {
915ab71188cSMarkus Niebel 		cmd.cmdidx = MMC_CMD_SET_DSR;
916ab71188cSMarkus Niebel 		cmd.cmdarg = (mmc->dsr & 0xffff) << 16;
917ab71188cSMarkus Niebel 		cmd.resp_type = MMC_RSP_NONE;
918ab71188cSMarkus Niebel 		if (mmc_send_cmd(mmc, &cmd, NULL))
919ab71188cSMarkus Niebel 			printf("MMC: SET_DSR failed\n");
920ab71188cSMarkus Niebel 	}
921ab71188cSMarkus Niebel 
922272cc70bSAndy Fleming 	/* Select the card, and put it into Transfer Mode */
923d52ebf10SThomas Chou 	if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
924272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_SELECT_CARD;
925fe8f7066SAjay Bhargav 		cmd.resp_type = MMC_RSP_R1;
926272cc70bSAndy Fleming 		cmd.cmdarg = mmc->rca << 16;
927272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
928272cc70bSAndy Fleming 
929272cc70bSAndy Fleming 		if (err)
930272cc70bSAndy Fleming 			return err;
931d52ebf10SThomas Chou 	}
932272cc70bSAndy Fleming 
933e6f99a56SLei Wen 	/*
934e6f99a56SLei Wen 	 * For SD, its erase group is always one sector
935e6f99a56SLei Wen 	 */
936e6f99a56SLei Wen 	mmc->erase_grp_size = 1;
937bc897b1dSLei Wen 	mmc->part_config = MMCPART_NOAVAILABLE;
938d23e2c09SSukumar Ghorai 	if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) {
939d23e2c09SSukumar Ghorai 		/* check  ext_csd version and capacity */
940d23e2c09SSukumar Ghorai 		err = mmc_send_ext_csd(mmc, ext_csd);
941fdbb873eSKim Phillips 		if (!err && (ext_csd[EXT_CSD_REV] >= 2)) {
942639b7827SYoshihiro Shimoda 			/*
943639b7827SYoshihiro Shimoda 			 * According to the JEDEC Standard, the value of
944639b7827SYoshihiro Shimoda 			 * ext_csd's capacity is valid if the value is more
945639b7827SYoshihiro Shimoda 			 * than 2GB
946639b7827SYoshihiro Shimoda 			 */
9470560db18SLei Wen 			capacity = ext_csd[EXT_CSD_SEC_CNT] << 0
9480560db18SLei Wen 					| ext_csd[EXT_CSD_SEC_CNT + 1] << 8
9490560db18SLei Wen 					| ext_csd[EXT_CSD_SEC_CNT + 2] << 16
9500560db18SLei Wen 					| ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
9518bfa195eSSimon Glass 			capacity *= MMC_MAX_BLOCK_LEN;
952b1f1e821SŁukasz Majewski 			if ((capacity >> 20) > 2 * 1024)
953f866a46dSStephen Warren 				mmc->capacity_user = capacity;
954d23e2c09SSukumar Ghorai 		}
955bc897b1dSLei Wen 
95664f4a619SJaehoon Chung 		switch (ext_csd[EXT_CSD_REV]) {
95764f4a619SJaehoon Chung 		case 1:
95864f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_1;
95964f4a619SJaehoon Chung 			break;
96064f4a619SJaehoon Chung 		case 2:
96164f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_2;
96264f4a619SJaehoon Chung 			break;
96364f4a619SJaehoon Chung 		case 3:
96464f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_3;
96564f4a619SJaehoon Chung 			break;
96664f4a619SJaehoon Chung 		case 5:
96764f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_41;
96864f4a619SJaehoon Chung 			break;
96964f4a619SJaehoon Chung 		case 6:
97064f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_5;
97164f4a619SJaehoon Chung 			break;
97264f4a619SJaehoon Chung 		}
97364f4a619SJaehoon Chung 
974e6f99a56SLei Wen 		/*
9751937e5aaSOliver Metz 		 * Host needs to enable ERASE_GRP_DEF bit if device is
9761937e5aaSOliver Metz 		 * partitioned. This bit will be lost every time after a reset
9771937e5aaSOliver Metz 		 * or power off. This will affect erase size.
978e6f99a56SLei Wen 		 */
9791937e5aaSOliver Metz 		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) &&
9801937e5aaSOliver Metz 		    (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) {
9811937e5aaSOliver Metz 			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
9821937e5aaSOliver Metz 				EXT_CSD_ERASE_GROUP_DEF, 1);
9831937e5aaSOliver Metz 
9841937e5aaSOliver Metz 			if (err)
9851937e5aaSOliver Metz 				return err;
9861937e5aaSOliver Metz 
9871937e5aaSOliver Metz 			/* Read out group size from ext_csd */
9880560db18SLei Wen 			mmc->erase_grp_size =
9898bfa195eSSimon Glass 				ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] *
9908bfa195eSSimon Glass 					MMC_MAX_BLOCK_LEN * 1024;
9918bfa195eSSimon Glass 		} else {
9921937e5aaSOliver Metz 			/* Calculate the group size from the csd value. */
993e6f99a56SLei Wen 			int erase_gsz, erase_gmul;
994e6f99a56SLei Wen 			erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10;
995e6f99a56SLei Wen 			erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5;
996e6f99a56SLei Wen 			mmc->erase_grp_size = (erase_gsz + 1)
997e6f99a56SLei Wen 				* (erase_gmul + 1);
998e6f99a56SLei Wen 		}
999e6f99a56SLei Wen 
1000bc897b1dSLei Wen 		/* store the partition info of emmc */
10018948ea83SStephen Warren 		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) ||
10028948ea83SStephen Warren 		    ext_csd[EXT_CSD_BOOT_MULT])
10030560db18SLei Wen 			mmc->part_config = ext_csd[EXT_CSD_PART_CONF];
1004f866a46dSStephen Warren 
1005f866a46dSStephen Warren 		mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17;
1006f866a46dSStephen Warren 
1007f866a46dSStephen Warren 		mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17;
1008f866a46dSStephen Warren 
1009f866a46dSStephen Warren 		for (i = 0; i < 4; i++) {
1010f866a46dSStephen Warren 			int idx = EXT_CSD_GP_SIZE_MULT + i * 3;
1011f866a46dSStephen Warren 			mmc->capacity_gp[i] = (ext_csd[idx + 2] << 16) +
1012f866a46dSStephen Warren 				(ext_csd[idx + 1] << 8) + ext_csd[idx];
1013f866a46dSStephen Warren 			mmc->capacity_gp[i] *=
1014f866a46dSStephen Warren 				ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
1015f866a46dSStephen Warren 			mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
1016d23e2c09SSukumar Ghorai 		}
1017f866a46dSStephen Warren 	}
1018f866a46dSStephen Warren 
1019f866a46dSStephen Warren 	err = mmc_set_capacity(mmc, mmc->part_num);
1020f866a46dSStephen Warren 	if (err)
1021f866a46dSStephen Warren 		return err;
1022d23e2c09SSukumar Ghorai 
1023272cc70bSAndy Fleming 	if (IS_SD(mmc))
1024272cc70bSAndy Fleming 		err = sd_change_freq(mmc);
1025272cc70bSAndy Fleming 	else
1026272cc70bSAndy Fleming 		err = mmc_change_freq(mmc);
1027272cc70bSAndy Fleming 
1028272cc70bSAndy Fleming 	if (err)
1029272cc70bSAndy Fleming 		return err;
1030272cc70bSAndy Fleming 
1031272cc70bSAndy Fleming 	/* Restrict card's capabilities by what the host can do */
103293bfd616SPantelis Antoniou 	mmc->card_caps &= mmc->cfg->host_caps;
1033272cc70bSAndy Fleming 
1034272cc70bSAndy Fleming 	if (IS_SD(mmc)) {
1035272cc70bSAndy Fleming 		if (mmc->card_caps & MMC_MODE_4BIT) {
1036272cc70bSAndy Fleming 			cmd.cmdidx = MMC_CMD_APP_CMD;
1037272cc70bSAndy Fleming 			cmd.resp_type = MMC_RSP_R1;
1038272cc70bSAndy Fleming 			cmd.cmdarg = mmc->rca << 16;
1039272cc70bSAndy Fleming 
1040272cc70bSAndy Fleming 			err = mmc_send_cmd(mmc, &cmd, NULL);
1041272cc70bSAndy Fleming 			if (err)
1042272cc70bSAndy Fleming 				return err;
1043272cc70bSAndy Fleming 
1044272cc70bSAndy Fleming 			cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH;
1045272cc70bSAndy Fleming 			cmd.resp_type = MMC_RSP_R1;
1046272cc70bSAndy Fleming 			cmd.cmdarg = 2;
1047272cc70bSAndy Fleming 			err = mmc_send_cmd(mmc, &cmd, NULL);
1048272cc70bSAndy Fleming 			if (err)
1049272cc70bSAndy Fleming 				return err;
1050272cc70bSAndy Fleming 
1051272cc70bSAndy Fleming 			mmc_set_bus_width(mmc, 4);
1052272cc70bSAndy Fleming 		}
1053272cc70bSAndy Fleming 
1054272cc70bSAndy Fleming 		if (mmc->card_caps & MMC_MODE_HS)
1055ad5fd922SJaehoon Chung 			mmc->tran_speed = 50000000;
1056272cc70bSAndy Fleming 		else
1057ad5fd922SJaehoon Chung 			mmc->tran_speed = 25000000;
1058272cc70bSAndy Fleming 	} else {
10597798f6dbSAndy Fleming 		int idx;
10607798f6dbSAndy Fleming 
10617798f6dbSAndy Fleming 		/* An array of possible bus widths in order of preference */
10627798f6dbSAndy Fleming 		static unsigned ext_csd_bits[] = {
1063*d22e3d46SJaehoon Chung 			EXT_CSD_DDR_BUS_WIDTH_8,
1064*d22e3d46SJaehoon Chung 			EXT_CSD_DDR_BUS_WIDTH_4,
10657798f6dbSAndy Fleming 			EXT_CSD_BUS_WIDTH_8,
10667798f6dbSAndy Fleming 			EXT_CSD_BUS_WIDTH_4,
10677798f6dbSAndy Fleming 			EXT_CSD_BUS_WIDTH_1,
10687798f6dbSAndy Fleming 		};
10697798f6dbSAndy Fleming 
10707798f6dbSAndy Fleming 		/* An array to map CSD bus widths to host cap bits */
10717798f6dbSAndy Fleming 		static unsigned ext_to_hostcaps[] = {
1072*d22e3d46SJaehoon Chung 			[EXT_CSD_DDR_BUS_WIDTH_4] = MMC_MODE_DDR_52MHz,
1073*d22e3d46SJaehoon Chung 			[EXT_CSD_DDR_BUS_WIDTH_8] = MMC_MODE_DDR_52MHz,
10747798f6dbSAndy Fleming 			[EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT,
10757798f6dbSAndy Fleming 			[EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT,
10767798f6dbSAndy Fleming 		};
10777798f6dbSAndy Fleming 
10787798f6dbSAndy Fleming 		/* An array to map chosen bus width to an integer */
10797798f6dbSAndy Fleming 		static unsigned widths[] = {
1080*d22e3d46SJaehoon Chung 			8, 4, 8, 4, 1,
10817798f6dbSAndy Fleming 		};
10827798f6dbSAndy Fleming 
10837798f6dbSAndy Fleming 		for (idx=0; idx < ARRAY_SIZE(ext_csd_bits); idx++) {
10847798f6dbSAndy Fleming 			unsigned int extw = ext_csd_bits[idx];
10857798f6dbSAndy Fleming 
10867798f6dbSAndy Fleming 			/*
10877798f6dbSAndy Fleming 			 * Check to make sure the controller supports
10887798f6dbSAndy Fleming 			 * this bus width, if it's more than 1
10897798f6dbSAndy Fleming 			 */
10907798f6dbSAndy Fleming 			if (extw != EXT_CSD_BUS_WIDTH_1 &&
109193bfd616SPantelis Antoniou 					!(mmc->cfg->host_caps & ext_to_hostcaps[extw]))
10927798f6dbSAndy Fleming 				continue;
10937798f6dbSAndy Fleming 
1094272cc70bSAndy Fleming 			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
10957798f6dbSAndy Fleming 					EXT_CSD_BUS_WIDTH, extw);
1096272cc70bSAndy Fleming 
1097272cc70bSAndy Fleming 			if (err)
10984137894eSLei Wen 				continue;
1099272cc70bSAndy Fleming 
11007798f6dbSAndy Fleming 			mmc_set_bus_width(mmc, widths[idx]);
1101272cc70bSAndy Fleming 
11024137894eSLei Wen 			err = mmc_send_ext_csd(mmc, test_csd);
11034137894eSLei Wen 			if (!err && ext_csd[EXT_CSD_PARTITIONING_SUPPORT] \
11044137894eSLei Wen 				    == test_csd[EXT_CSD_PARTITIONING_SUPPORT]
11054137894eSLei Wen 				 && ext_csd[EXT_CSD_ERASE_GROUP_DEF] \
11064137894eSLei Wen 				    == test_csd[EXT_CSD_ERASE_GROUP_DEF] \
11074137894eSLei Wen 				 && ext_csd[EXT_CSD_REV] \
11084137894eSLei Wen 				    == test_csd[EXT_CSD_REV]
11094137894eSLei Wen 				 && ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] \
11104137894eSLei Wen 				    == test_csd[EXT_CSD_HC_ERASE_GRP_SIZE]
11114137894eSLei Wen 				 && memcmp(&ext_csd[EXT_CSD_SEC_CNT], \
11124137894eSLei Wen 					&test_csd[EXT_CSD_SEC_CNT], 4) == 0) {
1113272cc70bSAndy Fleming 
11147798f6dbSAndy Fleming 				mmc->card_caps |= ext_to_hostcaps[extw];
11154137894eSLei Wen 				break;
11164137894eSLei Wen 			}
1117272cc70bSAndy Fleming 		}
1118272cc70bSAndy Fleming 
1119272cc70bSAndy Fleming 		if (mmc->card_caps & MMC_MODE_HS) {
1120272cc70bSAndy Fleming 			if (mmc->card_caps & MMC_MODE_HS_52MHz)
1121ad5fd922SJaehoon Chung 				mmc->tran_speed = 52000000;
1122272cc70bSAndy Fleming 			else
1123ad5fd922SJaehoon Chung 				mmc->tran_speed = 26000000;
1124272cc70bSAndy Fleming 		}
1125ad5fd922SJaehoon Chung 	}
1126ad5fd922SJaehoon Chung 
1127ad5fd922SJaehoon Chung 	mmc_set_clock(mmc, mmc->tran_speed);
1128272cc70bSAndy Fleming 
1129272cc70bSAndy Fleming 	/* fill in device description */
1130272cc70bSAndy Fleming 	mmc->block_dev.lun = 0;
1131272cc70bSAndy Fleming 	mmc->block_dev.type = 0;
1132272cc70bSAndy Fleming 	mmc->block_dev.blksz = mmc->read_bl_len;
11330472fbfdSEgbert Eich 	mmc->block_dev.log2blksz = LOG2(mmc->block_dev.blksz);
11349b1f942cSRabin Vincent 	mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len);
113556196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
1136babce5f6STaylor Hutt 	sprintf(mmc->block_dev.vendor, "Man %06x Snr %04x%04x",
1137babce5f6STaylor Hutt 		mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff),
1138babce5f6STaylor Hutt 		(mmc->cid[3] >> 16) & 0xffff);
1139babce5f6STaylor Hutt 	sprintf(mmc->block_dev.product, "%c%c%c%c%c%c", mmc->cid[0] & 0xff,
11400b453ffeSRabin Vincent 		(mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
1141babce5f6STaylor Hutt 		(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff,
1142babce5f6STaylor Hutt 		(mmc->cid[2] >> 24) & 0xff);
1143babce5f6STaylor Hutt 	sprintf(mmc->block_dev.revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf,
1144babce5f6STaylor Hutt 		(mmc->cid[2] >> 16) & 0xf);
114556196826SPaul Burton #else
114656196826SPaul Burton 	mmc->block_dev.vendor[0] = 0;
114756196826SPaul Burton 	mmc->block_dev.product[0] = 0;
114856196826SPaul Burton 	mmc->block_dev.revision[0] = 0;
114956196826SPaul Burton #endif
1150122efd43SMikhail Kshevetskiy #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT)
1151272cc70bSAndy Fleming 	init_part(&mmc->block_dev);
1152122efd43SMikhail Kshevetskiy #endif
1153272cc70bSAndy Fleming 
1154272cc70bSAndy Fleming 	return 0;
1155272cc70bSAndy Fleming }
1156272cc70bSAndy Fleming 
1157fdbb873eSKim Phillips static int mmc_send_if_cond(struct mmc *mmc)
1158272cc70bSAndy Fleming {
1159272cc70bSAndy Fleming 	struct mmc_cmd cmd;
1160272cc70bSAndy Fleming 	int err;
1161272cc70bSAndy Fleming 
1162272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_SEND_IF_COND;
1163272cc70bSAndy Fleming 	/* We set the bit if the host supports voltages between 2.7 and 3.6 V */
116493bfd616SPantelis Antoniou 	cmd.cmdarg = ((mmc->cfg->voltages & 0xff8000) != 0) << 8 | 0xaa;
1165272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R7;
1166272cc70bSAndy Fleming 
1167272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
1168272cc70bSAndy Fleming 
1169272cc70bSAndy Fleming 	if (err)
1170272cc70bSAndy Fleming 		return err;
1171272cc70bSAndy Fleming 
1172998be3ddSRabin Vincent 	if ((cmd.response[0] & 0xff) != 0xaa)
1173272cc70bSAndy Fleming 		return UNUSABLE_ERR;
1174272cc70bSAndy Fleming 	else
1175272cc70bSAndy Fleming 		mmc->version = SD_VERSION_2;
1176272cc70bSAndy Fleming 
1177272cc70bSAndy Fleming 	return 0;
1178272cc70bSAndy Fleming }
1179272cc70bSAndy Fleming 
118093bfd616SPantelis Antoniou /* not used any more */
118193bfd616SPantelis Antoniou int __deprecated mmc_register(struct mmc *mmc)
1182272cc70bSAndy Fleming {
118393bfd616SPantelis Antoniou #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
118493bfd616SPantelis Antoniou 	printf("%s is deprecated! use mmc_create() instead.\n", __func__);
118593bfd616SPantelis Antoniou #endif
118693bfd616SPantelis Antoniou 	return -1;
118793bfd616SPantelis Antoniou }
118893bfd616SPantelis Antoniou 
118993bfd616SPantelis Antoniou struct mmc *mmc_create(const struct mmc_config *cfg, void *priv)
119093bfd616SPantelis Antoniou {
119193bfd616SPantelis Antoniou 	struct mmc *mmc;
119293bfd616SPantelis Antoniou 
119393bfd616SPantelis Antoniou 	/* quick validation */
119493bfd616SPantelis Antoniou 	if (cfg == NULL || cfg->ops == NULL || cfg->ops->send_cmd == NULL ||
119593bfd616SPantelis Antoniou 			cfg->f_min == 0 || cfg->f_max == 0 || cfg->b_max == 0)
119693bfd616SPantelis Antoniou 		return NULL;
119793bfd616SPantelis Antoniou 
119893bfd616SPantelis Antoniou 	mmc = calloc(1, sizeof(*mmc));
119993bfd616SPantelis Antoniou 	if (mmc == NULL)
120093bfd616SPantelis Antoniou 		return NULL;
120193bfd616SPantelis Antoniou 
120293bfd616SPantelis Antoniou 	mmc->cfg = cfg;
120393bfd616SPantelis Antoniou 	mmc->priv = priv;
120493bfd616SPantelis Antoniou 
120593bfd616SPantelis Antoniou 	/* the following chunk was mmc_register() */
120693bfd616SPantelis Antoniou 
1207ab71188cSMarkus Niebel 	/* Setup dsr related values */
1208ab71188cSMarkus Niebel 	mmc->dsr_imp = 0;
1209ab71188cSMarkus Niebel 	mmc->dsr = 0xffffffff;
1210272cc70bSAndy Fleming 	/* Setup the universal parts of the block interface just once */
1211272cc70bSAndy Fleming 	mmc->block_dev.if_type = IF_TYPE_MMC;
1212272cc70bSAndy Fleming 	mmc->block_dev.dev = cur_dev_num++;
1213272cc70bSAndy Fleming 	mmc->block_dev.removable = 1;
1214272cc70bSAndy Fleming 	mmc->block_dev.block_read = mmc_bread;
1215272cc70bSAndy Fleming 	mmc->block_dev.block_write = mmc_bwrite;
1216e6f99a56SLei Wen 	mmc->block_dev.block_erase = mmc_berase;
121793bfd616SPantelis Antoniou 
121893bfd616SPantelis Antoniou 	/* setup initial part type */
121993bfd616SPantelis Antoniou 	mmc->block_dev.part_type = mmc->cfg->part_type;
1220272cc70bSAndy Fleming 
1221272cc70bSAndy Fleming 	INIT_LIST_HEAD(&mmc->link);
1222272cc70bSAndy Fleming 
1223272cc70bSAndy Fleming 	list_add_tail(&mmc->link, &mmc_devices);
1224272cc70bSAndy Fleming 
122593bfd616SPantelis Antoniou 	return mmc;
122693bfd616SPantelis Antoniou }
122793bfd616SPantelis Antoniou 
122893bfd616SPantelis Antoniou void mmc_destroy(struct mmc *mmc)
122993bfd616SPantelis Antoniou {
123093bfd616SPantelis Antoniou 	/* only freeing memory for now */
123193bfd616SPantelis Antoniou 	free(mmc);
1232272cc70bSAndy Fleming }
1233272cc70bSAndy Fleming 
1234df3fc526SMatthew McClintock #ifdef CONFIG_PARTITIONS
1235272cc70bSAndy Fleming block_dev_desc_t *mmc_get_dev(int dev)
1236272cc70bSAndy Fleming {
1237272cc70bSAndy Fleming 	struct mmc *mmc = find_mmc_device(dev);
12386bb4b4bcSBenoît Thébaudeau 	if (!mmc || mmc_init(mmc))
123940242bc3SŁukasz Majewski 		return NULL;
1240272cc70bSAndy Fleming 
124140242bc3SŁukasz Majewski 	return &mmc->block_dev;
1242272cc70bSAndy Fleming }
1243df3fc526SMatthew McClintock #endif
1244272cc70bSAndy Fleming 
1245e9550449SChe-Liang Chiou int mmc_start_init(struct mmc *mmc)
1246272cc70bSAndy Fleming {
1247afd5932bSMacpaul Lin 	int err;
1248272cc70bSAndy Fleming 
1249ab769f22SPantelis Antoniou 	/* we pretend there's no card when init is NULL */
125093bfd616SPantelis Antoniou 	if (mmc_getcd(mmc) == 0 || mmc->cfg->ops->init == NULL) {
125148972d90SThierry Reding 		mmc->has_init = 0;
125256196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
125348972d90SThierry Reding 		printf("MMC: no card present\n");
125456196826SPaul Burton #endif
125548972d90SThierry Reding 		return NO_CARD_ERR;
125648972d90SThierry Reding 	}
125748972d90SThierry Reding 
1258bc897b1dSLei Wen 	if (mmc->has_init)
1259bc897b1dSLei Wen 		return 0;
1260bc897b1dSLei Wen 
1261ab769f22SPantelis Antoniou 	/* made sure it's not NULL earlier */
126293bfd616SPantelis Antoniou 	err = mmc->cfg->ops->init(mmc);
1263272cc70bSAndy Fleming 
1264272cc70bSAndy Fleming 	if (err)
1265272cc70bSAndy Fleming 		return err;
1266272cc70bSAndy Fleming 
1267b86b85e2SIlya Yanok 	mmc_set_bus_width(mmc, 1);
1268b86b85e2SIlya Yanok 	mmc_set_clock(mmc, 1);
1269b86b85e2SIlya Yanok 
1270272cc70bSAndy Fleming 	/* Reset the Card */
1271272cc70bSAndy Fleming 	err = mmc_go_idle(mmc);
1272272cc70bSAndy Fleming 
1273272cc70bSAndy Fleming 	if (err)
1274272cc70bSAndy Fleming 		return err;
1275272cc70bSAndy Fleming 
1276bc897b1dSLei Wen 	/* The internal partition reset to user partition(0) at every CMD0*/
1277bc897b1dSLei Wen 	mmc->part_num = 0;
1278bc897b1dSLei Wen 
1279272cc70bSAndy Fleming 	/* Test for SD version 2 */
1280272cc70bSAndy Fleming 	err = mmc_send_if_cond(mmc);
1281272cc70bSAndy Fleming 
1282272cc70bSAndy Fleming 	/* Now try to get the SD card's operating condition */
1283272cc70bSAndy Fleming 	err = sd_send_op_cond(mmc);
1284272cc70bSAndy Fleming 
1285272cc70bSAndy Fleming 	/* If the command timed out, we check for an MMC card */
1286272cc70bSAndy Fleming 	if (err == TIMEOUT) {
1287272cc70bSAndy Fleming 		err = mmc_send_op_cond(mmc);
1288272cc70bSAndy Fleming 
1289e9550449SChe-Liang Chiou 		if (err && err != IN_PROGRESS) {
129056196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
1291272cc70bSAndy Fleming 			printf("Card did not respond to voltage select!\n");
129256196826SPaul Burton #endif
1293272cc70bSAndy Fleming 			return UNUSABLE_ERR;
1294272cc70bSAndy Fleming 		}
1295272cc70bSAndy Fleming 	}
1296272cc70bSAndy Fleming 
1297e9550449SChe-Liang Chiou 	if (err == IN_PROGRESS)
1298e9550449SChe-Liang Chiou 		mmc->init_in_progress = 1;
1299e9550449SChe-Liang Chiou 
1300e9550449SChe-Liang Chiou 	return err;
1301e9550449SChe-Liang Chiou }
1302e9550449SChe-Liang Chiou 
1303e9550449SChe-Liang Chiou static int mmc_complete_init(struct mmc *mmc)
1304e9550449SChe-Liang Chiou {
1305e9550449SChe-Liang Chiou 	int err = 0;
1306e9550449SChe-Liang Chiou 
1307e9550449SChe-Liang Chiou 	if (mmc->op_cond_pending)
1308e9550449SChe-Liang Chiou 		err = mmc_complete_op_cond(mmc);
1309e9550449SChe-Liang Chiou 
1310e9550449SChe-Liang Chiou 	if (!err)
1311bc897b1dSLei Wen 		err = mmc_startup(mmc);
1312bc897b1dSLei Wen 	if (err)
1313bc897b1dSLei Wen 		mmc->has_init = 0;
1314bc897b1dSLei Wen 	else
1315bc897b1dSLei Wen 		mmc->has_init = 1;
1316e9550449SChe-Liang Chiou 	mmc->init_in_progress = 0;
1317e9550449SChe-Liang Chiou 	return err;
1318e9550449SChe-Liang Chiou }
1319e9550449SChe-Liang Chiou 
1320e9550449SChe-Liang Chiou int mmc_init(struct mmc *mmc)
1321e9550449SChe-Liang Chiou {
1322e9550449SChe-Liang Chiou 	int err = IN_PROGRESS;
1323e9550449SChe-Liang Chiou 	unsigned start = get_timer(0);
1324e9550449SChe-Liang Chiou 
1325e9550449SChe-Liang Chiou 	if (mmc->has_init)
1326e9550449SChe-Liang Chiou 		return 0;
1327e9550449SChe-Liang Chiou 	if (!mmc->init_in_progress)
1328e9550449SChe-Liang Chiou 		err = mmc_start_init(mmc);
1329e9550449SChe-Liang Chiou 
1330e9550449SChe-Liang Chiou 	if (!err || err == IN_PROGRESS)
1331e9550449SChe-Liang Chiou 		err = mmc_complete_init(mmc);
1332e9550449SChe-Liang Chiou 	debug("%s: %d, time %lu\n", __func__, err, get_timer(start));
1333bc897b1dSLei Wen 	return err;
1334272cc70bSAndy Fleming }
1335272cc70bSAndy Fleming 
1336ab71188cSMarkus Niebel int mmc_set_dsr(struct mmc *mmc, u16 val)
1337ab71188cSMarkus Niebel {
1338ab71188cSMarkus Niebel 	mmc->dsr = val;
1339ab71188cSMarkus Niebel 	return 0;
1340ab71188cSMarkus Niebel }
1341ab71188cSMarkus Niebel 
1342272cc70bSAndy Fleming /*
1343272cc70bSAndy Fleming  * CPU and board-specific MMC initializations.  Aliased function
1344272cc70bSAndy Fleming  * signals caller to move on
1345272cc70bSAndy Fleming  */
1346272cc70bSAndy Fleming static int __def_mmc_init(bd_t *bis)
1347272cc70bSAndy Fleming {
1348272cc70bSAndy Fleming 	return -1;
1349272cc70bSAndy Fleming }
1350272cc70bSAndy Fleming 
1351f9a109b3SPeter Tyser int cpu_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init")));
1352f9a109b3SPeter Tyser int board_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init")));
1353272cc70bSAndy Fleming 
135456196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
135556196826SPaul Burton 
1356272cc70bSAndy Fleming void print_mmc_devices(char separator)
1357272cc70bSAndy Fleming {
1358272cc70bSAndy Fleming 	struct mmc *m;
1359272cc70bSAndy Fleming 	struct list_head *entry;
1360272cc70bSAndy Fleming 
1361272cc70bSAndy Fleming 	list_for_each(entry, &mmc_devices) {
1362272cc70bSAndy Fleming 		m = list_entry(entry, struct mmc, link);
1363272cc70bSAndy Fleming 
136493bfd616SPantelis Antoniou 		printf("%s: %d", m->cfg->name, m->block_dev.dev);
1365272cc70bSAndy Fleming 
1366272cc70bSAndy Fleming 		if (entry->next != &mmc_devices)
1367272cc70bSAndy Fleming 			printf("%c ", separator);
1368272cc70bSAndy Fleming 	}
1369272cc70bSAndy Fleming 
1370272cc70bSAndy Fleming 	printf("\n");
1371272cc70bSAndy Fleming }
1372272cc70bSAndy Fleming 
137356196826SPaul Burton #else
137456196826SPaul Burton void print_mmc_devices(char separator) { }
137556196826SPaul Burton #endif
137656196826SPaul Burton 
1377ea6ebe21SLei Wen int get_mmc_num(void)
1378ea6ebe21SLei Wen {
1379ea6ebe21SLei Wen 	return cur_dev_num;
1380ea6ebe21SLei Wen }
1381ea6ebe21SLei Wen 
1382e9550449SChe-Liang Chiou void mmc_set_preinit(struct mmc *mmc, int preinit)
1383e9550449SChe-Liang Chiou {
1384e9550449SChe-Liang Chiou 	mmc->preinit = preinit;
1385e9550449SChe-Liang Chiou }
1386e9550449SChe-Liang Chiou 
1387e9550449SChe-Liang Chiou static void do_preinit(void)
1388e9550449SChe-Liang Chiou {
1389e9550449SChe-Liang Chiou 	struct mmc *m;
1390e9550449SChe-Liang Chiou 	struct list_head *entry;
1391e9550449SChe-Liang Chiou 
1392e9550449SChe-Liang Chiou 	list_for_each(entry, &mmc_devices) {
1393e9550449SChe-Liang Chiou 		m = list_entry(entry, struct mmc, link);
1394e9550449SChe-Liang Chiou 
1395e9550449SChe-Liang Chiou 		if (m->preinit)
1396e9550449SChe-Liang Chiou 			mmc_start_init(m);
1397e9550449SChe-Liang Chiou 	}
1398e9550449SChe-Liang Chiou }
1399e9550449SChe-Liang Chiou 
1400e9550449SChe-Liang Chiou 
1401272cc70bSAndy Fleming int mmc_initialize(bd_t *bis)
1402272cc70bSAndy Fleming {
1403272cc70bSAndy Fleming 	INIT_LIST_HEAD (&mmc_devices);
1404272cc70bSAndy Fleming 	cur_dev_num = 0;
1405272cc70bSAndy Fleming 
1406272cc70bSAndy Fleming 	if (board_mmc_init(bis) < 0)
1407272cc70bSAndy Fleming 		cpu_mmc_init(bis);
1408272cc70bSAndy Fleming 
1409bb0dc108SYing Zhang #ifndef CONFIG_SPL_BUILD
1410272cc70bSAndy Fleming 	print_mmc_devices(',');
1411bb0dc108SYing Zhang #endif
1412272cc70bSAndy Fleming 
1413e9550449SChe-Liang Chiou 	do_preinit();
1414272cc70bSAndy Fleming 	return 0;
1415272cc70bSAndy Fleming }
14163690d6d6SAmar 
14173690d6d6SAmar #ifdef CONFIG_SUPPORT_EMMC_BOOT
14183690d6d6SAmar /*
14193690d6d6SAmar  * This function changes the size of boot partition and the size of rpmb
14203690d6d6SAmar  * partition present on EMMC devices.
14213690d6d6SAmar  *
14223690d6d6SAmar  * Input Parameters:
14233690d6d6SAmar  * struct *mmc: pointer for the mmc device strcuture
14243690d6d6SAmar  * bootsize: size of boot partition
14253690d6d6SAmar  * rpmbsize: size of rpmb partition
14263690d6d6SAmar  *
14273690d6d6SAmar  * Returns 0 on success.
14283690d6d6SAmar  */
14293690d6d6SAmar 
14303690d6d6SAmar int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize,
14313690d6d6SAmar 				unsigned long rpmbsize)
14323690d6d6SAmar {
14333690d6d6SAmar 	int err;
14343690d6d6SAmar 	struct mmc_cmd cmd;
14353690d6d6SAmar 
14363690d6d6SAmar 	/* Only use this command for raw EMMC moviNAND. Enter backdoor mode */
14373690d6d6SAmar 	cmd.cmdidx = MMC_CMD_RES_MAN;
14383690d6d6SAmar 	cmd.resp_type = MMC_RSP_R1b;
14393690d6d6SAmar 	cmd.cmdarg = MMC_CMD62_ARG1;
14403690d6d6SAmar 
14413690d6d6SAmar 	err = mmc_send_cmd(mmc, &cmd, NULL);
14423690d6d6SAmar 	if (err) {
14433690d6d6SAmar 		debug("mmc_boot_partition_size_change: Error1 = %d\n", err);
14443690d6d6SAmar 		return err;
14453690d6d6SAmar 	}
14463690d6d6SAmar 
14473690d6d6SAmar 	/* Boot partition changing mode */
14483690d6d6SAmar 	cmd.cmdidx = MMC_CMD_RES_MAN;
14493690d6d6SAmar 	cmd.resp_type = MMC_RSP_R1b;
14503690d6d6SAmar 	cmd.cmdarg = MMC_CMD62_ARG2;
14513690d6d6SAmar 
14523690d6d6SAmar 	err = mmc_send_cmd(mmc, &cmd, NULL);
14533690d6d6SAmar 	if (err) {
14543690d6d6SAmar 		debug("mmc_boot_partition_size_change: Error2 = %d\n", err);
14553690d6d6SAmar 		return err;
14563690d6d6SAmar 	}
14573690d6d6SAmar 	/* boot partition size is multiple of 128KB */
14583690d6d6SAmar 	bootsize = (bootsize * 1024) / 128;
14593690d6d6SAmar 
14603690d6d6SAmar 	/* Arg: boot partition size */
14613690d6d6SAmar 	cmd.cmdidx = MMC_CMD_RES_MAN;
14623690d6d6SAmar 	cmd.resp_type = MMC_RSP_R1b;
14633690d6d6SAmar 	cmd.cmdarg = bootsize;
14643690d6d6SAmar 
14653690d6d6SAmar 	err = mmc_send_cmd(mmc, &cmd, NULL);
14663690d6d6SAmar 	if (err) {
14673690d6d6SAmar 		debug("mmc_boot_partition_size_change: Error3 = %d\n", err);
14683690d6d6SAmar 		return err;
14693690d6d6SAmar 	}
14703690d6d6SAmar 	/* RPMB partition size is multiple of 128KB */
14713690d6d6SAmar 	rpmbsize = (rpmbsize * 1024) / 128;
14723690d6d6SAmar 	/* Arg: RPMB partition size */
14733690d6d6SAmar 	cmd.cmdidx = MMC_CMD_RES_MAN;
14743690d6d6SAmar 	cmd.resp_type = MMC_RSP_R1b;
14753690d6d6SAmar 	cmd.cmdarg = rpmbsize;
14763690d6d6SAmar 
14773690d6d6SAmar 	err = mmc_send_cmd(mmc, &cmd, NULL);
14783690d6d6SAmar 	if (err) {
14793690d6d6SAmar 		debug("mmc_boot_partition_size_change: Error4 = %d\n", err);
14803690d6d6SAmar 		return err;
14813690d6d6SAmar 	}
14823690d6d6SAmar 	return 0;
14833690d6d6SAmar }
14843690d6d6SAmar 
14853690d6d6SAmar /*
14865a99b9deSTom Rini  * Modify EXT_CSD[177] which is BOOT_BUS_WIDTH
14875a99b9deSTom Rini  * based on the passed in values for BOOT_BUS_WIDTH, RESET_BOOT_BUS_WIDTH
14885a99b9deSTom Rini  * and BOOT_MODE.
14895a99b9deSTom Rini  *
14905a99b9deSTom Rini  * Returns 0 on success.
14915a99b9deSTom Rini  */
14925a99b9deSTom Rini int mmc_set_boot_bus_width(struct mmc *mmc, u8 width, u8 reset, u8 mode)
14935a99b9deSTom Rini {
14945a99b9deSTom Rini 	int err;
14955a99b9deSTom Rini 
14965a99b9deSTom Rini 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_BUS_WIDTH,
14975a99b9deSTom Rini 			 EXT_CSD_BOOT_BUS_WIDTH_MODE(mode) |
14985a99b9deSTom Rini 			 EXT_CSD_BOOT_BUS_WIDTH_RESET(reset) |
14995a99b9deSTom Rini 			 EXT_CSD_BOOT_BUS_WIDTH_WIDTH(width));
15005a99b9deSTom Rini 
15015a99b9deSTom Rini 	if (err)
15025a99b9deSTom Rini 		return err;
15035a99b9deSTom Rini 	return 0;
15045a99b9deSTom Rini }
15055a99b9deSTom Rini 
15065a99b9deSTom Rini /*
1507792970b0STom Rini  * Modify EXT_CSD[179] which is PARTITION_CONFIG (formerly BOOT_CONFIG)
1508792970b0STom Rini  * based on the passed in values for BOOT_ACK, BOOT_PARTITION_ENABLE and
1509792970b0STom Rini  * PARTITION_ACCESS.
1510792970b0STom Rini  *
1511792970b0STom Rini  * Returns 0 on success.
1512792970b0STom Rini  */
1513792970b0STom Rini int mmc_set_part_conf(struct mmc *mmc, u8 ack, u8 part_num, u8 access)
1514792970b0STom Rini {
1515792970b0STom Rini 	int err;
1516792970b0STom Rini 
1517792970b0STom Rini 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
1518792970b0STom Rini 			 EXT_CSD_BOOT_ACK(ack) |
1519792970b0STom Rini 			 EXT_CSD_BOOT_PART_NUM(part_num) |
1520792970b0STom Rini 			 EXT_CSD_PARTITION_ACCESS(access));
1521792970b0STom Rini 
1522792970b0STom Rini 	if (err)
1523792970b0STom Rini 		return err;
1524792970b0STom Rini 	return 0;
1525792970b0STom Rini }
152633ace362STom Rini 
152733ace362STom Rini /*
152833ace362STom Rini  * Modify EXT_CSD[162] which is RST_n_FUNCTION based on the given value
152933ace362STom Rini  * for enable.  Note that this is a write-once field for non-zero values.
153033ace362STom Rini  *
153133ace362STom Rini  * Returns 0 on success.
153233ace362STom Rini  */
153333ace362STom Rini int mmc_set_rst_n_function(struct mmc *mmc, u8 enable)
153433ace362STom Rini {
153533ace362STom Rini 	return mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_RST_N_FUNCTION,
153633ace362STom Rini 			  enable);
153733ace362STom Rini }
15383690d6d6SAmar #endif
1539