xref: /rk3399_ARM-atf/drivers/mmc/mmc.c (revision 14cda5168de45bbbcce1a5152140111d4fc8fd21)
1ad71d45eSYann Gautier /*
2f85041a6SAhmad Fatoum  * Copyright (c) 2018-2022, ARM Limited and Contributors. All rights reserved.
3ad71d45eSYann Gautier  *
4ad71d45eSYann Gautier  * SPDX-License-Identifier: BSD-3-Clause
5ad71d45eSYann Gautier  */
6ad71d45eSYann Gautier 
7ad71d45eSYann Gautier /* Define a simple and generic interface to access eMMC and SD-card devices. */
8ad71d45eSYann Gautier 
9ad71d45eSYann Gautier #include <assert.h>
10ad71d45eSYann Gautier #include <errno.h>
11ad71d45eSYann Gautier #include <stdbool.h>
12ad71d45eSYann Gautier #include <string.h>
1309d40e0eSAntonio Nino Diaz 
1409d40e0eSAntonio Nino Diaz #include <arch_helpers.h>
1509d40e0eSAntonio Nino Diaz #include <common/debug.h>
1609d40e0eSAntonio Nino Diaz #include <drivers/delay_timer.h>
1709d40e0eSAntonio Nino Diaz #include <drivers/mmc.h>
1809d40e0eSAntonio Nino Diaz #include <lib/utils.h>
19ccf8392cSJayanth Dodderi Chidanand #include <plat/common/common_def.h>
20ad71d45eSYann Gautier 
21ad71d45eSYann Gautier #define MMC_DEFAULT_MAX_RETRIES		5
22ad71d45eSYann Gautier #define SEND_OP_COND_MAX_RETRIES	100
23ad71d45eSYann Gautier 
24ad71d45eSYann Gautier #define MULT_BY_512K_SHIFT		19
25ad71d45eSYann Gautier 
26ad71d45eSYann Gautier static const struct mmc_ops *ops;
27ad71d45eSYann Gautier static unsigned int mmc_ocr_value;
28ad71d45eSYann Gautier static struct mmc_csd_emmc mmc_csd;
29e5b267bbSYann Gautier static struct sd_switch_status sd_switch_func_status;
3007858dd8SHaojian Zhuang static unsigned char mmc_ext_csd[512] __aligned(16);
31ad71d45eSYann Gautier static unsigned int mmc_flags;
3270eb88b7SYann Gautier static struct mmc_device_info *mmc_dev_info;
33ad71d45eSYann Gautier static unsigned int rca;
34a468e756STien Hock, Loh static unsigned int scr[2]__aligned(16) = { 0 };
35ad71d45eSYann Gautier 
36ad71d45eSYann Gautier static const unsigned char tran_speed_base[16] = {
37ad71d45eSYann Gautier 	0, 10, 12, 13, 15, 20, 26, 30, 35, 40, 45, 52, 55, 60, 70, 80
38ad71d45eSYann Gautier };
39ad71d45eSYann Gautier 
40ad71d45eSYann Gautier static const unsigned char sd_tran_speed_base[16] = {
41ad71d45eSYann Gautier 	0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80
42ad71d45eSYann Gautier };
43ad71d45eSYann Gautier 
44ad71d45eSYann Gautier static bool is_cmd23_enabled(void)
45ad71d45eSYann Gautier {
46ad71d45eSYann Gautier 	return ((mmc_flags & MMC_FLAG_CMD23) != 0U);
47ad71d45eSYann Gautier }
48ad71d45eSYann Gautier 
49e5b267bbSYann Gautier static bool is_sd_cmd6_enabled(void)
50e5b267bbSYann Gautier {
51e5b267bbSYann Gautier 	return ((mmc_flags & MMC_FLAG_SD_CMD6) != 0U);
52e5b267bbSYann Gautier }
53e5b267bbSYann Gautier 
54ad71d45eSYann Gautier static int mmc_send_cmd(unsigned int idx, unsigned int arg,
55ad71d45eSYann Gautier 			unsigned int r_type, unsigned int *r_data)
56ad71d45eSYann Gautier {
57ad71d45eSYann Gautier 	struct mmc_cmd cmd;
58ad71d45eSYann Gautier 	int ret;
59ad71d45eSYann Gautier 
60ad71d45eSYann Gautier 	zeromem(&cmd, sizeof(struct mmc_cmd));
61ad71d45eSYann Gautier 
62ad71d45eSYann Gautier 	cmd.cmd_idx = idx;
63ad71d45eSYann Gautier 	cmd.cmd_arg = arg;
64ad71d45eSYann Gautier 	cmd.resp_type = r_type;
65ad71d45eSYann Gautier 
66ad71d45eSYann Gautier 	ret = ops->send_cmd(&cmd);
67ad71d45eSYann Gautier 
68ad71d45eSYann Gautier 	if ((ret == 0) && (r_data != NULL)) {
69ad71d45eSYann Gautier 		int i;
70ad71d45eSYann Gautier 
71ad71d45eSYann Gautier 		for (i = 0; i < 4; i++) {
72ad71d45eSYann Gautier 			*r_data = cmd.resp_data[i];
73ad71d45eSYann Gautier 			r_data++;
74ad71d45eSYann Gautier 		}
75ad71d45eSYann Gautier 	}
76ad71d45eSYann Gautier 
77ad71d45eSYann Gautier 	if (ret != 0) {
78ad71d45eSYann Gautier 		VERBOSE("Send command %u error: %d\n", idx, ret);
79ad71d45eSYann Gautier 	}
80ad71d45eSYann Gautier 
81ad71d45eSYann Gautier 	return ret;
82ad71d45eSYann Gautier }
83ad71d45eSYann Gautier 
84ad71d45eSYann Gautier static int mmc_device_state(void)
85ad71d45eSYann Gautier {
86ad71d45eSYann Gautier 	int retries = MMC_DEFAULT_MAX_RETRIES;
87ad71d45eSYann Gautier 	unsigned int resp_data[4];
88ad71d45eSYann Gautier 
89ad71d45eSYann Gautier 	do {
90ad71d45eSYann Gautier 		int ret;
91ad71d45eSYann Gautier 
92ad71d45eSYann Gautier 		if (retries == 0) {
93ad71d45eSYann Gautier 			ERROR("CMD13 failed after %d retries\n",
94ad71d45eSYann Gautier 			      MMC_DEFAULT_MAX_RETRIES);
95ad71d45eSYann Gautier 			return -EIO;
96ad71d45eSYann Gautier 		}
97ad71d45eSYann Gautier 
98ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(13), rca << RCA_SHIFT_OFFSET,
9997d5db8cSYann Gautier 				   MMC_RESPONSE_R1, &resp_data[0]);
100ad71d45eSYann Gautier 		if (ret != 0) {
101a468e756STien Hock, Loh 			retries--;
102a468e756STien Hock, Loh 			continue;
103ad71d45eSYann Gautier 		}
104ad71d45eSYann Gautier 
105ad71d45eSYann Gautier 		if ((resp_data[0] & STATUS_SWITCH_ERROR) != 0U) {
106ad71d45eSYann Gautier 			return -EIO;
107ad71d45eSYann Gautier 		}
108ad71d45eSYann Gautier 
109ad71d45eSYann Gautier 		retries--;
110ad71d45eSYann Gautier 	} while ((resp_data[0] & STATUS_READY_FOR_DATA) == 0U);
111ad71d45eSYann Gautier 
112ad71d45eSYann Gautier 	return MMC_GET_STATE(resp_data[0]);
113ad71d45eSYann Gautier }
114ad71d45eSYann Gautier 
1155014b52dSVyacheslav Yurkov static int mmc_send_part_switch_cmd(unsigned int part_config)
1165014b52dSVyacheslav Yurkov {
1175014b52dSVyacheslav Yurkov 	int ret;
1185014b52dSVyacheslav Yurkov 	unsigned int part_time = 0;
1195014b52dSVyacheslav Yurkov 
1205014b52dSVyacheslav Yurkov 	ret = mmc_send_cmd(MMC_CMD(6),
1215014b52dSVyacheslav Yurkov 			   EXTCSD_WRITE_BYTES |
1225014b52dSVyacheslav Yurkov 			   EXTCSD_CMD(CMD_EXTCSD_PARTITION_CONFIG) |
1235014b52dSVyacheslav Yurkov 			   EXTCSD_VALUE(part_config) |
1245014b52dSVyacheslav Yurkov 			   EXTCSD_CMD_SET_NORMAL,
1255014b52dSVyacheslav Yurkov 			   MMC_RESPONSE_R1B, NULL);
1265014b52dSVyacheslav Yurkov 	if (ret != 0) {
1275014b52dSVyacheslav Yurkov 		return ret;
1285014b52dSVyacheslav Yurkov 	}
1295014b52dSVyacheslav Yurkov 
1305014b52dSVyacheslav Yurkov 	/* Partition switch timing is in 10ms units */
1315014b52dSVyacheslav Yurkov 	part_time = mmc_ext_csd[CMD_EXTCSD_PART_SWITCH_TIME] * 10;
1325014b52dSVyacheslav Yurkov 
1335014b52dSVyacheslav Yurkov 	mdelay(part_time);
1345014b52dSVyacheslav Yurkov 
1355014b52dSVyacheslav Yurkov 	do {
1365014b52dSVyacheslav Yurkov 		ret = mmc_device_state();
1375014b52dSVyacheslav Yurkov 		if (ret < 0) {
1385014b52dSVyacheslav Yurkov 			return ret;
1395014b52dSVyacheslav Yurkov 		}
1405014b52dSVyacheslav Yurkov 	} while (ret == MMC_STATE_PRG);
1415014b52dSVyacheslav Yurkov 
1425014b52dSVyacheslav Yurkov 	return 0;
1435014b52dSVyacheslav Yurkov }
1445014b52dSVyacheslav Yurkov 
145ad71d45eSYann Gautier static int mmc_set_ext_csd(unsigned int ext_cmd, unsigned int value)
146ad71d45eSYann Gautier {
147ad71d45eSYann Gautier 	int ret;
148ad71d45eSYann Gautier 
149ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(6),
150ad71d45eSYann Gautier 			   EXTCSD_WRITE_BYTES | EXTCSD_CMD(ext_cmd) |
151ad71d45eSYann Gautier 			   EXTCSD_VALUE(value) | EXTCSD_CMD_SET_NORMAL,
15261752898SBryan O'Donoghue 			   MMC_RESPONSE_R1B, NULL);
153ad71d45eSYann Gautier 	if (ret != 0) {
154ad71d45eSYann Gautier 		return ret;
155ad71d45eSYann Gautier 	}
156ad71d45eSYann Gautier 
157ad71d45eSYann Gautier 	do {
158ad71d45eSYann Gautier 		ret = mmc_device_state();
159ad71d45eSYann Gautier 		if (ret < 0) {
160ad71d45eSYann Gautier 			return ret;
161ad71d45eSYann Gautier 		}
162ad71d45eSYann Gautier 	} while (ret == MMC_STATE_PRG);
163ad71d45eSYann Gautier 
164ad71d45eSYann Gautier 	return 0;
165ad71d45eSYann Gautier }
166ad71d45eSYann Gautier 
167ad71d45eSYann Gautier static int mmc_sd_switch(unsigned int bus_width)
168ad71d45eSYann Gautier {
169ad71d45eSYann Gautier 	int ret;
170ad71d45eSYann Gautier 	int retries = MMC_DEFAULT_MAX_RETRIES;
171ad71d45eSYann Gautier 	unsigned int bus_width_arg = 0;
172ad71d45eSYann Gautier 
173ad71d45eSYann Gautier 	ret = ops->prepare(0, (uintptr_t)&scr, sizeof(scr));
174ad71d45eSYann Gautier 	if (ret != 0) {
175ad71d45eSYann Gautier 		return ret;
176ad71d45eSYann Gautier 	}
177ad71d45eSYann Gautier 
178ad71d45eSYann Gautier 	/* CMD55: Application Specific Command */
179ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET,
18097d5db8cSYann Gautier 			   MMC_RESPONSE_R5, NULL);
181ad71d45eSYann Gautier 	if (ret != 0) {
182ad71d45eSYann Gautier 		return ret;
183ad71d45eSYann Gautier 	}
184ad71d45eSYann Gautier 
185ad71d45eSYann Gautier 	/* ACMD51: SEND_SCR */
186ad71d45eSYann Gautier 	do {
18797d5db8cSYann Gautier 		ret = mmc_send_cmd(MMC_ACMD(51), 0, MMC_RESPONSE_R1, NULL);
188ad71d45eSYann Gautier 		if ((ret != 0) && (retries == 0)) {
189ad71d45eSYann Gautier 			ERROR("ACMD51 failed after %d retries (ret=%d)\n",
190ad71d45eSYann Gautier 			      MMC_DEFAULT_MAX_RETRIES, ret);
191ad71d45eSYann Gautier 			return ret;
192ad71d45eSYann Gautier 		}
193ad71d45eSYann Gautier 
194ad71d45eSYann Gautier 		retries--;
195ad71d45eSYann Gautier 	} while (ret != 0);
196ad71d45eSYann Gautier 
197ad71d45eSYann Gautier 	ret = ops->read(0, (uintptr_t)&scr, sizeof(scr));
198ad71d45eSYann Gautier 	if (ret != 0) {
199ad71d45eSYann Gautier 		return ret;
200ad71d45eSYann Gautier 	}
201ad71d45eSYann Gautier 
202ad71d45eSYann Gautier 	if (((scr[0] & SD_SCR_BUS_WIDTH_4) != 0U) &&
203ad71d45eSYann Gautier 	    (bus_width == MMC_BUS_WIDTH_4)) {
204ad71d45eSYann Gautier 		bus_width_arg = 2;
205ad71d45eSYann Gautier 	}
206ad71d45eSYann Gautier 
207ad71d45eSYann Gautier 	/* CMD55: Application Specific Command */
208ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET,
20997d5db8cSYann Gautier 			   MMC_RESPONSE_R5, NULL);
210ad71d45eSYann Gautier 	if (ret != 0) {
211ad71d45eSYann Gautier 		return ret;
212ad71d45eSYann Gautier 	}
213ad71d45eSYann Gautier 
214ad71d45eSYann Gautier 	/* ACMD6: SET_BUS_WIDTH */
21597d5db8cSYann Gautier 	ret = mmc_send_cmd(MMC_ACMD(6), bus_width_arg, MMC_RESPONSE_R1, NULL);
216ad71d45eSYann Gautier 	if (ret != 0) {
217ad71d45eSYann Gautier 		return ret;
218ad71d45eSYann Gautier 	}
219ad71d45eSYann Gautier 
220ad71d45eSYann Gautier 	do {
221ad71d45eSYann Gautier 		ret = mmc_device_state();
222ad71d45eSYann Gautier 		if (ret < 0) {
223ad71d45eSYann Gautier 			return ret;
224ad71d45eSYann Gautier 		}
225ad71d45eSYann Gautier 	} while (ret == MMC_STATE_PRG);
226ad71d45eSYann Gautier 
227ad71d45eSYann Gautier 	return 0;
228ad71d45eSYann Gautier }
229ad71d45eSYann Gautier 
230ad71d45eSYann Gautier static int mmc_set_ios(unsigned int clk, unsigned int bus_width)
231ad71d45eSYann Gautier {
232ad71d45eSYann Gautier 	int ret;
233ad71d45eSYann Gautier 	unsigned int width = bus_width;
234ad71d45eSYann Gautier 
23570eb88b7SYann Gautier 	if (mmc_dev_info->mmc_dev_type != MMC_IS_EMMC) {
236ad71d45eSYann Gautier 		if (width == MMC_BUS_WIDTH_8) {
237ad71d45eSYann Gautier 			WARN("Wrong bus config for SD-card, force to 4\n");
238ad71d45eSYann Gautier 			width = MMC_BUS_WIDTH_4;
239ad71d45eSYann Gautier 		}
240ad71d45eSYann Gautier 		ret = mmc_sd_switch(width);
241ad71d45eSYann Gautier 		if (ret != 0) {
242ad71d45eSYann Gautier 			return ret;
243ad71d45eSYann Gautier 		}
244ad71d45eSYann Gautier 	} else if (mmc_csd.spec_vers == 4U) {
245ad71d45eSYann Gautier 		ret = mmc_set_ext_csd(CMD_EXTCSD_BUS_WIDTH,
246ad71d45eSYann Gautier 				      (unsigned int)width);
247ad71d45eSYann Gautier 		if (ret != 0) {
248ad71d45eSYann Gautier 			return ret;
249ad71d45eSYann Gautier 		}
250ad71d45eSYann Gautier 	} else {
251ad71d45eSYann Gautier 		VERBOSE("Wrong MMC type or spec version\n");
252ad71d45eSYann Gautier 	}
253ad71d45eSYann Gautier 
254ad71d45eSYann Gautier 	return ops->set_ios(clk, width);
255ad71d45eSYann Gautier }
256ad71d45eSYann Gautier 
257ad71d45eSYann Gautier static int mmc_fill_device_info(void)
258ad71d45eSYann Gautier {
259ad71d45eSYann Gautier 	unsigned long long c_size;
260ad71d45eSYann Gautier 	unsigned int speed_idx;
261ad71d45eSYann Gautier 	unsigned int nb_blocks;
262ad71d45eSYann Gautier 	unsigned int freq_unit;
263cadb36cbSAntonio Nino Diaz 	int ret = 0;
264ad71d45eSYann Gautier 	struct mmc_csd_sd_v2 *csd_sd_v2;
265ad71d45eSYann Gautier 
26670eb88b7SYann Gautier 	switch (mmc_dev_info->mmc_dev_type) {
267ad71d45eSYann Gautier 	case MMC_IS_EMMC:
26870eb88b7SYann Gautier 		mmc_dev_info->block_size = MMC_BLOCK_SIZE;
269ad71d45eSYann Gautier 
270ad71d45eSYann Gautier 		ret = ops->prepare(0, (uintptr_t)&mmc_ext_csd,
271ad71d45eSYann Gautier 				   sizeof(mmc_ext_csd));
272ad71d45eSYann Gautier 		if (ret != 0) {
273ad71d45eSYann Gautier 			return ret;
274ad71d45eSYann Gautier 		}
275ad71d45eSYann Gautier 
276ad71d45eSYann Gautier 		/* MMC CMD8: SEND_EXT_CSD */
27797d5db8cSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(8), 0, MMC_RESPONSE_R1, NULL);
278ad71d45eSYann Gautier 		if (ret != 0) {
279ad71d45eSYann Gautier 			return ret;
280ad71d45eSYann Gautier 		}
281ad71d45eSYann Gautier 
282ad71d45eSYann Gautier 		ret = ops->read(0, (uintptr_t)&mmc_ext_csd,
283ad71d45eSYann Gautier 				sizeof(mmc_ext_csd));
284ad71d45eSYann Gautier 		if (ret != 0) {
285ad71d45eSYann Gautier 			return ret;
286ad71d45eSYann Gautier 		}
287ad71d45eSYann Gautier 
28893768644SHaojian Zhuang 		do {
28993768644SHaojian Zhuang 			ret = mmc_device_state();
29093768644SHaojian Zhuang 			if (ret < 0) {
29193768644SHaojian Zhuang 				return ret;
29293768644SHaojian Zhuang 			}
29393768644SHaojian Zhuang 		} while (ret != MMC_STATE_TRAN);
29493768644SHaojian Zhuang 
295ad71d45eSYann Gautier 		nb_blocks = (mmc_ext_csd[CMD_EXTCSD_SEC_CNT] << 0) |
296ad71d45eSYann Gautier 			    (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 1] << 8) |
297ad71d45eSYann Gautier 			    (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 2] << 16) |
298ad71d45eSYann Gautier 			    (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 3] << 24);
299ad71d45eSYann Gautier 
30070eb88b7SYann Gautier 		mmc_dev_info->device_size = (unsigned long long)nb_blocks *
30170eb88b7SYann Gautier 			mmc_dev_info->block_size;
302ad71d45eSYann Gautier 
303ad71d45eSYann Gautier 		break;
304ad71d45eSYann Gautier 
305ad71d45eSYann Gautier 	case MMC_IS_SD:
306ad71d45eSYann Gautier 		/*
307ad71d45eSYann Gautier 		 * Use the same mmc_csd struct, as required fields here
308ad71d45eSYann Gautier 		 * (READ_BL_LEN, C_SIZE, CSIZE_MULT) are common with eMMC.
309ad71d45eSYann Gautier 		 */
31070eb88b7SYann Gautier 		mmc_dev_info->block_size = BIT_32(mmc_csd.read_bl_len);
311ad71d45eSYann Gautier 
312ad71d45eSYann Gautier 		c_size = ((unsigned long long)mmc_csd.c_size_high << 2U) |
313ad71d45eSYann Gautier 			 (unsigned long long)mmc_csd.c_size_low;
314ad71d45eSYann Gautier 		assert(c_size != 0xFFFU);
315ad71d45eSYann Gautier 
31670eb88b7SYann Gautier 		mmc_dev_info->device_size = (c_size + 1U) *
317ad71d45eSYann Gautier 					    BIT_64(mmc_csd.c_size_mult + 2U) *
31870eb88b7SYann Gautier 					    mmc_dev_info->block_size;
319ad71d45eSYann Gautier 
320ad71d45eSYann Gautier 		break;
321ad71d45eSYann Gautier 
322ad71d45eSYann Gautier 	case MMC_IS_SD_HC:
323ad71d45eSYann Gautier 		assert(mmc_csd.csd_structure == 1U);
324ad71d45eSYann Gautier 
32570eb88b7SYann Gautier 		mmc_dev_info->block_size = MMC_BLOCK_SIZE;
326ad71d45eSYann Gautier 
327ad71d45eSYann Gautier 		/* Need to use mmc_csd_sd_v2 struct */
328ad71d45eSYann Gautier 		csd_sd_v2 = (struct mmc_csd_sd_v2 *)&mmc_csd;
329ad71d45eSYann Gautier 		c_size = ((unsigned long long)csd_sd_v2->c_size_high << 16) |
330ad71d45eSYann Gautier 			 (unsigned long long)csd_sd_v2->c_size_low;
331ad71d45eSYann Gautier 
33270eb88b7SYann Gautier 		mmc_dev_info->device_size = (c_size + 1U) << MULT_BY_512K_SHIFT;
333ad71d45eSYann Gautier 
334ad71d45eSYann Gautier 		break;
335ad71d45eSYann Gautier 
336ad71d45eSYann Gautier 	default:
337ad71d45eSYann Gautier 		ret = -EINVAL;
338ad71d45eSYann Gautier 		break;
339ad71d45eSYann Gautier 	}
340ad71d45eSYann Gautier 
3415f9984efSYann Gautier 	if (ret < 0) {
342ad71d45eSYann Gautier 		return ret;
343ad71d45eSYann Gautier 	}
344ad71d45eSYann Gautier 
345ad71d45eSYann Gautier 	speed_idx = (mmc_csd.tran_speed & CSD_TRAN_SPEED_MULT_MASK) >>
346ad71d45eSYann Gautier 			 CSD_TRAN_SPEED_MULT_SHIFT;
347ad71d45eSYann Gautier 
348ad71d45eSYann Gautier 	assert(speed_idx > 0U);
349ad71d45eSYann Gautier 
35070eb88b7SYann Gautier 	if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) {
35170eb88b7SYann Gautier 		mmc_dev_info->max_bus_freq = tran_speed_base[speed_idx];
352ad71d45eSYann Gautier 	} else {
35370eb88b7SYann Gautier 		mmc_dev_info->max_bus_freq = sd_tran_speed_base[speed_idx];
354ad71d45eSYann Gautier 	}
355ad71d45eSYann Gautier 
356ad71d45eSYann Gautier 	freq_unit = mmc_csd.tran_speed & CSD_TRAN_SPEED_UNIT_MASK;
357ad71d45eSYann Gautier 	while (freq_unit != 0U) {
35870eb88b7SYann Gautier 		mmc_dev_info->max_bus_freq *= 10U;
359ad71d45eSYann Gautier 		--freq_unit;
360ad71d45eSYann Gautier 	}
361ad71d45eSYann Gautier 
36270eb88b7SYann Gautier 	mmc_dev_info->max_bus_freq *= 10000U;
363ad71d45eSYann Gautier 
364ad71d45eSYann Gautier 	return 0;
365ad71d45eSYann Gautier }
366ad71d45eSYann Gautier 
367e5b267bbSYann Gautier static int sd_switch(unsigned int mode, unsigned char group,
368e5b267bbSYann Gautier 		     unsigned char func)
369e5b267bbSYann Gautier {
370e5b267bbSYann Gautier 	unsigned int group_shift = (group - 1U) * 4U;
371e5b267bbSYann Gautier 	unsigned int group_mask = GENMASK(group_shift + 3U,  group_shift);
372e5b267bbSYann Gautier 	unsigned int arg;
373e5b267bbSYann Gautier 	int ret;
374e5b267bbSYann Gautier 
375e5b267bbSYann Gautier 	ret = ops->prepare(0, (uintptr_t)&sd_switch_func_status,
376e5b267bbSYann Gautier 			   sizeof(sd_switch_func_status));
377e5b267bbSYann Gautier 	if (ret != 0) {
378e5b267bbSYann Gautier 		return ret;
379e5b267bbSYann Gautier 	}
380e5b267bbSYann Gautier 
381e5b267bbSYann Gautier 	/* MMC CMD6: SWITCH_FUNC */
382e5b267bbSYann Gautier 	arg = mode | SD_SWITCH_ALL_GROUPS_MASK;
383e5b267bbSYann Gautier 	arg &= ~group_mask;
384e5b267bbSYann Gautier 	arg |= func << group_shift;
385e5b267bbSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(6), arg, MMC_RESPONSE_R1, NULL);
386e5b267bbSYann Gautier 	if (ret != 0) {
387e5b267bbSYann Gautier 		return ret;
388e5b267bbSYann Gautier 	}
389e5b267bbSYann Gautier 
390e5b267bbSYann Gautier 	return ops->read(0, (uintptr_t)&sd_switch_func_status,
391e5b267bbSYann Gautier 			 sizeof(sd_switch_func_status));
392e5b267bbSYann Gautier }
393e5b267bbSYann Gautier 
394ad71d45eSYann Gautier static int sd_send_op_cond(void)
395ad71d45eSYann Gautier {
39615e913d4SYann Gautier 	int n;
397ad71d45eSYann Gautier 	unsigned int resp_data[4];
398ad71d45eSYann Gautier 
39915e913d4SYann Gautier 	for (n = 0; n < SEND_OP_COND_MAX_RETRIES; n++) {
400ad71d45eSYann Gautier 		int ret;
401ad71d45eSYann Gautier 
402ad71d45eSYann Gautier 		/* CMD55: Application Specific Command */
40397d5db8cSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(55), 0, MMC_RESPONSE_R1, NULL);
404ad71d45eSYann Gautier 		if (ret != 0) {
405ad71d45eSYann Gautier 			return ret;
406ad71d45eSYann Gautier 		}
407ad71d45eSYann Gautier 
408ad71d45eSYann Gautier 		/* ACMD41: SD_SEND_OP_COND */
409a468e756STien Hock, Loh 		ret = mmc_send_cmd(MMC_ACMD(41), OCR_HCS |
41070eb88b7SYann Gautier 			mmc_dev_info->ocr_voltage, MMC_RESPONSE_R3,
411ad71d45eSYann Gautier 			&resp_data[0]);
412ad71d45eSYann Gautier 		if (ret != 0) {
413ad71d45eSYann Gautier 			return ret;
414ad71d45eSYann Gautier 		}
415ad71d45eSYann Gautier 
41615e913d4SYann Gautier 		if ((resp_data[0] & OCR_POWERUP) != 0U) {
417ad71d45eSYann Gautier 			mmc_ocr_value = resp_data[0];
418ad71d45eSYann Gautier 
419ad71d45eSYann Gautier 			if ((mmc_ocr_value & OCR_HCS) != 0U) {
42070eb88b7SYann Gautier 				mmc_dev_info->mmc_dev_type = MMC_IS_SD_HC;
421ad71d45eSYann Gautier 			} else {
42270eb88b7SYann Gautier 				mmc_dev_info->mmc_dev_type = MMC_IS_SD;
423ad71d45eSYann Gautier 			}
424ad71d45eSYann Gautier 
425ad71d45eSYann Gautier 			return 0;
426ad71d45eSYann Gautier 		}
427ad71d45eSYann Gautier 
42857f4b6f8SYann Gautier 		mdelay(10);
42915e913d4SYann Gautier 	}
43015e913d4SYann Gautier 
43115e913d4SYann Gautier 	ERROR("ACMD41 failed after %d retries\n", SEND_OP_COND_MAX_RETRIES);
43215e913d4SYann Gautier 
43315e913d4SYann Gautier 	return -EIO;
43415e913d4SYann Gautier }
43515e913d4SYann Gautier 
43615e913d4SYann Gautier static int mmc_reset_to_idle(void)
437ad71d45eSYann Gautier {
438ad71d45eSYann Gautier 	int ret;
43915e913d4SYann Gautier 
440ad71d45eSYann Gautier 	/* CMD0: reset to IDLE */
441ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(0), 0, 0, NULL);
442ad71d45eSYann Gautier 	if (ret != 0) {
443ad71d45eSYann Gautier 		return ret;
444ad71d45eSYann Gautier 	}
445ad71d45eSYann Gautier 
44615e913d4SYann Gautier 	mdelay(2);
44715e913d4SYann Gautier 
44815e913d4SYann Gautier 	return 0;
449ad71d45eSYann Gautier }
450ad71d45eSYann Gautier 
45115e913d4SYann Gautier static int mmc_send_op_cond(void)
45215e913d4SYann Gautier {
45315e913d4SYann Gautier 	int ret, n;
45415e913d4SYann Gautier 	unsigned int resp_data[4];
45515e913d4SYann Gautier 
45677614a99SYann Gautier 	ret = mmc_reset_to_idle();
45777614a99SYann Gautier 	if (ret != 0) {
45877614a99SYann Gautier 		return ret;
4594ecd2417SYann Gautier 	}
46015e913d4SYann Gautier 
46115e913d4SYann Gautier 	for (n = 0; n < SEND_OP_COND_MAX_RETRIES; n++) {
462ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(1), OCR_SECTOR_MODE |
463ad71d45eSYann Gautier 				   OCR_VDD_MIN_2V7 | OCR_VDD_MIN_1V7,
46494522ff7SBryan O'Donoghue 				   MMC_RESPONSE_R3, &resp_data[0]);
465ad71d45eSYann Gautier 		if (ret != 0) {
466ad71d45eSYann Gautier 			return ret;
467ad71d45eSYann Gautier 		}
468ad71d45eSYann Gautier 
46915e913d4SYann Gautier 		if ((resp_data[0] & OCR_POWERUP) != 0U) {
470ad71d45eSYann Gautier 			mmc_ocr_value = resp_data[0];
471ad71d45eSYann Gautier 			return 0;
472ad71d45eSYann Gautier 		}
473ad71d45eSYann Gautier 
4747d639429SJoakim Bech 		mdelay(10);
47515e913d4SYann Gautier 	}
47615e913d4SYann Gautier 
47715e913d4SYann Gautier 	ERROR("CMD1 failed after %d retries\n", SEND_OP_COND_MAX_RETRIES);
47815e913d4SYann Gautier 
47915e913d4SYann Gautier 	return -EIO;
48015e913d4SYann Gautier }
48115e913d4SYann Gautier 
482ad71d45eSYann Gautier static int mmc_enumerate(unsigned int clk, unsigned int bus_width)
483ad71d45eSYann Gautier {
484ad71d45eSYann Gautier 	int ret;
485ad71d45eSYann Gautier 	unsigned int resp_data[4];
486ad71d45eSYann Gautier 
487ad71d45eSYann Gautier 	ops->init();
488ad71d45eSYann Gautier 
48977614a99SYann Gautier 	ret = mmc_reset_to_idle();
49077614a99SYann Gautier 	if (ret != 0) {
49177614a99SYann Gautier 		return ret;
4924ecd2417SYann Gautier 	}
493ad71d45eSYann Gautier 
49470eb88b7SYann Gautier 	if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) {
495e74dc940SHaojian Zhuang 		ret = mmc_send_op_cond();
496e74dc940SHaojian Zhuang 	} else {
497ad71d45eSYann Gautier 		/* CMD8: Send Interface Condition Command */
498ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(8), VHS_2_7_3_6_V | CMD8_CHECK_PATTERN,
49997d5db8cSYann Gautier 				   MMC_RESPONSE_R5, &resp_data[0]);
500ad71d45eSYann Gautier 
501ad71d45eSYann Gautier 		if ((ret == 0) && ((resp_data[0] & 0xffU) == CMD8_CHECK_PATTERN)) {
502ad71d45eSYann Gautier 			ret = sd_send_op_cond();
503e74dc940SHaojian Zhuang 		}
504ad71d45eSYann Gautier 	}
505ad71d45eSYann Gautier 	if (ret != 0) {
506ad71d45eSYann Gautier 		return ret;
507ad71d45eSYann Gautier 	}
508ad71d45eSYann Gautier 
509ad71d45eSYann Gautier 	/* CMD2: Card Identification */
510a2a69bc8SShawn Guo 	ret = mmc_send_cmd(MMC_CMD(2), 0, MMC_RESPONSE_R2, NULL);
511ad71d45eSYann Gautier 	if (ret != 0) {
512ad71d45eSYann Gautier 		return ret;
513ad71d45eSYann Gautier 	}
514ad71d45eSYann Gautier 
515ad71d45eSYann Gautier 	/* CMD3: Set Relative Address */
51670eb88b7SYann Gautier 	if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) {
517ad71d45eSYann Gautier 		rca = MMC_FIX_RCA;
518ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(3), rca << RCA_SHIFT_OFFSET,
51997d5db8cSYann Gautier 				   MMC_RESPONSE_R1, NULL);
520ad71d45eSYann Gautier 		if (ret != 0) {
521ad71d45eSYann Gautier 			return ret;
522ad71d45eSYann Gautier 		}
523ad71d45eSYann Gautier 	} else {
524ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(3), 0,
52597d5db8cSYann Gautier 				   MMC_RESPONSE_R6, &resp_data[0]);
526ad71d45eSYann Gautier 		if (ret != 0) {
527ad71d45eSYann Gautier 			return ret;
528ad71d45eSYann Gautier 		}
529ad71d45eSYann Gautier 
530ad71d45eSYann Gautier 		rca = (resp_data[0] & 0xFFFF0000U) >> 16;
531ad71d45eSYann Gautier 	}
532ad71d45eSYann Gautier 
533ad71d45eSYann Gautier 	/* CMD9: CSD Register */
534ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(9), rca << RCA_SHIFT_OFFSET,
535a2a69bc8SShawn Guo 			   MMC_RESPONSE_R2, &resp_data[0]);
536ad71d45eSYann Gautier 	if (ret != 0) {
537ad71d45eSYann Gautier 		return ret;
538ad71d45eSYann Gautier 	}
539ad71d45eSYann Gautier 
540ad71d45eSYann Gautier 	memcpy(&mmc_csd, &resp_data, sizeof(resp_data));
541ad71d45eSYann Gautier 
542ad71d45eSYann Gautier 	/* CMD7: Select Card */
543ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(7), rca << RCA_SHIFT_OFFSET,
54497d5db8cSYann Gautier 			   MMC_RESPONSE_R1, NULL);
545ad71d45eSYann Gautier 	if (ret != 0) {
546ad71d45eSYann Gautier 		return ret;
547ad71d45eSYann Gautier 	}
548ad71d45eSYann Gautier 
549ad71d45eSYann Gautier 	do {
550ad71d45eSYann Gautier 		ret = mmc_device_state();
551ad71d45eSYann Gautier 		if (ret < 0) {
552ad71d45eSYann Gautier 			return ret;
553ad71d45eSYann Gautier 		}
554ad71d45eSYann Gautier 	} while (ret != MMC_STATE_TRAN);
555ad71d45eSYann Gautier 
556bd4e3deeSHaojian Zhuang 	ret = mmc_set_ios(clk, bus_width);
557ad71d45eSYann Gautier 	if (ret != 0) {
558ad71d45eSYann Gautier 		return ret;
559ad71d45eSYann Gautier 	}
560ad71d45eSYann Gautier 
561e5b267bbSYann Gautier 	ret = mmc_fill_device_info();
562e5b267bbSYann Gautier 	if (ret != 0) {
563e5b267bbSYann Gautier 		return ret;
564e5b267bbSYann Gautier 	}
565e5b267bbSYann Gautier 
566e5b267bbSYann Gautier 	if (is_sd_cmd6_enabled() &&
567e5b267bbSYann Gautier 	    (mmc_dev_info->mmc_dev_type == MMC_IS_SD_HC)) {
568e5b267bbSYann Gautier 		/* Try to switch to High Speed Mode */
569e5b267bbSYann Gautier 		ret = sd_switch(SD_SWITCH_FUNC_CHECK, 1U, 1U);
570e5b267bbSYann Gautier 		if (ret != 0) {
571e5b267bbSYann Gautier 			return ret;
572e5b267bbSYann Gautier 		}
573e5b267bbSYann Gautier 
574e5b267bbSYann Gautier 		if ((sd_switch_func_status.support_g1 & BIT(9)) == 0U) {
575e5b267bbSYann Gautier 			/* High speed not supported, keep default speed */
576e5b267bbSYann Gautier 			return 0;
577e5b267bbSYann Gautier 		}
578e5b267bbSYann Gautier 
579e5b267bbSYann Gautier 		ret = sd_switch(SD_SWITCH_FUNC_SWITCH, 1U, 1U);
580e5b267bbSYann Gautier 		if (ret != 0) {
581e5b267bbSYann Gautier 			return ret;
582e5b267bbSYann Gautier 		}
583e5b267bbSYann Gautier 
584e5b267bbSYann Gautier 		if ((sd_switch_func_status.sel_g2_g1 & 0x1U) == 0U) {
585e5b267bbSYann Gautier 			/* Cannot switch to high speed, keep default speed */
586e5b267bbSYann Gautier 			return 0;
587e5b267bbSYann Gautier 		}
588e5b267bbSYann Gautier 
589e5b267bbSYann Gautier 		mmc_dev_info->max_bus_freq = 50000000U;
590e5b267bbSYann Gautier 		ret = ops->set_ios(clk, bus_width);
591e5b267bbSYann Gautier 	}
592e5b267bbSYann Gautier 
593e5b267bbSYann Gautier 	return ret;
594ad71d45eSYann Gautier }
595ad71d45eSYann Gautier 
596ea315a69SHaojian Zhuang size_t mmc_read_blocks(int lba, uintptr_t buf, size_t size)
597ad71d45eSYann Gautier {
598ad71d45eSYann Gautier 	int ret;
599ad71d45eSYann Gautier 	unsigned int cmd_idx, cmd_arg;
600ad71d45eSYann Gautier 
601ad71d45eSYann Gautier 	assert((ops != NULL) &&
602ad71d45eSYann Gautier 	       (ops->read != NULL) &&
603ad71d45eSYann Gautier 	       (size != 0U) &&
604ad71d45eSYann Gautier 	       ((size & MMC_BLOCK_MASK) == 0U));
605ad71d45eSYann Gautier 
606ad71d45eSYann Gautier 	ret = ops->prepare(lba, buf, size);
607ad71d45eSYann Gautier 	if (ret != 0) {
608ad71d45eSYann Gautier 		return 0;
609ad71d45eSYann Gautier 	}
610ad71d45eSYann Gautier 
611ad71d45eSYann Gautier 	if (is_cmd23_enabled()) {
612ad71d45eSYann Gautier 		/* Set block count */
613ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE,
61497d5db8cSYann Gautier 				   MMC_RESPONSE_R1, NULL);
615ad71d45eSYann Gautier 		if (ret != 0) {
616ad71d45eSYann Gautier 			return 0;
617ad71d45eSYann Gautier 		}
618ad71d45eSYann Gautier 
619ad71d45eSYann Gautier 		cmd_idx = MMC_CMD(18);
620ad71d45eSYann Gautier 	} else {
621ad71d45eSYann Gautier 		if (size > MMC_BLOCK_SIZE) {
622ad71d45eSYann Gautier 			cmd_idx = MMC_CMD(18);
623ad71d45eSYann Gautier 		} else {
624ad71d45eSYann Gautier 			cmd_idx = MMC_CMD(17);
625ad71d45eSYann Gautier 		}
626ad71d45eSYann Gautier 	}
627ad71d45eSYann Gautier 
628ad71d45eSYann Gautier 	if (((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) &&
62970eb88b7SYann Gautier 	    (mmc_dev_info->mmc_dev_type != MMC_IS_SD_HC)) {
630ad71d45eSYann Gautier 		cmd_arg = lba * MMC_BLOCK_SIZE;
631ad71d45eSYann Gautier 	} else {
632ad71d45eSYann Gautier 		cmd_arg = lba;
633ad71d45eSYann Gautier 	}
634ad71d45eSYann Gautier 
63597d5db8cSYann Gautier 	ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R1, NULL);
636ad71d45eSYann Gautier 	if (ret != 0) {
637ad71d45eSYann Gautier 		return 0;
638ad71d45eSYann Gautier 	}
639ad71d45eSYann Gautier 
640ad71d45eSYann Gautier 	ret = ops->read(lba, buf, size);
641ad71d45eSYann Gautier 	if (ret != 0) {
642ad71d45eSYann Gautier 		return 0;
643ad71d45eSYann Gautier 	}
644ad71d45eSYann Gautier 
645ad71d45eSYann Gautier 	/* Wait buffer empty */
646ad71d45eSYann Gautier 	do {
647ad71d45eSYann Gautier 		ret = mmc_device_state();
648ad71d45eSYann Gautier 		if (ret < 0) {
649ad71d45eSYann Gautier 			return 0;
650ad71d45eSYann Gautier 		}
651ad71d45eSYann Gautier 	} while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_DATA));
652ad71d45eSYann Gautier 
653ad71d45eSYann Gautier 	if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) {
65461752898SBryan O'Donoghue 		ret = mmc_send_cmd(MMC_CMD(12), 0, MMC_RESPONSE_R1B, NULL);
655ad71d45eSYann Gautier 		if (ret != 0) {
656ad71d45eSYann Gautier 			return 0;
657ad71d45eSYann Gautier 		}
658ad71d45eSYann Gautier 	}
659ad71d45eSYann Gautier 
660ad71d45eSYann Gautier 	return size;
661ad71d45eSYann Gautier }
662ad71d45eSYann Gautier 
663ea315a69SHaojian Zhuang size_t mmc_write_blocks(int lba, const uintptr_t buf, size_t size)
664ad71d45eSYann Gautier {
665ad71d45eSYann Gautier 	int ret;
666ad71d45eSYann Gautier 	unsigned int cmd_idx, cmd_arg;
667ad71d45eSYann Gautier 
668ad71d45eSYann Gautier 	assert((ops != NULL) &&
669ad71d45eSYann Gautier 	       (ops->write != NULL) &&
670ad71d45eSYann Gautier 	       (size != 0U) &&
671ad71d45eSYann Gautier 	       ((buf & MMC_BLOCK_MASK) == 0U) &&
672ad71d45eSYann Gautier 	       ((size & MMC_BLOCK_MASK) == 0U));
673ad71d45eSYann Gautier 
674ad71d45eSYann Gautier 	ret = ops->prepare(lba, buf, size);
675ad71d45eSYann Gautier 	if (ret != 0) {
676ad71d45eSYann Gautier 		return 0;
677ad71d45eSYann Gautier 	}
678ad71d45eSYann Gautier 
679ad71d45eSYann Gautier 	if (is_cmd23_enabled()) {
680ad71d45eSYann Gautier 		/* Set block count */
681ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE,
68297d5db8cSYann Gautier 				   MMC_RESPONSE_R1, NULL);
683ad71d45eSYann Gautier 		if (ret != 0) {
684ad71d45eSYann Gautier 			return 0;
685ad71d45eSYann Gautier 		}
686ad71d45eSYann Gautier 
687ad71d45eSYann Gautier 		cmd_idx = MMC_CMD(25);
688ad71d45eSYann Gautier 	} else {
689ad71d45eSYann Gautier 		if (size > MMC_BLOCK_SIZE) {
690ad71d45eSYann Gautier 			cmd_idx = MMC_CMD(25);
691ad71d45eSYann Gautier 		} else {
692ad71d45eSYann Gautier 			cmd_idx = MMC_CMD(24);
693ad71d45eSYann Gautier 		}
694ad71d45eSYann Gautier 	}
695ad71d45eSYann Gautier 
696ad71d45eSYann Gautier 	if ((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) {
697ad71d45eSYann Gautier 		cmd_arg = lba * MMC_BLOCK_SIZE;
698ad71d45eSYann Gautier 	} else {
699ad71d45eSYann Gautier 		cmd_arg = lba;
700ad71d45eSYann Gautier 	}
701ad71d45eSYann Gautier 
70297d5db8cSYann Gautier 	ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R1, NULL);
703ad71d45eSYann Gautier 	if (ret != 0) {
704ad71d45eSYann Gautier 		return 0;
705ad71d45eSYann Gautier 	}
706ad71d45eSYann Gautier 
707ad71d45eSYann Gautier 	ret = ops->write(lba, buf, size);
708ad71d45eSYann Gautier 	if (ret != 0) {
709ad71d45eSYann Gautier 		return 0;
710ad71d45eSYann Gautier 	}
711ad71d45eSYann Gautier 
712ad71d45eSYann Gautier 	/* Wait buffer empty */
713ad71d45eSYann Gautier 	do {
714ad71d45eSYann Gautier 		ret = mmc_device_state();
715ad71d45eSYann Gautier 		if (ret < 0) {
716ad71d45eSYann Gautier 			return 0;
717ad71d45eSYann Gautier 		}
718ad71d45eSYann Gautier 	} while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_RCV));
719ad71d45eSYann Gautier 
720ad71d45eSYann Gautier 	if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) {
72161752898SBryan O'Donoghue 		ret = mmc_send_cmd(MMC_CMD(12), 0, MMC_RESPONSE_R1B, NULL);
722ad71d45eSYann Gautier 		if (ret != 0) {
723ad71d45eSYann Gautier 			return 0;
724ad71d45eSYann Gautier 		}
725ad71d45eSYann Gautier 	}
726ad71d45eSYann Gautier 
727ad71d45eSYann Gautier 	return size;
728ad71d45eSYann Gautier }
729ad71d45eSYann Gautier 
730ea315a69SHaojian Zhuang size_t mmc_erase_blocks(int lba, size_t size)
731ad71d45eSYann Gautier {
732ad71d45eSYann Gautier 	int ret;
733ad71d45eSYann Gautier 
734ad71d45eSYann Gautier 	assert(ops != NULL);
735ad71d45eSYann Gautier 	assert((size != 0U) && ((size & MMC_BLOCK_MASK) == 0U));
736ad71d45eSYann Gautier 
73797d5db8cSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(35), lba, MMC_RESPONSE_R1, NULL);
738ad71d45eSYann Gautier 	if (ret != 0) {
739ad71d45eSYann Gautier 		return 0;
740ad71d45eSYann Gautier 	}
741ad71d45eSYann Gautier 
742ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(36), lba + (size / MMC_BLOCK_SIZE) - 1U,
74397d5db8cSYann Gautier 			   MMC_RESPONSE_R1, NULL);
744ad71d45eSYann Gautier 	if (ret != 0) {
745ad71d45eSYann Gautier 		return 0;
746ad71d45eSYann Gautier 	}
747ad71d45eSYann Gautier 
74897d5db8cSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(38), lba, MMC_RESPONSE_R1B, NULL);
749ad71d45eSYann Gautier 	if (ret != 0) {
750ad71d45eSYann Gautier 		return 0;
751ad71d45eSYann Gautier 	}
752ad71d45eSYann Gautier 
753ad71d45eSYann Gautier 	do {
754ad71d45eSYann Gautier 		ret = mmc_device_state();
755ad71d45eSYann Gautier 		if (ret < 0) {
756ad71d45eSYann Gautier 			return 0;
757ad71d45eSYann Gautier 		}
758ad71d45eSYann Gautier 	} while (ret != MMC_STATE_TRAN);
759ad71d45eSYann Gautier 
760ad71d45eSYann Gautier 	return size;
761ad71d45eSYann Gautier }
762ad71d45eSYann Gautier 
7635014b52dSVyacheslav Yurkov static int mmc_part_switch(unsigned int part_type)
7645014b52dSVyacheslav Yurkov {
7655014b52dSVyacheslav Yurkov 	uint8_t part_config = mmc_ext_csd[CMD_EXTCSD_PARTITION_CONFIG];
7665014b52dSVyacheslav Yurkov 
7675014b52dSVyacheslav Yurkov 	part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
7685014b52dSVyacheslav Yurkov 	part_config |= part_type;
7695014b52dSVyacheslav Yurkov 
7705014b52dSVyacheslav Yurkov 	return mmc_send_part_switch_cmd(part_config);
7715014b52dSVyacheslav Yurkov }
7725014b52dSVyacheslav Yurkov 
7735014b52dSVyacheslav Yurkov static unsigned char mmc_current_boot_part(void)
7745014b52dSVyacheslav Yurkov {
7755014b52dSVyacheslav Yurkov 	return PART_CFG_CURRENT_BOOT_PARTITION(mmc_ext_csd[CMD_EXTCSD_PARTITION_CONFIG]);
7765014b52dSVyacheslav Yurkov }
7775014b52dSVyacheslav Yurkov 
778f85041a6SAhmad Fatoum int mmc_part_switch_current_boot(void)
7795014b52dSVyacheslav Yurkov {
7805014b52dSVyacheslav Yurkov 	unsigned char current_boot_part = mmc_current_boot_part();
781f85041a6SAhmad Fatoum 	int ret;
7825014b52dSVyacheslav Yurkov 
783*14cda516SYann Gautier 	if ((current_boot_part != 1U) && (current_boot_part != 2U)) {
7845014b52dSVyacheslav Yurkov 		ERROR("Got unexpected value for active boot partition, %u\n", current_boot_part);
785f85041a6SAhmad Fatoum 		return -EIO;
7865014b52dSVyacheslav Yurkov 	}
7875014b52dSVyacheslav Yurkov 
7885014b52dSVyacheslav Yurkov 	ret = mmc_part_switch(current_boot_part);
7895014b52dSVyacheslav Yurkov 	if (ret < 0) {
7905014b52dSVyacheslav Yurkov 		ERROR("Failed to switch to boot partition, %d\n", ret);
791f85041a6SAhmad Fatoum 	}
792f85041a6SAhmad Fatoum 
793f85041a6SAhmad Fatoum 	return ret;
794f85041a6SAhmad Fatoum }
795f85041a6SAhmad Fatoum 
796f85041a6SAhmad Fatoum int mmc_part_switch_user(void)
797f85041a6SAhmad Fatoum {
798f85041a6SAhmad Fatoum 	int ret;
799f85041a6SAhmad Fatoum 
80001c5dd5eSAhmad Fatoum 	ret = mmc_part_switch(PART_CFG_BOOT_PARTITION_NO_ACCESS);
801f85041a6SAhmad Fatoum 	if (ret < 0) {
802f85041a6SAhmad Fatoum 		ERROR("Failed to switch to user partition, %d\n", ret);
803f85041a6SAhmad Fatoum 	}
804f85041a6SAhmad Fatoum 
805f85041a6SAhmad Fatoum 	return ret;
806f85041a6SAhmad Fatoum }
807f85041a6SAhmad Fatoum 
808f462c124SYann Gautier size_t mmc_boot_part_size(void)
809f462c124SYann Gautier {
810f462c124SYann Gautier 	return mmc_ext_csd[CMD_EXTCSD_BOOT_SIZE_MULT] * SZ_128K;
811f462c124SYann Gautier }
812f462c124SYann Gautier 
813f85041a6SAhmad Fatoum size_t mmc_boot_part_read_blocks(int lba, uintptr_t buf, size_t size)
814f85041a6SAhmad Fatoum {
815f85041a6SAhmad Fatoum 	size_t size_read;
816f85041a6SAhmad Fatoum 	int ret;
817f85041a6SAhmad Fatoum 
818f85041a6SAhmad Fatoum 	ret = mmc_part_switch_current_boot();
819f85041a6SAhmad Fatoum 	if (ret < 0) {
8205014b52dSVyacheslav Yurkov 		return 0;
8215014b52dSVyacheslav Yurkov 	}
8225014b52dSVyacheslav Yurkov 
8235014b52dSVyacheslav Yurkov 	size_read = mmc_read_blocks(lba, buf, size);
8245014b52dSVyacheslav Yurkov 
825f85041a6SAhmad Fatoum 	ret = mmc_part_switch_user();
8265014b52dSVyacheslav Yurkov 	if (ret < 0) {
8275014b52dSVyacheslav Yurkov 		return 0;
8285014b52dSVyacheslav Yurkov 	}
8295014b52dSVyacheslav Yurkov 
8305014b52dSVyacheslav Yurkov 	return size_read;
8315014b52dSVyacheslav Yurkov }
8325014b52dSVyacheslav Yurkov 
833ad71d45eSYann Gautier int mmc_init(const struct mmc_ops *ops_ptr, unsigned int clk,
834ad71d45eSYann Gautier 	     unsigned int width, unsigned int flags,
835ad71d45eSYann Gautier 	     struct mmc_device_info *device_info)
836ad71d45eSYann Gautier {
837ad71d45eSYann Gautier 	assert((ops_ptr != NULL) &&
838ad71d45eSYann Gautier 	       (ops_ptr->init != NULL) &&
839ad71d45eSYann Gautier 	       (ops_ptr->send_cmd != NULL) &&
840ad71d45eSYann Gautier 	       (ops_ptr->set_ios != NULL) &&
841ad71d45eSYann Gautier 	       (ops_ptr->prepare != NULL) &&
842ad71d45eSYann Gautier 	       (ops_ptr->read != NULL) &&
843ad71d45eSYann Gautier 	       (ops_ptr->write != NULL) &&
844ad71d45eSYann Gautier 	       (device_info != NULL) &&
845ad71d45eSYann Gautier 	       (clk != 0) &&
846ad71d45eSYann Gautier 	       ((width == MMC_BUS_WIDTH_1) ||
847ad71d45eSYann Gautier 		(width == MMC_BUS_WIDTH_4) ||
848ad71d45eSYann Gautier 		(width == MMC_BUS_WIDTH_8) ||
849ad71d45eSYann Gautier 		(width == MMC_BUS_WIDTH_DDR_4) ||
850ad71d45eSYann Gautier 		(width == MMC_BUS_WIDTH_DDR_8)));
851ad71d45eSYann Gautier 
852ad71d45eSYann Gautier 	ops = ops_ptr;
853ad71d45eSYann Gautier 	mmc_flags = flags;
85470eb88b7SYann Gautier 	mmc_dev_info = device_info;
855ad71d45eSYann Gautier 
856ad71d45eSYann Gautier 	return mmc_enumerate(clk, width);
857ad71d45eSYann Gautier }
858