xref: /aosp_15_r20/external/arm-trusted-firmware/drivers/mmc/mmc.c (revision 54fd6939e177f8ff529b10183254802c76df6d08)
1*54fd6939SJiyong Park /*
2*54fd6939SJiyong Park  * Copyright (c) 2018-2021, ARM Limited and Contributors. All rights reserved.
3*54fd6939SJiyong Park  *
4*54fd6939SJiyong Park  * SPDX-License-Identifier: BSD-3-Clause
5*54fd6939SJiyong Park  */
6*54fd6939SJiyong Park 
7*54fd6939SJiyong Park /* Define a simple and generic interface to access eMMC and SD-card devices. */
8*54fd6939SJiyong Park 
9*54fd6939SJiyong Park #include <assert.h>
10*54fd6939SJiyong Park #include <errno.h>
11*54fd6939SJiyong Park #include <stdbool.h>
12*54fd6939SJiyong Park #include <string.h>
13*54fd6939SJiyong Park 
14*54fd6939SJiyong Park #include <arch_helpers.h>
15*54fd6939SJiyong Park #include <common/debug.h>
16*54fd6939SJiyong Park #include <drivers/delay_timer.h>
17*54fd6939SJiyong Park #include <drivers/mmc.h>
18*54fd6939SJiyong Park #include <lib/utils.h>
19*54fd6939SJiyong Park 
20*54fd6939SJiyong Park #define MMC_DEFAULT_MAX_RETRIES		5
21*54fd6939SJiyong Park #define SEND_OP_COND_MAX_RETRIES	100
22*54fd6939SJiyong Park 
23*54fd6939SJiyong Park #define MULT_BY_512K_SHIFT		19
24*54fd6939SJiyong Park 
25*54fd6939SJiyong Park static const struct mmc_ops *ops;
26*54fd6939SJiyong Park static unsigned int mmc_ocr_value;
27*54fd6939SJiyong Park static struct mmc_csd_emmc mmc_csd;
28*54fd6939SJiyong Park static unsigned char mmc_ext_csd[512] __aligned(16);
29*54fd6939SJiyong Park static unsigned int mmc_flags;
30*54fd6939SJiyong Park static struct mmc_device_info *mmc_dev_info;
31*54fd6939SJiyong Park static unsigned int rca;
32*54fd6939SJiyong Park static unsigned int scr[2]__aligned(16) = { 0 };
33*54fd6939SJiyong Park 
34*54fd6939SJiyong Park static const unsigned char tran_speed_base[16] = {
35*54fd6939SJiyong Park 	0, 10, 12, 13, 15, 20, 26, 30, 35, 40, 45, 52, 55, 60, 70, 80
36*54fd6939SJiyong Park };
37*54fd6939SJiyong Park 
38*54fd6939SJiyong Park static const unsigned char sd_tran_speed_base[16] = {
39*54fd6939SJiyong Park 	0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80
40*54fd6939SJiyong Park };
41*54fd6939SJiyong Park 
is_cmd23_enabled(void)42*54fd6939SJiyong Park static bool is_cmd23_enabled(void)
43*54fd6939SJiyong Park {
44*54fd6939SJiyong Park 	return ((mmc_flags & MMC_FLAG_CMD23) != 0U);
45*54fd6939SJiyong Park }
46*54fd6939SJiyong Park 
mmc_send_cmd(unsigned int idx,unsigned int arg,unsigned int r_type,unsigned int * r_data)47*54fd6939SJiyong Park static int mmc_send_cmd(unsigned int idx, unsigned int arg,
48*54fd6939SJiyong Park 			unsigned int r_type, unsigned int *r_data)
49*54fd6939SJiyong Park {
50*54fd6939SJiyong Park 	struct mmc_cmd cmd;
51*54fd6939SJiyong Park 	int ret;
52*54fd6939SJiyong Park 
53*54fd6939SJiyong Park 	zeromem(&cmd, sizeof(struct mmc_cmd));
54*54fd6939SJiyong Park 
55*54fd6939SJiyong Park 	cmd.cmd_idx = idx;
56*54fd6939SJiyong Park 	cmd.cmd_arg = arg;
57*54fd6939SJiyong Park 	cmd.resp_type = r_type;
58*54fd6939SJiyong Park 
59*54fd6939SJiyong Park 	ret = ops->send_cmd(&cmd);
60*54fd6939SJiyong Park 
61*54fd6939SJiyong Park 	if ((ret == 0) && (r_data != NULL)) {
62*54fd6939SJiyong Park 		int i;
63*54fd6939SJiyong Park 
64*54fd6939SJiyong Park 		for (i = 0; i < 4; i++) {
65*54fd6939SJiyong Park 			*r_data = cmd.resp_data[i];
66*54fd6939SJiyong Park 			r_data++;
67*54fd6939SJiyong Park 		}
68*54fd6939SJiyong Park 	}
69*54fd6939SJiyong Park 
70*54fd6939SJiyong Park 	if (ret != 0) {
71*54fd6939SJiyong Park 		VERBOSE("Send command %u error: %d\n", idx, ret);
72*54fd6939SJiyong Park 	}
73*54fd6939SJiyong Park 
74*54fd6939SJiyong Park 	return ret;
75*54fd6939SJiyong Park }
76*54fd6939SJiyong Park 
mmc_device_state(void)77*54fd6939SJiyong Park static int mmc_device_state(void)
78*54fd6939SJiyong Park {
79*54fd6939SJiyong Park 	int retries = MMC_DEFAULT_MAX_RETRIES;
80*54fd6939SJiyong Park 	unsigned int resp_data[4];
81*54fd6939SJiyong Park 
82*54fd6939SJiyong Park 	do {
83*54fd6939SJiyong Park 		int ret;
84*54fd6939SJiyong Park 
85*54fd6939SJiyong Park 		if (retries == 0) {
86*54fd6939SJiyong Park 			ERROR("CMD13 failed after %d retries\n",
87*54fd6939SJiyong Park 			      MMC_DEFAULT_MAX_RETRIES);
88*54fd6939SJiyong Park 			return -EIO;
89*54fd6939SJiyong Park 		}
90*54fd6939SJiyong Park 
91*54fd6939SJiyong Park 		ret = mmc_send_cmd(MMC_CMD(13), rca << RCA_SHIFT_OFFSET,
92*54fd6939SJiyong Park 				   MMC_RESPONSE_R1, &resp_data[0]);
93*54fd6939SJiyong Park 		if (ret != 0) {
94*54fd6939SJiyong Park 			retries--;
95*54fd6939SJiyong Park 			continue;
96*54fd6939SJiyong Park 		}
97*54fd6939SJiyong Park 
98*54fd6939SJiyong Park 		if ((resp_data[0] & STATUS_SWITCH_ERROR) != 0U) {
99*54fd6939SJiyong Park 			return -EIO;
100*54fd6939SJiyong Park 		}
101*54fd6939SJiyong Park 
102*54fd6939SJiyong Park 		retries--;
103*54fd6939SJiyong Park 	} while ((resp_data[0] & STATUS_READY_FOR_DATA) == 0U);
104*54fd6939SJiyong Park 
105*54fd6939SJiyong Park 	return MMC_GET_STATE(resp_data[0]);
106*54fd6939SJiyong Park }
107*54fd6939SJiyong Park 
mmc_send_part_switch_cmd(unsigned int part_config)108*54fd6939SJiyong Park static int mmc_send_part_switch_cmd(unsigned int part_config)
109*54fd6939SJiyong Park {
110*54fd6939SJiyong Park 	int ret;
111*54fd6939SJiyong Park 	unsigned int part_time = 0;
112*54fd6939SJiyong Park 
113*54fd6939SJiyong Park 	ret = mmc_send_cmd(MMC_CMD(6),
114*54fd6939SJiyong Park 			   EXTCSD_WRITE_BYTES |
115*54fd6939SJiyong Park 			   EXTCSD_CMD(CMD_EXTCSD_PARTITION_CONFIG) |
116*54fd6939SJiyong Park 			   EXTCSD_VALUE(part_config) |
117*54fd6939SJiyong Park 			   EXTCSD_CMD_SET_NORMAL,
118*54fd6939SJiyong Park 			   MMC_RESPONSE_R1B, NULL);
119*54fd6939SJiyong Park 	if (ret != 0) {
120*54fd6939SJiyong Park 		return ret;
121*54fd6939SJiyong Park 	}
122*54fd6939SJiyong Park 
123*54fd6939SJiyong Park 	/* Partition switch timing is in 10ms units */
124*54fd6939SJiyong Park 	part_time = mmc_ext_csd[CMD_EXTCSD_PART_SWITCH_TIME] * 10;
125*54fd6939SJiyong Park 
126*54fd6939SJiyong Park 	mdelay(part_time);
127*54fd6939SJiyong Park 
128*54fd6939SJiyong Park 	do {
129*54fd6939SJiyong Park 		ret = mmc_device_state();
130*54fd6939SJiyong Park 		if (ret < 0) {
131*54fd6939SJiyong Park 			return ret;
132*54fd6939SJiyong Park 		}
133*54fd6939SJiyong Park 	} while (ret == MMC_STATE_PRG);
134*54fd6939SJiyong Park 
135*54fd6939SJiyong Park 	return 0;
136*54fd6939SJiyong Park }
137*54fd6939SJiyong Park 
mmc_set_ext_csd(unsigned int ext_cmd,unsigned int value)138*54fd6939SJiyong Park static int mmc_set_ext_csd(unsigned int ext_cmd, unsigned int value)
139*54fd6939SJiyong Park {
140*54fd6939SJiyong Park 	int ret;
141*54fd6939SJiyong Park 
142*54fd6939SJiyong Park 	ret = mmc_send_cmd(MMC_CMD(6),
143*54fd6939SJiyong Park 			   EXTCSD_WRITE_BYTES | EXTCSD_CMD(ext_cmd) |
144*54fd6939SJiyong Park 			   EXTCSD_VALUE(value) | EXTCSD_CMD_SET_NORMAL,
145*54fd6939SJiyong Park 			   MMC_RESPONSE_R1B, NULL);
146*54fd6939SJiyong Park 	if (ret != 0) {
147*54fd6939SJiyong Park 		return ret;
148*54fd6939SJiyong Park 	}
149*54fd6939SJiyong Park 
150*54fd6939SJiyong Park 	do {
151*54fd6939SJiyong Park 		ret = mmc_device_state();
152*54fd6939SJiyong Park 		if (ret < 0) {
153*54fd6939SJiyong Park 			return ret;
154*54fd6939SJiyong Park 		}
155*54fd6939SJiyong Park 	} while (ret == MMC_STATE_PRG);
156*54fd6939SJiyong Park 
157*54fd6939SJiyong Park 	return 0;
158*54fd6939SJiyong Park }
159*54fd6939SJiyong Park 
mmc_sd_switch(unsigned int bus_width)160*54fd6939SJiyong Park static int mmc_sd_switch(unsigned int bus_width)
161*54fd6939SJiyong Park {
162*54fd6939SJiyong Park 	int ret;
163*54fd6939SJiyong Park 	int retries = MMC_DEFAULT_MAX_RETRIES;
164*54fd6939SJiyong Park 	unsigned int bus_width_arg = 0;
165*54fd6939SJiyong Park 
166*54fd6939SJiyong Park 	ret = ops->prepare(0, (uintptr_t)&scr, sizeof(scr));
167*54fd6939SJiyong Park 	if (ret != 0) {
168*54fd6939SJiyong Park 		return ret;
169*54fd6939SJiyong Park 	}
170*54fd6939SJiyong Park 
171*54fd6939SJiyong Park 	/* CMD55: Application Specific Command */
172*54fd6939SJiyong Park 	ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET,
173*54fd6939SJiyong Park 			   MMC_RESPONSE_R5, NULL);
174*54fd6939SJiyong Park 	if (ret != 0) {
175*54fd6939SJiyong Park 		return ret;
176*54fd6939SJiyong Park 	}
177*54fd6939SJiyong Park 
178*54fd6939SJiyong Park 	/* ACMD51: SEND_SCR */
179*54fd6939SJiyong Park 	do {
180*54fd6939SJiyong Park 		ret = mmc_send_cmd(MMC_ACMD(51), 0, MMC_RESPONSE_R1, NULL);
181*54fd6939SJiyong Park 		if ((ret != 0) && (retries == 0)) {
182*54fd6939SJiyong Park 			ERROR("ACMD51 failed after %d retries (ret=%d)\n",
183*54fd6939SJiyong Park 			      MMC_DEFAULT_MAX_RETRIES, ret);
184*54fd6939SJiyong Park 			return ret;
185*54fd6939SJiyong Park 		}
186*54fd6939SJiyong Park 
187*54fd6939SJiyong Park 		retries--;
188*54fd6939SJiyong Park 	} while (ret != 0);
189*54fd6939SJiyong Park 
190*54fd6939SJiyong Park 	ret = ops->read(0, (uintptr_t)&scr, sizeof(scr));
191*54fd6939SJiyong Park 	if (ret != 0) {
192*54fd6939SJiyong Park 		return ret;
193*54fd6939SJiyong Park 	}
194*54fd6939SJiyong Park 
195*54fd6939SJiyong Park 	if (((scr[0] & SD_SCR_BUS_WIDTH_4) != 0U) &&
196*54fd6939SJiyong Park 	    (bus_width == MMC_BUS_WIDTH_4)) {
197*54fd6939SJiyong Park 		bus_width_arg = 2;
198*54fd6939SJiyong Park 	}
199*54fd6939SJiyong Park 
200*54fd6939SJiyong Park 	/* CMD55: Application Specific Command */
201*54fd6939SJiyong Park 	ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET,
202*54fd6939SJiyong Park 			   MMC_RESPONSE_R5, NULL);
203*54fd6939SJiyong Park 	if (ret != 0) {
204*54fd6939SJiyong Park 		return ret;
205*54fd6939SJiyong Park 	}
206*54fd6939SJiyong Park 
207*54fd6939SJiyong Park 	/* ACMD6: SET_BUS_WIDTH */
208*54fd6939SJiyong Park 	ret = mmc_send_cmd(MMC_ACMD(6), bus_width_arg, MMC_RESPONSE_R1, NULL);
209*54fd6939SJiyong Park 	if (ret != 0) {
210*54fd6939SJiyong Park 		return ret;
211*54fd6939SJiyong Park 	}
212*54fd6939SJiyong Park 
213*54fd6939SJiyong Park 	do {
214*54fd6939SJiyong Park 		ret = mmc_device_state();
215*54fd6939SJiyong Park 		if (ret < 0) {
216*54fd6939SJiyong Park 			return ret;
217*54fd6939SJiyong Park 		}
218*54fd6939SJiyong Park 	} while (ret == MMC_STATE_PRG);
219*54fd6939SJiyong Park 
220*54fd6939SJiyong Park 	return 0;
221*54fd6939SJiyong Park }
222*54fd6939SJiyong Park 
mmc_set_ios(unsigned int clk,unsigned int bus_width)223*54fd6939SJiyong Park static int mmc_set_ios(unsigned int clk, unsigned int bus_width)
224*54fd6939SJiyong Park {
225*54fd6939SJiyong Park 	int ret;
226*54fd6939SJiyong Park 	unsigned int width = bus_width;
227*54fd6939SJiyong Park 
228*54fd6939SJiyong Park 	if (mmc_dev_info->mmc_dev_type != MMC_IS_EMMC) {
229*54fd6939SJiyong Park 		if (width == MMC_BUS_WIDTH_8) {
230*54fd6939SJiyong Park 			WARN("Wrong bus config for SD-card, force to 4\n");
231*54fd6939SJiyong Park 			width = MMC_BUS_WIDTH_4;
232*54fd6939SJiyong Park 		}
233*54fd6939SJiyong Park 		ret = mmc_sd_switch(width);
234*54fd6939SJiyong Park 		if (ret != 0) {
235*54fd6939SJiyong Park 			return ret;
236*54fd6939SJiyong Park 		}
237*54fd6939SJiyong Park 	} else if (mmc_csd.spec_vers == 4U) {
238*54fd6939SJiyong Park 		ret = mmc_set_ext_csd(CMD_EXTCSD_BUS_WIDTH,
239*54fd6939SJiyong Park 				      (unsigned int)width);
240*54fd6939SJiyong Park 		if (ret != 0) {
241*54fd6939SJiyong Park 			return ret;
242*54fd6939SJiyong Park 		}
243*54fd6939SJiyong Park 	} else {
244*54fd6939SJiyong Park 		VERBOSE("Wrong MMC type or spec version\n");
245*54fd6939SJiyong Park 	}
246*54fd6939SJiyong Park 
247*54fd6939SJiyong Park 	return ops->set_ios(clk, width);
248*54fd6939SJiyong Park }
249*54fd6939SJiyong Park 
mmc_fill_device_info(void)250*54fd6939SJiyong Park static int mmc_fill_device_info(void)
251*54fd6939SJiyong Park {
252*54fd6939SJiyong Park 	unsigned long long c_size;
253*54fd6939SJiyong Park 	unsigned int speed_idx;
254*54fd6939SJiyong Park 	unsigned int nb_blocks;
255*54fd6939SJiyong Park 	unsigned int freq_unit;
256*54fd6939SJiyong Park 	int ret = 0;
257*54fd6939SJiyong Park 	struct mmc_csd_sd_v2 *csd_sd_v2;
258*54fd6939SJiyong Park 
259*54fd6939SJiyong Park 	switch (mmc_dev_info->mmc_dev_type) {
260*54fd6939SJiyong Park 	case MMC_IS_EMMC:
261*54fd6939SJiyong Park 		mmc_dev_info->block_size = MMC_BLOCK_SIZE;
262*54fd6939SJiyong Park 
263*54fd6939SJiyong Park 		ret = ops->prepare(0, (uintptr_t)&mmc_ext_csd,
264*54fd6939SJiyong Park 				   sizeof(mmc_ext_csd));
265*54fd6939SJiyong Park 		if (ret != 0) {
266*54fd6939SJiyong Park 			return ret;
267*54fd6939SJiyong Park 		}
268*54fd6939SJiyong Park 
269*54fd6939SJiyong Park 		/* MMC CMD8: SEND_EXT_CSD */
270*54fd6939SJiyong Park 		ret = mmc_send_cmd(MMC_CMD(8), 0, MMC_RESPONSE_R1, NULL);
271*54fd6939SJiyong Park 		if (ret != 0) {
272*54fd6939SJiyong Park 			return ret;
273*54fd6939SJiyong Park 		}
274*54fd6939SJiyong Park 
275*54fd6939SJiyong Park 		ret = ops->read(0, (uintptr_t)&mmc_ext_csd,
276*54fd6939SJiyong Park 				sizeof(mmc_ext_csd));
277*54fd6939SJiyong Park 		if (ret != 0) {
278*54fd6939SJiyong Park 			return ret;
279*54fd6939SJiyong Park 		}
280*54fd6939SJiyong Park 
281*54fd6939SJiyong Park 		do {
282*54fd6939SJiyong Park 			ret = mmc_device_state();
283*54fd6939SJiyong Park 			if (ret < 0) {
284*54fd6939SJiyong Park 				return ret;
285*54fd6939SJiyong Park 			}
286*54fd6939SJiyong Park 		} while (ret != MMC_STATE_TRAN);
287*54fd6939SJiyong Park 
288*54fd6939SJiyong Park 		nb_blocks = (mmc_ext_csd[CMD_EXTCSD_SEC_CNT] << 0) |
289*54fd6939SJiyong Park 			    (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 1] << 8) |
290*54fd6939SJiyong Park 			    (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 2] << 16) |
291*54fd6939SJiyong Park 			    (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 3] << 24);
292*54fd6939SJiyong Park 
293*54fd6939SJiyong Park 		mmc_dev_info->device_size = (unsigned long long)nb_blocks *
294*54fd6939SJiyong Park 			mmc_dev_info->block_size;
295*54fd6939SJiyong Park 
296*54fd6939SJiyong Park 		break;
297*54fd6939SJiyong Park 
298*54fd6939SJiyong Park 	case MMC_IS_SD:
299*54fd6939SJiyong Park 		/*
300*54fd6939SJiyong Park 		 * Use the same mmc_csd struct, as required fields here
301*54fd6939SJiyong Park 		 * (READ_BL_LEN, C_SIZE, CSIZE_MULT) are common with eMMC.
302*54fd6939SJiyong Park 		 */
303*54fd6939SJiyong Park 		mmc_dev_info->block_size = BIT_32(mmc_csd.read_bl_len);
304*54fd6939SJiyong Park 
305*54fd6939SJiyong Park 		c_size = ((unsigned long long)mmc_csd.c_size_high << 2U) |
306*54fd6939SJiyong Park 			 (unsigned long long)mmc_csd.c_size_low;
307*54fd6939SJiyong Park 		assert(c_size != 0xFFFU);
308*54fd6939SJiyong Park 
309*54fd6939SJiyong Park 		mmc_dev_info->device_size = (c_size + 1U) *
310*54fd6939SJiyong Park 					    BIT_64(mmc_csd.c_size_mult + 2U) *
311*54fd6939SJiyong Park 					    mmc_dev_info->block_size;
312*54fd6939SJiyong Park 
313*54fd6939SJiyong Park 		break;
314*54fd6939SJiyong Park 
315*54fd6939SJiyong Park 	case MMC_IS_SD_HC:
316*54fd6939SJiyong Park 		assert(mmc_csd.csd_structure == 1U);
317*54fd6939SJiyong Park 
318*54fd6939SJiyong Park 		mmc_dev_info->block_size = MMC_BLOCK_SIZE;
319*54fd6939SJiyong Park 
320*54fd6939SJiyong Park 		/* Need to use mmc_csd_sd_v2 struct */
321*54fd6939SJiyong Park 		csd_sd_v2 = (struct mmc_csd_sd_v2 *)&mmc_csd;
322*54fd6939SJiyong Park 		c_size = ((unsigned long long)csd_sd_v2->c_size_high << 16) |
323*54fd6939SJiyong Park 			 (unsigned long long)csd_sd_v2->c_size_low;
324*54fd6939SJiyong Park 
325*54fd6939SJiyong Park 		mmc_dev_info->device_size = (c_size + 1U) << MULT_BY_512K_SHIFT;
326*54fd6939SJiyong Park 
327*54fd6939SJiyong Park 		break;
328*54fd6939SJiyong Park 
329*54fd6939SJiyong Park 	default:
330*54fd6939SJiyong Park 		ret = -EINVAL;
331*54fd6939SJiyong Park 		break;
332*54fd6939SJiyong Park 	}
333*54fd6939SJiyong Park 
334*54fd6939SJiyong Park 	if (ret < 0) {
335*54fd6939SJiyong Park 		return ret;
336*54fd6939SJiyong Park 	}
337*54fd6939SJiyong Park 
338*54fd6939SJiyong Park 	speed_idx = (mmc_csd.tran_speed & CSD_TRAN_SPEED_MULT_MASK) >>
339*54fd6939SJiyong Park 			 CSD_TRAN_SPEED_MULT_SHIFT;
340*54fd6939SJiyong Park 
341*54fd6939SJiyong Park 	assert(speed_idx > 0U);
342*54fd6939SJiyong Park 
343*54fd6939SJiyong Park 	if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) {
344*54fd6939SJiyong Park 		mmc_dev_info->max_bus_freq = tran_speed_base[speed_idx];
345*54fd6939SJiyong Park 	} else {
346*54fd6939SJiyong Park 		mmc_dev_info->max_bus_freq = sd_tran_speed_base[speed_idx];
347*54fd6939SJiyong Park 	}
348*54fd6939SJiyong Park 
349*54fd6939SJiyong Park 	freq_unit = mmc_csd.tran_speed & CSD_TRAN_SPEED_UNIT_MASK;
350*54fd6939SJiyong Park 	while (freq_unit != 0U) {
351*54fd6939SJiyong Park 		mmc_dev_info->max_bus_freq *= 10U;
352*54fd6939SJiyong Park 		--freq_unit;
353*54fd6939SJiyong Park 	}
354*54fd6939SJiyong Park 
355*54fd6939SJiyong Park 	mmc_dev_info->max_bus_freq *= 10000U;
356*54fd6939SJiyong Park 
357*54fd6939SJiyong Park 	return 0;
358*54fd6939SJiyong Park }
359*54fd6939SJiyong Park 
sd_send_op_cond(void)360*54fd6939SJiyong Park static int sd_send_op_cond(void)
361*54fd6939SJiyong Park {
362*54fd6939SJiyong Park 	int n;
363*54fd6939SJiyong Park 	unsigned int resp_data[4];
364*54fd6939SJiyong Park 
365*54fd6939SJiyong Park 	for (n = 0; n < SEND_OP_COND_MAX_RETRIES; n++) {
366*54fd6939SJiyong Park 		int ret;
367*54fd6939SJiyong Park 
368*54fd6939SJiyong Park 		/* CMD55: Application Specific Command */
369*54fd6939SJiyong Park 		ret = mmc_send_cmd(MMC_CMD(55), 0, MMC_RESPONSE_R1, NULL);
370*54fd6939SJiyong Park 		if (ret != 0) {
371*54fd6939SJiyong Park 			return ret;
372*54fd6939SJiyong Park 		}
373*54fd6939SJiyong Park 
374*54fd6939SJiyong Park 		/* ACMD41: SD_SEND_OP_COND */
375*54fd6939SJiyong Park 		ret = mmc_send_cmd(MMC_ACMD(41), OCR_HCS |
376*54fd6939SJiyong Park 			mmc_dev_info->ocr_voltage, MMC_RESPONSE_R3,
377*54fd6939SJiyong Park 			&resp_data[0]);
378*54fd6939SJiyong Park 		if (ret != 0) {
379*54fd6939SJiyong Park 			return ret;
380*54fd6939SJiyong Park 		}
381*54fd6939SJiyong Park 
382*54fd6939SJiyong Park 		if ((resp_data[0] & OCR_POWERUP) != 0U) {
383*54fd6939SJiyong Park 			mmc_ocr_value = resp_data[0];
384*54fd6939SJiyong Park 
385*54fd6939SJiyong Park 			if ((mmc_ocr_value & OCR_HCS) != 0U) {
386*54fd6939SJiyong Park 				mmc_dev_info->mmc_dev_type = MMC_IS_SD_HC;
387*54fd6939SJiyong Park 			} else {
388*54fd6939SJiyong Park 				mmc_dev_info->mmc_dev_type = MMC_IS_SD;
389*54fd6939SJiyong Park 			}
390*54fd6939SJiyong Park 
391*54fd6939SJiyong Park 			return 0;
392*54fd6939SJiyong Park 		}
393*54fd6939SJiyong Park 
394*54fd6939SJiyong Park 		mdelay(10);
395*54fd6939SJiyong Park 	}
396*54fd6939SJiyong Park 
397*54fd6939SJiyong Park 	ERROR("ACMD41 failed after %d retries\n", SEND_OP_COND_MAX_RETRIES);
398*54fd6939SJiyong Park 
399*54fd6939SJiyong Park 	return -EIO;
400*54fd6939SJiyong Park }
401*54fd6939SJiyong Park 
mmc_reset_to_idle(void)402*54fd6939SJiyong Park static int mmc_reset_to_idle(void)
403*54fd6939SJiyong Park {
404*54fd6939SJiyong Park 	int ret;
405*54fd6939SJiyong Park 
406*54fd6939SJiyong Park 	/* CMD0: reset to IDLE */
407*54fd6939SJiyong Park 	ret = mmc_send_cmd(MMC_CMD(0), 0, 0, NULL);
408*54fd6939SJiyong Park 	if (ret != 0) {
409*54fd6939SJiyong Park 		return ret;
410*54fd6939SJiyong Park 	}
411*54fd6939SJiyong Park 
412*54fd6939SJiyong Park 	mdelay(2);
413*54fd6939SJiyong Park 
414*54fd6939SJiyong Park 	return 0;
415*54fd6939SJiyong Park }
416*54fd6939SJiyong Park 
mmc_send_op_cond(void)417*54fd6939SJiyong Park static int mmc_send_op_cond(void)
418*54fd6939SJiyong Park {
419*54fd6939SJiyong Park 	int ret, n;
420*54fd6939SJiyong Park 	unsigned int resp_data[4];
421*54fd6939SJiyong Park 
422*54fd6939SJiyong Park 	ret = mmc_reset_to_idle();
423*54fd6939SJiyong Park 	if (ret != 0) {
424*54fd6939SJiyong Park 		return ret;
425*54fd6939SJiyong Park 	}
426*54fd6939SJiyong Park 
427*54fd6939SJiyong Park 	for (n = 0; n < SEND_OP_COND_MAX_RETRIES; n++) {
428*54fd6939SJiyong Park 		ret = mmc_send_cmd(MMC_CMD(1), OCR_SECTOR_MODE |
429*54fd6939SJiyong Park 				   OCR_VDD_MIN_2V7 | OCR_VDD_MIN_1V7,
430*54fd6939SJiyong Park 				   MMC_RESPONSE_R3, &resp_data[0]);
431*54fd6939SJiyong Park 		if (ret != 0) {
432*54fd6939SJiyong Park 			return ret;
433*54fd6939SJiyong Park 		}
434*54fd6939SJiyong Park 
435*54fd6939SJiyong Park 		if ((resp_data[0] & OCR_POWERUP) != 0U) {
436*54fd6939SJiyong Park 			mmc_ocr_value = resp_data[0];
437*54fd6939SJiyong Park 			return 0;
438*54fd6939SJiyong Park 		}
439*54fd6939SJiyong Park 
440*54fd6939SJiyong Park 		mdelay(10);
441*54fd6939SJiyong Park 	}
442*54fd6939SJiyong Park 
443*54fd6939SJiyong Park 	ERROR("CMD1 failed after %d retries\n", SEND_OP_COND_MAX_RETRIES);
444*54fd6939SJiyong Park 
445*54fd6939SJiyong Park 	return -EIO;
446*54fd6939SJiyong Park }
447*54fd6939SJiyong Park 
mmc_enumerate(unsigned int clk,unsigned int bus_width)448*54fd6939SJiyong Park static int mmc_enumerate(unsigned int clk, unsigned int bus_width)
449*54fd6939SJiyong Park {
450*54fd6939SJiyong Park 	int ret;
451*54fd6939SJiyong Park 	unsigned int resp_data[4];
452*54fd6939SJiyong Park 
453*54fd6939SJiyong Park 	ops->init();
454*54fd6939SJiyong Park 
455*54fd6939SJiyong Park 	ret = mmc_reset_to_idle();
456*54fd6939SJiyong Park 	if (ret != 0) {
457*54fd6939SJiyong Park 		return ret;
458*54fd6939SJiyong Park 	}
459*54fd6939SJiyong Park 
460*54fd6939SJiyong Park 	if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) {
461*54fd6939SJiyong Park 		ret = mmc_send_op_cond();
462*54fd6939SJiyong Park 	} else {
463*54fd6939SJiyong Park 		/* CMD8: Send Interface Condition Command */
464*54fd6939SJiyong Park 		ret = mmc_send_cmd(MMC_CMD(8), VHS_2_7_3_6_V | CMD8_CHECK_PATTERN,
465*54fd6939SJiyong Park 				   MMC_RESPONSE_R5, &resp_data[0]);
466*54fd6939SJiyong Park 
467*54fd6939SJiyong Park 		if ((ret == 0) && ((resp_data[0] & 0xffU) == CMD8_CHECK_PATTERN)) {
468*54fd6939SJiyong Park 			ret = sd_send_op_cond();
469*54fd6939SJiyong Park 		}
470*54fd6939SJiyong Park 	}
471*54fd6939SJiyong Park 	if (ret != 0) {
472*54fd6939SJiyong Park 		return ret;
473*54fd6939SJiyong Park 	}
474*54fd6939SJiyong Park 
475*54fd6939SJiyong Park 	/* CMD2: Card Identification */
476*54fd6939SJiyong Park 	ret = mmc_send_cmd(MMC_CMD(2), 0, MMC_RESPONSE_R2, NULL);
477*54fd6939SJiyong Park 	if (ret != 0) {
478*54fd6939SJiyong Park 		return ret;
479*54fd6939SJiyong Park 	}
480*54fd6939SJiyong Park 
481*54fd6939SJiyong Park 	/* CMD3: Set Relative Address */
482*54fd6939SJiyong Park 	if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) {
483*54fd6939SJiyong Park 		rca = MMC_FIX_RCA;
484*54fd6939SJiyong Park 		ret = mmc_send_cmd(MMC_CMD(3), rca << RCA_SHIFT_OFFSET,
485*54fd6939SJiyong Park 				   MMC_RESPONSE_R1, NULL);
486*54fd6939SJiyong Park 		if (ret != 0) {
487*54fd6939SJiyong Park 			return ret;
488*54fd6939SJiyong Park 		}
489*54fd6939SJiyong Park 	} else {
490*54fd6939SJiyong Park 		ret = mmc_send_cmd(MMC_CMD(3), 0,
491*54fd6939SJiyong Park 				   MMC_RESPONSE_R6, &resp_data[0]);
492*54fd6939SJiyong Park 		if (ret != 0) {
493*54fd6939SJiyong Park 			return ret;
494*54fd6939SJiyong Park 		}
495*54fd6939SJiyong Park 
496*54fd6939SJiyong Park 		rca = (resp_data[0] & 0xFFFF0000U) >> 16;
497*54fd6939SJiyong Park 	}
498*54fd6939SJiyong Park 
499*54fd6939SJiyong Park 	/* CMD9: CSD Register */
500*54fd6939SJiyong Park 	ret = mmc_send_cmd(MMC_CMD(9), rca << RCA_SHIFT_OFFSET,
501*54fd6939SJiyong Park 			   MMC_RESPONSE_R2, &resp_data[0]);
502*54fd6939SJiyong Park 	if (ret != 0) {
503*54fd6939SJiyong Park 		return ret;
504*54fd6939SJiyong Park 	}
505*54fd6939SJiyong Park 
506*54fd6939SJiyong Park 	memcpy(&mmc_csd, &resp_data, sizeof(resp_data));
507*54fd6939SJiyong Park 
508*54fd6939SJiyong Park 	/* CMD7: Select Card */
509*54fd6939SJiyong Park 	ret = mmc_send_cmd(MMC_CMD(7), rca << RCA_SHIFT_OFFSET,
510*54fd6939SJiyong Park 			   MMC_RESPONSE_R1, NULL);
511*54fd6939SJiyong Park 	if (ret != 0) {
512*54fd6939SJiyong Park 		return ret;
513*54fd6939SJiyong Park 	}
514*54fd6939SJiyong Park 
515*54fd6939SJiyong Park 	do {
516*54fd6939SJiyong Park 		ret = mmc_device_state();
517*54fd6939SJiyong Park 		if (ret < 0) {
518*54fd6939SJiyong Park 			return ret;
519*54fd6939SJiyong Park 		}
520*54fd6939SJiyong Park 	} while (ret != MMC_STATE_TRAN);
521*54fd6939SJiyong Park 
522*54fd6939SJiyong Park 	ret = mmc_set_ios(clk, bus_width);
523*54fd6939SJiyong Park 	if (ret != 0) {
524*54fd6939SJiyong Park 		return ret;
525*54fd6939SJiyong Park 	}
526*54fd6939SJiyong Park 
527*54fd6939SJiyong Park 	return mmc_fill_device_info();
528*54fd6939SJiyong Park }
529*54fd6939SJiyong Park 
mmc_read_blocks(int lba,uintptr_t buf,size_t size)530*54fd6939SJiyong Park size_t mmc_read_blocks(int lba, uintptr_t buf, size_t size)
531*54fd6939SJiyong Park {
532*54fd6939SJiyong Park 	int ret;
533*54fd6939SJiyong Park 	unsigned int cmd_idx, cmd_arg;
534*54fd6939SJiyong Park 
535*54fd6939SJiyong Park 	assert((ops != NULL) &&
536*54fd6939SJiyong Park 	       (ops->read != NULL) &&
537*54fd6939SJiyong Park 	       (size != 0U) &&
538*54fd6939SJiyong Park 	       ((size & MMC_BLOCK_MASK) == 0U));
539*54fd6939SJiyong Park 
540*54fd6939SJiyong Park 	ret = ops->prepare(lba, buf, size);
541*54fd6939SJiyong Park 	if (ret != 0) {
542*54fd6939SJiyong Park 		return 0;
543*54fd6939SJiyong Park 	}
544*54fd6939SJiyong Park 
545*54fd6939SJiyong Park 	if (is_cmd23_enabled()) {
546*54fd6939SJiyong Park 		/* Set block count */
547*54fd6939SJiyong Park 		ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE,
548*54fd6939SJiyong Park 				   MMC_RESPONSE_R1, NULL);
549*54fd6939SJiyong Park 		if (ret != 0) {
550*54fd6939SJiyong Park 			return 0;
551*54fd6939SJiyong Park 		}
552*54fd6939SJiyong Park 
553*54fd6939SJiyong Park 		cmd_idx = MMC_CMD(18);
554*54fd6939SJiyong Park 	} else {
555*54fd6939SJiyong Park 		if (size > MMC_BLOCK_SIZE) {
556*54fd6939SJiyong Park 			cmd_idx = MMC_CMD(18);
557*54fd6939SJiyong Park 		} else {
558*54fd6939SJiyong Park 			cmd_idx = MMC_CMD(17);
559*54fd6939SJiyong Park 		}
560*54fd6939SJiyong Park 	}
561*54fd6939SJiyong Park 
562*54fd6939SJiyong Park 	if (((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) &&
563*54fd6939SJiyong Park 	    (mmc_dev_info->mmc_dev_type != MMC_IS_SD_HC)) {
564*54fd6939SJiyong Park 		cmd_arg = lba * MMC_BLOCK_SIZE;
565*54fd6939SJiyong Park 	} else {
566*54fd6939SJiyong Park 		cmd_arg = lba;
567*54fd6939SJiyong Park 	}
568*54fd6939SJiyong Park 
569*54fd6939SJiyong Park 	ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R1, NULL);
570*54fd6939SJiyong Park 	if (ret != 0) {
571*54fd6939SJiyong Park 		return 0;
572*54fd6939SJiyong Park 	}
573*54fd6939SJiyong Park 
574*54fd6939SJiyong Park 	ret = ops->read(lba, buf, size);
575*54fd6939SJiyong Park 	if (ret != 0) {
576*54fd6939SJiyong Park 		return 0;
577*54fd6939SJiyong Park 	}
578*54fd6939SJiyong Park 
579*54fd6939SJiyong Park 	/* Wait buffer empty */
580*54fd6939SJiyong Park 	do {
581*54fd6939SJiyong Park 		ret = mmc_device_state();
582*54fd6939SJiyong Park 		if (ret < 0) {
583*54fd6939SJiyong Park 			return 0;
584*54fd6939SJiyong Park 		}
585*54fd6939SJiyong Park 	} while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_DATA));
586*54fd6939SJiyong Park 
587*54fd6939SJiyong Park 	if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) {
588*54fd6939SJiyong Park 		ret = mmc_send_cmd(MMC_CMD(12), 0, MMC_RESPONSE_R1B, NULL);
589*54fd6939SJiyong Park 		if (ret != 0) {
590*54fd6939SJiyong Park 			return 0;
591*54fd6939SJiyong Park 		}
592*54fd6939SJiyong Park 	}
593*54fd6939SJiyong Park 
594*54fd6939SJiyong Park 	return size;
595*54fd6939SJiyong Park }
596*54fd6939SJiyong Park 
mmc_write_blocks(int lba,const uintptr_t buf,size_t size)597*54fd6939SJiyong Park size_t mmc_write_blocks(int lba, const uintptr_t buf, size_t size)
598*54fd6939SJiyong Park {
599*54fd6939SJiyong Park 	int ret;
600*54fd6939SJiyong Park 	unsigned int cmd_idx, cmd_arg;
601*54fd6939SJiyong Park 
602*54fd6939SJiyong Park 	assert((ops != NULL) &&
603*54fd6939SJiyong Park 	       (ops->write != NULL) &&
604*54fd6939SJiyong Park 	       (size != 0U) &&
605*54fd6939SJiyong Park 	       ((buf & MMC_BLOCK_MASK) == 0U) &&
606*54fd6939SJiyong Park 	       ((size & MMC_BLOCK_MASK) == 0U));
607*54fd6939SJiyong Park 
608*54fd6939SJiyong Park 	ret = ops->prepare(lba, buf, size);
609*54fd6939SJiyong Park 	if (ret != 0) {
610*54fd6939SJiyong Park 		return 0;
611*54fd6939SJiyong Park 	}
612*54fd6939SJiyong Park 
613*54fd6939SJiyong Park 	if (is_cmd23_enabled()) {
614*54fd6939SJiyong Park 		/* Set block count */
615*54fd6939SJiyong Park 		ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE,
616*54fd6939SJiyong Park 				   MMC_RESPONSE_R1, NULL);
617*54fd6939SJiyong Park 		if (ret != 0) {
618*54fd6939SJiyong Park 			return 0;
619*54fd6939SJiyong Park 		}
620*54fd6939SJiyong Park 
621*54fd6939SJiyong Park 		cmd_idx = MMC_CMD(25);
622*54fd6939SJiyong Park 	} else {
623*54fd6939SJiyong Park 		if (size > MMC_BLOCK_SIZE) {
624*54fd6939SJiyong Park 			cmd_idx = MMC_CMD(25);
625*54fd6939SJiyong Park 		} else {
626*54fd6939SJiyong Park 			cmd_idx = MMC_CMD(24);
627*54fd6939SJiyong Park 		}
628*54fd6939SJiyong Park 	}
629*54fd6939SJiyong Park 
630*54fd6939SJiyong Park 	if ((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) {
631*54fd6939SJiyong Park 		cmd_arg = lba * MMC_BLOCK_SIZE;
632*54fd6939SJiyong Park 	} else {
633*54fd6939SJiyong Park 		cmd_arg = lba;
634*54fd6939SJiyong Park 	}
635*54fd6939SJiyong Park 
636*54fd6939SJiyong Park 	ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R1, NULL);
637*54fd6939SJiyong Park 	if (ret != 0) {
638*54fd6939SJiyong Park 		return 0;
639*54fd6939SJiyong Park 	}
640*54fd6939SJiyong Park 
641*54fd6939SJiyong Park 	ret = ops->write(lba, buf, size);
642*54fd6939SJiyong Park 	if (ret != 0) {
643*54fd6939SJiyong Park 		return 0;
644*54fd6939SJiyong Park 	}
645*54fd6939SJiyong Park 
646*54fd6939SJiyong Park 	/* Wait buffer empty */
647*54fd6939SJiyong Park 	do {
648*54fd6939SJiyong Park 		ret = mmc_device_state();
649*54fd6939SJiyong Park 		if (ret < 0) {
650*54fd6939SJiyong Park 			return 0;
651*54fd6939SJiyong Park 		}
652*54fd6939SJiyong Park 	} while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_RCV));
653*54fd6939SJiyong Park 
654*54fd6939SJiyong Park 	if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) {
655*54fd6939SJiyong Park 		ret = mmc_send_cmd(MMC_CMD(12), 0, MMC_RESPONSE_R1B, NULL);
656*54fd6939SJiyong Park 		if (ret != 0) {
657*54fd6939SJiyong Park 			return 0;
658*54fd6939SJiyong Park 		}
659*54fd6939SJiyong Park 	}
660*54fd6939SJiyong Park 
661*54fd6939SJiyong Park 	return size;
662*54fd6939SJiyong Park }
663*54fd6939SJiyong Park 
mmc_erase_blocks(int lba,size_t size)664*54fd6939SJiyong Park size_t mmc_erase_blocks(int lba, size_t size)
665*54fd6939SJiyong Park {
666*54fd6939SJiyong Park 	int ret;
667*54fd6939SJiyong Park 
668*54fd6939SJiyong Park 	assert(ops != NULL);
669*54fd6939SJiyong Park 	assert((size != 0U) && ((size & MMC_BLOCK_MASK) == 0U));
670*54fd6939SJiyong Park 
671*54fd6939SJiyong Park 	ret = mmc_send_cmd(MMC_CMD(35), lba, MMC_RESPONSE_R1, NULL);
672*54fd6939SJiyong Park 	if (ret != 0) {
673*54fd6939SJiyong Park 		return 0;
674*54fd6939SJiyong Park 	}
675*54fd6939SJiyong Park 
676*54fd6939SJiyong Park 	ret = mmc_send_cmd(MMC_CMD(36), lba + (size / MMC_BLOCK_SIZE) - 1U,
677*54fd6939SJiyong Park 			   MMC_RESPONSE_R1, NULL);
678*54fd6939SJiyong Park 	if (ret != 0) {
679*54fd6939SJiyong Park 		return 0;
680*54fd6939SJiyong Park 	}
681*54fd6939SJiyong Park 
682*54fd6939SJiyong Park 	ret = mmc_send_cmd(MMC_CMD(38), lba, MMC_RESPONSE_R1B, NULL);
683*54fd6939SJiyong Park 	if (ret != 0) {
684*54fd6939SJiyong Park 		return 0;
685*54fd6939SJiyong Park 	}
686*54fd6939SJiyong Park 
687*54fd6939SJiyong Park 	do {
688*54fd6939SJiyong Park 		ret = mmc_device_state();
689*54fd6939SJiyong Park 		if (ret < 0) {
690*54fd6939SJiyong Park 			return 0;
691*54fd6939SJiyong Park 		}
692*54fd6939SJiyong Park 	} while (ret != MMC_STATE_TRAN);
693*54fd6939SJiyong Park 
694*54fd6939SJiyong Park 	return size;
695*54fd6939SJiyong Park }
696*54fd6939SJiyong Park 
mmc_rpmb_enable(void)697*54fd6939SJiyong Park static inline void mmc_rpmb_enable(void)
698*54fd6939SJiyong Park {
699*54fd6939SJiyong Park 	mmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG,
700*54fd6939SJiyong Park 			PART_CFG_BOOT_PARTITION1_ENABLE |
701*54fd6939SJiyong Park 			PART_CFG_BOOT_PARTITION1_ACCESS);
702*54fd6939SJiyong Park }
703*54fd6939SJiyong Park 
mmc_rpmb_disable(void)704*54fd6939SJiyong Park static inline void mmc_rpmb_disable(void)
705*54fd6939SJiyong Park {
706*54fd6939SJiyong Park 	mmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG,
707*54fd6939SJiyong Park 			PART_CFG_BOOT_PARTITION1_ENABLE);
708*54fd6939SJiyong Park }
709*54fd6939SJiyong Park 
mmc_rpmb_read_blocks(int lba,uintptr_t buf,size_t size)710*54fd6939SJiyong Park size_t mmc_rpmb_read_blocks(int lba, uintptr_t buf, size_t size)
711*54fd6939SJiyong Park {
712*54fd6939SJiyong Park 	size_t size_read;
713*54fd6939SJiyong Park 
714*54fd6939SJiyong Park 	mmc_rpmb_enable();
715*54fd6939SJiyong Park 	size_read = mmc_read_blocks(lba, buf, size);
716*54fd6939SJiyong Park 	mmc_rpmb_disable();
717*54fd6939SJiyong Park 
718*54fd6939SJiyong Park 	return size_read;
719*54fd6939SJiyong Park }
720*54fd6939SJiyong Park 
mmc_rpmb_write_blocks(int lba,const uintptr_t buf,size_t size)721*54fd6939SJiyong Park size_t mmc_rpmb_write_blocks(int lba, const uintptr_t buf, size_t size)
722*54fd6939SJiyong Park {
723*54fd6939SJiyong Park 	size_t size_written;
724*54fd6939SJiyong Park 
725*54fd6939SJiyong Park 	mmc_rpmb_enable();
726*54fd6939SJiyong Park 	size_written = mmc_write_blocks(lba, buf, size);
727*54fd6939SJiyong Park 	mmc_rpmb_disable();
728*54fd6939SJiyong Park 
729*54fd6939SJiyong Park 	return size_written;
730*54fd6939SJiyong Park }
731*54fd6939SJiyong Park 
mmc_rpmb_erase_blocks(int lba,size_t size)732*54fd6939SJiyong Park size_t mmc_rpmb_erase_blocks(int lba, size_t size)
733*54fd6939SJiyong Park {
734*54fd6939SJiyong Park 	size_t size_erased;
735*54fd6939SJiyong Park 
736*54fd6939SJiyong Park 	mmc_rpmb_enable();
737*54fd6939SJiyong Park 	size_erased = mmc_erase_blocks(lba, size);
738*54fd6939SJiyong Park 	mmc_rpmb_disable();
739*54fd6939SJiyong Park 
740*54fd6939SJiyong Park 	return size_erased;
741*54fd6939SJiyong Park }
742*54fd6939SJiyong Park 
mmc_part_switch(unsigned int part_type)743*54fd6939SJiyong Park static int mmc_part_switch(unsigned int part_type)
744*54fd6939SJiyong Park {
745*54fd6939SJiyong Park 	uint8_t part_config = mmc_ext_csd[CMD_EXTCSD_PARTITION_CONFIG];
746*54fd6939SJiyong Park 
747*54fd6939SJiyong Park 	part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
748*54fd6939SJiyong Park 	part_config |= part_type;
749*54fd6939SJiyong Park 
750*54fd6939SJiyong Park 	return mmc_send_part_switch_cmd(part_config);
751*54fd6939SJiyong Park }
752*54fd6939SJiyong Park 
mmc_current_boot_part(void)753*54fd6939SJiyong Park static unsigned char mmc_current_boot_part(void)
754*54fd6939SJiyong Park {
755*54fd6939SJiyong Park 	return PART_CFG_CURRENT_BOOT_PARTITION(mmc_ext_csd[CMD_EXTCSD_PARTITION_CONFIG]);
756*54fd6939SJiyong Park }
757*54fd6939SJiyong Park 
mmc_boot_part_read_blocks(int lba,uintptr_t buf,size_t size)758*54fd6939SJiyong Park size_t mmc_boot_part_read_blocks(int lba, uintptr_t buf, size_t size)
759*54fd6939SJiyong Park {
760*54fd6939SJiyong Park 	size_t size_read;
761*54fd6939SJiyong Park 	int ret;
762*54fd6939SJiyong Park 	unsigned char current_boot_part = mmc_current_boot_part();
763*54fd6939SJiyong Park 
764*54fd6939SJiyong Park 	if (current_boot_part != 1U &&
765*54fd6939SJiyong Park 	    current_boot_part != 2U) {
766*54fd6939SJiyong Park 		ERROR("Got unexpected value for active boot partition, %u\n", current_boot_part);
767*54fd6939SJiyong Park 		return 0;
768*54fd6939SJiyong Park 	}
769*54fd6939SJiyong Park 
770*54fd6939SJiyong Park 	ret = mmc_part_switch(current_boot_part);
771*54fd6939SJiyong Park 	if (ret < 0) {
772*54fd6939SJiyong Park 		ERROR("Failed to switch to boot partition, %d\n", ret);
773*54fd6939SJiyong Park 		return 0;
774*54fd6939SJiyong Park 	}
775*54fd6939SJiyong Park 
776*54fd6939SJiyong Park 	size_read = mmc_read_blocks(lba, buf, size);
777*54fd6939SJiyong Park 
778*54fd6939SJiyong Park 	ret = mmc_part_switch(0);
779*54fd6939SJiyong Park 	if (ret < 0) {
780*54fd6939SJiyong Park 		ERROR("Failed to switch back to user partition, %d\n", ret);
781*54fd6939SJiyong Park 		return 0;
782*54fd6939SJiyong Park 	}
783*54fd6939SJiyong Park 
784*54fd6939SJiyong Park 	return size_read;
785*54fd6939SJiyong Park }
786*54fd6939SJiyong Park 
mmc_init(const struct mmc_ops * ops_ptr,unsigned int clk,unsigned int width,unsigned int flags,struct mmc_device_info * device_info)787*54fd6939SJiyong Park int mmc_init(const struct mmc_ops *ops_ptr, unsigned int clk,
788*54fd6939SJiyong Park 	     unsigned int width, unsigned int flags,
789*54fd6939SJiyong Park 	     struct mmc_device_info *device_info)
790*54fd6939SJiyong Park {
791*54fd6939SJiyong Park 	assert((ops_ptr != NULL) &&
792*54fd6939SJiyong Park 	       (ops_ptr->init != NULL) &&
793*54fd6939SJiyong Park 	       (ops_ptr->send_cmd != NULL) &&
794*54fd6939SJiyong Park 	       (ops_ptr->set_ios != NULL) &&
795*54fd6939SJiyong Park 	       (ops_ptr->prepare != NULL) &&
796*54fd6939SJiyong Park 	       (ops_ptr->read != NULL) &&
797*54fd6939SJiyong Park 	       (ops_ptr->write != NULL) &&
798*54fd6939SJiyong Park 	       (device_info != NULL) &&
799*54fd6939SJiyong Park 	       (clk != 0) &&
800*54fd6939SJiyong Park 	       ((width == MMC_BUS_WIDTH_1) ||
801*54fd6939SJiyong Park 		(width == MMC_BUS_WIDTH_4) ||
802*54fd6939SJiyong Park 		(width == MMC_BUS_WIDTH_8) ||
803*54fd6939SJiyong Park 		(width == MMC_BUS_WIDTH_DDR_4) ||
804*54fd6939SJiyong Park 		(width == MMC_BUS_WIDTH_DDR_8)));
805*54fd6939SJiyong Park 
806*54fd6939SJiyong Park 	ops = ops_ptr;
807*54fd6939SJiyong Park 	mmc_flags = flags;
808*54fd6939SJiyong Park 	mmc_dev_info = device_info;
809*54fd6939SJiyong Park 
810*54fd6939SJiyong Park 	return mmc_enumerate(clk, width);
811*54fd6939SJiyong Park }
812