xref: /openbmc/u-boot/drivers/mmc/mmc.c (revision d235628434657cf1eba58821445cb5ad88e10763)
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 	}
1536b2221b0SAndrew Gabbasov 	if (cmd.response[0] & MMC_STATUS_SWITCH_ERROR)
1546b2221b0SAndrew Gabbasov 		return SWITCH_ERR;
1555d4fc8d9SRaffaele Recalcati 
1565d4fc8d9SRaffaele Recalcati 	return 0;
1575d4fc8d9SRaffaele Recalcati }
1585d4fc8d9SRaffaele Recalcati 
159da61fa5fSPaul Burton int mmc_set_blocklen(struct mmc *mmc, int len)
160272cc70bSAndy Fleming {
161272cc70bSAndy Fleming 	struct mmc_cmd cmd;
162272cc70bSAndy Fleming 
163272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SET_BLOCKLEN;
164272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
165272cc70bSAndy Fleming 	cmd.cmdarg = len;
166272cc70bSAndy Fleming 
167272cc70bSAndy Fleming 	return mmc_send_cmd(mmc, &cmd, NULL);
168272cc70bSAndy Fleming }
169272cc70bSAndy Fleming 
170272cc70bSAndy Fleming struct mmc *find_mmc_device(int dev_num)
171272cc70bSAndy Fleming {
172272cc70bSAndy Fleming 	struct mmc *m;
173272cc70bSAndy Fleming 	struct list_head *entry;
174272cc70bSAndy Fleming 
175272cc70bSAndy Fleming 	list_for_each(entry, &mmc_devices) {
176272cc70bSAndy Fleming 		m = list_entry(entry, struct mmc, link);
177272cc70bSAndy Fleming 
178272cc70bSAndy Fleming 		if (m->block_dev.dev == dev_num)
179272cc70bSAndy Fleming 			return m;
180272cc70bSAndy Fleming 	}
181272cc70bSAndy Fleming 
18256196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
183272cc70bSAndy Fleming 	printf("MMC Device %d not found\n", dev_num);
18456196826SPaul Burton #endif
185272cc70bSAndy Fleming 
186272cc70bSAndy Fleming 	return NULL;
187272cc70bSAndy Fleming }
188272cc70bSAndy Fleming 
189ff8fef56SSascha Silbe static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start,
190fdbb873eSKim Phillips 			   lbaint_t blkcnt)
191272cc70bSAndy Fleming {
192272cc70bSAndy Fleming 	struct mmc_cmd cmd;
193272cc70bSAndy Fleming 	struct mmc_data data;
194272cc70bSAndy Fleming 
1954a1a06bcSAlagu Sankar 	if (blkcnt > 1)
1964a1a06bcSAlagu Sankar 		cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
1974a1a06bcSAlagu Sankar 	else
198272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
199272cc70bSAndy Fleming 
200272cc70bSAndy Fleming 	if (mmc->high_capacity)
2014a1a06bcSAlagu Sankar 		cmd.cmdarg = start;
202272cc70bSAndy Fleming 	else
2034a1a06bcSAlagu Sankar 		cmd.cmdarg = start * mmc->read_bl_len;
204272cc70bSAndy Fleming 
205272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
206272cc70bSAndy Fleming 
207272cc70bSAndy Fleming 	data.dest = dst;
2084a1a06bcSAlagu Sankar 	data.blocks = blkcnt;
209272cc70bSAndy Fleming 	data.blocksize = mmc->read_bl_len;
210272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
211272cc70bSAndy Fleming 
2124a1a06bcSAlagu Sankar 	if (mmc_send_cmd(mmc, &cmd, &data))
2134a1a06bcSAlagu Sankar 		return 0;
2144a1a06bcSAlagu Sankar 
2154a1a06bcSAlagu Sankar 	if (blkcnt > 1) {
2164a1a06bcSAlagu Sankar 		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
2174a1a06bcSAlagu Sankar 		cmd.cmdarg = 0;
2184a1a06bcSAlagu Sankar 		cmd.resp_type = MMC_RSP_R1b;
2194a1a06bcSAlagu Sankar 		if (mmc_send_cmd(mmc, &cmd, NULL)) {
22056196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
2214a1a06bcSAlagu Sankar 			printf("mmc fail to send stop cmd\n");
22256196826SPaul Burton #endif
2234a1a06bcSAlagu Sankar 			return 0;
2244a1a06bcSAlagu Sankar 		}
225272cc70bSAndy Fleming 	}
226272cc70bSAndy Fleming 
2274a1a06bcSAlagu Sankar 	return blkcnt;
228272cc70bSAndy Fleming }
229272cc70bSAndy Fleming 
230ff8fef56SSascha Silbe static ulong mmc_bread(int dev_num, lbaint_t start, lbaint_t blkcnt, void *dst)
231272cc70bSAndy Fleming {
2324a1a06bcSAlagu Sankar 	lbaint_t cur, blocks_todo = blkcnt;
233272cc70bSAndy Fleming 
2344a1a06bcSAlagu Sankar 	if (blkcnt == 0)
2354a1a06bcSAlagu Sankar 		return 0;
2364a1a06bcSAlagu Sankar 
2374a1a06bcSAlagu Sankar 	struct mmc *mmc = find_mmc_device(dev_num);
238272cc70bSAndy Fleming 	if (!mmc)
239272cc70bSAndy Fleming 		return 0;
240272cc70bSAndy Fleming 
241d2bf29e3SLei Wen 	if ((start + blkcnt) > mmc->block_dev.lba) {
24256196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
243ff8fef56SSascha Silbe 		printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
244d2bf29e3SLei Wen 			start + blkcnt, mmc->block_dev.lba);
24556196826SPaul Burton #endif
246d2bf29e3SLei Wen 		return 0;
247d2bf29e3SLei Wen 	}
248272cc70bSAndy Fleming 
2494a1a06bcSAlagu Sankar 	if (mmc_set_blocklen(mmc, mmc->read_bl_len))
250272cc70bSAndy Fleming 		return 0;
251272cc70bSAndy Fleming 
2524a1a06bcSAlagu Sankar 	do {
25393bfd616SPantelis Antoniou 		cur = (blocks_todo > mmc->cfg->b_max) ?
25493bfd616SPantelis Antoniou 			mmc->cfg->b_max : blocks_todo;
2554a1a06bcSAlagu Sankar 		if(mmc_read_blocks(mmc, dst, start, cur) != cur)
2564a1a06bcSAlagu Sankar 			return 0;
2574a1a06bcSAlagu Sankar 		blocks_todo -= cur;
2584a1a06bcSAlagu Sankar 		start += cur;
2594a1a06bcSAlagu Sankar 		dst += cur * mmc->read_bl_len;
2604a1a06bcSAlagu Sankar 	} while (blocks_todo > 0);
261272cc70bSAndy Fleming 
262272cc70bSAndy Fleming 	return blkcnt;
263272cc70bSAndy Fleming }
264272cc70bSAndy Fleming 
265fdbb873eSKim Phillips static int mmc_go_idle(struct mmc *mmc)
266272cc70bSAndy Fleming {
267272cc70bSAndy Fleming 	struct mmc_cmd cmd;
268272cc70bSAndy Fleming 	int err;
269272cc70bSAndy Fleming 
270272cc70bSAndy Fleming 	udelay(1000);
271272cc70bSAndy Fleming 
272272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_GO_IDLE_STATE;
273272cc70bSAndy Fleming 	cmd.cmdarg = 0;
274272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_NONE;
275272cc70bSAndy Fleming 
276272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
277272cc70bSAndy Fleming 
278272cc70bSAndy Fleming 	if (err)
279272cc70bSAndy Fleming 		return err;
280272cc70bSAndy Fleming 
281272cc70bSAndy Fleming 	udelay(2000);
282272cc70bSAndy Fleming 
283272cc70bSAndy Fleming 	return 0;
284272cc70bSAndy Fleming }
285272cc70bSAndy Fleming 
286fdbb873eSKim Phillips static int sd_send_op_cond(struct mmc *mmc)
287272cc70bSAndy Fleming {
288272cc70bSAndy Fleming 	int timeout = 1000;
289272cc70bSAndy Fleming 	int err;
290272cc70bSAndy Fleming 	struct mmc_cmd cmd;
291272cc70bSAndy Fleming 
292272cc70bSAndy Fleming 	do {
293272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_APP_CMD;
294272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R1;
295272cc70bSAndy Fleming 		cmd.cmdarg = 0;
296272cc70bSAndy Fleming 
297272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
298272cc70bSAndy Fleming 
299272cc70bSAndy Fleming 		if (err)
300272cc70bSAndy Fleming 			return err;
301272cc70bSAndy Fleming 
302272cc70bSAndy Fleming 		cmd.cmdidx = SD_CMD_APP_SEND_OP_COND;
303272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R3;
304250de12bSStefano Babic 
305250de12bSStefano Babic 		/*
306250de12bSStefano Babic 		 * Most cards do not answer if some reserved bits
307250de12bSStefano Babic 		 * in the ocr are set. However, Some controller
308250de12bSStefano Babic 		 * can set bit 7 (reserved for low voltages), but
309250de12bSStefano Babic 		 * how to manage low voltages SD card is not yet
310250de12bSStefano Babic 		 * specified.
311250de12bSStefano Babic 		 */
312d52ebf10SThomas Chou 		cmd.cmdarg = mmc_host_is_spi(mmc) ? 0 :
31393bfd616SPantelis Antoniou 			(mmc->cfg->voltages & 0xff8000);
314272cc70bSAndy Fleming 
315272cc70bSAndy Fleming 		if (mmc->version == SD_VERSION_2)
316272cc70bSAndy Fleming 			cmd.cmdarg |= OCR_HCS;
317272cc70bSAndy Fleming 
318272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
319272cc70bSAndy Fleming 
320272cc70bSAndy Fleming 		if (err)
321272cc70bSAndy Fleming 			return err;
322272cc70bSAndy Fleming 
323272cc70bSAndy Fleming 		udelay(1000);
324272cc70bSAndy Fleming 	} while ((!(cmd.response[0] & OCR_BUSY)) && timeout--);
325272cc70bSAndy Fleming 
326272cc70bSAndy Fleming 	if (timeout <= 0)
327272cc70bSAndy Fleming 		return UNUSABLE_ERR;
328272cc70bSAndy Fleming 
329272cc70bSAndy Fleming 	if (mmc->version != SD_VERSION_2)
330272cc70bSAndy Fleming 		mmc->version = SD_VERSION_1_0;
331272cc70bSAndy Fleming 
332d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc)) { /* read OCR for spi */
333d52ebf10SThomas Chou 		cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
334d52ebf10SThomas Chou 		cmd.resp_type = MMC_RSP_R3;
335d52ebf10SThomas Chou 		cmd.cmdarg = 0;
336d52ebf10SThomas Chou 
337d52ebf10SThomas Chou 		err = mmc_send_cmd(mmc, &cmd, NULL);
338d52ebf10SThomas Chou 
339d52ebf10SThomas Chou 		if (err)
340d52ebf10SThomas Chou 			return err;
341d52ebf10SThomas Chou 	}
342d52ebf10SThomas Chou 
343998be3ddSRabin Vincent 	mmc->ocr = cmd.response[0];
344272cc70bSAndy Fleming 
345272cc70bSAndy Fleming 	mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
346272cc70bSAndy Fleming 	mmc->rca = 0;
347272cc70bSAndy Fleming 
348272cc70bSAndy Fleming 	return 0;
349272cc70bSAndy Fleming }
350272cc70bSAndy Fleming 
351e9550449SChe-Liang Chiou /* We pass in the cmd since otherwise the init seems to fail */
352e9550449SChe-Liang Chiou static int mmc_send_op_cond_iter(struct mmc *mmc, struct mmc_cmd *cmd,
353e9550449SChe-Liang Chiou 		int use_arg)
354272cc70bSAndy Fleming {
355272cc70bSAndy Fleming 	int err;
356272cc70bSAndy Fleming 
357e9550449SChe-Liang Chiou 	cmd->cmdidx = MMC_CMD_SEND_OP_COND;
358e9550449SChe-Liang Chiou 	cmd->resp_type = MMC_RSP_R3;
359e9550449SChe-Liang Chiou 	cmd->cmdarg = 0;
360e9550449SChe-Liang Chiou 	if (use_arg && !mmc_host_is_spi(mmc)) {
361e9550449SChe-Liang Chiou 		cmd->cmdarg =
36293bfd616SPantelis Antoniou 			(mmc->cfg->voltages &
363e9550449SChe-Liang Chiou 			(mmc->op_cond_response & OCR_VOLTAGE_MASK)) |
364e9550449SChe-Liang Chiou 			(mmc->op_cond_response & OCR_ACCESS_MODE);
365e9550449SChe-Liang Chiou 
36693bfd616SPantelis Antoniou 		if (mmc->cfg->host_caps & MMC_MODE_HC)
367e9550449SChe-Liang Chiou 			cmd->cmdarg |= OCR_HCS;
368e9550449SChe-Liang Chiou 	}
369e9550449SChe-Liang Chiou 	err = mmc_send_cmd(mmc, cmd, NULL);
370e9550449SChe-Liang Chiou 	if (err)
371e9550449SChe-Liang Chiou 		return err;
372e9550449SChe-Liang Chiou 	mmc->op_cond_response = cmd->response[0];
373e9550449SChe-Liang Chiou 	return 0;
374e9550449SChe-Liang Chiou }
375e9550449SChe-Liang Chiou 
376e9550449SChe-Liang Chiou int mmc_send_op_cond(struct mmc *mmc)
377e9550449SChe-Liang Chiou {
378e9550449SChe-Liang Chiou 	struct mmc_cmd cmd;
379e9550449SChe-Liang Chiou 	int err, i;
380e9550449SChe-Liang Chiou 
381272cc70bSAndy Fleming 	/* Some cards seem to need this */
382272cc70bSAndy Fleming 	mmc_go_idle(mmc);
383272cc70bSAndy Fleming 
38431cacbabSRaffaele Recalcati  	/* Asking to the card its capabilities */
385e9550449SChe-Liang Chiou 	mmc->op_cond_pending = 1;
386e9550449SChe-Liang Chiou 	for (i = 0; i < 2; i++) {
387e9550449SChe-Liang Chiou 		err = mmc_send_op_cond_iter(mmc, &cmd, i != 0);
38831cacbabSRaffaele Recalcati 		if (err)
38931cacbabSRaffaele Recalcati 			return err;
39031cacbabSRaffaele Recalcati 
391e9550449SChe-Liang Chiou 		/* exit if not busy (flag seems to be inverted) */
392e9550449SChe-Liang Chiou 		if (mmc->op_cond_response & OCR_BUSY)
393e9550449SChe-Liang Chiou 			return 0;
394e9550449SChe-Liang Chiou 	}
395e9550449SChe-Liang Chiou 	return IN_PROGRESS;
396e9550449SChe-Liang Chiou }
39731cacbabSRaffaele Recalcati 
398e9550449SChe-Liang Chiou int mmc_complete_op_cond(struct mmc *mmc)
399e9550449SChe-Liang Chiou {
400e9550449SChe-Liang Chiou 	struct mmc_cmd cmd;
401e9550449SChe-Liang Chiou 	int timeout = 1000;
402e9550449SChe-Liang Chiou 	uint start;
403e9550449SChe-Liang Chiou 	int err;
404e9550449SChe-Liang Chiou 
405e9550449SChe-Liang Chiou 	mmc->op_cond_pending = 0;
406e9550449SChe-Liang Chiou 	start = get_timer(0);
407272cc70bSAndy Fleming 	do {
408e9550449SChe-Liang Chiou 		err = mmc_send_op_cond_iter(mmc, &cmd, 1);
409272cc70bSAndy Fleming 		if (err)
410272cc70bSAndy Fleming 			return err;
411e9550449SChe-Liang Chiou 		if (get_timer(start) > timeout)
412272cc70bSAndy Fleming 			return UNUSABLE_ERR;
413e9550449SChe-Liang Chiou 		udelay(100);
414e9550449SChe-Liang Chiou 	} while (!(mmc->op_cond_response & OCR_BUSY));
415272cc70bSAndy Fleming 
416d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc)) { /* read OCR for spi */
417d52ebf10SThomas Chou 		cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
418d52ebf10SThomas Chou 		cmd.resp_type = MMC_RSP_R3;
419d52ebf10SThomas Chou 		cmd.cmdarg = 0;
420d52ebf10SThomas Chou 
421d52ebf10SThomas Chou 		err = mmc_send_cmd(mmc, &cmd, NULL);
422d52ebf10SThomas Chou 
423d52ebf10SThomas Chou 		if (err)
424d52ebf10SThomas Chou 			return err;
425d52ebf10SThomas Chou 	}
426d52ebf10SThomas Chou 
427272cc70bSAndy Fleming 	mmc->version = MMC_VERSION_UNKNOWN;
428998be3ddSRabin Vincent 	mmc->ocr = cmd.response[0];
429272cc70bSAndy Fleming 
430272cc70bSAndy Fleming 	mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
431def816a2SStephen Warren 	mmc->rca = 1;
432272cc70bSAndy Fleming 
433272cc70bSAndy Fleming 	return 0;
434272cc70bSAndy Fleming }
435272cc70bSAndy Fleming 
436272cc70bSAndy Fleming 
437fdbb873eSKim Phillips static int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd)
438272cc70bSAndy Fleming {
439272cc70bSAndy Fleming 	struct mmc_cmd cmd;
440272cc70bSAndy Fleming 	struct mmc_data data;
441272cc70bSAndy Fleming 	int err;
442272cc70bSAndy Fleming 
443272cc70bSAndy Fleming 	/* Get the Card Status Register */
444272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SEND_EXT_CSD;
445272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
446272cc70bSAndy Fleming 	cmd.cmdarg = 0;
447272cc70bSAndy Fleming 
448cdfd1ac6SYoshihiro Shimoda 	data.dest = (char *)ext_csd;
449272cc70bSAndy Fleming 	data.blocks = 1;
4508bfa195eSSimon Glass 	data.blocksize = MMC_MAX_BLOCK_LEN;
451272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
452272cc70bSAndy Fleming 
453272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, &data);
454272cc70bSAndy Fleming 
455272cc70bSAndy Fleming 	return err;
456272cc70bSAndy Fleming }
457272cc70bSAndy Fleming 
458272cc70bSAndy Fleming 
459fdbb873eSKim Phillips static int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)
460272cc70bSAndy Fleming {
461272cc70bSAndy Fleming 	struct mmc_cmd cmd;
4625d4fc8d9SRaffaele Recalcati 	int timeout = 1000;
4635d4fc8d9SRaffaele Recalcati 	int ret;
464272cc70bSAndy Fleming 
465272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SWITCH;
466272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1b;
467272cc70bSAndy Fleming 	cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
468272cc70bSAndy Fleming 				 (index << 16) |
469272cc70bSAndy Fleming 				 (value << 8);
470272cc70bSAndy Fleming 
4715d4fc8d9SRaffaele Recalcati 	ret = mmc_send_cmd(mmc, &cmd, NULL);
4725d4fc8d9SRaffaele Recalcati 
4735d4fc8d9SRaffaele Recalcati 	/* Waiting for the ready status */
47493ad0d18SJan Kloetzke 	if (!ret)
47593ad0d18SJan Kloetzke 		ret = mmc_send_status(mmc, timeout);
4765d4fc8d9SRaffaele Recalcati 
4775d4fc8d9SRaffaele Recalcati 	return ret;
4785d4fc8d9SRaffaele Recalcati 
479272cc70bSAndy Fleming }
480272cc70bSAndy Fleming 
481fdbb873eSKim Phillips static int mmc_change_freq(struct mmc *mmc)
482272cc70bSAndy Fleming {
4838bfa195eSSimon Glass 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
484272cc70bSAndy Fleming 	char cardtype;
485272cc70bSAndy Fleming 	int err;
486272cc70bSAndy Fleming 
487272cc70bSAndy Fleming 	mmc->card_caps = 0;
488272cc70bSAndy Fleming 
489d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc))
490d52ebf10SThomas Chou 		return 0;
491d52ebf10SThomas Chou 
492272cc70bSAndy Fleming 	/* Only version 4 supports high-speed */
493272cc70bSAndy Fleming 	if (mmc->version < MMC_VERSION_4)
494272cc70bSAndy Fleming 		return 0;
495272cc70bSAndy Fleming 
496272cc70bSAndy Fleming 	err = mmc_send_ext_csd(mmc, ext_csd);
497272cc70bSAndy Fleming 
498272cc70bSAndy Fleming 	if (err)
499272cc70bSAndy Fleming 		return err;
500272cc70bSAndy Fleming 
5010560db18SLei Wen 	cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf;
502272cc70bSAndy Fleming 
503272cc70bSAndy Fleming 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1);
504272cc70bSAndy Fleming 
505272cc70bSAndy Fleming 	if (err)
5066b2221b0SAndrew Gabbasov 		return err == SWITCH_ERR ? 0 : err;
507272cc70bSAndy Fleming 
508272cc70bSAndy Fleming 	/* Now check to see that it worked */
509272cc70bSAndy Fleming 	err = mmc_send_ext_csd(mmc, ext_csd);
510272cc70bSAndy Fleming 
511272cc70bSAndy Fleming 	if (err)
512272cc70bSAndy Fleming 		return err;
513272cc70bSAndy Fleming 
514272cc70bSAndy Fleming 	/* No high-speed support */
5150560db18SLei Wen 	if (!ext_csd[EXT_CSD_HS_TIMING])
516272cc70bSAndy Fleming 		return 0;
517272cc70bSAndy Fleming 
518272cc70bSAndy Fleming 	/* High Speed is set, there are two types: 52MHz and 26MHz */
519272cc70bSAndy Fleming 	if (cardtype & MMC_HS_52MHZ)
520272cc70bSAndy Fleming 		mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
521272cc70bSAndy Fleming 	else
522272cc70bSAndy Fleming 		mmc->card_caps |= MMC_MODE_HS;
523272cc70bSAndy Fleming 
524272cc70bSAndy Fleming 	return 0;
525272cc70bSAndy Fleming }
526272cc70bSAndy Fleming 
527f866a46dSStephen Warren static int mmc_set_capacity(struct mmc *mmc, int part_num)
528f866a46dSStephen Warren {
529f866a46dSStephen Warren 	switch (part_num) {
530f866a46dSStephen Warren 	case 0:
531f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_user;
532f866a46dSStephen Warren 		break;
533f866a46dSStephen Warren 	case 1:
534f866a46dSStephen Warren 	case 2:
535f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_boot;
536f866a46dSStephen Warren 		break;
537f866a46dSStephen Warren 	case 3:
538f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_rpmb;
539f866a46dSStephen Warren 		break;
540f866a46dSStephen Warren 	case 4:
541f866a46dSStephen Warren 	case 5:
542f866a46dSStephen Warren 	case 6:
543f866a46dSStephen Warren 	case 7:
544f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_gp[part_num - 4];
545f866a46dSStephen Warren 		break;
546f866a46dSStephen Warren 	default:
547f866a46dSStephen Warren 		return -1;
548f866a46dSStephen Warren 	}
549f866a46dSStephen Warren 
550f866a46dSStephen Warren 	mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len);
551f866a46dSStephen Warren 
552f866a46dSStephen Warren 	return 0;
553f866a46dSStephen Warren }
554f866a46dSStephen Warren 
555*d2356284SStephen Warren int mmc_select_hwpart(int dev_num, int hwpart)
556*d2356284SStephen Warren {
557*d2356284SStephen Warren 	struct mmc *mmc = find_mmc_device(dev_num);
558*d2356284SStephen Warren 	int ret;
559*d2356284SStephen Warren 
560*d2356284SStephen Warren 	if (!mmc)
561*d2356284SStephen Warren 		return -1;
562*d2356284SStephen Warren 
563*d2356284SStephen Warren 	if (mmc->part_num == hwpart)
564*d2356284SStephen Warren 		return 0;
565*d2356284SStephen Warren 
566*d2356284SStephen Warren 	if (mmc->part_config == MMCPART_NOAVAILABLE) {
567*d2356284SStephen Warren 		printf("Card doesn't support part_switch\n");
568*d2356284SStephen Warren 		return -1;
569*d2356284SStephen Warren 	}
570*d2356284SStephen Warren 
571*d2356284SStephen Warren 	ret = mmc_switch_part(dev_num, hwpart);
572*d2356284SStephen Warren 	if (ret)
573*d2356284SStephen Warren 		return -1;
574*d2356284SStephen Warren 
575*d2356284SStephen Warren 	mmc->part_num = hwpart;
576*d2356284SStephen Warren 
577*d2356284SStephen Warren 	return 0;
578*d2356284SStephen Warren }
579*d2356284SStephen Warren 
580*d2356284SStephen Warren 
581bc897b1dSLei Wen int mmc_switch_part(int dev_num, unsigned int part_num)
582bc897b1dSLei Wen {
583bc897b1dSLei Wen 	struct mmc *mmc = find_mmc_device(dev_num);
584f866a46dSStephen Warren 	int ret;
585bc897b1dSLei Wen 
586bc897b1dSLei Wen 	if (!mmc)
587bc897b1dSLei Wen 		return -1;
588bc897b1dSLei Wen 
589f866a46dSStephen Warren 	ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
590bc897b1dSLei Wen 			 (mmc->part_config & ~PART_ACCESS_MASK)
591bc897b1dSLei Wen 			 | (part_num & PART_ACCESS_MASK));
592f866a46dSStephen Warren 	if (ret)
593f866a46dSStephen Warren 		return ret;
594f866a46dSStephen Warren 
595f866a46dSStephen Warren 	return mmc_set_capacity(mmc, part_num);
596bc897b1dSLei Wen }
597bc897b1dSLei Wen 
59848972d90SThierry Reding int mmc_getcd(struct mmc *mmc)
59948972d90SThierry Reding {
60048972d90SThierry Reding 	int cd;
60148972d90SThierry Reding 
60248972d90SThierry Reding 	cd = board_mmc_getcd(mmc);
60348972d90SThierry Reding 
604d4e1da4eSPeter Korsgaard 	if (cd < 0) {
60593bfd616SPantelis Antoniou 		if (mmc->cfg->ops->getcd)
60693bfd616SPantelis Antoniou 			cd = mmc->cfg->ops->getcd(mmc);
607d4e1da4eSPeter Korsgaard 		else
608d4e1da4eSPeter Korsgaard 			cd = 1;
609d4e1da4eSPeter Korsgaard 	}
61048972d90SThierry Reding 
61148972d90SThierry Reding 	return cd;
61248972d90SThierry Reding }
61348972d90SThierry Reding 
614fdbb873eSKim Phillips static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)
615272cc70bSAndy Fleming {
616272cc70bSAndy Fleming 	struct mmc_cmd cmd;
617272cc70bSAndy Fleming 	struct mmc_data data;
618272cc70bSAndy Fleming 
619272cc70bSAndy Fleming 	/* Switch the frequency */
620272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_SWITCH_FUNC;
621272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
622272cc70bSAndy Fleming 	cmd.cmdarg = (mode << 31) | 0xffffff;
623272cc70bSAndy Fleming 	cmd.cmdarg &= ~(0xf << (group * 4));
624272cc70bSAndy Fleming 	cmd.cmdarg |= value << (group * 4);
625272cc70bSAndy Fleming 
626272cc70bSAndy Fleming 	data.dest = (char *)resp;
627272cc70bSAndy Fleming 	data.blocksize = 64;
628272cc70bSAndy Fleming 	data.blocks = 1;
629272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
630272cc70bSAndy Fleming 
631272cc70bSAndy Fleming 	return mmc_send_cmd(mmc, &cmd, &data);
632272cc70bSAndy Fleming }
633272cc70bSAndy Fleming 
634272cc70bSAndy Fleming 
635fdbb873eSKim Phillips static int sd_change_freq(struct mmc *mmc)
636272cc70bSAndy Fleming {
637272cc70bSAndy Fleming 	int err;
638272cc70bSAndy Fleming 	struct mmc_cmd cmd;
639f781dd38SAnton staaf 	ALLOC_CACHE_ALIGN_BUFFER(uint, scr, 2);
640f781dd38SAnton staaf 	ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16);
641272cc70bSAndy Fleming 	struct mmc_data data;
642272cc70bSAndy Fleming 	int timeout;
643272cc70bSAndy Fleming 
644272cc70bSAndy Fleming 	mmc->card_caps = 0;
645272cc70bSAndy Fleming 
646d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc))
647d52ebf10SThomas Chou 		return 0;
648d52ebf10SThomas Chou 
649272cc70bSAndy Fleming 	/* Read the SCR to find out if this card supports higher speeds */
650272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_APP_CMD;
651272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
652272cc70bSAndy Fleming 	cmd.cmdarg = mmc->rca << 16;
653272cc70bSAndy Fleming 
654272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
655272cc70bSAndy Fleming 
656272cc70bSAndy Fleming 	if (err)
657272cc70bSAndy Fleming 		return err;
658272cc70bSAndy Fleming 
659272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_APP_SEND_SCR;
660272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
661272cc70bSAndy Fleming 	cmd.cmdarg = 0;
662272cc70bSAndy Fleming 
663272cc70bSAndy Fleming 	timeout = 3;
664272cc70bSAndy Fleming 
665272cc70bSAndy Fleming retry_scr:
666f781dd38SAnton staaf 	data.dest = (char *)scr;
667272cc70bSAndy Fleming 	data.blocksize = 8;
668272cc70bSAndy Fleming 	data.blocks = 1;
669272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
670272cc70bSAndy Fleming 
671272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, &data);
672272cc70bSAndy Fleming 
673272cc70bSAndy Fleming 	if (err) {
674272cc70bSAndy Fleming 		if (timeout--)
675272cc70bSAndy Fleming 			goto retry_scr;
676272cc70bSAndy Fleming 
677272cc70bSAndy Fleming 		return err;
678272cc70bSAndy Fleming 	}
679272cc70bSAndy Fleming 
6804e3d89baSYauhen Kharuzhy 	mmc->scr[0] = __be32_to_cpu(scr[0]);
6814e3d89baSYauhen Kharuzhy 	mmc->scr[1] = __be32_to_cpu(scr[1]);
682272cc70bSAndy Fleming 
683272cc70bSAndy Fleming 	switch ((mmc->scr[0] >> 24) & 0xf) {
684272cc70bSAndy Fleming 		case 0:
685272cc70bSAndy Fleming 			mmc->version = SD_VERSION_1_0;
686272cc70bSAndy Fleming 			break;
687272cc70bSAndy Fleming 		case 1:
688272cc70bSAndy Fleming 			mmc->version = SD_VERSION_1_10;
689272cc70bSAndy Fleming 			break;
690272cc70bSAndy Fleming 		case 2:
691272cc70bSAndy Fleming 			mmc->version = SD_VERSION_2;
6921741c64dSJaehoon Chung 			if ((mmc->scr[0] >> 15) & 0x1)
6931741c64dSJaehoon Chung 				mmc->version = SD_VERSION_3;
694272cc70bSAndy Fleming 			break;
695272cc70bSAndy Fleming 		default:
696272cc70bSAndy Fleming 			mmc->version = SD_VERSION_1_0;
697272cc70bSAndy Fleming 			break;
698272cc70bSAndy Fleming 	}
699272cc70bSAndy Fleming 
700b44c7083SAlagu Sankar 	if (mmc->scr[0] & SD_DATA_4BIT)
701b44c7083SAlagu Sankar 		mmc->card_caps |= MMC_MODE_4BIT;
702b44c7083SAlagu Sankar 
703272cc70bSAndy Fleming 	/* Version 1.0 doesn't support switching */
704272cc70bSAndy Fleming 	if (mmc->version == SD_VERSION_1_0)
705272cc70bSAndy Fleming 		return 0;
706272cc70bSAndy Fleming 
707272cc70bSAndy Fleming 	timeout = 4;
708272cc70bSAndy Fleming 	while (timeout--) {
709272cc70bSAndy Fleming 		err = sd_switch(mmc, SD_SWITCH_CHECK, 0, 1,
710f781dd38SAnton staaf 				(u8 *)switch_status);
711272cc70bSAndy Fleming 
712272cc70bSAndy Fleming 		if (err)
713272cc70bSAndy Fleming 			return err;
714272cc70bSAndy Fleming 
715272cc70bSAndy Fleming 		/* The high-speed function is busy.  Try again */
7164e3d89baSYauhen Kharuzhy 		if (!(__be32_to_cpu(switch_status[7]) & SD_HIGHSPEED_BUSY))
717272cc70bSAndy Fleming 			break;
718272cc70bSAndy Fleming 	}
719272cc70bSAndy Fleming 
720272cc70bSAndy Fleming 	/* If high-speed isn't supported, we return */
7214e3d89baSYauhen Kharuzhy 	if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED))
722272cc70bSAndy Fleming 		return 0;
723272cc70bSAndy Fleming 
7242c3fbf4cSMacpaul Lin 	/*
7252c3fbf4cSMacpaul Lin 	 * If the host doesn't support SD_HIGHSPEED, do not switch card to
7262c3fbf4cSMacpaul Lin 	 * HIGHSPEED mode even if the card support SD_HIGHSPPED.
7272c3fbf4cSMacpaul Lin 	 * This can avoid furthur problem when the card runs in different
7282c3fbf4cSMacpaul Lin 	 * mode between the host.
7292c3fbf4cSMacpaul Lin 	 */
73093bfd616SPantelis Antoniou 	if (!((mmc->cfg->host_caps & MMC_MODE_HS_52MHz) &&
73193bfd616SPantelis Antoniou 		(mmc->cfg->host_caps & MMC_MODE_HS)))
7322c3fbf4cSMacpaul Lin 		return 0;
7332c3fbf4cSMacpaul Lin 
734f781dd38SAnton staaf 	err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status);
735272cc70bSAndy Fleming 
736272cc70bSAndy Fleming 	if (err)
737272cc70bSAndy Fleming 		return err;
738272cc70bSAndy Fleming 
7394e3d89baSYauhen Kharuzhy 	if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) == 0x01000000)
740272cc70bSAndy Fleming 		mmc->card_caps |= MMC_MODE_HS;
741272cc70bSAndy Fleming 
742272cc70bSAndy Fleming 	return 0;
743272cc70bSAndy Fleming }
744272cc70bSAndy Fleming 
745272cc70bSAndy Fleming /* frequency bases */
746272cc70bSAndy Fleming /* divided by 10 to be nice to platforms without floating point */
7475f837c2cSMike Frysinger static const int fbase[] = {
748272cc70bSAndy Fleming 	10000,
749272cc70bSAndy Fleming 	100000,
750272cc70bSAndy Fleming 	1000000,
751272cc70bSAndy Fleming 	10000000,
752272cc70bSAndy Fleming };
753272cc70bSAndy Fleming 
754272cc70bSAndy Fleming /* Multiplier values for TRAN_SPEED.  Multiplied by 10 to be nice
755272cc70bSAndy Fleming  * to platforms without floating point.
756272cc70bSAndy Fleming  */
7575f837c2cSMike Frysinger static const int multipliers[] = {
758272cc70bSAndy Fleming 	0,	/* reserved */
759272cc70bSAndy Fleming 	10,
760272cc70bSAndy Fleming 	12,
761272cc70bSAndy Fleming 	13,
762272cc70bSAndy Fleming 	15,
763272cc70bSAndy Fleming 	20,
764272cc70bSAndy Fleming 	25,
765272cc70bSAndy Fleming 	30,
766272cc70bSAndy Fleming 	35,
767272cc70bSAndy Fleming 	40,
768272cc70bSAndy Fleming 	45,
769272cc70bSAndy Fleming 	50,
770272cc70bSAndy Fleming 	55,
771272cc70bSAndy Fleming 	60,
772272cc70bSAndy Fleming 	70,
773272cc70bSAndy Fleming 	80,
774272cc70bSAndy Fleming };
775272cc70bSAndy Fleming 
776fdbb873eSKim Phillips static void mmc_set_ios(struct mmc *mmc)
777272cc70bSAndy Fleming {
77893bfd616SPantelis Antoniou 	if (mmc->cfg->ops->set_ios)
77993bfd616SPantelis Antoniou 		mmc->cfg->ops->set_ios(mmc);
780272cc70bSAndy Fleming }
781272cc70bSAndy Fleming 
782272cc70bSAndy Fleming void mmc_set_clock(struct mmc *mmc, uint clock)
783272cc70bSAndy Fleming {
78493bfd616SPantelis Antoniou 	if (clock > mmc->cfg->f_max)
78593bfd616SPantelis Antoniou 		clock = mmc->cfg->f_max;
786272cc70bSAndy Fleming 
78793bfd616SPantelis Antoniou 	if (clock < mmc->cfg->f_min)
78893bfd616SPantelis Antoniou 		clock = mmc->cfg->f_min;
789272cc70bSAndy Fleming 
790272cc70bSAndy Fleming 	mmc->clock = clock;
791272cc70bSAndy Fleming 
792272cc70bSAndy Fleming 	mmc_set_ios(mmc);
793272cc70bSAndy Fleming }
794272cc70bSAndy Fleming 
795fdbb873eSKim Phillips static void mmc_set_bus_width(struct mmc *mmc, uint width)
796272cc70bSAndy Fleming {
797272cc70bSAndy Fleming 	mmc->bus_width = width;
798272cc70bSAndy Fleming 
799272cc70bSAndy Fleming 	mmc_set_ios(mmc);
800272cc70bSAndy Fleming }
801272cc70bSAndy Fleming 
802fdbb873eSKim Phillips static int mmc_startup(struct mmc *mmc)
803272cc70bSAndy Fleming {
804f866a46dSStephen Warren 	int err, i;
805272cc70bSAndy Fleming 	uint mult, freq;
806639b7827SYoshihiro Shimoda 	u64 cmult, csize, capacity;
807272cc70bSAndy Fleming 	struct mmc_cmd cmd;
8088bfa195eSSimon Glass 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
8098bfa195eSSimon Glass 	ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN);
8105d4fc8d9SRaffaele Recalcati 	int timeout = 1000;
811272cc70bSAndy Fleming 
812d52ebf10SThomas Chou #ifdef CONFIG_MMC_SPI_CRC_ON
813d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */
814d52ebf10SThomas Chou 		cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF;
815d52ebf10SThomas Chou 		cmd.resp_type = MMC_RSP_R1;
816d52ebf10SThomas Chou 		cmd.cmdarg = 1;
817d52ebf10SThomas Chou 		err = mmc_send_cmd(mmc, &cmd, NULL);
818d52ebf10SThomas Chou 
819d52ebf10SThomas Chou 		if (err)
820d52ebf10SThomas Chou 			return err;
821d52ebf10SThomas Chou 	}
822d52ebf10SThomas Chou #endif
823d52ebf10SThomas Chou 
824272cc70bSAndy Fleming 	/* Put the Card in Identify Mode */
825d52ebf10SThomas Chou 	cmd.cmdidx = mmc_host_is_spi(mmc) ? MMC_CMD_SEND_CID :
826d52ebf10SThomas Chou 		MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */
827272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R2;
828272cc70bSAndy Fleming 	cmd.cmdarg = 0;
829272cc70bSAndy Fleming 
830272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
831272cc70bSAndy Fleming 
832272cc70bSAndy Fleming 	if (err)
833272cc70bSAndy Fleming 		return err;
834272cc70bSAndy Fleming 
835272cc70bSAndy Fleming 	memcpy(mmc->cid, cmd.response, 16);
836272cc70bSAndy Fleming 
837272cc70bSAndy Fleming 	/*
838272cc70bSAndy Fleming 	 * For MMC cards, set the Relative Address.
839272cc70bSAndy Fleming 	 * For SD cards, get the Relatvie Address.
840272cc70bSAndy Fleming 	 * This also puts the cards into Standby State
841272cc70bSAndy Fleming 	 */
842d52ebf10SThomas Chou 	if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
843272cc70bSAndy Fleming 		cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;
844272cc70bSAndy Fleming 		cmd.cmdarg = mmc->rca << 16;
845272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R6;
846272cc70bSAndy Fleming 
847272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
848272cc70bSAndy Fleming 
849272cc70bSAndy Fleming 		if (err)
850272cc70bSAndy Fleming 			return err;
851272cc70bSAndy Fleming 
852272cc70bSAndy Fleming 		if (IS_SD(mmc))
853998be3ddSRabin Vincent 			mmc->rca = (cmd.response[0] >> 16) & 0xffff;
854d52ebf10SThomas Chou 	}
855272cc70bSAndy Fleming 
856272cc70bSAndy Fleming 	/* Get the Card-Specific Data */
857272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SEND_CSD;
858272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R2;
859272cc70bSAndy Fleming 	cmd.cmdarg = mmc->rca << 16;
860272cc70bSAndy Fleming 
861272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
862272cc70bSAndy Fleming 
8635d4fc8d9SRaffaele Recalcati 	/* Waiting for the ready status */
8645d4fc8d9SRaffaele Recalcati 	mmc_send_status(mmc, timeout);
8655d4fc8d9SRaffaele Recalcati 
866272cc70bSAndy Fleming 	if (err)
867272cc70bSAndy Fleming 		return err;
868272cc70bSAndy Fleming 
869998be3ddSRabin Vincent 	mmc->csd[0] = cmd.response[0];
870998be3ddSRabin Vincent 	mmc->csd[1] = cmd.response[1];
871998be3ddSRabin Vincent 	mmc->csd[2] = cmd.response[2];
872998be3ddSRabin Vincent 	mmc->csd[3] = cmd.response[3];
873272cc70bSAndy Fleming 
874272cc70bSAndy Fleming 	if (mmc->version == MMC_VERSION_UNKNOWN) {
8750b453ffeSRabin Vincent 		int version = (cmd.response[0] >> 26) & 0xf;
876272cc70bSAndy Fleming 
877272cc70bSAndy Fleming 		switch (version) {
878272cc70bSAndy Fleming 			case 0:
879272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_1_2;
880272cc70bSAndy Fleming 				break;
881272cc70bSAndy Fleming 			case 1:
882272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_1_4;
883272cc70bSAndy Fleming 				break;
884272cc70bSAndy Fleming 			case 2:
885272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_2_2;
886272cc70bSAndy Fleming 				break;
887272cc70bSAndy Fleming 			case 3:
888272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_3;
889272cc70bSAndy Fleming 				break;
890272cc70bSAndy Fleming 			case 4:
891272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_4;
892272cc70bSAndy Fleming 				break;
893272cc70bSAndy Fleming 			default:
894272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_1_2;
895272cc70bSAndy Fleming 				break;
896272cc70bSAndy Fleming 		}
897272cc70bSAndy Fleming 	}
898272cc70bSAndy Fleming 
899272cc70bSAndy Fleming 	/* divide frequency by 10, since the mults are 10x bigger */
9000b453ffeSRabin Vincent 	freq = fbase[(cmd.response[0] & 0x7)];
9010b453ffeSRabin Vincent 	mult = multipliers[((cmd.response[0] >> 3) & 0xf)];
902272cc70bSAndy Fleming 
903272cc70bSAndy Fleming 	mmc->tran_speed = freq * mult;
904272cc70bSAndy Fleming 
905ab71188cSMarkus Niebel 	mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1);
906998be3ddSRabin Vincent 	mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf);
907272cc70bSAndy Fleming 
908272cc70bSAndy Fleming 	if (IS_SD(mmc))
909272cc70bSAndy Fleming 		mmc->write_bl_len = mmc->read_bl_len;
910272cc70bSAndy Fleming 	else
911998be3ddSRabin Vincent 		mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf);
912272cc70bSAndy Fleming 
913272cc70bSAndy Fleming 	if (mmc->high_capacity) {
914272cc70bSAndy Fleming 		csize = (mmc->csd[1] & 0x3f) << 16
915272cc70bSAndy Fleming 			| (mmc->csd[2] & 0xffff0000) >> 16;
916272cc70bSAndy Fleming 		cmult = 8;
917272cc70bSAndy Fleming 	} else {
918272cc70bSAndy Fleming 		csize = (mmc->csd[1] & 0x3ff) << 2
919272cc70bSAndy Fleming 			| (mmc->csd[2] & 0xc0000000) >> 30;
920272cc70bSAndy Fleming 		cmult = (mmc->csd[2] & 0x00038000) >> 15;
921272cc70bSAndy Fleming 	}
922272cc70bSAndy Fleming 
923f866a46dSStephen Warren 	mmc->capacity_user = (csize + 1) << (cmult + 2);
924f866a46dSStephen Warren 	mmc->capacity_user *= mmc->read_bl_len;
925f866a46dSStephen Warren 	mmc->capacity_boot = 0;
926f866a46dSStephen Warren 	mmc->capacity_rpmb = 0;
927f866a46dSStephen Warren 	for (i = 0; i < 4; i++)
928f866a46dSStephen Warren 		mmc->capacity_gp[i] = 0;
929272cc70bSAndy Fleming 
9308bfa195eSSimon Glass 	if (mmc->read_bl_len > MMC_MAX_BLOCK_LEN)
9318bfa195eSSimon Glass 		mmc->read_bl_len = MMC_MAX_BLOCK_LEN;
932272cc70bSAndy Fleming 
9338bfa195eSSimon Glass 	if (mmc->write_bl_len > MMC_MAX_BLOCK_LEN)
9348bfa195eSSimon Glass 		mmc->write_bl_len = MMC_MAX_BLOCK_LEN;
935272cc70bSAndy Fleming 
936ab71188cSMarkus Niebel 	if ((mmc->dsr_imp) && (0xffffffff != mmc->dsr)) {
937ab71188cSMarkus Niebel 		cmd.cmdidx = MMC_CMD_SET_DSR;
938ab71188cSMarkus Niebel 		cmd.cmdarg = (mmc->dsr & 0xffff) << 16;
939ab71188cSMarkus Niebel 		cmd.resp_type = MMC_RSP_NONE;
940ab71188cSMarkus Niebel 		if (mmc_send_cmd(mmc, &cmd, NULL))
941ab71188cSMarkus Niebel 			printf("MMC: SET_DSR failed\n");
942ab71188cSMarkus Niebel 	}
943ab71188cSMarkus Niebel 
944272cc70bSAndy Fleming 	/* Select the card, and put it into Transfer Mode */
945d52ebf10SThomas Chou 	if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
946272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_SELECT_CARD;
947fe8f7066SAjay Bhargav 		cmd.resp_type = MMC_RSP_R1;
948272cc70bSAndy Fleming 		cmd.cmdarg = mmc->rca << 16;
949272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
950272cc70bSAndy Fleming 
951272cc70bSAndy Fleming 		if (err)
952272cc70bSAndy Fleming 			return err;
953d52ebf10SThomas Chou 	}
954272cc70bSAndy Fleming 
955e6f99a56SLei Wen 	/*
956e6f99a56SLei Wen 	 * For SD, its erase group is always one sector
957e6f99a56SLei Wen 	 */
958e6f99a56SLei Wen 	mmc->erase_grp_size = 1;
959bc897b1dSLei Wen 	mmc->part_config = MMCPART_NOAVAILABLE;
960d23e2c09SSukumar Ghorai 	if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) {
961d23e2c09SSukumar Ghorai 		/* check  ext_csd version and capacity */
962d23e2c09SSukumar Ghorai 		err = mmc_send_ext_csd(mmc, ext_csd);
963fdbb873eSKim Phillips 		if (!err && (ext_csd[EXT_CSD_REV] >= 2)) {
964639b7827SYoshihiro Shimoda 			/*
965639b7827SYoshihiro Shimoda 			 * According to the JEDEC Standard, the value of
966639b7827SYoshihiro Shimoda 			 * ext_csd's capacity is valid if the value is more
967639b7827SYoshihiro Shimoda 			 * than 2GB
968639b7827SYoshihiro Shimoda 			 */
9690560db18SLei Wen 			capacity = ext_csd[EXT_CSD_SEC_CNT] << 0
9700560db18SLei Wen 					| ext_csd[EXT_CSD_SEC_CNT + 1] << 8
9710560db18SLei Wen 					| ext_csd[EXT_CSD_SEC_CNT + 2] << 16
9720560db18SLei Wen 					| ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
9738bfa195eSSimon Glass 			capacity *= MMC_MAX_BLOCK_LEN;
974b1f1e821SŁukasz Majewski 			if ((capacity >> 20) > 2 * 1024)
975f866a46dSStephen Warren 				mmc->capacity_user = capacity;
976d23e2c09SSukumar Ghorai 		}
977bc897b1dSLei Wen 
97864f4a619SJaehoon Chung 		switch (ext_csd[EXT_CSD_REV]) {
97964f4a619SJaehoon Chung 		case 1:
98064f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_1;
98164f4a619SJaehoon Chung 			break;
98264f4a619SJaehoon Chung 		case 2:
98364f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_2;
98464f4a619SJaehoon Chung 			break;
98564f4a619SJaehoon Chung 		case 3:
98664f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_3;
98764f4a619SJaehoon Chung 			break;
98864f4a619SJaehoon Chung 		case 5:
98964f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_41;
99064f4a619SJaehoon Chung 			break;
99164f4a619SJaehoon Chung 		case 6:
99264f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_5;
99364f4a619SJaehoon Chung 			break;
99464f4a619SJaehoon Chung 		}
99564f4a619SJaehoon Chung 
996e6f99a56SLei Wen 		/*
9971937e5aaSOliver Metz 		 * Host needs to enable ERASE_GRP_DEF bit if device is
9981937e5aaSOliver Metz 		 * partitioned. This bit will be lost every time after a reset
9991937e5aaSOliver Metz 		 * or power off. This will affect erase size.
1000e6f99a56SLei Wen 		 */
10011937e5aaSOliver Metz 		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) &&
10021937e5aaSOliver Metz 		    (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) {
10031937e5aaSOliver Metz 			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
10041937e5aaSOliver Metz 				EXT_CSD_ERASE_GROUP_DEF, 1);
10051937e5aaSOliver Metz 
10061937e5aaSOliver Metz 			if (err)
10071937e5aaSOliver Metz 				return err;
10081937e5aaSOliver Metz 
10091937e5aaSOliver Metz 			/* Read out group size from ext_csd */
10100560db18SLei Wen 			mmc->erase_grp_size =
10118bfa195eSSimon Glass 				ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] *
10128bfa195eSSimon Glass 					MMC_MAX_BLOCK_LEN * 1024;
10138bfa195eSSimon Glass 		} else {
10141937e5aaSOliver Metz 			/* Calculate the group size from the csd value. */
1015e6f99a56SLei Wen 			int erase_gsz, erase_gmul;
1016e6f99a56SLei Wen 			erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10;
1017e6f99a56SLei Wen 			erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5;
1018e6f99a56SLei Wen 			mmc->erase_grp_size = (erase_gsz + 1)
1019e6f99a56SLei Wen 				* (erase_gmul + 1);
1020e6f99a56SLei Wen 		}
1021e6f99a56SLei Wen 
1022bc897b1dSLei Wen 		/* store the partition info of emmc */
10238948ea83SStephen Warren 		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) ||
10248948ea83SStephen Warren 		    ext_csd[EXT_CSD_BOOT_MULT])
10250560db18SLei Wen 			mmc->part_config = ext_csd[EXT_CSD_PART_CONF];
1026f866a46dSStephen Warren 
1027f866a46dSStephen Warren 		mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17;
1028f866a46dSStephen Warren 
1029f866a46dSStephen Warren 		mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17;
1030f866a46dSStephen Warren 
1031f866a46dSStephen Warren 		for (i = 0; i < 4; i++) {
1032f866a46dSStephen Warren 			int idx = EXT_CSD_GP_SIZE_MULT + i * 3;
1033f866a46dSStephen Warren 			mmc->capacity_gp[i] = (ext_csd[idx + 2] << 16) +
1034f866a46dSStephen Warren 				(ext_csd[idx + 1] << 8) + ext_csd[idx];
1035f866a46dSStephen Warren 			mmc->capacity_gp[i] *=
1036f866a46dSStephen Warren 				ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
1037f866a46dSStephen Warren 			mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
1038d23e2c09SSukumar Ghorai 		}
1039f866a46dSStephen Warren 	}
1040f866a46dSStephen Warren 
1041f866a46dSStephen Warren 	err = mmc_set_capacity(mmc, mmc->part_num);
1042f866a46dSStephen Warren 	if (err)
1043f866a46dSStephen Warren 		return err;
1044d23e2c09SSukumar Ghorai 
1045272cc70bSAndy Fleming 	if (IS_SD(mmc))
1046272cc70bSAndy Fleming 		err = sd_change_freq(mmc);
1047272cc70bSAndy Fleming 	else
1048272cc70bSAndy Fleming 		err = mmc_change_freq(mmc);
1049272cc70bSAndy Fleming 
1050272cc70bSAndy Fleming 	if (err)
1051272cc70bSAndy Fleming 		return err;
1052272cc70bSAndy Fleming 
1053272cc70bSAndy Fleming 	/* Restrict card's capabilities by what the host can do */
105493bfd616SPantelis Antoniou 	mmc->card_caps &= mmc->cfg->host_caps;
1055272cc70bSAndy Fleming 
1056272cc70bSAndy Fleming 	if (IS_SD(mmc)) {
1057272cc70bSAndy Fleming 		if (mmc->card_caps & MMC_MODE_4BIT) {
1058272cc70bSAndy Fleming 			cmd.cmdidx = MMC_CMD_APP_CMD;
1059272cc70bSAndy Fleming 			cmd.resp_type = MMC_RSP_R1;
1060272cc70bSAndy Fleming 			cmd.cmdarg = mmc->rca << 16;
1061272cc70bSAndy Fleming 
1062272cc70bSAndy Fleming 			err = mmc_send_cmd(mmc, &cmd, NULL);
1063272cc70bSAndy Fleming 			if (err)
1064272cc70bSAndy Fleming 				return err;
1065272cc70bSAndy Fleming 
1066272cc70bSAndy Fleming 			cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH;
1067272cc70bSAndy Fleming 			cmd.resp_type = MMC_RSP_R1;
1068272cc70bSAndy Fleming 			cmd.cmdarg = 2;
1069272cc70bSAndy Fleming 			err = mmc_send_cmd(mmc, &cmd, NULL);
1070272cc70bSAndy Fleming 			if (err)
1071272cc70bSAndy Fleming 				return err;
1072272cc70bSAndy Fleming 
1073272cc70bSAndy Fleming 			mmc_set_bus_width(mmc, 4);
1074272cc70bSAndy Fleming 		}
1075272cc70bSAndy Fleming 
1076272cc70bSAndy Fleming 		if (mmc->card_caps & MMC_MODE_HS)
1077ad5fd922SJaehoon Chung 			mmc->tran_speed = 50000000;
1078272cc70bSAndy Fleming 		else
1079ad5fd922SJaehoon Chung 			mmc->tran_speed = 25000000;
1080272cc70bSAndy Fleming 	} else {
10817798f6dbSAndy Fleming 		int idx;
10827798f6dbSAndy Fleming 
10837798f6dbSAndy Fleming 		/* An array of possible bus widths in order of preference */
10847798f6dbSAndy Fleming 		static unsigned ext_csd_bits[] = {
10857798f6dbSAndy Fleming 			EXT_CSD_BUS_WIDTH_8,
10867798f6dbSAndy Fleming 			EXT_CSD_BUS_WIDTH_4,
10877798f6dbSAndy Fleming 			EXT_CSD_BUS_WIDTH_1,
10887798f6dbSAndy Fleming 		};
10897798f6dbSAndy Fleming 
10907798f6dbSAndy Fleming 		/* An array to map CSD bus widths to host cap bits */
10917798f6dbSAndy Fleming 		static unsigned ext_to_hostcaps[] = {
10927798f6dbSAndy Fleming 			[EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT,
10937798f6dbSAndy Fleming 			[EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT,
10947798f6dbSAndy Fleming 		};
10957798f6dbSAndy Fleming 
10967798f6dbSAndy Fleming 		/* An array to map chosen bus width to an integer */
10977798f6dbSAndy Fleming 		static unsigned widths[] = {
10987798f6dbSAndy Fleming 			8, 4, 1,
10997798f6dbSAndy Fleming 		};
11007798f6dbSAndy Fleming 
11017798f6dbSAndy Fleming 		for (idx=0; idx < ARRAY_SIZE(ext_csd_bits); idx++) {
11027798f6dbSAndy Fleming 			unsigned int extw = ext_csd_bits[idx];
11037798f6dbSAndy Fleming 
11047798f6dbSAndy Fleming 			/*
11057798f6dbSAndy Fleming 			 * Check to make sure the controller supports
11067798f6dbSAndy Fleming 			 * this bus width, if it's more than 1
11077798f6dbSAndy Fleming 			 */
11087798f6dbSAndy Fleming 			if (extw != EXT_CSD_BUS_WIDTH_1 &&
110993bfd616SPantelis Antoniou 					!(mmc->cfg->host_caps & ext_to_hostcaps[extw]))
11107798f6dbSAndy Fleming 				continue;
11117798f6dbSAndy Fleming 
1112272cc70bSAndy Fleming 			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
11137798f6dbSAndy Fleming 					EXT_CSD_BUS_WIDTH, extw);
1114272cc70bSAndy Fleming 
1115272cc70bSAndy Fleming 			if (err)
11164137894eSLei Wen 				continue;
1117272cc70bSAndy Fleming 
11187798f6dbSAndy Fleming 			mmc_set_bus_width(mmc, widths[idx]);
1119272cc70bSAndy Fleming 
11204137894eSLei Wen 			err = mmc_send_ext_csd(mmc, test_csd);
11214137894eSLei Wen 			if (!err && ext_csd[EXT_CSD_PARTITIONING_SUPPORT] \
11224137894eSLei Wen 				    == test_csd[EXT_CSD_PARTITIONING_SUPPORT]
11234137894eSLei Wen 				 && ext_csd[EXT_CSD_ERASE_GROUP_DEF] \
11244137894eSLei Wen 				    == test_csd[EXT_CSD_ERASE_GROUP_DEF] \
11254137894eSLei Wen 				 && ext_csd[EXT_CSD_REV] \
11264137894eSLei Wen 				    == test_csd[EXT_CSD_REV]
11274137894eSLei Wen 				 && ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] \
11284137894eSLei Wen 				    == test_csd[EXT_CSD_HC_ERASE_GRP_SIZE]
11294137894eSLei Wen 				 && memcmp(&ext_csd[EXT_CSD_SEC_CNT], \
11304137894eSLei Wen 					&test_csd[EXT_CSD_SEC_CNT], 4) == 0) {
1131272cc70bSAndy Fleming 
11327798f6dbSAndy Fleming 				mmc->card_caps |= ext_to_hostcaps[extw];
11334137894eSLei Wen 				break;
11344137894eSLei Wen 			}
1135272cc70bSAndy Fleming 		}
1136272cc70bSAndy Fleming 
1137272cc70bSAndy Fleming 		if (mmc->card_caps & MMC_MODE_HS) {
1138272cc70bSAndy Fleming 			if (mmc->card_caps & MMC_MODE_HS_52MHz)
1139ad5fd922SJaehoon Chung 				mmc->tran_speed = 52000000;
1140272cc70bSAndy Fleming 			else
1141ad5fd922SJaehoon Chung 				mmc->tran_speed = 26000000;
1142272cc70bSAndy Fleming 		}
1143ad5fd922SJaehoon Chung 	}
1144ad5fd922SJaehoon Chung 
1145ad5fd922SJaehoon Chung 	mmc_set_clock(mmc, mmc->tran_speed);
1146272cc70bSAndy Fleming 
1147272cc70bSAndy Fleming 	/* fill in device description */
1148272cc70bSAndy Fleming 	mmc->block_dev.lun = 0;
1149272cc70bSAndy Fleming 	mmc->block_dev.type = 0;
1150272cc70bSAndy Fleming 	mmc->block_dev.blksz = mmc->read_bl_len;
11510472fbfdSEgbert Eich 	mmc->block_dev.log2blksz = LOG2(mmc->block_dev.blksz);
11529b1f942cSRabin Vincent 	mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len);
115356196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
1154babce5f6STaylor Hutt 	sprintf(mmc->block_dev.vendor, "Man %06x Snr %04x%04x",
1155babce5f6STaylor Hutt 		mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff),
1156babce5f6STaylor Hutt 		(mmc->cid[3] >> 16) & 0xffff);
1157babce5f6STaylor Hutt 	sprintf(mmc->block_dev.product, "%c%c%c%c%c%c", mmc->cid[0] & 0xff,
11580b453ffeSRabin Vincent 		(mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
1159babce5f6STaylor Hutt 		(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff,
1160babce5f6STaylor Hutt 		(mmc->cid[2] >> 24) & 0xff);
1161babce5f6STaylor Hutt 	sprintf(mmc->block_dev.revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf,
1162babce5f6STaylor Hutt 		(mmc->cid[2] >> 16) & 0xf);
116356196826SPaul Burton #else
116456196826SPaul Burton 	mmc->block_dev.vendor[0] = 0;
116556196826SPaul Burton 	mmc->block_dev.product[0] = 0;
116656196826SPaul Burton 	mmc->block_dev.revision[0] = 0;
116756196826SPaul Burton #endif
1168122efd43SMikhail Kshevetskiy #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT)
1169272cc70bSAndy Fleming 	init_part(&mmc->block_dev);
1170122efd43SMikhail Kshevetskiy #endif
1171272cc70bSAndy Fleming 
1172272cc70bSAndy Fleming 	return 0;
1173272cc70bSAndy Fleming }
1174272cc70bSAndy Fleming 
1175fdbb873eSKim Phillips static int mmc_send_if_cond(struct mmc *mmc)
1176272cc70bSAndy Fleming {
1177272cc70bSAndy Fleming 	struct mmc_cmd cmd;
1178272cc70bSAndy Fleming 	int err;
1179272cc70bSAndy Fleming 
1180272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_SEND_IF_COND;
1181272cc70bSAndy Fleming 	/* We set the bit if the host supports voltages between 2.7 and 3.6 V */
118293bfd616SPantelis Antoniou 	cmd.cmdarg = ((mmc->cfg->voltages & 0xff8000) != 0) << 8 | 0xaa;
1183272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R7;
1184272cc70bSAndy Fleming 
1185272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
1186272cc70bSAndy Fleming 
1187272cc70bSAndy Fleming 	if (err)
1188272cc70bSAndy Fleming 		return err;
1189272cc70bSAndy Fleming 
1190998be3ddSRabin Vincent 	if ((cmd.response[0] & 0xff) != 0xaa)
1191272cc70bSAndy Fleming 		return UNUSABLE_ERR;
1192272cc70bSAndy Fleming 	else
1193272cc70bSAndy Fleming 		mmc->version = SD_VERSION_2;
1194272cc70bSAndy Fleming 
1195272cc70bSAndy Fleming 	return 0;
1196272cc70bSAndy Fleming }
1197272cc70bSAndy Fleming 
119893bfd616SPantelis Antoniou /* not used any more */
119993bfd616SPantelis Antoniou int __deprecated mmc_register(struct mmc *mmc)
1200272cc70bSAndy Fleming {
120193bfd616SPantelis Antoniou #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
120293bfd616SPantelis Antoniou 	printf("%s is deprecated! use mmc_create() instead.\n", __func__);
120393bfd616SPantelis Antoniou #endif
120493bfd616SPantelis Antoniou 	return -1;
120593bfd616SPantelis Antoniou }
120693bfd616SPantelis Antoniou 
120793bfd616SPantelis Antoniou struct mmc *mmc_create(const struct mmc_config *cfg, void *priv)
120893bfd616SPantelis Antoniou {
120993bfd616SPantelis Antoniou 	struct mmc *mmc;
121093bfd616SPantelis Antoniou 
121193bfd616SPantelis Antoniou 	/* quick validation */
121293bfd616SPantelis Antoniou 	if (cfg == NULL || cfg->ops == NULL || cfg->ops->send_cmd == NULL ||
121393bfd616SPantelis Antoniou 			cfg->f_min == 0 || cfg->f_max == 0 || cfg->b_max == 0)
121493bfd616SPantelis Antoniou 		return NULL;
121593bfd616SPantelis Antoniou 
121693bfd616SPantelis Antoniou 	mmc = calloc(1, sizeof(*mmc));
121793bfd616SPantelis Antoniou 	if (mmc == NULL)
121893bfd616SPantelis Antoniou 		return NULL;
121993bfd616SPantelis Antoniou 
122093bfd616SPantelis Antoniou 	mmc->cfg = cfg;
122193bfd616SPantelis Antoniou 	mmc->priv = priv;
122293bfd616SPantelis Antoniou 
122393bfd616SPantelis Antoniou 	/* the following chunk was mmc_register() */
122493bfd616SPantelis Antoniou 
1225ab71188cSMarkus Niebel 	/* Setup dsr related values */
1226ab71188cSMarkus Niebel 	mmc->dsr_imp = 0;
1227ab71188cSMarkus Niebel 	mmc->dsr = 0xffffffff;
1228272cc70bSAndy Fleming 	/* Setup the universal parts of the block interface just once */
1229272cc70bSAndy Fleming 	mmc->block_dev.if_type = IF_TYPE_MMC;
1230272cc70bSAndy Fleming 	mmc->block_dev.dev = cur_dev_num++;
1231272cc70bSAndy Fleming 	mmc->block_dev.removable = 1;
1232272cc70bSAndy Fleming 	mmc->block_dev.block_read = mmc_bread;
1233272cc70bSAndy Fleming 	mmc->block_dev.block_write = mmc_bwrite;
1234e6f99a56SLei Wen 	mmc->block_dev.block_erase = mmc_berase;
123593bfd616SPantelis Antoniou 
123693bfd616SPantelis Antoniou 	/* setup initial part type */
123793bfd616SPantelis Antoniou 	mmc->block_dev.part_type = mmc->cfg->part_type;
1238272cc70bSAndy Fleming 
1239272cc70bSAndy Fleming 	INIT_LIST_HEAD(&mmc->link);
1240272cc70bSAndy Fleming 
1241272cc70bSAndy Fleming 	list_add_tail(&mmc->link, &mmc_devices);
1242272cc70bSAndy Fleming 
124393bfd616SPantelis Antoniou 	return mmc;
124493bfd616SPantelis Antoniou }
124593bfd616SPantelis Antoniou 
124693bfd616SPantelis Antoniou void mmc_destroy(struct mmc *mmc)
124793bfd616SPantelis Antoniou {
124893bfd616SPantelis Antoniou 	/* only freeing memory for now */
124993bfd616SPantelis Antoniou 	free(mmc);
1250272cc70bSAndy Fleming }
1251272cc70bSAndy Fleming 
1252df3fc526SMatthew McClintock #ifdef CONFIG_PARTITIONS
1253272cc70bSAndy Fleming block_dev_desc_t *mmc_get_dev(int dev)
1254272cc70bSAndy Fleming {
1255272cc70bSAndy Fleming 	struct mmc *mmc = find_mmc_device(dev);
12566bb4b4bcSBenoît Thébaudeau 	if (!mmc || mmc_init(mmc))
125740242bc3SŁukasz Majewski 		return NULL;
1258272cc70bSAndy Fleming 
125940242bc3SŁukasz Majewski 	return &mmc->block_dev;
1260272cc70bSAndy Fleming }
1261df3fc526SMatthew McClintock #endif
1262272cc70bSAndy Fleming 
1263e9550449SChe-Liang Chiou int mmc_start_init(struct mmc *mmc)
1264272cc70bSAndy Fleming {
1265afd5932bSMacpaul Lin 	int err;
1266272cc70bSAndy Fleming 
1267ab769f22SPantelis Antoniou 	/* we pretend there's no card when init is NULL */
126893bfd616SPantelis Antoniou 	if (mmc_getcd(mmc) == 0 || mmc->cfg->ops->init == NULL) {
126948972d90SThierry Reding 		mmc->has_init = 0;
127056196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
127148972d90SThierry Reding 		printf("MMC: no card present\n");
127256196826SPaul Burton #endif
127348972d90SThierry Reding 		return NO_CARD_ERR;
127448972d90SThierry Reding 	}
127548972d90SThierry Reding 
1276bc897b1dSLei Wen 	if (mmc->has_init)
1277bc897b1dSLei Wen 		return 0;
1278bc897b1dSLei Wen 
1279ab769f22SPantelis Antoniou 	/* made sure it's not NULL earlier */
128093bfd616SPantelis Antoniou 	err = mmc->cfg->ops->init(mmc);
1281272cc70bSAndy Fleming 
1282272cc70bSAndy Fleming 	if (err)
1283272cc70bSAndy Fleming 		return err;
1284272cc70bSAndy Fleming 
1285b86b85e2SIlya Yanok 	mmc_set_bus_width(mmc, 1);
1286b86b85e2SIlya Yanok 	mmc_set_clock(mmc, 1);
1287b86b85e2SIlya Yanok 
1288272cc70bSAndy Fleming 	/* Reset the Card */
1289272cc70bSAndy Fleming 	err = mmc_go_idle(mmc);
1290272cc70bSAndy Fleming 
1291272cc70bSAndy Fleming 	if (err)
1292272cc70bSAndy Fleming 		return err;
1293272cc70bSAndy Fleming 
1294bc897b1dSLei Wen 	/* The internal partition reset to user partition(0) at every CMD0*/
1295bc897b1dSLei Wen 	mmc->part_num = 0;
1296bc897b1dSLei Wen 
1297272cc70bSAndy Fleming 	/* Test for SD version 2 */
1298272cc70bSAndy Fleming 	err = mmc_send_if_cond(mmc);
1299272cc70bSAndy Fleming 
1300272cc70bSAndy Fleming 	/* Now try to get the SD card's operating condition */
1301272cc70bSAndy Fleming 	err = sd_send_op_cond(mmc);
1302272cc70bSAndy Fleming 
1303272cc70bSAndy Fleming 	/* If the command timed out, we check for an MMC card */
1304272cc70bSAndy Fleming 	if (err == TIMEOUT) {
1305272cc70bSAndy Fleming 		err = mmc_send_op_cond(mmc);
1306272cc70bSAndy Fleming 
1307e9550449SChe-Liang Chiou 		if (err && err != IN_PROGRESS) {
130856196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
1309272cc70bSAndy Fleming 			printf("Card did not respond to voltage select!\n");
131056196826SPaul Burton #endif
1311272cc70bSAndy Fleming 			return UNUSABLE_ERR;
1312272cc70bSAndy Fleming 		}
1313272cc70bSAndy Fleming 	}
1314272cc70bSAndy Fleming 
1315e9550449SChe-Liang Chiou 	if (err == IN_PROGRESS)
1316e9550449SChe-Liang Chiou 		mmc->init_in_progress = 1;
1317e9550449SChe-Liang Chiou 
1318e9550449SChe-Liang Chiou 	return err;
1319e9550449SChe-Liang Chiou }
1320e9550449SChe-Liang Chiou 
1321e9550449SChe-Liang Chiou static int mmc_complete_init(struct mmc *mmc)
1322e9550449SChe-Liang Chiou {
1323e9550449SChe-Liang Chiou 	int err = 0;
1324e9550449SChe-Liang Chiou 
1325e9550449SChe-Liang Chiou 	if (mmc->op_cond_pending)
1326e9550449SChe-Liang Chiou 		err = mmc_complete_op_cond(mmc);
1327e9550449SChe-Liang Chiou 
1328e9550449SChe-Liang Chiou 	if (!err)
1329bc897b1dSLei Wen 		err = mmc_startup(mmc);
1330bc897b1dSLei Wen 	if (err)
1331bc897b1dSLei Wen 		mmc->has_init = 0;
1332bc897b1dSLei Wen 	else
1333bc897b1dSLei Wen 		mmc->has_init = 1;
1334e9550449SChe-Liang Chiou 	mmc->init_in_progress = 0;
1335e9550449SChe-Liang Chiou 	return err;
1336e9550449SChe-Liang Chiou }
1337e9550449SChe-Liang Chiou 
1338e9550449SChe-Liang Chiou int mmc_init(struct mmc *mmc)
1339e9550449SChe-Liang Chiou {
1340e9550449SChe-Liang Chiou 	int err = IN_PROGRESS;
1341d803fea5SMateusz Zalega 	unsigned start;
1342e9550449SChe-Liang Chiou 
1343e9550449SChe-Liang Chiou 	if (mmc->has_init)
1344e9550449SChe-Liang Chiou 		return 0;
1345d803fea5SMateusz Zalega 
1346d803fea5SMateusz Zalega 	start = get_timer(0);
1347d803fea5SMateusz Zalega 
1348e9550449SChe-Liang Chiou 	if (!mmc->init_in_progress)
1349e9550449SChe-Liang Chiou 		err = mmc_start_init(mmc);
1350e9550449SChe-Liang Chiou 
1351e9550449SChe-Liang Chiou 	if (!err || err == IN_PROGRESS)
1352e9550449SChe-Liang Chiou 		err = mmc_complete_init(mmc);
1353e9550449SChe-Liang Chiou 	debug("%s: %d, time %lu\n", __func__, err, get_timer(start));
1354bc897b1dSLei Wen 	return err;
1355272cc70bSAndy Fleming }
1356272cc70bSAndy Fleming 
1357ab71188cSMarkus Niebel int mmc_set_dsr(struct mmc *mmc, u16 val)
1358ab71188cSMarkus Niebel {
1359ab71188cSMarkus Niebel 	mmc->dsr = val;
1360ab71188cSMarkus Niebel 	return 0;
1361ab71188cSMarkus Niebel }
1362ab71188cSMarkus Niebel 
1363272cc70bSAndy Fleming /*
1364272cc70bSAndy Fleming  * CPU and board-specific MMC initializations.  Aliased function
1365272cc70bSAndy Fleming  * signals caller to move on
1366272cc70bSAndy Fleming  */
1367272cc70bSAndy Fleming static int __def_mmc_init(bd_t *bis)
1368272cc70bSAndy Fleming {
1369272cc70bSAndy Fleming 	return -1;
1370272cc70bSAndy Fleming }
1371272cc70bSAndy Fleming 
1372f9a109b3SPeter Tyser int cpu_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init")));
1373f9a109b3SPeter Tyser int board_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init")));
1374272cc70bSAndy Fleming 
137556196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
137656196826SPaul Burton 
1377272cc70bSAndy Fleming void print_mmc_devices(char separator)
1378272cc70bSAndy Fleming {
1379272cc70bSAndy Fleming 	struct mmc *m;
1380272cc70bSAndy Fleming 	struct list_head *entry;
1381272cc70bSAndy Fleming 
1382272cc70bSAndy Fleming 	list_for_each(entry, &mmc_devices) {
1383272cc70bSAndy Fleming 		m = list_entry(entry, struct mmc, link);
1384272cc70bSAndy Fleming 
138593bfd616SPantelis Antoniou 		printf("%s: %d", m->cfg->name, m->block_dev.dev);
1386272cc70bSAndy Fleming 
1387272cc70bSAndy Fleming 		if (entry->next != &mmc_devices)
1388272cc70bSAndy Fleming 			printf("%c ", separator);
1389272cc70bSAndy Fleming 	}
1390272cc70bSAndy Fleming 
1391272cc70bSAndy Fleming 	printf("\n");
1392272cc70bSAndy Fleming }
1393272cc70bSAndy Fleming 
139456196826SPaul Burton #else
139556196826SPaul Burton void print_mmc_devices(char separator) { }
139656196826SPaul Burton #endif
139756196826SPaul Burton 
1398ea6ebe21SLei Wen int get_mmc_num(void)
1399ea6ebe21SLei Wen {
1400ea6ebe21SLei Wen 	return cur_dev_num;
1401ea6ebe21SLei Wen }
1402ea6ebe21SLei Wen 
1403e9550449SChe-Liang Chiou void mmc_set_preinit(struct mmc *mmc, int preinit)
1404e9550449SChe-Liang Chiou {
1405e9550449SChe-Liang Chiou 	mmc->preinit = preinit;
1406e9550449SChe-Liang Chiou }
1407e9550449SChe-Liang Chiou 
1408e9550449SChe-Liang Chiou static void do_preinit(void)
1409e9550449SChe-Liang Chiou {
1410e9550449SChe-Liang Chiou 	struct mmc *m;
1411e9550449SChe-Liang Chiou 	struct list_head *entry;
1412e9550449SChe-Liang Chiou 
1413e9550449SChe-Liang Chiou 	list_for_each(entry, &mmc_devices) {
1414e9550449SChe-Liang Chiou 		m = list_entry(entry, struct mmc, link);
1415e9550449SChe-Liang Chiou 
1416e9550449SChe-Liang Chiou 		if (m->preinit)
1417e9550449SChe-Liang Chiou 			mmc_start_init(m);
1418e9550449SChe-Liang Chiou 	}
1419e9550449SChe-Liang Chiou }
1420e9550449SChe-Liang Chiou 
1421e9550449SChe-Liang Chiou 
1422272cc70bSAndy Fleming int mmc_initialize(bd_t *bis)
1423272cc70bSAndy Fleming {
1424272cc70bSAndy Fleming 	INIT_LIST_HEAD (&mmc_devices);
1425272cc70bSAndy Fleming 	cur_dev_num = 0;
1426272cc70bSAndy Fleming 
1427272cc70bSAndy Fleming 	if (board_mmc_init(bis) < 0)
1428272cc70bSAndy Fleming 		cpu_mmc_init(bis);
1429272cc70bSAndy Fleming 
1430bb0dc108SYing Zhang #ifndef CONFIG_SPL_BUILD
1431272cc70bSAndy Fleming 	print_mmc_devices(',');
1432bb0dc108SYing Zhang #endif
1433272cc70bSAndy Fleming 
1434e9550449SChe-Liang Chiou 	do_preinit();
1435272cc70bSAndy Fleming 	return 0;
1436272cc70bSAndy Fleming }
14373690d6d6SAmar 
14383690d6d6SAmar #ifdef CONFIG_SUPPORT_EMMC_BOOT
14393690d6d6SAmar /*
14403690d6d6SAmar  * This function changes the size of boot partition and the size of rpmb
14413690d6d6SAmar  * partition present on EMMC devices.
14423690d6d6SAmar  *
14433690d6d6SAmar  * Input Parameters:
14443690d6d6SAmar  * struct *mmc: pointer for the mmc device strcuture
14453690d6d6SAmar  * bootsize: size of boot partition
14463690d6d6SAmar  * rpmbsize: size of rpmb partition
14473690d6d6SAmar  *
14483690d6d6SAmar  * Returns 0 on success.
14493690d6d6SAmar  */
14503690d6d6SAmar 
14513690d6d6SAmar int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize,
14523690d6d6SAmar 				unsigned long rpmbsize)
14533690d6d6SAmar {
14543690d6d6SAmar 	int err;
14553690d6d6SAmar 	struct mmc_cmd cmd;
14563690d6d6SAmar 
14573690d6d6SAmar 	/* Only use this command for raw EMMC moviNAND. Enter backdoor mode */
14583690d6d6SAmar 	cmd.cmdidx = MMC_CMD_RES_MAN;
14593690d6d6SAmar 	cmd.resp_type = MMC_RSP_R1b;
14603690d6d6SAmar 	cmd.cmdarg = MMC_CMD62_ARG1;
14613690d6d6SAmar 
14623690d6d6SAmar 	err = mmc_send_cmd(mmc, &cmd, NULL);
14633690d6d6SAmar 	if (err) {
14643690d6d6SAmar 		debug("mmc_boot_partition_size_change: Error1 = %d\n", err);
14653690d6d6SAmar 		return err;
14663690d6d6SAmar 	}
14673690d6d6SAmar 
14683690d6d6SAmar 	/* Boot partition changing mode */
14693690d6d6SAmar 	cmd.cmdidx = MMC_CMD_RES_MAN;
14703690d6d6SAmar 	cmd.resp_type = MMC_RSP_R1b;
14713690d6d6SAmar 	cmd.cmdarg = MMC_CMD62_ARG2;
14723690d6d6SAmar 
14733690d6d6SAmar 	err = mmc_send_cmd(mmc, &cmd, NULL);
14743690d6d6SAmar 	if (err) {
14753690d6d6SAmar 		debug("mmc_boot_partition_size_change: Error2 = %d\n", err);
14763690d6d6SAmar 		return err;
14773690d6d6SAmar 	}
14783690d6d6SAmar 	/* boot partition size is multiple of 128KB */
14793690d6d6SAmar 	bootsize = (bootsize * 1024) / 128;
14803690d6d6SAmar 
14813690d6d6SAmar 	/* Arg: boot partition size */
14823690d6d6SAmar 	cmd.cmdidx = MMC_CMD_RES_MAN;
14833690d6d6SAmar 	cmd.resp_type = MMC_RSP_R1b;
14843690d6d6SAmar 	cmd.cmdarg = bootsize;
14853690d6d6SAmar 
14863690d6d6SAmar 	err = mmc_send_cmd(mmc, &cmd, NULL);
14873690d6d6SAmar 	if (err) {
14883690d6d6SAmar 		debug("mmc_boot_partition_size_change: Error3 = %d\n", err);
14893690d6d6SAmar 		return err;
14903690d6d6SAmar 	}
14913690d6d6SAmar 	/* RPMB partition size is multiple of 128KB */
14923690d6d6SAmar 	rpmbsize = (rpmbsize * 1024) / 128;
14933690d6d6SAmar 	/* Arg: RPMB partition size */
14943690d6d6SAmar 	cmd.cmdidx = MMC_CMD_RES_MAN;
14953690d6d6SAmar 	cmd.resp_type = MMC_RSP_R1b;
14963690d6d6SAmar 	cmd.cmdarg = rpmbsize;
14973690d6d6SAmar 
14983690d6d6SAmar 	err = mmc_send_cmd(mmc, &cmd, NULL);
14993690d6d6SAmar 	if (err) {
15003690d6d6SAmar 		debug("mmc_boot_partition_size_change: Error4 = %d\n", err);
15013690d6d6SAmar 		return err;
15023690d6d6SAmar 	}
15033690d6d6SAmar 	return 0;
15043690d6d6SAmar }
15053690d6d6SAmar 
15063690d6d6SAmar /*
15075a99b9deSTom Rini  * Modify EXT_CSD[177] which is BOOT_BUS_WIDTH
15085a99b9deSTom Rini  * based on the passed in values for BOOT_BUS_WIDTH, RESET_BOOT_BUS_WIDTH
15095a99b9deSTom Rini  * and BOOT_MODE.
15105a99b9deSTom Rini  *
15115a99b9deSTom Rini  * Returns 0 on success.
15125a99b9deSTom Rini  */
15135a99b9deSTom Rini int mmc_set_boot_bus_width(struct mmc *mmc, u8 width, u8 reset, u8 mode)
15145a99b9deSTom Rini {
15155a99b9deSTom Rini 	int err;
15165a99b9deSTom Rini 
15175a99b9deSTom Rini 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_BUS_WIDTH,
15185a99b9deSTom Rini 			 EXT_CSD_BOOT_BUS_WIDTH_MODE(mode) |
15195a99b9deSTom Rini 			 EXT_CSD_BOOT_BUS_WIDTH_RESET(reset) |
15205a99b9deSTom Rini 			 EXT_CSD_BOOT_BUS_WIDTH_WIDTH(width));
15215a99b9deSTom Rini 
15225a99b9deSTom Rini 	if (err)
15235a99b9deSTom Rini 		return err;
15245a99b9deSTom Rini 	return 0;
15255a99b9deSTom Rini }
15265a99b9deSTom Rini 
15275a99b9deSTom Rini /*
1528792970b0STom Rini  * Modify EXT_CSD[179] which is PARTITION_CONFIG (formerly BOOT_CONFIG)
1529792970b0STom Rini  * based on the passed in values for BOOT_ACK, BOOT_PARTITION_ENABLE and
1530792970b0STom Rini  * PARTITION_ACCESS.
1531792970b0STom Rini  *
1532792970b0STom Rini  * Returns 0 on success.
1533792970b0STom Rini  */
1534792970b0STom Rini int mmc_set_part_conf(struct mmc *mmc, u8 ack, u8 part_num, u8 access)
1535792970b0STom Rini {
1536792970b0STom Rini 	int err;
1537792970b0STom Rini 
1538792970b0STom Rini 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
1539792970b0STom Rini 			 EXT_CSD_BOOT_ACK(ack) |
1540792970b0STom Rini 			 EXT_CSD_BOOT_PART_NUM(part_num) |
1541792970b0STom Rini 			 EXT_CSD_PARTITION_ACCESS(access));
1542792970b0STom Rini 
1543792970b0STom Rini 	if (err)
1544792970b0STom Rini 		return err;
1545792970b0STom Rini 	return 0;
1546792970b0STom Rini }
154733ace362STom Rini 
154833ace362STom Rini /*
154933ace362STom Rini  * Modify EXT_CSD[162] which is RST_n_FUNCTION based on the given value
155033ace362STom Rini  * for enable.  Note that this is a write-once field for non-zero values.
155133ace362STom Rini  *
155233ace362STom Rini  * Returns 0 on success.
155333ace362STom Rini  */
155433ace362STom Rini int mmc_set_rst_n_function(struct mmc *mmc, u8 enable)
155533ace362STom Rini {
155633ace362STom Rini 	return mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_RST_N_FUNCTION,
155733ace362STom Rini 			  enable);
155833ace362STom Rini }
15593690d6d6SAmar #endif
1560