xref: /openbmc/u-boot/drivers/mmc/mmc.c (revision f866a46d)
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  *
7272cc70bSAndy Fleming  * See file CREDITS for list of people who contributed to this
8272cc70bSAndy Fleming  * project.
9272cc70bSAndy Fleming  *
10272cc70bSAndy Fleming  * This program is free software; you can redistribute it and/or
11272cc70bSAndy Fleming  * modify it under the terms of the GNU General Public License as
12272cc70bSAndy Fleming  * published by the Free Software Foundation; either version 2 of
13272cc70bSAndy Fleming  * the License, or (at your option) any later version.
14272cc70bSAndy Fleming  *
15272cc70bSAndy Fleming  * This program is distributed in the hope that it will be useful,
16272cc70bSAndy Fleming  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17272cc70bSAndy Fleming  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18272cc70bSAndy Fleming  * GNU General Public License for more details.
19272cc70bSAndy Fleming  *
20272cc70bSAndy Fleming  * You should have received a copy of the GNU General Public License
21272cc70bSAndy Fleming  * along with this program; if not, write to the Free Software
22272cc70bSAndy Fleming  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23272cc70bSAndy Fleming  * MA 02111-1307 USA
24272cc70bSAndy Fleming  */
25272cc70bSAndy Fleming 
26272cc70bSAndy Fleming #include <config.h>
27272cc70bSAndy Fleming #include <common.h>
28272cc70bSAndy Fleming #include <command.h>
29272cc70bSAndy Fleming #include <mmc.h>
30272cc70bSAndy Fleming #include <part.h>
31272cc70bSAndy Fleming #include <malloc.h>
32272cc70bSAndy Fleming #include <linux/list.h>
339b1f942cSRabin Vincent #include <div64.h>
34272cc70bSAndy Fleming 
35ce0fbcd2SMatt Waddel /* Set block count limit because of 16 bit register limit on some hardware*/
36ce0fbcd2SMatt Waddel #ifndef CONFIG_SYS_MMC_MAX_BLK_COUNT
37ce0fbcd2SMatt Waddel #define CONFIG_SYS_MMC_MAX_BLK_COUNT 65535
38ce0fbcd2SMatt Waddel #endif
39ce0fbcd2SMatt Waddel 
40272cc70bSAndy Fleming static struct list_head mmc_devices;
41272cc70bSAndy Fleming static int cur_dev_num = -1;
42272cc70bSAndy Fleming 
43d23d8d7eSNikita Kiryanov int __weak board_mmc_getwp(struct mmc *mmc)
44d23d8d7eSNikita Kiryanov {
45d23d8d7eSNikita Kiryanov 	return -1;
46d23d8d7eSNikita Kiryanov }
47d23d8d7eSNikita Kiryanov 
48d23d8d7eSNikita Kiryanov int mmc_getwp(struct mmc *mmc)
49d23d8d7eSNikita Kiryanov {
50d23d8d7eSNikita Kiryanov 	int wp;
51d23d8d7eSNikita Kiryanov 
52d23d8d7eSNikita Kiryanov 	wp = board_mmc_getwp(mmc);
53d23d8d7eSNikita Kiryanov 
54d4e1da4eSPeter Korsgaard 	if (wp < 0) {
55d4e1da4eSPeter Korsgaard 		if (mmc->getwp)
56d23d8d7eSNikita Kiryanov 			wp = mmc->getwp(mmc);
57d4e1da4eSPeter Korsgaard 		else
58d4e1da4eSPeter Korsgaard 			wp = 0;
59d4e1da4eSPeter Korsgaard 	}
60d23d8d7eSNikita Kiryanov 
61d23d8d7eSNikita Kiryanov 	return wp;
62d23d8d7eSNikita Kiryanov }
63d23d8d7eSNikita Kiryanov 
64314284b1SThierry Reding int __board_mmc_getcd(struct mmc *mmc) {
6511fdade2SStefano Babic 	return -1;
6611fdade2SStefano Babic }
6711fdade2SStefano Babic 
68314284b1SThierry Reding int board_mmc_getcd(struct mmc *mmc)__attribute__((weak,
6911fdade2SStefano Babic 	alias("__board_mmc_getcd")));
7011fdade2SStefano Babic 
71fdbb873eSKim Phillips static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
72fdbb873eSKim Phillips 			struct mmc_data *data)
73272cc70bSAndy Fleming {
748635ff9eSMarek Vasut 	struct mmc_data backup;
755db2fe3aSRaffaele Recalcati 	int ret;
768635ff9eSMarek Vasut 
778635ff9eSMarek Vasut 	memset(&backup, 0, sizeof(backup));
788635ff9eSMarek Vasut 
798635ff9eSMarek Vasut #ifdef CONFIG_MMC_TRACE
805db2fe3aSRaffaele Recalcati 	int i;
815db2fe3aSRaffaele Recalcati 	u8 *ptr;
825db2fe3aSRaffaele Recalcati 
835db2fe3aSRaffaele Recalcati 	printf("CMD_SEND:%d\n", cmd->cmdidx);
845db2fe3aSRaffaele Recalcati 	printf("\t\tARG\t\t\t 0x%08X\n", cmd->cmdarg);
855db2fe3aSRaffaele Recalcati 	ret = mmc->send_cmd(mmc, cmd, data);
865db2fe3aSRaffaele Recalcati 	switch (cmd->resp_type) {
875db2fe3aSRaffaele Recalcati 		case MMC_RSP_NONE:
885db2fe3aSRaffaele Recalcati 			printf("\t\tMMC_RSP_NONE\n");
895db2fe3aSRaffaele Recalcati 			break;
905db2fe3aSRaffaele Recalcati 		case MMC_RSP_R1:
915db2fe3aSRaffaele Recalcati 			printf("\t\tMMC_RSP_R1,5,6,7 \t 0x%08X \n",
925db2fe3aSRaffaele Recalcati 				cmd->response[0]);
935db2fe3aSRaffaele Recalcati 			break;
945db2fe3aSRaffaele Recalcati 		case MMC_RSP_R1b:
955db2fe3aSRaffaele Recalcati 			printf("\t\tMMC_RSP_R1b\t\t 0x%08X \n",
965db2fe3aSRaffaele Recalcati 				cmd->response[0]);
975db2fe3aSRaffaele Recalcati 			break;
985db2fe3aSRaffaele Recalcati 		case MMC_RSP_R2:
995db2fe3aSRaffaele Recalcati 			printf("\t\tMMC_RSP_R2\t\t 0x%08X \n",
1005db2fe3aSRaffaele Recalcati 				cmd->response[0]);
1015db2fe3aSRaffaele Recalcati 			printf("\t\t          \t\t 0x%08X \n",
1025db2fe3aSRaffaele Recalcati 				cmd->response[1]);
1035db2fe3aSRaffaele Recalcati 			printf("\t\t          \t\t 0x%08X \n",
1045db2fe3aSRaffaele Recalcati 				cmd->response[2]);
1055db2fe3aSRaffaele Recalcati 			printf("\t\t          \t\t 0x%08X \n",
1065db2fe3aSRaffaele Recalcati 				cmd->response[3]);
1075db2fe3aSRaffaele Recalcati 			printf("\n");
1085db2fe3aSRaffaele Recalcati 			printf("\t\t\t\t\tDUMPING DATA\n");
1095db2fe3aSRaffaele Recalcati 			for (i = 0; i < 4; i++) {
1105db2fe3aSRaffaele Recalcati 				int j;
1115db2fe3aSRaffaele Recalcati 				printf("\t\t\t\t\t%03d - ", i*4);
112146bec79SDirk Behme 				ptr = (u8 *)&cmd->response[i];
1135db2fe3aSRaffaele Recalcati 				ptr += 3;
1145db2fe3aSRaffaele Recalcati 				for (j = 0; j < 4; j++)
1155db2fe3aSRaffaele Recalcati 					printf("%02X ", *ptr--);
1165db2fe3aSRaffaele Recalcati 				printf("\n");
1175db2fe3aSRaffaele Recalcati 			}
1185db2fe3aSRaffaele Recalcati 			break;
1195db2fe3aSRaffaele Recalcati 		case MMC_RSP_R3:
1205db2fe3aSRaffaele Recalcati 			printf("\t\tMMC_RSP_R3,4\t\t 0x%08X \n",
1215db2fe3aSRaffaele Recalcati 				cmd->response[0]);
1225db2fe3aSRaffaele Recalcati 			break;
1235db2fe3aSRaffaele Recalcati 		default:
1245db2fe3aSRaffaele Recalcati 			printf("\t\tERROR MMC rsp not supported\n");
1255db2fe3aSRaffaele Recalcati 			break;
1265db2fe3aSRaffaele Recalcati 	}
1275db2fe3aSRaffaele Recalcati #else
1288635ff9eSMarek Vasut 	ret = mmc->send_cmd(mmc, cmd, data);
1295db2fe3aSRaffaele Recalcati #endif
1308635ff9eSMarek Vasut 	return ret;
131272cc70bSAndy Fleming }
132272cc70bSAndy Fleming 
133fdbb873eSKim Phillips static int mmc_send_status(struct mmc *mmc, int timeout)
1345d4fc8d9SRaffaele Recalcati {
1355d4fc8d9SRaffaele Recalcati 	struct mmc_cmd cmd;
136d617c426SJan Kloetzke 	int err, retries = 5;
1375d4fc8d9SRaffaele Recalcati #ifdef CONFIG_MMC_TRACE
1385d4fc8d9SRaffaele Recalcati 	int status;
1395d4fc8d9SRaffaele Recalcati #endif
1405d4fc8d9SRaffaele Recalcati 
1415d4fc8d9SRaffaele Recalcati 	cmd.cmdidx = MMC_CMD_SEND_STATUS;
1425d4fc8d9SRaffaele Recalcati 	cmd.resp_type = MMC_RSP_R1;
143aaf3d41aSMarek Vasut 	if (!mmc_host_is_spi(mmc))
144aaf3d41aSMarek Vasut 		cmd.cmdarg = mmc->rca << 16;
1455d4fc8d9SRaffaele Recalcati 
1465d4fc8d9SRaffaele Recalcati 	do {
1475d4fc8d9SRaffaele Recalcati 		err = mmc_send_cmd(mmc, &cmd, NULL);
148d617c426SJan Kloetzke 		if (!err) {
149d617c426SJan Kloetzke 			if ((cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) &&
150d617c426SJan Kloetzke 			    (cmd.response[0] & MMC_STATUS_CURR_STATE) !=
151d617c426SJan Kloetzke 			     MMC_STATE_PRG)
1525d4fc8d9SRaffaele Recalcati 				break;
153d617c426SJan Kloetzke 			else if (cmd.response[0] & MMC_STATUS_MASK) {
154d617c426SJan Kloetzke 				printf("Status Error: 0x%08X\n",
155d617c426SJan Kloetzke 					cmd.response[0]);
156d617c426SJan Kloetzke 				return COMM_ERR;
157d617c426SJan Kloetzke 			}
158d617c426SJan Kloetzke 		} else if (--retries < 0)
159d617c426SJan Kloetzke 			return err;
1605d4fc8d9SRaffaele Recalcati 
1615d4fc8d9SRaffaele Recalcati 		udelay(1000);
1625d4fc8d9SRaffaele Recalcati 
1635d4fc8d9SRaffaele Recalcati 	} while (timeout--);
1645d4fc8d9SRaffaele Recalcati 
1655db2fe3aSRaffaele Recalcati #ifdef CONFIG_MMC_TRACE
1665db2fe3aSRaffaele Recalcati 	status = (cmd.response[0] & MMC_STATUS_CURR_STATE) >> 9;
1675db2fe3aSRaffaele Recalcati 	printf("CURR STATE:%d\n", status);
1685db2fe3aSRaffaele Recalcati #endif
1695b0c942fSJongman Heo 	if (timeout <= 0) {
1705d4fc8d9SRaffaele Recalcati 		printf("Timeout waiting card ready\n");
1715d4fc8d9SRaffaele Recalcati 		return TIMEOUT;
1725d4fc8d9SRaffaele Recalcati 	}
1735d4fc8d9SRaffaele Recalcati 
1745d4fc8d9SRaffaele Recalcati 	return 0;
1755d4fc8d9SRaffaele Recalcati }
1765d4fc8d9SRaffaele Recalcati 
177fdbb873eSKim Phillips static int mmc_set_blocklen(struct mmc *mmc, int len)
178272cc70bSAndy Fleming {
179272cc70bSAndy Fleming 	struct mmc_cmd cmd;
180272cc70bSAndy Fleming 
181272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SET_BLOCKLEN;
182272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
183272cc70bSAndy Fleming 	cmd.cmdarg = len;
184272cc70bSAndy Fleming 
185272cc70bSAndy Fleming 	return mmc_send_cmd(mmc, &cmd, NULL);
186272cc70bSAndy Fleming }
187272cc70bSAndy Fleming 
188272cc70bSAndy Fleming struct mmc *find_mmc_device(int dev_num)
189272cc70bSAndy Fleming {
190272cc70bSAndy Fleming 	struct mmc *m;
191272cc70bSAndy Fleming 	struct list_head *entry;
192272cc70bSAndy Fleming 
193272cc70bSAndy Fleming 	list_for_each(entry, &mmc_devices) {
194272cc70bSAndy Fleming 		m = list_entry(entry, struct mmc, link);
195272cc70bSAndy Fleming 
196272cc70bSAndy Fleming 		if (m->block_dev.dev == dev_num)
197272cc70bSAndy Fleming 			return m;
198272cc70bSAndy Fleming 	}
199272cc70bSAndy Fleming 
200272cc70bSAndy Fleming 	printf("MMC Device %d not found\n", dev_num);
201272cc70bSAndy Fleming 
202272cc70bSAndy Fleming 	return NULL;
203272cc70bSAndy Fleming }
204272cc70bSAndy Fleming 
205e6f99a56SLei Wen static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt)
206e6f99a56SLei Wen {
207e6f99a56SLei Wen 	struct mmc_cmd cmd;
208e6f99a56SLei Wen 	ulong end;
209e6f99a56SLei Wen 	int err, start_cmd, end_cmd;
210e6f99a56SLei Wen 
211e6f99a56SLei Wen 	if (mmc->high_capacity)
212e6f99a56SLei Wen 		end = start + blkcnt - 1;
213e6f99a56SLei Wen 	else {
214e6f99a56SLei Wen 		end = (start + blkcnt - 1) * mmc->write_bl_len;
215e6f99a56SLei Wen 		start *= mmc->write_bl_len;
216e6f99a56SLei Wen 	}
217e6f99a56SLei Wen 
218e6f99a56SLei Wen 	if (IS_SD(mmc)) {
219e6f99a56SLei Wen 		start_cmd = SD_CMD_ERASE_WR_BLK_START;
220e6f99a56SLei Wen 		end_cmd = SD_CMD_ERASE_WR_BLK_END;
221e6f99a56SLei Wen 	} else {
222e6f99a56SLei Wen 		start_cmd = MMC_CMD_ERASE_GROUP_START;
223e6f99a56SLei Wen 		end_cmd = MMC_CMD_ERASE_GROUP_END;
224e6f99a56SLei Wen 	}
225e6f99a56SLei Wen 
226e6f99a56SLei Wen 	cmd.cmdidx = start_cmd;
227e6f99a56SLei Wen 	cmd.cmdarg = start;
228e6f99a56SLei Wen 	cmd.resp_type = MMC_RSP_R1;
229e6f99a56SLei Wen 
230e6f99a56SLei Wen 	err = mmc_send_cmd(mmc, &cmd, NULL);
231e6f99a56SLei Wen 	if (err)
232e6f99a56SLei Wen 		goto err_out;
233e6f99a56SLei Wen 
234e6f99a56SLei Wen 	cmd.cmdidx = end_cmd;
235e6f99a56SLei Wen 	cmd.cmdarg = end;
236e6f99a56SLei Wen 
237e6f99a56SLei Wen 	err = mmc_send_cmd(mmc, &cmd, NULL);
238e6f99a56SLei Wen 	if (err)
239e6f99a56SLei Wen 		goto err_out;
240e6f99a56SLei Wen 
241e6f99a56SLei Wen 	cmd.cmdidx = MMC_CMD_ERASE;
242e6f99a56SLei Wen 	cmd.cmdarg = SECURE_ERASE;
243e6f99a56SLei Wen 	cmd.resp_type = MMC_RSP_R1b;
244e6f99a56SLei Wen 
245e6f99a56SLei Wen 	err = mmc_send_cmd(mmc, &cmd, NULL);
246e6f99a56SLei Wen 	if (err)
247e6f99a56SLei Wen 		goto err_out;
248e6f99a56SLei Wen 
249e6f99a56SLei Wen 	return 0;
250e6f99a56SLei Wen 
251e6f99a56SLei Wen err_out:
252e6f99a56SLei Wen 	puts("mmc erase failed\n");
253e6f99a56SLei Wen 	return err;
254e6f99a56SLei Wen }
255e6f99a56SLei Wen 
256e6f99a56SLei Wen static unsigned long
257e6f99a56SLei Wen mmc_berase(int dev_num, unsigned long start, lbaint_t blkcnt)
258e6f99a56SLei Wen {
259e6f99a56SLei Wen 	int err = 0;
260e6f99a56SLei Wen 	struct mmc *mmc = find_mmc_device(dev_num);
261e6f99a56SLei Wen 	lbaint_t blk = 0, blk_r = 0;
262d2d8afaeSJerry Huang 	int timeout = 1000;
263e6f99a56SLei Wen 
264e6f99a56SLei Wen 	if (!mmc)
265e6f99a56SLei Wen 		return -1;
266e6f99a56SLei Wen 
267e6f99a56SLei Wen 	if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size))
268e6f99a56SLei Wen 		printf("\n\nCaution! Your devices Erase group is 0x%x\n"
269e6f99a56SLei Wen 			"The erase range would be change to 0x%lx~0x%lx\n\n",
270e6f99a56SLei Wen 		       mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1),
271e6f99a56SLei Wen 		       ((start + blkcnt + mmc->erase_grp_size)
272e6f99a56SLei Wen 		       & ~(mmc->erase_grp_size - 1)) - 1);
273e6f99a56SLei Wen 
274e6f99a56SLei Wen 	while (blk < blkcnt) {
275e6f99a56SLei Wen 		blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ?
276e6f99a56SLei Wen 			mmc->erase_grp_size : (blkcnt - blk);
277e6f99a56SLei Wen 		err = mmc_erase_t(mmc, start + blk, blk_r);
278e6f99a56SLei Wen 		if (err)
279e6f99a56SLei Wen 			break;
280e6f99a56SLei Wen 
281e6f99a56SLei Wen 		blk += blk_r;
282d2d8afaeSJerry Huang 
283d2d8afaeSJerry Huang 		/* Waiting for the ready status */
284d2d8afaeSJerry Huang 		if (mmc_send_status(mmc, timeout))
285d2d8afaeSJerry Huang 			return 0;
286e6f99a56SLei Wen 	}
287e6f99a56SLei Wen 
288e6f99a56SLei Wen 	return blk;
289e6f99a56SLei Wen }
290e6f99a56SLei Wen 
291272cc70bSAndy Fleming static ulong
2920158126eSLei Wen mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src)
293272cc70bSAndy Fleming {
294272cc70bSAndy Fleming 	struct mmc_cmd cmd;
295272cc70bSAndy Fleming 	struct mmc_data data;
2965d4fc8d9SRaffaele Recalcati 	int timeout = 1000;
297272cc70bSAndy Fleming 
298d2bf29e3SLei Wen 	if ((start + blkcnt) > mmc->block_dev.lba) {
299def412b6SSteve Sakoman 		printf("MMC: block number 0x%lx exceeds max(0x%lx)\n",
300d2bf29e3SLei Wen 			start + blkcnt, mmc->block_dev.lba);
301d2bf29e3SLei Wen 		return 0;
302d2bf29e3SLei Wen 	}
303272cc70bSAndy Fleming 
304a586c0aaSRuud Commandeur 	if (blkcnt == 0)
305a586c0aaSRuud Commandeur 		return 0;
306a586c0aaSRuud Commandeur 	else if (blkcnt == 1)
307272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK;
308a586c0aaSRuud Commandeur 	else
309a586c0aaSRuud Commandeur 		cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
310272cc70bSAndy Fleming 
311272cc70bSAndy Fleming 	if (mmc->high_capacity)
312272cc70bSAndy Fleming 		cmd.cmdarg = start;
313272cc70bSAndy Fleming 	else
314def412b6SSteve Sakoman 		cmd.cmdarg = start * mmc->write_bl_len;
315272cc70bSAndy Fleming 
316272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
317272cc70bSAndy Fleming 
318272cc70bSAndy Fleming 	data.src = src;
319272cc70bSAndy Fleming 	data.blocks = blkcnt;
320def412b6SSteve Sakoman 	data.blocksize = mmc->write_bl_len;
321272cc70bSAndy Fleming 	data.flags = MMC_DATA_WRITE;
322272cc70bSAndy Fleming 
323def412b6SSteve Sakoman 	if (mmc_send_cmd(mmc, &cmd, &data)) {
324def412b6SSteve Sakoman 		printf("mmc write failed\n");
325def412b6SSteve Sakoman 		return 0;
326272cc70bSAndy Fleming 	}
327272cc70bSAndy Fleming 
328d52ebf10SThomas Chou 	/* SPI multiblock writes terminate using a special
329d52ebf10SThomas Chou 	 * token, not a STOP_TRANSMISSION request.
330d52ebf10SThomas Chou 	 */
331d52ebf10SThomas Chou 	if (!mmc_host_is_spi(mmc) && blkcnt > 1) {
332272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
333272cc70bSAndy Fleming 		cmd.cmdarg = 0;
334272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R1b;
335def412b6SSteve Sakoman 		if (mmc_send_cmd(mmc, &cmd, NULL)) {
336def412b6SSteve Sakoman 			printf("mmc fail to send stop cmd\n");
337def412b6SSteve Sakoman 			return 0;
338272cc70bSAndy Fleming 		}
33993ad0d18SJan Kloetzke 	}
3405d4fc8d9SRaffaele Recalcati 
3415d4fc8d9SRaffaele Recalcati 	/* Waiting for the ready status */
34293ad0d18SJan Kloetzke 	if (mmc_send_status(mmc, timeout))
34393ad0d18SJan Kloetzke 		return 0;
3440158126eSLei Wen 
3450158126eSLei Wen 	return blkcnt;
3460158126eSLei Wen }
3470158126eSLei Wen 
3480158126eSLei Wen static ulong
3490158126eSLei Wen mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src)
3500158126eSLei Wen {
3510158126eSLei Wen 	lbaint_t cur, blocks_todo = blkcnt;
3520158126eSLei Wen 
353def412b6SSteve Sakoman 	struct mmc *mmc = find_mmc_device(dev_num);
3540158126eSLei Wen 	if (!mmc)
355def412b6SSteve Sakoman 		return 0;
3560158126eSLei Wen 
357def412b6SSteve Sakoman 	if (mmc_set_blocklen(mmc, mmc->write_bl_len))
358def412b6SSteve Sakoman 		return 0;
3590158126eSLei Wen 
3600158126eSLei Wen 	do {
3618feafcc4SJohn Rigby 		cur = (blocks_todo > mmc->b_max) ?  mmc->b_max : blocks_todo;
3620158126eSLei Wen 		if(mmc_write_blocks(mmc, start, cur, src) != cur)
363def412b6SSteve Sakoman 			return 0;
3640158126eSLei Wen 		blocks_todo -= cur;
3650158126eSLei Wen 		start += cur;
3660158126eSLei Wen 		src += cur * mmc->write_bl_len;
3670158126eSLei Wen 	} while (blocks_todo > 0);
368272cc70bSAndy Fleming 
369272cc70bSAndy Fleming 	return blkcnt;
370272cc70bSAndy Fleming }
371272cc70bSAndy Fleming 
372fdbb873eSKim Phillips static int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start,
373fdbb873eSKim Phillips 			   lbaint_t blkcnt)
374272cc70bSAndy Fleming {
375272cc70bSAndy Fleming 	struct mmc_cmd cmd;
376272cc70bSAndy Fleming 	struct mmc_data data;
377272cc70bSAndy Fleming 
3784a1a06bcSAlagu Sankar 	if (blkcnt > 1)
3794a1a06bcSAlagu Sankar 		cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
3804a1a06bcSAlagu Sankar 	else
381272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
382272cc70bSAndy Fleming 
383272cc70bSAndy Fleming 	if (mmc->high_capacity)
3844a1a06bcSAlagu Sankar 		cmd.cmdarg = start;
385272cc70bSAndy Fleming 	else
3864a1a06bcSAlagu Sankar 		cmd.cmdarg = start * mmc->read_bl_len;
387272cc70bSAndy Fleming 
388272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
389272cc70bSAndy Fleming 
390272cc70bSAndy Fleming 	data.dest = dst;
3914a1a06bcSAlagu Sankar 	data.blocks = blkcnt;
392272cc70bSAndy Fleming 	data.blocksize = mmc->read_bl_len;
393272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
394272cc70bSAndy Fleming 
3954a1a06bcSAlagu Sankar 	if (mmc_send_cmd(mmc, &cmd, &data))
3964a1a06bcSAlagu Sankar 		return 0;
3974a1a06bcSAlagu Sankar 
3984a1a06bcSAlagu Sankar 	if (blkcnt > 1) {
3994a1a06bcSAlagu Sankar 		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
4004a1a06bcSAlagu Sankar 		cmd.cmdarg = 0;
4014a1a06bcSAlagu Sankar 		cmd.resp_type = MMC_RSP_R1b;
4024a1a06bcSAlagu Sankar 		if (mmc_send_cmd(mmc, &cmd, NULL)) {
4034a1a06bcSAlagu Sankar 			printf("mmc fail to send stop cmd\n");
4044a1a06bcSAlagu Sankar 			return 0;
4054a1a06bcSAlagu Sankar 		}
406272cc70bSAndy Fleming 	}
407272cc70bSAndy Fleming 
4084a1a06bcSAlagu Sankar 	return blkcnt;
409272cc70bSAndy Fleming }
410272cc70bSAndy Fleming 
411272cc70bSAndy Fleming static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst)
412272cc70bSAndy Fleming {
4134a1a06bcSAlagu Sankar 	lbaint_t cur, blocks_todo = blkcnt;
414272cc70bSAndy Fleming 
4154a1a06bcSAlagu Sankar 	if (blkcnt == 0)
4164a1a06bcSAlagu Sankar 		return 0;
4174a1a06bcSAlagu Sankar 
4184a1a06bcSAlagu Sankar 	struct mmc *mmc = find_mmc_device(dev_num);
419272cc70bSAndy Fleming 	if (!mmc)
420272cc70bSAndy Fleming 		return 0;
421272cc70bSAndy Fleming 
422d2bf29e3SLei Wen 	if ((start + blkcnt) > mmc->block_dev.lba) {
4234a1a06bcSAlagu Sankar 		printf("MMC: block number 0x%lx exceeds max(0x%lx)\n",
424d2bf29e3SLei Wen 			start + blkcnt, mmc->block_dev.lba);
425d2bf29e3SLei Wen 		return 0;
426d2bf29e3SLei Wen 	}
427272cc70bSAndy Fleming 
4284a1a06bcSAlagu Sankar 	if (mmc_set_blocklen(mmc, mmc->read_bl_len))
429272cc70bSAndy Fleming 		return 0;
430272cc70bSAndy Fleming 
4314a1a06bcSAlagu Sankar 	do {
4328feafcc4SJohn Rigby 		cur = (blocks_todo > mmc->b_max) ?  mmc->b_max : blocks_todo;
4334a1a06bcSAlagu Sankar 		if(mmc_read_blocks(mmc, dst, start, cur) != cur)
4344a1a06bcSAlagu Sankar 			return 0;
4354a1a06bcSAlagu Sankar 		blocks_todo -= cur;
4364a1a06bcSAlagu Sankar 		start += cur;
4374a1a06bcSAlagu Sankar 		dst += cur * mmc->read_bl_len;
4384a1a06bcSAlagu Sankar 	} while (blocks_todo > 0);
439272cc70bSAndy Fleming 
440272cc70bSAndy Fleming 	return blkcnt;
441272cc70bSAndy Fleming }
442272cc70bSAndy Fleming 
443fdbb873eSKim Phillips static int mmc_go_idle(struct mmc *mmc)
444272cc70bSAndy Fleming {
445272cc70bSAndy Fleming 	struct mmc_cmd cmd;
446272cc70bSAndy Fleming 	int err;
447272cc70bSAndy Fleming 
448272cc70bSAndy Fleming 	udelay(1000);
449272cc70bSAndy Fleming 
450272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_GO_IDLE_STATE;
451272cc70bSAndy Fleming 	cmd.cmdarg = 0;
452272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_NONE;
453272cc70bSAndy Fleming 
454272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
455272cc70bSAndy Fleming 
456272cc70bSAndy Fleming 	if (err)
457272cc70bSAndy Fleming 		return err;
458272cc70bSAndy Fleming 
459272cc70bSAndy Fleming 	udelay(2000);
460272cc70bSAndy Fleming 
461272cc70bSAndy Fleming 	return 0;
462272cc70bSAndy Fleming }
463272cc70bSAndy Fleming 
464fdbb873eSKim Phillips static int sd_send_op_cond(struct mmc *mmc)
465272cc70bSAndy Fleming {
466272cc70bSAndy Fleming 	int timeout = 1000;
467272cc70bSAndy Fleming 	int err;
468272cc70bSAndy Fleming 	struct mmc_cmd cmd;
469272cc70bSAndy Fleming 
470272cc70bSAndy Fleming 	do {
471272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_APP_CMD;
472272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R1;
473272cc70bSAndy Fleming 		cmd.cmdarg = 0;
474272cc70bSAndy Fleming 
475272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
476272cc70bSAndy Fleming 
477272cc70bSAndy Fleming 		if (err)
478272cc70bSAndy Fleming 			return err;
479272cc70bSAndy Fleming 
480272cc70bSAndy Fleming 		cmd.cmdidx = SD_CMD_APP_SEND_OP_COND;
481272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R3;
482250de12bSStefano Babic 
483250de12bSStefano Babic 		/*
484250de12bSStefano Babic 		 * Most cards do not answer if some reserved bits
485250de12bSStefano Babic 		 * in the ocr are set. However, Some controller
486250de12bSStefano Babic 		 * can set bit 7 (reserved for low voltages), but
487250de12bSStefano Babic 		 * how to manage low voltages SD card is not yet
488250de12bSStefano Babic 		 * specified.
489250de12bSStefano Babic 		 */
490d52ebf10SThomas Chou 		cmd.cmdarg = mmc_host_is_spi(mmc) ? 0 :
491d52ebf10SThomas Chou 			(mmc->voltages & 0xff8000);
492272cc70bSAndy Fleming 
493272cc70bSAndy Fleming 		if (mmc->version == SD_VERSION_2)
494272cc70bSAndy Fleming 			cmd.cmdarg |= OCR_HCS;
495272cc70bSAndy Fleming 
496272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
497272cc70bSAndy Fleming 
498272cc70bSAndy Fleming 		if (err)
499272cc70bSAndy Fleming 			return err;
500272cc70bSAndy Fleming 
501272cc70bSAndy Fleming 		udelay(1000);
502272cc70bSAndy Fleming 	} while ((!(cmd.response[0] & OCR_BUSY)) && timeout--);
503272cc70bSAndy Fleming 
504272cc70bSAndy Fleming 	if (timeout <= 0)
505272cc70bSAndy Fleming 		return UNUSABLE_ERR;
506272cc70bSAndy Fleming 
507272cc70bSAndy Fleming 	if (mmc->version != SD_VERSION_2)
508272cc70bSAndy Fleming 		mmc->version = SD_VERSION_1_0;
509272cc70bSAndy Fleming 
510d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc)) { /* read OCR for spi */
511d52ebf10SThomas Chou 		cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
512d52ebf10SThomas Chou 		cmd.resp_type = MMC_RSP_R3;
513d52ebf10SThomas Chou 		cmd.cmdarg = 0;
514d52ebf10SThomas Chou 
515d52ebf10SThomas Chou 		err = mmc_send_cmd(mmc, &cmd, NULL);
516d52ebf10SThomas Chou 
517d52ebf10SThomas Chou 		if (err)
518d52ebf10SThomas Chou 			return err;
519d52ebf10SThomas Chou 	}
520d52ebf10SThomas Chou 
521998be3ddSRabin Vincent 	mmc->ocr = cmd.response[0];
522272cc70bSAndy Fleming 
523272cc70bSAndy Fleming 	mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
524272cc70bSAndy Fleming 	mmc->rca = 0;
525272cc70bSAndy Fleming 
526272cc70bSAndy Fleming 	return 0;
527272cc70bSAndy Fleming }
528272cc70bSAndy Fleming 
529e9550449SChe-Liang Chiou /* We pass in the cmd since otherwise the init seems to fail */
530e9550449SChe-Liang Chiou static int mmc_send_op_cond_iter(struct mmc *mmc, struct mmc_cmd *cmd,
531e9550449SChe-Liang Chiou 		int use_arg)
532272cc70bSAndy Fleming {
533272cc70bSAndy Fleming 	int err;
534272cc70bSAndy Fleming 
535e9550449SChe-Liang Chiou 	cmd->cmdidx = MMC_CMD_SEND_OP_COND;
536e9550449SChe-Liang Chiou 	cmd->resp_type = MMC_RSP_R3;
537e9550449SChe-Liang Chiou 	cmd->cmdarg = 0;
538e9550449SChe-Liang Chiou 	if (use_arg && !mmc_host_is_spi(mmc)) {
539e9550449SChe-Liang Chiou 		cmd->cmdarg =
540e9550449SChe-Liang Chiou 			(mmc->voltages &
541e9550449SChe-Liang Chiou 			(mmc->op_cond_response & OCR_VOLTAGE_MASK)) |
542e9550449SChe-Liang Chiou 			(mmc->op_cond_response & OCR_ACCESS_MODE);
543e9550449SChe-Liang Chiou 
544e9550449SChe-Liang Chiou 		if (mmc->host_caps & MMC_MODE_HC)
545e9550449SChe-Liang Chiou 			cmd->cmdarg |= OCR_HCS;
546e9550449SChe-Liang Chiou 	}
547e9550449SChe-Liang Chiou 	err = mmc_send_cmd(mmc, cmd, NULL);
548e9550449SChe-Liang Chiou 	if (err)
549e9550449SChe-Liang Chiou 		return err;
550e9550449SChe-Liang Chiou 	mmc->op_cond_response = cmd->response[0];
551e9550449SChe-Liang Chiou 	return 0;
552e9550449SChe-Liang Chiou }
553e9550449SChe-Liang Chiou 
554e9550449SChe-Liang Chiou int mmc_send_op_cond(struct mmc *mmc)
555e9550449SChe-Liang Chiou {
556e9550449SChe-Liang Chiou 	struct mmc_cmd cmd;
557e9550449SChe-Liang Chiou 	int err, i;
558e9550449SChe-Liang Chiou 
559272cc70bSAndy Fleming 	/* Some cards seem to need this */
560272cc70bSAndy Fleming 	mmc_go_idle(mmc);
561272cc70bSAndy Fleming 
56231cacbabSRaffaele Recalcati  	/* Asking to the card its capabilities */
563e9550449SChe-Liang Chiou 	mmc->op_cond_pending = 1;
564e9550449SChe-Liang Chiou 	for (i = 0; i < 2; i++) {
565e9550449SChe-Liang Chiou 		err = mmc_send_op_cond_iter(mmc, &cmd, i != 0);
56631cacbabSRaffaele Recalcati 		if (err)
56731cacbabSRaffaele Recalcati 			return err;
56831cacbabSRaffaele Recalcati 
569e9550449SChe-Liang Chiou 		/* exit if not busy (flag seems to be inverted) */
570e9550449SChe-Liang Chiou 		if (mmc->op_cond_response & OCR_BUSY)
571e9550449SChe-Liang Chiou 			return 0;
572e9550449SChe-Liang Chiou 	}
573e9550449SChe-Liang Chiou 	return IN_PROGRESS;
574e9550449SChe-Liang Chiou }
57531cacbabSRaffaele Recalcati 
576e9550449SChe-Liang Chiou int mmc_complete_op_cond(struct mmc *mmc)
577e9550449SChe-Liang Chiou {
578e9550449SChe-Liang Chiou 	struct mmc_cmd cmd;
579e9550449SChe-Liang Chiou 	int timeout = 1000;
580e9550449SChe-Liang Chiou 	uint start;
581e9550449SChe-Liang Chiou 	int err;
582e9550449SChe-Liang Chiou 
583e9550449SChe-Liang Chiou 	mmc->op_cond_pending = 0;
584e9550449SChe-Liang Chiou 	start = get_timer(0);
585272cc70bSAndy Fleming 	do {
586e9550449SChe-Liang Chiou 		err = mmc_send_op_cond_iter(mmc, &cmd, 1);
587272cc70bSAndy Fleming 		if (err)
588272cc70bSAndy Fleming 			return err;
589e9550449SChe-Liang Chiou 		if (get_timer(start) > timeout)
590272cc70bSAndy Fleming 			return UNUSABLE_ERR;
591e9550449SChe-Liang Chiou 		udelay(100);
592e9550449SChe-Liang Chiou 	} while (!(mmc->op_cond_response & OCR_BUSY));
593272cc70bSAndy Fleming 
594d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc)) { /* read OCR for spi */
595d52ebf10SThomas Chou 		cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
596d52ebf10SThomas Chou 		cmd.resp_type = MMC_RSP_R3;
597d52ebf10SThomas Chou 		cmd.cmdarg = 0;
598d52ebf10SThomas Chou 
599d52ebf10SThomas Chou 		err = mmc_send_cmd(mmc, &cmd, NULL);
600d52ebf10SThomas Chou 
601d52ebf10SThomas Chou 		if (err)
602d52ebf10SThomas Chou 			return err;
603d52ebf10SThomas Chou 	}
604d52ebf10SThomas Chou 
605272cc70bSAndy Fleming 	mmc->version = MMC_VERSION_UNKNOWN;
606998be3ddSRabin Vincent 	mmc->ocr = cmd.response[0];
607272cc70bSAndy Fleming 
608272cc70bSAndy Fleming 	mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
609272cc70bSAndy Fleming 	mmc->rca = 0;
610272cc70bSAndy Fleming 
611272cc70bSAndy Fleming 	return 0;
612272cc70bSAndy Fleming }
613272cc70bSAndy Fleming 
614272cc70bSAndy Fleming 
615fdbb873eSKim Phillips static int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd)
616272cc70bSAndy Fleming {
617272cc70bSAndy Fleming 	struct mmc_cmd cmd;
618272cc70bSAndy Fleming 	struct mmc_data data;
619272cc70bSAndy Fleming 	int err;
620272cc70bSAndy Fleming 
621272cc70bSAndy Fleming 	/* Get the Card Status Register */
622272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SEND_EXT_CSD;
623272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
624272cc70bSAndy Fleming 	cmd.cmdarg = 0;
625272cc70bSAndy Fleming 
626cdfd1ac6SYoshihiro Shimoda 	data.dest = (char *)ext_csd;
627272cc70bSAndy Fleming 	data.blocks = 1;
6288bfa195eSSimon Glass 	data.blocksize = MMC_MAX_BLOCK_LEN;
629272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
630272cc70bSAndy Fleming 
631272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, &data);
632272cc70bSAndy Fleming 
633272cc70bSAndy Fleming 	return err;
634272cc70bSAndy Fleming }
635272cc70bSAndy Fleming 
636272cc70bSAndy Fleming 
637fdbb873eSKim Phillips static int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)
638272cc70bSAndy Fleming {
639272cc70bSAndy Fleming 	struct mmc_cmd cmd;
6405d4fc8d9SRaffaele Recalcati 	int timeout = 1000;
6415d4fc8d9SRaffaele Recalcati 	int ret;
642272cc70bSAndy Fleming 
643272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SWITCH;
644272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1b;
645272cc70bSAndy Fleming 	cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
646272cc70bSAndy Fleming 				 (index << 16) |
647272cc70bSAndy Fleming 				 (value << 8);
648272cc70bSAndy Fleming 
6495d4fc8d9SRaffaele Recalcati 	ret = mmc_send_cmd(mmc, &cmd, NULL);
6505d4fc8d9SRaffaele Recalcati 
6515d4fc8d9SRaffaele Recalcati 	/* Waiting for the ready status */
65293ad0d18SJan Kloetzke 	if (!ret)
65393ad0d18SJan Kloetzke 		ret = mmc_send_status(mmc, timeout);
6545d4fc8d9SRaffaele Recalcati 
6555d4fc8d9SRaffaele Recalcati 	return ret;
6565d4fc8d9SRaffaele Recalcati 
657272cc70bSAndy Fleming }
658272cc70bSAndy Fleming 
659fdbb873eSKim Phillips static int mmc_change_freq(struct mmc *mmc)
660272cc70bSAndy Fleming {
6618bfa195eSSimon Glass 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
662272cc70bSAndy Fleming 	char cardtype;
663272cc70bSAndy Fleming 	int err;
664272cc70bSAndy Fleming 
665272cc70bSAndy Fleming 	mmc->card_caps = 0;
666272cc70bSAndy Fleming 
667d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc))
668d52ebf10SThomas Chou 		return 0;
669d52ebf10SThomas Chou 
670272cc70bSAndy Fleming 	/* Only version 4 supports high-speed */
671272cc70bSAndy Fleming 	if (mmc->version < MMC_VERSION_4)
672272cc70bSAndy Fleming 		return 0;
673272cc70bSAndy Fleming 
674272cc70bSAndy Fleming 	err = mmc_send_ext_csd(mmc, ext_csd);
675272cc70bSAndy Fleming 
676272cc70bSAndy Fleming 	if (err)
677272cc70bSAndy Fleming 		return err;
678272cc70bSAndy Fleming 
6790560db18SLei Wen 	cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf;
680272cc70bSAndy Fleming 
681272cc70bSAndy Fleming 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1);
682272cc70bSAndy Fleming 
683272cc70bSAndy Fleming 	if (err)
684272cc70bSAndy Fleming 		return err;
685272cc70bSAndy Fleming 
686272cc70bSAndy Fleming 	/* Now check to see that it worked */
687272cc70bSAndy Fleming 	err = mmc_send_ext_csd(mmc, ext_csd);
688272cc70bSAndy Fleming 
689272cc70bSAndy Fleming 	if (err)
690272cc70bSAndy Fleming 		return err;
691272cc70bSAndy Fleming 
692272cc70bSAndy Fleming 	/* No high-speed support */
6930560db18SLei Wen 	if (!ext_csd[EXT_CSD_HS_TIMING])
694272cc70bSAndy Fleming 		return 0;
695272cc70bSAndy Fleming 
696272cc70bSAndy Fleming 	/* High Speed is set, there are two types: 52MHz and 26MHz */
697272cc70bSAndy Fleming 	if (cardtype & MMC_HS_52MHZ)
698272cc70bSAndy Fleming 		mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
699272cc70bSAndy Fleming 	else
700272cc70bSAndy Fleming 		mmc->card_caps |= MMC_MODE_HS;
701272cc70bSAndy Fleming 
702272cc70bSAndy Fleming 	return 0;
703272cc70bSAndy Fleming }
704272cc70bSAndy Fleming 
705*f866a46dSStephen Warren static int mmc_set_capacity(struct mmc *mmc, int part_num)
706*f866a46dSStephen Warren {
707*f866a46dSStephen Warren 	switch (part_num) {
708*f866a46dSStephen Warren 	case 0:
709*f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_user;
710*f866a46dSStephen Warren 		break;
711*f866a46dSStephen Warren 	case 1:
712*f866a46dSStephen Warren 	case 2:
713*f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_boot;
714*f866a46dSStephen Warren 		break;
715*f866a46dSStephen Warren 	case 3:
716*f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_rpmb;
717*f866a46dSStephen Warren 		break;
718*f866a46dSStephen Warren 	case 4:
719*f866a46dSStephen Warren 	case 5:
720*f866a46dSStephen Warren 	case 6:
721*f866a46dSStephen Warren 	case 7:
722*f866a46dSStephen Warren 		mmc->capacity = mmc->capacity_gp[part_num - 4];
723*f866a46dSStephen Warren 		break;
724*f866a46dSStephen Warren 	default:
725*f866a46dSStephen Warren 		return -1;
726*f866a46dSStephen Warren 	}
727*f866a46dSStephen Warren 
728*f866a46dSStephen Warren 	mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len);
729*f866a46dSStephen Warren 
730*f866a46dSStephen Warren 	return 0;
731*f866a46dSStephen Warren }
732*f866a46dSStephen Warren 
733bc897b1dSLei Wen int mmc_switch_part(int dev_num, unsigned int part_num)
734bc897b1dSLei Wen {
735bc897b1dSLei Wen 	struct mmc *mmc = find_mmc_device(dev_num);
736*f866a46dSStephen Warren 	int ret;
737bc897b1dSLei Wen 
738bc897b1dSLei Wen 	if (!mmc)
739bc897b1dSLei Wen 		return -1;
740bc897b1dSLei Wen 
741*f866a46dSStephen Warren 	ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
742bc897b1dSLei Wen 			 (mmc->part_config & ~PART_ACCESS_MASK)
743bc897b1dSLei Wen 			 | (part_num & PART_ACCESS_MASK));
744*f866a46dSStephen Warren 	if (ret)
745*f866a46dSStephen Warren 		return ret;
746*f866a46dSStephen Warren 
747*f866a46dSStephen Warren 	return mmc_set_capacity(mmc, part_num);
748bc897b1dSLei Wen }
749bc897b1dSLei Wen 
75048972d90SThierry Reding int mmc_getcd(struct mmc *mmc)
75148972d90SThierry Reding {
75248972d90SThierry Reding 	int cd;
75348972d90SThierry Reding 
75448972d90SThierry Reding 	cd = board_mmc_getcd(mmc);
75548972d90SThierry Reding 
756d4e1da4eSPeter Korsgaard 	if (cd < 0) {
757d4e1da4eSPeter Korsgaard 		if (mmc->getcd)
75848972d90SThierry Reding 			cd = mmc->getcd(mmc);
759d4e1da4eSPeter Korsgaard 		else
760d4e1da4eSPeter Korsgaard 			cd = 1;
761d4e1da4eSPeter Korsgaard 	}
76248972d90SThierry Reding 
76348972d90SThierry Reding 	return cd;
76448972d90SThierry Reding }
76548972d90SThierry Reding 
766fdbb873eSKim Phillips static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)
767272cc70bSAndy Fleming {
768272cc70bSAndy Fleming 	struct mmc_cmd cmd;
769272cc70bSAndy Fleming 	struct mmc_data data;
770272cc70bSAndy Fleming 
771272cc70bSAndy Fleming 	/* Switch the frequency */
772272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_SWITCH_FUNC;
773272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
774272cc70bSAndy Fleming 	cmd.cmdarg = (mode << 31) | 0xffffff;
775272cc70bSAndy Fleming 	cmd.cmdarg &= ~(0xf << (group * 4));
776272cc70bSAndy Fleming 	cmd.cmdarg |= value << (group * 4);
777272cc70bSAndy Fleming 
778272cc70bSAndy Fleming 	data.dest = (char *)resp;
779272cc70bSAndy Fleming 	data.blocksize = 64;
780272cc70bSAndy Fleming 	data.blocks = 1;
781272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
782272cc70bSAndy Fleming 
783272cc70bSAndy Fleming 	return mmc_send_cmd(mmc, &cmd, &data);
784272cc70bSAndy Fleming }
785272cc70bSAndy Fleming 
786272cc70bSAndy Fleming 
787fdbb873eSKim Phillips static int sd_change_freq(struct mmc *mmc)
788272cc70bSAndy Fleming {
789272cc70bSAndy Fleming 	int err;
790272cc70bSAndy Fleming 	struct mmc_cmd cmd;
791f781dd38SAnton staaf 	ALLOC_CACHE_ALIGN_BUFFER(uint, scr, 2);
792f781dd38SAnton staaf 	ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16);
793272cc70bSAndy Fleming 	struct mmc_data data;
794272cc70bSAndy Fleming 	int timeout;
795272cc70bSAndy Fleming 
796272cc70bSAndy Fleming 	mmc->card_caps = 0;
797272cc70bSAndy Fleming 
798d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc))
799d52ebf10SThomas Chou 		return 0;
800d52ebf10SThomas Chou 
801272cc70bSAndy Fleming 	/* Read the SCR to find out if this card supports higher speeds */
802272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_APP_CMD;
803272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
804272cc70bSAndy Fleming 	cmd.cmdarg = mmc->rca << 16;
805272cc70bSAndy Fleming 
806272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
807272cc70bSAndy Fleming 
808272cc70bSAndy Fleming 	if (err)
809272cc70bSAndy Fleming 		return err;
810272cc70bSAndy Fleming 
811272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_APP_SEND_SCR;
812272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R1;
813272cc70bSAndy Fleming 	cmd.cmdarg = 0;
814272cc70bSAndy Fleming 
815272cc70bSAndy Fleming 	timeout = 3;
816272cc70bSAndy Fleming 
817272cc70bSAndy Fleming retry_scr:
818f781dd38SAnton staaf 	data.dest = (char *)scr;
819272cc70bSAndy Fleming 	data.blocksize = 8;
820272cc70bSAndy Fleming 	data.blocks = 1;
821272cc70bSAndy Fleming 	data.flags = MMC_DATA_READ;
822272cc70bSAndy Fleming 
823272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, &data);
824272cc70bSAndy Fleming 
825272cc70bSAndy Fleming 	if (err) {
826272cc70bSAndy Fleming 		if (timeout--)
827272cc70bSAndy Fleming 			goto retry_scr;
828272cc70bSAndy Fleming 
829272cc70bSAndy Fleming 		return err;
830272cc70bSAndy Fleming 	}
831272cc70bSAndy Fleming 
8324e3d89baSYauhen Kharuzhy 	mmc->scr[0] = __be32_to_cpu(scr[0]);
8334e3d89baSYauhen Kharuzhy 	mmc->scr[1] = __be32_to_cpu(scr[1]);
834272cc70bSAndy Fleming 
835272cc70bSAndy Fleming 	switch ((mmc->scr[0] >> 24) & 0xf) {
836272cc70bSAndy Fleming 		case 0:
837272cc70bSAndy Fleming 			mmc->version = SD_VERSION_1_0;
838272cc70bSAndy Fleming 			break;
839272cc70bSAndy Fleming 		case 1:
840272cc70bSAndy Fleming 			mmc->version = SD_VERSION_1_10;
841272cc70bSAndy Fleming 			break;
842272cc70bSAndy Fleming 		case 2:
843272cc70bSAndy Fleming 			mmc->version = SD_VERSION_2;
8441741c64dSJaehoon Chung 			if ((mmc->scr[0] >> 15) & 0x1)
8451741c64dSJaehoon Chung 				mmc->version = SD_VERSION_3;
846272cc70bSAndy Fleming 			break;
847272cc70bSAndy Fleming 		default:
848272cc70bSAndy Fleming 			mmc->version = SD_VERSION_1_0;
849272cc70bSAndy Fleming 			break;
850272cc70bSAndy Fleming 	}
851272cc70bSAndy Fleming 
852b44c7083SAlagu Sankar 	if (mmc->scr[0] & SD_DATA_4BIT)
853b44c7083SAlagu Sankar 		mmc->card_caps |= MMC_MODE_4BIT;
854b44c7083SAlagu Sankar 
855272cc70bSAndy Fleming 	/* Version 1.0 doesn't support switching */
856272cc70bSAndy Fleming 	if (mmc->version == SD_VERSION_1_0)
857272cc70bSAndy Fleming 		return 0;
858272cc70bSAndy Fleming 
859272cc70bSAndy Fleming 	timeout = 4;
860272cc70bSAndy Fleming 	while (timeout--) {
861272cc70bSAndy Fleming 		err = sd_switch(mmc, SD_SWITCH_CHECK, 0, 1,
862f781dd38SAnton staaf 				(u8 *)switch_status);
863272cc70bSAndy Fleming 
864272cc70bSAndy Fleming 		if (err)
865272cc70bSAndy Fleming 			return err;
866272cc70bSAndy Fleming 
867272cc70bSAndy Fleming 		/* The high-speed function is busy.  Try again */
8684e3d89baSYauhen Kharuzhy 		if (!(__be32_to_cpu(switch_status[7]) & SD_HIGHSPEED_BUSY))
869272cc70bSAndy Fleming 			break;
870272cc70bSAndy Fleming 	}
871272cc70bSAndy Fleming 
872272cc70bSAndy Fleming 	/* If high-speed isn't supported, we return */
8734e3d89baSYauhen Kharuzhy 	if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED))
874272cc70bSAndy Fleming 		return 0;
875272cc70bSAndy Fleming 
8762c3fbf4cSMacpaul Lin 	/*
8772c3fbf4cSMacpaul Lin 	 * If the host doesn't support SD_HIGHSPEED, do not switch card to
8782c3fbf4cSMacpaul Lin 	 * HIGHSPEED mode even if the card support SD_HIGHSPPED.
8792c3fbf4cSMacpaul Lin 	 * This can avoid furthur problem when the card runs in different
8802c3fbf4cSMacpaul Lin 	 * mode between the host.
8812c3fbf4cSMacpaul Lin 	 */
8822c3fbf4cSMacpaul Lin 	if (!((mmc->host_caps & MMC_MODE_HS_52MHz) &&
8832c3fbf4cSMacpaul Lin 		(mmc->host_caps & MMC_MODE_HS)))
8842c3fbf4cSMacpaul Lin 		return 0;
8852c3fbf4cSMacpaul Lin 
886f781dd38SAnton staaf 	err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status);
887272cc70bSAndy Fleming 
888272cc70bSAndy Fleming 	if (err)
889272cc70bSAndy Fleming 		return err;
890272cc70bSAndy Fleming 
8914e3d89baSYauhen Kharuzhy 	if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) == 0x01000000)
892272cc70bSAndy Fleming 		mmc->card_caps |= MMC_MODE_HS;
893272cc70bSAndy Fleming 
894272cc70bSAndy Fleming 	return 0;
895272cc70bSAndy Fleming }
896272cc70bSAndy Fleming 
897272cc70bSAndy Fleming /* frequency bases */
898272cc70bSAndy Fleming /* divided by 10 to be nice to platforms without floating point */
8995f837c2cSMike Frysinger static const int fbase[] = {
900272cc70bSAndy Fleming 	10000,
901272cc70bSAndy Fleming 	100000,
902272cc70bSAndy Fleming 	1000000,
903272cc70bSAndy Fleming 	10000000,
904272cc70bSAndy Fleming };
905272cc70bSAndy Fleming 
906272cc70bSAndy Fleming /* Multiplier values for TRAN_SPEED.  Multiplied by 10 to be nice
907272cc70bSAndy Fleming  * to platforms without floating point.
908272cc70bSAndy Fleming  */
9095f837c2cSMike Frysinger static const int multipliers[] = {
910272cc70bSAndy Fleming 	0,	/* reserved */
911272cc70bSAndy Fleming 	10,
912272cc70bSAndy Fleming 	12,
913272cc70bSAndy Fleming 	13,
914272cc70bSAndy Fleming 	15,
915272cc70bSAndy Fleming 	20,
916272cc70bSAndy Fleming 	25,
917272cc70bSAndy Fleming 	30,
918272cc70bSAndy Fleming 	35,
919272cc70bSAndy Fleming 	40,
920272cc70bSAndy Fleming 	45,
921272cc70bSAndy Fleming 	50,
922272cc70bSAndy Fleming 	55,
923272cc70bSAndy Fleming 	60,
924272cc70bSAndy Fleming 	70,
925272cc70bSAndy Fleming 	80,
926272cc70bSAndy Fleming };
927272cc70bSAndy Fleming 
928fdbb873eSKim Phillips static void mmc_set_ios(struct mmc *mmc)
929272cc70bSAndy Fleming {
930272cc70bSAndy Fleming 	mmc->set_ios(mmc);
931272cc70bSAndy Fleming }
932272cc70bSAndy Fleming 
933272cc70bSAndy Fleming void mmc_set_clock(struct mmc *mmc, uint clock)
934272cc70bSAndy Fleming {
935272cc70bSAndy Fleming 	if (clock > mmc->f_max)
936272cc70bSAndy Fleming 		clock = mmc->f_max;
937272cc70bSAndy Fleming 
938272cc70bSAndy Fleming 	if (clock < mmc->f_min)
939272cc70bSAndy Fleming 		clock = mmc->f_min;
940272cc70bSAndy Fleming 
941272cc70bSAndy Fleming 	mmc->clock = clock;
942272cc70bSAndy Fleming 
943272cc70bSAndy Fleming 	mmc_set_ios(mmc);
944272cc70bSAndy Fleming }
945272cc70bSAndy Fleming 
946fdbb873eSKim Phillips static void mmc_set_bus_width(struct mmc *mmc, uint width)
947272cc70bSAndy Fleming {
948272cc70bSAndy Fleming 	mmc->bus_width = width;
949272cc70bSAndy Fleming 
950272cc70bSAndy Fleming 	mmc_set_ios(mmc);
951272cc70bSAndy Fleming }
952272cc70bSAndy Fleming 
953fdbb873eSKim Phillips static int mmc_startup(struct mmc *mmc)
954272cc70bSAndy Fleming {
955*f866a46dSStephen Warren 	int err, i;
956272cc70bSAndy Fleming 	uint mult, freq;
957639b7827SYoshihiro Shimoda 	u64 cmult, csize, capacity;
958272cc70bSAndy Fleming 	struct mmc_cmd cmd;
9598bfa195eSSimon Glass 	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
9608bfa195eSSimon Glass 	ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN);
9615d4fc8d9SRaffaele Recalcati 	int timeout = 1000;
962272cc70bSAndy Fleming 
963d52ebf10SThomas Chou #ifdef CONFIG_MMC_SPI_CRC_ON
964d52ebf10SThomas Chou 	if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */
965d52ebf10SThomas Chou 		cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF;
966d52ebf10SThomas Chou 		cmd.resp_type = MMC_RSP_R1;
967d52ebf10SThomas Chou 		cmd.cmdarg = 1;
968d52ebf10SThomas Chou 		err = mmc_send_cmd(mmc, &cmd, NULL);
969d52ebf10SThomas Chou 
970d52ebf10SThomas Chou 		if (err)
971d52ebf10SThomas Chou 			return err;
972d52ebf10SThomas Chou 	}
973d52ebf10SThomas Chou #endif
974d52ebf10SThomas Chou 
975272cc70bSAndy Fleming 	/* Put the Card in Identify Mode */
976d52ebf10SThomas Chou 	cmd.cmdidx = mmc_host_is_spi(mmc) ? MMC_CMD_SEND_CID :
977d52ebf10SThomas Chou 		MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */
978272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R2;
979272cc70bSAndy Fleming 	cmd.cmdarg = 0;
980272cc70bSAndy Fleming 
981272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
982272cc70bSAndy Fleming 
983272cc70bSAndy Fleming 	if (err)
984272cc70bSAndy Fleming 		return err;
985272cc70bSAndy Fleming 
986272cc70bSAndy Fleming 	memcpy(mmc->cid, cmd.response, 16);
987272cc70bSAndy Fleming 
988272cc70bSAndy Fleming 	/*
989272cc70bSAndy Fleming 	 * For MMC cards, set the Relative Address.
990272cc70bSAndy Fleming 	 * For SD cards, get the Relatvie Address.
991272cc70bSAndy Fleming 	 * This also puts the cards into Standby State
992272cc70bSAndy Fleming 	 */
993d52ebf10SThomas Chou 	if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
994272cc70bSAndy Fleming 		cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;
995272cc70bSAndy Fleming 		cmd.cmdarg = mmc->rca << 16;
996272cc70bSAndy Fleming 		cmd.resp_type = MMC_RSP_R6;
997272cc70bSAndy Fleming 
998272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
999272cc70bSAndy Fleming 
1000272cc70bSAndy Fleming 		if (err)
1001272cc70bSAndy Fleming 			return err;
1002272cc70bSAndy Fleming 
1003272cc70bSAndy Fleming 		if (IS_SD(mmc))
1004998be3ddSRabin Vincent 			mmc->rca = (cmd.response[0] >> 16) & 0xffff;
1005d52ebf10SThomas Chou 	}
1006272cc70bSAndy Fleming 
1007272cc70bSAndy Fleming 	/* Get the Card-Specific Data */
1008272cc70bSAndy Fleming 	cmd.cmdidx = MMC_CMD_SEND_CSD;
1009272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R2;
1010272cc70bSAndy Fleming 	cmd.cmdarg = mmc->rca << 16;
1011272cc70bSAndy Fleming 
1012272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
1013272cc70bSAndy Fleming 
10145d4fc8d9SRaffaele Recalcati 	/* Waiting for the ready status */
10155d4fc8d9SRaffaele Recalcati 	mmc_send_status(mmc, timeout);
10165d4fc8d9SRaffaele Recalcati 
1017272cc70bSAndy Fleming 	if (err)
1018272cc70bSAndy Fleming 		return err;
1019272cc70bSAndy Fleming 
1020998be3ddSRabin Vincent 	mmc->csd[0] = cmd.response[0];
1021998be3ddSRabin Vincent 	mmc->csd[1] = cmd.response[1];
1022998be3ddSRabin Vincent 	mmc->csd[2] = cmd.response[2];
1023998be3ddSRabin Vincent 	mmc->csd[3] = cmd.response[3];
1024272cc70bSAndy Fleming 
1025272cc70bSAndy Fleming 	if (mmc->version == MMC_VERSION_UNKNOWN) {
10260b453ffeSRabin Vincent 		int version = (cmd.response[0] >> 26) & 0xf;
1027272cc70bSAndy Fleming 
1028272cc70bSAndy Fleming 		switch (version) {
1029272cc70bSAndy Fleming 			case 0:
1030272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_1_2;
1031272cc70bSAndy Fleming 				break;
1032272cc70bSAndy Fleming 			case 1:
1033272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_1_4;
1034272cc70bSAndy Fleming 				break;
1035272cc70bSAndy Fleming 			case 2:
1036272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_2_2;
1037272cc70bSAndy Fleming 				break;
1038272cc70bSAndy Fleming 			case 3:
1039272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_3;
1040272cc70bSAndy Fleming 				break;
1041272cc70bSAndy Fleming 			case 4:
1042272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_4;
1043272cc70bSAndy Fleming 				break;
1044272cc70bSAndy Fleming 			default:
1045272cc70bSAndy Fleming 				mmc->version = MMC_VERSION_1_2;
1046272cc70bSAndy Fleming 				break;
1047272cc70bSAndy Fleming 		}
1048272cc70bSAndy Fleming 	}
1049272cc70bSAndy Fleming 
1050272cc70bSAndy Fleming 	/* divide frequency by 10, since the mults are 10x bigger */
10510b453ffeSRabin Vincent 	freq = fbase[(cmd.response[0] & 0x7)];
10520b453ffeSRabin Vincent 	mult = multipliers[((cmd.response[0] >> 3) & 0xf)];
1053272cc70bSAndy Fleming 
1054272cc70bSAndy Fleming 	mmc->tran_speed = freq * mult;
1055272cc70bSAndy Fleming 
1056998be3ddSRabin Vincent 	mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf);
1057272cc70bSAndy Fleming 
1058272cc70bSAndy Fleming 	if (IS_SD(mmc))
1059272cc70bSAndy Fleming 		mmc->write_bl_len = mmc->read_bl_len;
1060272cc70bSAndy Fleming 	else
1061998be3ddSRabin Vincent 		mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf);
1062272cc70bSAndy Fleming 
1063272cc70bSAndy Fleming 	if (mmc->high_capacity) {
1064272cc70bSAndy Fleming 		csize = (mmc->csd[1] & 0x3f) << 16
1065272cc70bSAndy Fleming 			| (mmc->csd[2] & 0xffff0000) >> 16;
1066272cc70bSAndy Fleming 		cmult = 8;
1067272cc70bSAndy Fleming 	} else {
1068272cc70bSAndy Fleming 		csize = (mmc->csd[1] & 0x3ff) << 2
1069272cc70bSAndy Fleming 			| (mmc->csd[2] & 0xc0000000) >> 30;
1070272cc70bSAndy Fleming 		cmult = (mmc->csd[2] & 0x00038000) >> 15;
1071272cc70bSAndy Fleming 	}
1072272cc70bSAndy Fleming 
1073*f866a46dSStephen Warren 	mmc->capacity_user = (csize + 1) << (cmult + 2);
1074*f866a46dSStephen Warren 	mmc->capacity_user *= mmc->read_bl_len;
1075*f866a46dSStephen Warren 	mmc->capacity_boot = 0;
1076*f866a46dSStephen Warren 	mmc->capacity_rpmb = 0;
1077*f866a46dSStephen Warren 	for (i = 0; i < 4; i++)
1078*f866a46dSStephen Warren 		mmc->capacity_gp[i] = 0;
1079272cc70bSAndy Fleming 
10808bfa195eSSimon Glass 	if (mmc->read_bl_len > MMC_MAX_BLOCK_LEN)
10818bfa195eSSimon Glass 		mmc->read_bl_len = MMC_MAX_BLOCK_LEN;
1082272cc70bSAndy Fleming 
10838bfa195eSSimon Glass 	if (mmc->write_bl_len > MMC_MAX_BLOCK_LEN)
10848bfa195eSSimon Glass 		mmc->write_bl_len = MMC_MAX_BLOCK_LEN;
1085272cc70bSAndy Fleming 
1086272cc70bSAndy Fleming 	/* Select the card, and put it into Transfer Mode */
1087d52ebf10SThomas Chou 	if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
1088272cc70bSAndy Fleming 		cmd.cmdidx = MMC_CMD_SELECT_CARD;
1089fe8f7066SAjay Bhargav 		cmd.resp_type = MMC_RSP_R1;
1090272cc70bSAndy Fleming 		cmd.cmdarg = mmc->rca << 16;
1091272cc70bSAndy Fleming 		err = mmc_send_cmd(mmc, &cmd, NULL);
1092272cc70bSAndy Fleming 
1093272cc70bSAndy Fleming 		if (err)
1094272cc70bSAndy Fleming 			return err;
1095d52ebf10SThomas Chou 	}
1096272cc70bSAndy Fleming 
1097e6f99a56SLei Wen 	/*
1098e6f99a56SLei Wen 	 * For SD, its erase group is always one sector
1099e6f99a56SLei Wen 	 */
1100e6f99a56SLei Wen 	mmc->erase_grp_size = 1;
1101bc897b1dSLei Wen 	mmc->part_config = MMCPART_NOAVAILABLE;
1102d23e2c09SSukumar Ghorai 	if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) {
1103d23e2c09SSukumar Ghorai 		/* check  ext_csd version and capacity */
1104d23e2c09SSukumar Ghorai 		err = mmc_send_ext_csd(mmc, ext_csd);
1105fdbb873eSKim Phillips 		if (!err && (ext_csd[EXT_CSD_REV] >= 2)) {
1106639b7827SYoshihiro Shimoda 			/*
1107639b7827SYoshihiro Shimoda 			 * According to the JEDEC Standard, the value of
1108639b7827SYoshihiro Shimoda 			 * ext_csd's capacity is valid if the value is more
1109639b7827SYoshihiro Shimoda 			 * than 2GB
1110639b7827SYoshihiro Shimoda 			 */
11110560db18SLei Wen 			capacity = ext_csd[EXT_CSD_SEC_CNT] << 0
11120560db18SLei Wen 					| ext_csd[EXT_CSD_SEC_CNT + 1] << 8
11130560db18SLei Wen 					| ext_csd[EXT_CSD_SEC_CNT + 2] << 16
11140560db18SLei Wen 					| ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
11158bfa195eSSimon Glass 			capacity *= MMC_MAX_BLOCK_LEN;
1116b1f1e821SŁukasz Majewski 			if ((capacity >> 20) > 2 * 1024)
1117*f866a46dSStephen Warren 				mmc->capacity_user = capacity;
1118d23e2c09SSukumar Ghorai 		}
1119bc897b1dSLei Wen 
112064f4a619SJaehoon Chung 		switch (ext_csd[EXT_CSD_REV]) {
112164f4a619SJaehoon Chung 		case 1:
112264f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_1;
112364f4a619SJaehoon Chung 			break;
112464f4a619SJaehoon Chung 		case 2:
112564f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_2;
112664f4a619SJaehoon Chung 			break;
112764f4a619SJaehoon Chung 		case 3:
112864f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_3;
112964f4a619SJaehoon Chung 			break;
113064f4a619SJaehoon Chung 		case 5:
113164f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_41;
113264f4a619SJaehoon Chung 			break;
113364f4a619SJaehoon Chung 		case 6:
113464f4a619SJaehoon Chung 			mmc->version = MMC_VERSION_4_5;
113564f4a619SJaehoon Chung 			break;
113664f4a619SJaehoon Chung 		}
113764f4a619SJaehoon Chung 
1138e6f99a56SLei Wen 		/*
1139e6f99a56SLei Wen 		 * Check whether GROUP_DEF is set, if yes, read out
1140e6f99a56SLei Wen 		 * group size from ext_csd directly, or calculate
1141e6f99a56SLei Wen 		 * the group size from the csd value.
1142e6f99a56SLei Wen 		 */
11438bfa195eSSimon Glass 		if (ext_csd[EXT_CSD_ERASE_GROUP_DEF]) {
11440560db18SLei Wen 			mmc->erase_grp_size =
11458bfa195eSSimon Glass 				ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] *
11468bfa195eSSimon Glass 					MMC_MAX_BLOCK_LEN * 1024;
11478bfa195eSSimon Glass 		} else {
1148e6f99a56SLei Wen 			int erase_gsz, erase_gmul;
1149e6f99a56SLei Wen 			erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10;
1150e6f99a56SLei Wen 			erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5;
1151e6f99a56SLei Wen 			mmc->erase_grp_size = (erase_gsz + 1)
1152e6f99a56SLei Wen 				* (erase_gmul + 1);
1153e6f99a56SLei Wen 		}
1154e6f99a56SLei Wen 
1155bc897b1dSLei Wen 		/* store the partition info of emmc */
11568948ea83SStephen Warren 		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) ||
11578948ea83SStephen Warren 		    ext_csd[EXT_CSD_BOOT_MULT])
11580560db18SLei Wen 			mmc->part_config = ext_csd[EXT_CSD_PART_CONF];
1159*f866a46dSStephen Warren 
1160*f866a46dSStephen Warren 		mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17;
1161*f866a46dSStephen Warren 
1162*f866a46dSStephen Warren 		mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17;
1163*f866a46dSStephen Warren 
1164*f866a46dSStephen Warren 		for (i = 0; i < 4; i++) {
1165*f866a46dSStephen Warren 			int idx = EXT_CSD_GP_SIZE_MULT + i * 3;
1166*f866a46dSStephen Warren 			mmc->capacity_gp[i] = (ext_csd[idx + 2] << 16) +
1167*f866a46dSStephen Warren 				(ext_csd[idx + 1] << 8) + ext_csd[idx];
1168*f866a46dSStephen Warren 			mmc->capacity_gp[i] *=
1169*f866a46dSStephen Warren 				ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
1170*f866a46dSStephen Warren 			mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
1171d23e2c09SSukumar Ghorai 		}
1172*f866a46dSStephen Warren 	}
1173*f866a46dSStephen Warren 
1174*f866a46dSStephen Warren 	err = mmc_set_capacity(mmc, mmc->part_num);
1175*f866a46dSStephen Warren 	if (err)
1176*f866a46dSStephen Warren 		return err;
1177d23e2c09SSukumar Ghorai 
1178272cc70bSAndy Fleming 	if (IS_SD(mmc))
1179272cc70bSAndy Fleming 		err = sd_change_freq(mmc);
1180272cc70bSAndy Fleming 	else
1181272cc70bSAndy Fleming 		err = mmc_change_freq(mmc);
1182272cc70bSAndy Fleming 
1183272cc70bSAndy Fleming 	if (err)
1184272cc70bSAndy Fleming 		return err;
1185272cc70bSAndy Fleming 
1186272cc70bSAndy Fleming 	/* Restrict card's capabilities by what the host can do */
1187272cc70bSAndy Fleming 	mmc->card_caps &= mmc->host_caps;
1188272cc70bSAndy Fleming 
1189272cc70bSAndy Fleming 	if (IS_SD(mmc)) {
1190272cc70bSAndy Fleming 		if (mmc->card_caps & MMC_MODE_4BIT) {
1191272cc70bSAndy Fleming 			cmd.cmdidx = MMC_CMD_APP_CMD;
1192272cc70bSAndy Fleming 			cmd.resp_type = MMC_RSP_R1;
1193272cc70bSAndy Fleming 			cmd.cmdarg = mmc->rca << 16;
1194272cc70bSAndy Fleming 
1195272cc70bSAndy Fleming 			err = mmc_send_cmd(mmc, &cmd, NULL);
1196272cc70bSAndy Fleming 			if (err)
1197272cc70bSAndy Fleming 				return err;
1198272cc70bSAndy Fleming 
1199272cc70bSAndy Fleming 			cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH;
1200272cc70bSAndy Fleming 			cmd.resp_type = MMC_RSP_R1;
1201272cc70bSAndy Fleming 			cmd.cmdarg = 2;
1202272cc70bSAndy Fleming 			err = mmc_send_cmd(mmc, &cmd, NULL);
1203272cc70bSAndy Fleming 			if (err)
1204272cc70bSAndy Fleming 				return err;
1205272cc70bSAndy Fleming 
1206272cc70bSAndy Fleming 			mmc_set_bus_width(mmc, 4);
1207272cc70bSAndy Fleming 		}
1208272cc70bSAndy Fleming 
1209272cc70bSAndy Fleming 		if (mmc->card_caps & MMC_MODE_HS)
1210ad5fd922SJaehoon Chung 			mmc->tran_speed = 50000000;
1211272cc70bSAndy Fleming 		else
1212ad5fd922SJaehoon Chung 			mmc->tran_speed = 25000000;
1213272cc70bSAndy Fleming 	} else {
12147798f6dbSAndy Fleming 		int idx;
12157798f6dbSAndy Fleming 
12167798f6dbSAndy Fleming 		/* An array of possible bus widths in order of preference */
12177798f6dbSAndy Fleming 		static unsigned ext_csd_bits[] = {
12187798f6dbSAndy Fleming 			EXT_CSD_BUS_WIDTH_8,
12197798f6dbSAndy Fleming 			EXT_CSD_BUS_WIDTH_4,
12207798f6dbSAndy Fleming 			EXT_CSD_BUS_WIDTH_1,
12217798f6dbSAndy Fleming 		};
12227798f6dbSAndy Fleming 
12237798f6dbSAndy Fleming 		/* An array to map CSD bus widths to host cap bits */
12247798f6dbSAndy Fleming 		static unsigned ext_to_hostcaps[] = {
12257798f6dbSAndy Fleming 			[EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT,
12267798f6dbSAndy Fleming 			[EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT,
12277798f6dbSAndy Fleming 		};
12287798f6dbSAndy Fleming 
12297798f6dbSAndy Fleming 		/* An array to map chosen bus width to an integer */
12307798f6dbSAndy Fleming 		static unsigned widths[] = {
12317798f6dbSAndy Fleming 			8, 4, 1,
12327798f6dbSAndy Fleming 		};
12337798f6dbSAndy Fleming 
12347798f6dbSAndy Fleming 		for (idx=0; idx < ARRAY_SIZE(ext_csd_bits); idx++) {
12357798f6dbSAndy Fleming 			unsigned int extw = ext_csd_bits[idx];
12367798f6dbSAndy Fleming 
12377798f6dbSAndy Fleming 			/*
12387798f6dbSAndy Fleming 			 * Check to make sure the controller supports
12397798f6dbSAndy Fleming 			 * this bus width, if it's more than 1
12407798f6dbSAndy Fleming 			 */
12417798f6dbSAndy Fleming 			if (extw != EXT_CSD_BUS_WIDTH_1 &&
12427798f6dbSAndy Fleming 					!(mmc->host_caps & ext_to_hostcaps[extw]))
12437798f6dbSAndy Fleming 				continue;
12447798f6dbSAndy Fleming 
1245272cc70bSAndy Fleming 			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
12467798f6dbSAndy Fleming 					EXT_CSD_BUS_WIDTH, extw);
1247272cc70bSAndy Fleming 
1248272cc70bSAndy Fleming 			if (err)
12494137894eSLei Wen 				continue;
1250272cc70bSAndy Fleming 
12517798f6dbSAndy Fleming 			mmc_set_bus_width(mmc, widths[idx]);
1252272cc70bSAndy Fleming 
12534137894eSLei Wen 			err = mmc_send_ext_csd(mmc, test_csd);
12544137894eSLei Wen 			if (!err && ext_csd[EXT_CSD_PARTITIONING_SUPPORT] \
12554137894eSLei Wen 				    == test_csd[EXT_CSD_PARTITIONING_SUPPORT]
12564137894eSLei Wen 				 && ext_csd[EXT_CSD_ERASE_GROUP_DEF] \
12574137894eSLei Wen 				    == test_csd[EXT_CSD_ERASE_GROUP_DEF] \
12584137894eSLei Wen 				 && ext_csd[EXT_CSD_REV] \
12594137894eSLei Wen 				    == test_csd[EXT_CSD_REV]
12604137894eSLei Wen 				 && ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] \
12614137894eSLei Wen 				    == test_csd[EXT_CSD_HC_ERASE_GRP_SIZE]
12624137894eSLei Wen 				 && memcmp(&ext_csd[EXT_CSD_SEC_CNT], \
12634137894eSLei Wen 					&test_csd[EXT_CSD_SEC_CNT], 4) == 0) {
1264272cc70bSAndy Fleming 
12657798f6dbSAndy Fleming 				mmc->card_caps |= ext_to_hostcaps[extw];
12664137894eSLei Wen 				break;
12674137894eSLei Wen 			}
1268272cc70bSAndy Fleming 		}
1269272cc70bSAndy Fleming 
1270272cc70bSAndy Fleming 		if (mmc->card_caps & MMC_MODE_HS) {
1271272cc70bSAndy Fleming 			if (mmc->card_caps & MMC_MODE_HS_52MHz)
1272ad5fd922SJaehoon Chung 				mmc->tran_speed = 52000000;
1273272cc70bSAndy Fleming 			else
1274ad5fd922SJaehoon Chung 				mmc->tran_speed = 26000000;
1275272cc70bSAndy Fleming 		}
1276ad5fd922SJaehoon Chung 	}
1277ad5fd922SJaehoon Chung 
1278ad5fd922SJaehoon Chung 	mmc_set_clock(mmc, mmc->tran_speed);
1279272cc70bSAndy Fleming 
1280272cc70bSAndy Fleming 	/* fill in device description */
1281272cc70bSAndy Fleming 	mmc->block_dev.lun = 0;
1282272cc70bSAndy Fleming 	mmc->block_dev.type = 0;
1283272cc70bSAndy Fleming 	mmc->block_dev.blksz = mmc->read_bl_len;
12840472fbfdSEgbert Eich 	mmc->block_dev.log2blksz = LOG2(mmc->block_dev.blksz);
12859b1f942cSRabin Vincent 	mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len);
1286babce5f6STaylor Hutt 	sprintf(mmc->block_dev.vendor, "Man %06x Snr %04x%04x",
1287babce5f6STaylor Hutt 		mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff),
1288babce5f6STaylor Hutt 		(mmc->cid[3] >> 16) & 0xffff);
1289babce5f6STaylor Hutt 	sprintf(mmc->block_dev.product, "%c%c%c%c%c%c", mmc->cid[0] & 0xff,
12900b453ffeSRabin Vincent 		(mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
1291babce5f6STaylor Hutt 		(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff,
1292babce5f6STaylor Hutt 		(mmc->cid[2] >> 24) & 0xff);
1293babce5f6STaylor Hutt 	sprintf(mmc->block_dev.revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf,
1294babce5f6STaylor Hutt 		(mmc->cid[2] >> 16) & 0xf);
1295122efd43SMikhail Kshevetskiy #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT)
1296272cc70bSAndy Fleming 	init_part(&mmc->block_dev);
1297122efd43SMikhail Kshevetskiy #endif
1298272cc70bSAndy Fleming 
1299272cc70bSAndy Fleming 	return 0;
1300272cc70bSAndy Fleming }
1301272cc70bSAndy Fleming 
1302fdbb873eSKim Phillips static int mmc_send_if_cond(struct mmc *mmc)
1303272cc70bSAndy Fleming {
1304272cc70bSAndy Fleming 	struct mmc_cmd cmd;
1305272cc70bSAndy Fleming 	int err;
1306272cc70bSAndy Fleming 
1307272cc70bSAndy Fleming 	cmd.cmdidx = SD_CMD_SEND_IF_COND;
1308272cc70bSAndy Fleming 	/* We set the bit if the host supports voltages between 2.7 and 3.6 V */
1309272cc70bSAndy Fleming 	cmd.cmdarg = ((mmc->voltages & 0xff8000) != 0) << 8 | 0xaa;
1310272cc70bSAndy Fleming 	cmd.resp_type = MMC_RSP_R7;
1311272cc70bSAndy Fleming 
1312272cc70bSAndy Fleming 	err = mmc_send_cmd(mmc, &cmd, NULL);
1313272cc70bSAndy Fleming 
1314272cc70bSAndy Fleming 	if (err)
1315272cc70bSAndy Fleming 		return err;
1316272cc70bSAndy Fleming 
1317998be3ddSRabin Vincent 	if ((cmd.response[0] & 0xff) != 0xaa)
1318272cc70bSAndy Fleming 		return UNUSABLE_ERR;
1319272cc70bSAndy Fleming 	else
1320272cc70bSAndy Fleming 		mmc->version = SD_VERSION_2;
1321272cc70bSAndy Fleming 
1322272cc70bSAndy Fleming 	return 0;
1323272cc70bSAndy Fleming }
1324272cc70bSAndy Fleming 
1325272cc70bSAndy Fleming int mmc_register(struct mmc *mmc)
1326272cc70bSAndy Fleming {
1327272cc70bSAndy Fleming 	/* Setup the universal parts of the block interface just once */
1328272cc70bSAndy Fleming 	mmc->block_dev.if_type = IF_TYPE_MMC;
1329272cc70bSAndy Fleming 	mmc->block_dev.dev = cur_dev_num++;
1330272cc70bSAndy Fleming 	mmc->block_dev.removable = 1;
1331272cc70bSAndy Fleming 	mmc->block_dev.block_read = mmc_bread;
1332272cc70bSAndy Fleming 	mmc->block_dev.block_write = mmc_bwrite;
1333e6f99a56SLei Wen 	mmc->block_dev.block_erase = mmc_berase;
13348feafcc4SJohn Rigby 	if (!mmc->b_max)
13358feafcc4SJohn Rigby 		mmc->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
1336272cc70bSAndy Fleming 
1337272cc70bSAndy Fleming 	INIT_LIST_HEAD (&mmc->link);
1338272cc70bSAndy Fleming 
1339272cc70bSAndy Fleming 	list_add_tail (&mmc->link, &mmc_devices);
1340272cc70bSAndy Fleming 
1341272cc70bSAndy Fleming 	return 0;
1342272cc70bSAndy Fleming }
1343272cc70bSAndy Fleming 
1344df3fc526SMatthew McClintock #ifdef CONFIG_PARTITIONS
1345272cc70bSAndy Fleming block_dev_desc_t *mmc_get_dev(int dev)
1346272cc70bSAndy Fleming {
1347272cc70bSAndy Fleming 	struct mmc *mmc = find_mmc_device(dev);
13486bb4b4bcSBenoît Thébaudeau 	if (!mmc || mmc_init(mmc))
134940242bc3SŁukasz Majewski 		return NULL;
1350272cc70bSAndy Fleming 
135140242bc3SŁukasz Majewski 	return &mmc->block_dev;
1352272cc70bSAndy Fleming }
1353df3fc526SMatthew McClintock #endif
1354272cc70bSAndy Fleming 
1355e9550449SChe-Liang Chiou int mmc_start_init(struct mmc *mmc)
1356272cc70bSAndy Fleming {
1357afd5932bSMacpaul Lin 	int err;
1358272cc70bSAndy Fleming 
135948972d90SThierry Reding 	if (mmc_getcd(mmc) == 0) {
136048972d90SThierry Reding 		mmc->has_init = 0;
136148972d90SThierry Reding 		printf("MMC: no card present\n");
136248972d90SThierry Reding 		return NO_CARD_ERR;
136348972d90SThierry Reding 	}
136448972d90SThierry Reding 
1365bc897b1dSLei Wen 	if (mmc->has_init)
1366bc897b1dSLei Wen 		return 0;
1367bc897b1dSLei Wen 
1368272cc70bSAndy Fleming 	err = mmc->init(mmc);
1369272cc70bSAndy Fleming 
1370272cc70bSAndy Fleming 	if (err)
1371272cc70bSAndy Fleming 		return err;
1372272cc70bSAndy Fleming 
1373b86b85e2SIlya Yanok 	mmc_set_bus_width(mmc, 1);
1374b86b85e2SIlya Yanok 	mmc_set_clock(mmc, 1);
1375b86b85e2SIlya Yanok 
1376272cc70bSAndy Fleming 	/* Reset the Card */
1377272cc70bSAndy Fleming 	err = mmc_go_idle(mmc);
1378272cc70bSAndy Fleming 
1379272cc70bSAndy Fleming 	if (err)
1380272cc70bSAndy Fleming 		return err;
1381272cc70bSAndy Fleming 
1382bc897b1dSLei Wen 	/* The internal partition reset to user partition(0) at every CMD0*/
1383bc897b1dSLei Wen 	mmc->part_num = 0;
1384bc897b1dSLei Wen 
1385272cc70bSAndy Fleming 	/* Test for SD version 2 */
1386272cc70bSAndy Fleming 	err = mmc_send_if_cond(mmc);
1387272cc70bSAndy Fleming 
1388272cc70bSAndy Fleming 	/* Now try to get the SD card's operating condition */
1389272cc70bSAndy Fleming 	err = sd_send_op_cond(mmc);
1390272cc70bSAndy Fleming 
1391272cc70bSAndy Fleming 	/* If the command timed out, we check for an MMC card */
1392272cc70bSAndy Fleming 	if (err == TIMEOUT) {
1393272cc70bSAndy Fleming 		err = mmc_send_op_cond(mmc);
1394272cc70bSAndy Fleming 
1395e9550449SChe-Liang Chiou 		if (err && err != IN_PROGRESS) {
1396272cc70bSAndy Fleming 			printf("Card did not respond to voltage select!\n");
1397272cc70bSAndy Fleming 			return UNUSABLE_ERR;
1398272cc70bSAndy Fleming 		}
1399272cc70bSAndy Fleming 	}
1400272cc70bSAndy Fleming 
1401e9550449SChe-Liang Chiou 	if (err == IN_PROGRESS)
1402e9550449SChe-Liang Chiou 		mmc->init_in_progress = 1;
1403e9550449SChe-Liang Chiou 
1404e9550449SChe-Liang Chiou 	return err;
1405e9550449SChe-Liang Chiou }
1406e9550449SChe-Liang Chiou 
1407e9550449SChe-Liang Chiou static int mmc_complete_init(struct mmc *mmc)
1408e9550449SChe-Liang Chiou {
1409e9550449SChe-Liang Chiou 	int err = 0;
1410e9550449SChe-Liang Chiou 
1411e9550449SChe-Liang Chiou 	if (mmc->op_cond_pending)
1412e9550449SChe-Liang Chiou 		err = mmc_complete_op_cond(mmc);
1413e9550449SChe-Liang Chiou 
1414e9550449SChe-Liang Chiou 	if (!err)
1415bc897b1dSLei Wen 		err = mmc_startup(mmc);
1416bc897b1dSLei Wen 	if (err)
1417bc897b1dSLei Wen 		mmc->has_init = 0;
1418bc897b1dSLei Wen 	else
1419bc897b1dSLei Wen 		mmc->has_init = 1;
1420e9550449SChe-Liang Chiou 	mmc->init_in_progress = 0;
1421e9550449SChe-Liang Chiou 	return err;
1422e9550449SChe-Liang Chiou }
1423e9550449SChe-Liang Chiou 
1424e9550449SChe-Liang Chiou int mmc_init(struct mmc *mmc)
1425e9550449SChe-Liang Chiou {
1426e9550449SChe-Liang Chiou 	int err = IN_PROGRESS;
1427e9550449SChe-Liang Chiou 	unsigned start = get_timer(0);
1428e9550449SChe-Liang Chiou 
1429e9550449SChe-Liang Chiou 	if (mmc->has_init)
1430e9550449SChe-Liang Chiou 		return 0;
1431e9550449SChe-Liang Chiou 	if (!mmc->init_in_progress)
1432e9550449SChe-Liang Chiou 		err = mmc_start_init(mmc);
1433e9550449SChe-Liang Chiou 
1434e9550449SChe-Liang Chiou 	if (!err || err == IN_PROGRESS)
1435e9550449SChe-Liang Chiou 		err = mmc_complete_init(mmc);
1436e9550449SChe-Liang Chiou 	debug("%s: %d, time %lu\n", __func__, err, get_timer(start));
1437bc897b1dSLei Wen 	return err;
1438272cc70bSAndy Fleming }
1439272cc70bSAndy Fleming 
1440272cc70bSAndy Fleming /*
1441272cc70bSAndy Fleming  * CPU and board-specific MMC initializations.  Aliased function
1442272cc70bSAndy Fleming  * signals caller to move on
1443272cc70bSAndy Fleming  */
1444272cc70bSAndy Fleming static int __def_mmc_init(bd_t *bis)
1445272cc70bSAndy Fleming {
1446272cc70bSAndy Fleming 	return -1;
1447272cc70bSAndy Fleming }
1448272cc70bSAndy Fleming 
1449f9a109b3SPeter Tyser int cpu_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init")));
1450f9a109b3SPeter Tyser int board_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init")));
1451272cc70bSAndy Fleming 
1452272cc70bSAndy Fleming void print_mmc_devices(char separator)
1453272cc70bSAndy Fleming {
1454272cc70bSAndy Fleming 	struct mmc *m;
1455272cc70bSAndy Fleming 	struct list_head *entry;
1456272cc70bSAndy Fleming 
1457272cc70bSAndy Fleming 	list_for_each(entry, &mmc_devices) {
1458272cc70bSAndy Fleming 		m = list_entry(entry, struct mmc, link);
1459272cc70bSAndy Fleming 
1460272cc70bSAndy Fleming 		printf("%s: %d", m->name, m->block_dev.dev);
1461272cc70bSAndy Fleming 
1462272cc70bSAndy Fleming 		if (entry->next != &mmc_devices)
1463272cc70bSAndy Fleming 			printf("%c ", separator);
1464272cc70bSAndy Fleming 	}
1465272cc70bSAndy Fleming 
1466272cc70bSAndy Fleming 	printf("\n");
1467272cc70bSAndy Fleming }
1468272cc70bSAndy Fleming 
1469ea6ebe21SLei Wen int get_mmc_num(void)
1470ea6ebe21SLei Wen {
1471ea6ebe21SLei Wen 	return cur_dev_num;
1472ea6ebe21SLei Wen }
1473ea6ebe21SLei Wen 
1474e9550449SChe-Liang Chiou void mmc_set_preinit(struct mmc *mmc, int preinit)
1475e9550449SChe-Liang Chiou {
1476e9550449SChe-Liang Chiou 	mmc->preinit = preinit;
1477e9550449SChe-Liang Chiou }
1478e9550449SChe-Liang Chiou 
1479e9550449SChe-Liang Chiou static void do_preinit(void)
1480e9550449SChe-Liang Chiou {
1481e9550449SChe-Liang Chiou 	struct mmc *m;
1482e9550449SChe-Liang Chiou 	struct list_head *entry;
1483e9550449SChe-Liang Chiou 
1484e9550449SChe-Liang Chiou 	list_for_each(entry, &mmc_devices) {
1485e9550449SChe-Liang Chiou 		m = list_entry(entry, struct mmc, link);
1486e9550449SChe-Liang Chiou 
1487e9550449SChe-Liang Chiou 		if (m->preinit)
1488e9550449SChe-Liang Chiou 			mmc_start_init(m);
1489e9550449SChe-Liang Chiou 	}
1490e9550449SChe-Liang Chiou }
1491e9550449SChe-Liang Chiou 
1492e9550449SChe-Liang Chiou 
1493272cc70bSAndy Fleming int mmc_initialize(bd_t *bis)
1494272cc70bSAndy Fleming {
1495272cc70bSAndy Fleming 	INIT_LIST_HEAD (&mmc_devices);
1496272cc70bSAndy Fleming 	cur_dev_num = 0;
1497272cc70bSAndy Fleming 
1498272cc70bSAndy Fleming 	if (board_mmc_init(bis) < 0)
1499272cc70bSAndy Fleming 		cpu_mmc_init(bis);
1500272cc70bSAndy Fleming 
1501272cc70bSAndy Fleming 	print_mmc_devices(',');
1502272cc70bSAndy Fleming 
1503e9550449SChe-Liang Chiou 	do_preinit();
1504272cc70bSAndy Fleming 	return 0;
1505272cc70bSAndy Fleming }
1506