xref: /rk3399_ARM-atf/drivers/mmc/mmc.c (revision 7d6394297dca05165f52232e74c31da85c1c816c)
1ad71d45eSYann Gautier /*
2ad71d45eSYann Gautier  * Copyright (c) 2018, 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 <arch_helpers.h>
10ad71d45eSYann Gautier #include <assert.h>
11ad71d45eSYann Gautier #include <debug.h>
1215e913d4SYann Gautier #include <delay_timer.h>
13ad71d45eSYann Gautier #include <errno.h>
14ad71d45eSYann Gautier #include <mmc.h>
15ad71d45eSYann Gautier #include <stdbool.h>
16ad71d45eSYann Gautier #include <string.h>
17ad71d45eSYann Gautier #include <utils.h>
18ad71d45eSYann Gautier 
19ad71d45eSYann Gautier #define MMC_DEFAULT_MAX_RETRIES		5
20ad71d45eSYann Gautier #define SEND_OP_COND_MAX_RETRIES	100
21ad71d45eSYann Gautier 
22ad71d45eSYann Gautier #define MULT_BY_512K_SHIFT		19
23ad71d45eSYann Gautier 
24ad71d45eSYann Gautier static const struct mmc_ops *ops;
25ad71d45eSYann Gautier static unsigned int mmc_ocr_value;
26ad71d45eSYann Gautier static struct mmc_csd_emmc mmc_csd;
2707858dd8SHaojian Zhuang static unsigned char mmc_ext_csd[512] __aligned(16);
28ad71d45eSYann Gautier static unsigned int mmc_flags;
29ad71d45eSYann Gautier static struct mmc_device_info *mmc_dev_info;
30ad71d45eSYann Gautier static unsigned int rca;
31ad71d45eSYann Gautier 
32ad71d45eSYann Gautier static const unsigned char tran_speed_base[16] = {
33ad71d45eSYann Gautier 	0, 10, 12, 13, 15, 20, 26, 30, 35, 40, 45, 52, 55, 60, 70, 80
34ad71d45eSYann Gautier };
35ad71d45eSYann Gautier 
36ad71d45eSYann Gautier static const unsigned char sd_tran_speed_base[16] = {
37ad71d45eSYann Gautier 	0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80
38ad71d45eSYann Gautier };
39ad71d45eSYann Gautier 
40ad71d45eSYann Gautier static bool is_cmd23_enabled(void)
41ad71d45eSYann Gautier {
42ad71d45eSYann Gautier 	return ((mmc_flags & MMC_FLAG_CMD23) != 0U);
43ad71d45eSYann Gautier }
44ad71d45eSYann Gautier 
45ad71d45eSYann Gautier static int mmc_send_cmd(unsigned int idx, unsigned int arg,
46ad71d45eSYann Gautier 			unsigned int r_type, unsigned int *r_data)
47ad71d45eSYann Gautier {
48ad71d45eSYann Gautier 	struct mmc_cmd cmd;
49ad71d45eSYann Gautier 	int ret;
50ad71d45eSYann Gautier 
51ad71d45eSYann Gautier 	zeromem(&cmd, sizeof(struct mmc_cmd));
52ad71d45eSYann Gautier 
53ad71d45eSYann Gautier 	cmd.cmd_idx = idx;
54ad71d45eSYann Gautier 	cmd.cmd_arg = arg;
55ad71d45eSYann Gautier 	cmd.resp_type = r_type;
56ad71d45eSYann Gautier 
57ad71d45eSYann Gautier 	ret = ops->send_cmd(&cmd);
58ad71d45eSYann Gautier 
59ad71d45eSYann Gautier 	if ((ret == 0) && (r_data != NULL)) {
60ad71d45eSYann Gautier 		int i;
61ad71d45eSYann Gautier 
62ad71d45eSYann Gautier 		for (i = 0; i < 4; i++) {
63ad71d45eSYann Gautier 			*r_data = cmd.resp_data[i];
64ad71d45eSYann Gautier 			r_data++;
65ad71d45eSYann Gautier 		}
66ad71d45eSYann Gautier 	}
67ad71d45eSYann Gautier 
68ad71d45eSYann Gautier 	if (ret != 0) {
69ad71d45eSYann Gautier 		VERBOSE("Send command %u error: %d\n", idx, ret);
70ad71d45eSYann Gautier 	}
71ad71d45eSYann Gautier 
72ad71d45eSYann Gautier 	return ret;
73ad71d45eSYann Gautier }
74ad71d45eSYann Gautier 
75ad71d45eSYann Gautier static int mmc_device_state(void)
76ad71d45eSYann Gautier {
77ad71d45eSYann Gautier 	int retries = MMC_DEFAULT_MAX_RETRIES;
78ad71d45eSYann Gautier 	unsigned int resp_data[4];
79ad71d45eSYann Gautier 
80ad71d45eSYann Gautier 	do {
81ad71d45eSYann Gautier 		int ret;
82ad71d45eSYann Gautier 
83ad71d45eSYann Gautier 		if (retries == 0) {
84ad71d45eSYann Gautier 			ERROR("CMD13 failed after %d retries\n",
85ad71d45eSYann Gautier 			      MMC_DEFAULT_MAX_RETRIES);
86ad71d45eSYann Gautier 			return -EIO;
87ad71d45eSYann Gautier 		}
88ad71d45eSYann Gautier 
89ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(13), rca << RCA_SHIFT_OFFSET,
9097d5db8cSYann Gautier 				   MMC_RESPONSE_R1, &resp_data[0]);
91ad71d45eSYann Gautier 		if (ret != 0) {
92ad71d45eSYann Gautier 			return ret;
93ad71d45eSYann Gautier 		}
94ad71d45eSYann Gautier 
95ad71d45eSYann Gautier 		if ((resp_data[0] & STATUS_SWITCH_ERROR) != 0U) {
96ad71d45eSYann Gautier 			return -EIO;
97ad71d45eSYann Gautier 		}
98ad71d45eSYann Gautier 
99ad71d45eSYann Gautier 		retries--;
100ad71d45eSYann Gautier 	} while ((resp_data[0] & STATUS_READY_FOR_DATA) == 0U);
101ad71d45eSYann Gautier 
102ad71d45eSYann Gautier 	return MMC_GET_STATE(resp_data[0]);
103ad71d45eSYann Gautier }
104ad71d45eSYann Gautier 
105ad71d45eSYann Gautier static int mmc_set_ext_csd(unsigned int ext_cmd, unsigned int value)
106ad71d45eSYann Gautier {
107ad71d45eSYann Gautier 	int ret;
108ad71d45eSYann Gautier 
109ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(6),
110ad71d45eSYann Gautier 			   EXTCSD_WRITE_BYTES | EXTCSD_CMD(ext_cmd) |
111ad71d45eSYann Gautier 			   EXTCSD_VALUE(value) | EXTCSD_CMD_SET_NORMAL,
11261752898SBryan O'Donoghue 			   MMC_RESPONSE_R1B, NULL);
113ad71d45eSYann Gautier 	if (ret != 0) {
114ad71d45eSYann Gautier 		return ret;
115ad71d45eSYann Gautier 	}
116ad71d45eSYann Gautier 
117ad71d45eSYann Gautier 	do {
118ad71d45eSYann Gautier 		ret = mmc_device_state();
119ad71d45eSYann Gautier 		if (ret < 0) {
120ad71d45eSYann Gautier 			return ret;
121ad71d45eSYann Gautier 		}
122ad71d45eSYann Gautier 	} while (ret == MMC_STATE_PRG);
123ad71d45eSYann Gautier 
124ad71d45eSYann Gautier 	return 0;
125ad71d45eSYann Gautier }
126ad71d45eSYann Gautier 
127ad71d45eSYann Gautier static int mmc_sd_switch(unsigned int bus_width)
128ad71d45eSYann Gautier {
129ad71d45eSYann Gautier 	int ret;
130ad71d45eSYann Gautier 	int retries = MMC_DEFAULT_MAX_RETRIES;
131ad71d45eSYann Gautier 	unsigned int scr[2] = { 0 };
132ad71d45eSYann Gautier 	unsigned int bus_width_arg = 0;
133ad71d45eSYann Gautier 
134ad71d45eSYann Gautier 	ret = ops->prepare(0, (uintptr_t)&scr, sizeof(scr));
135ad71d45eSYann Gautier 	if (ret != 0) {
136ad71d45eSYann Gautier 		return ret;
137ad71d45eSYann Gautier 	}
138ad71d45eSYann Gautier 
139ad71d45eSYann Gautier 	/* CMD55: Application Specific Command */
140ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET,
14197d5db8cSYann Gautier 			   MMC_RESPONSE_R5, NULL);
142ad71d45eSYann Gautier 	if (ret != 0) {
143ad71d45eSYann Gautier 		return ret;
144ad71d45eSYann Gautier 	}
145ad71d45eSYann Gautier 
146ad71d45eSYann Gautier 	/* ACMD51: SEND_SCR */
147ad71d45eSYann Gautier 	do {
14897d5db8cSYann Gautier 		ret = mmc_send_cmd(MMC_ACMD(51), 0, MMC_RESPONSE_R1, NULL);
149ad71d45eSYann Gautier 		if ((ret != 0) && (retries == 0)) {
150ad71d45eSYann Gautier 			ERROR("ACMD51 failed after %d retries (ret=%d)\n",
151ad71d45eSYann Gautier 			      MMC_DEFAULT_MAX_RETRIES, ret);
152ad71d45eSYann Gautier 			return ret;
153ad71d45eSYann Gautier 		}
154ad71d45eSYann Gautier 
155ad71d45eSYann Gautier 		retries--;
156ad71d45eSYann Gautier 	} while (ret != 0);
157ad71d45eSYann Gautier 
158ad71d45eSYann Gautier 	ret = ops->read(0, (uintptr_t)&scr, sizeof(scr));
159ad71d45eSYann Gautier 	if (ret != 0) {
160ad71d45eSYann Gautier 		return ret;
161ad71d45eSYann Gautier 	}
162ad71d45eSYann Gautier 
163ad71d45eSYann Gautier 	if (((scr[0] & SD_SCR_BUS_WIDTH_4) != 0U) &&
164ad71d45eSYann Gautier 	    (bus_width == MMC_BUS_WIDTH_4)) {
165ad71d45eSYann Gautier 		bus_width_arg = 2;
166ad71d45eSYann Gautier 	}
167ad71d45eSYann Gautier 
168ad71d45eSYann Gautier 	/* CMD55: Application Specific Command */
169ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET,
17097d5db8cSYann Gautier 			   MMC_RESPONSE_R5, NULL);
171ad71d45eSYann Gautier 	if (ret != 0) {
172ad71d45eSYann Gautier 		return ret;
173ad71d45eSYann Gautier 	}
174ad71d45eSYann Gautier 
175ad71d45eSYann Gautier 	/* ACMD6: SET_BUS_WIDTH */
17697d5db8cSYann Gautier 	ret = mmc_send_cmd(MMC_ACMD(6), bus_width_arg, MMC_RESPONSE_R1, NULL);
177ad71d45eSYann Gautier 	if (ret != 0) {
178ad71d45eSYann Gautier 		return ret;
179ad71d45eSYann Gautier 	}
180ad71d45eSYann Gautier 
181ad71d45eSYann Gautier 	do {
182ad71d45eSYann Gautier 		ret = mmc_device_state();
183ad71d45eSYann Gautier 		if (ret < 0) {
184ad71d45eSYann Gautier 			return ret;
185ad71d45eSYann Gautier 		}
186ad71d45eSYann Gautier 	} while (ret == MMC_STATE_PRG);
187ad71d45eSYann Gautier 
188ad71d45eSYann Gautier 	return 0;
189ad71d45eSYann Gautier }
190ad71d45eSYann Gautier 
191ad71d45eSYann Gautier static int mmc_set_ios(unsigned int clk, unsigned int bus_width)
192ad71d45eSYann Gautier {
193ad71d45eSYann Gautier 	int ret;
194ad71d45eSYann Gautier 	unsigned int width = bus_width;
195ad71d45eSYann Gautier 
196ad71d45eSYann Gautier 	if (mmc_dev_info->mmc_dev_type != MMC_IS_EMMC) {
197ad71d45eSYann Gautier 		if (width == MMC_BUS_WIDTH_8) {
198ad71d45eSYann Gautier 			WARN("Wrong bus config for SD-card, force to 4\n");
199ad71d45eSYann Gautier 			width = MMC_BUS_WIDTH_4;
200ad71d45eSYann Gautier 		}
201ad71d45eSYann Gautier 		ret = mmc_sd_switch(width);
202ad71d45eSYann Gautier 		if (ret != 0) {
203ad71d45eSYann Gautier 			return ret;
204ad71d45eSYann Gautier 		}
205ad71d45eSYann Gautier 	} else if (mmc_csd.spec_vers == 4U) {
206ad71d45eSYann Gautier 		ret = mmc_set_ext_csd(CMD_EXTCSD_BUS_WIDTH,
207ad71d45eSYann Gautier 				      (unsigned int)width);
208ad71d45eSYann Gautier 		if (ret != 0) {
209ad71d45eSYann Gautier 			return ret;
210ad71d45eSYann Gautier 		}
211ad71d45eSYann Gautier 	} else {
212ad71d45eSYann Gautier 		VERBOSE("Wrong MMC type or spec version\n");
213ad71d45eSYann Gautier 	}
214ad71d45eSYann Gautier 
215ad71d45eSYann Gautier 	return ops->set_ios(clk, width);
216ad71d45eSYann Gautier }
217ad71d45eSYann Gautier 
218ad71d45eSYann Gautier static int mmc_fill_device_info(void)
219ad71d45eSYann Gautier {
220ad71d45eSYann Gautier 	unsigned long long c_size;
221ad71d45eSYann Gautier 	unsigned int speed_idx;
222ad71d45eSYann Gautier 	unsigned int nb_blocks;
223ad71d45eSYann Gautier 	unsigned int freq_unit;
224cadb36cbSAntonio Nino Diaz 	int ret = 0;
225ad71d45eSYann Gautier 	struct mmc_csd_sd_v2 *csd_sd_v2;
226ad71d45eSYann Gautier 
227ad71d45eSYann Gautier 	switch (mmc_dev_info->mmc_dev_type) {
228ad71d45eSYann Gautier 	case MMC_IS_EMMC:
229ad71d45eSYann Gautier 		mmc_dev_info->block_size = MMC_BLOCK_SIZE;
230ad71d45eSYann Gautier 
231ad71d45eSYann Gautier 		ret = ops->prepare(0, (uintptr_t)&mmc_ext_csd,
232ad71d45eSYann Gautier 				   sizeof(mmc_ext_csd));
233ad71d45eSYann Gautier 		if (ret != 0) {
234ad71d45eSYann Gautier 			return ret;
235ad71d45eSYann Gautier 		}
236ad71d45eSYann Gautier 
237ad71d45eSYann Gautier 		/* MMC CMD8: SEND_EXT_CSD */
23897d5db8cSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(8), 0, MMC_RESPONSE_R1, NULL);
239ad71d45eSYann Gautier 		if (ret != 0) {
240ad71d45eSYann Gautier 			return ret;
241ad71d45eSYann Gautier 		}
242ad71d45eSYann Gautier 
243ad71d45eSYann Gautier 		ret = ops->read(0, (uintptr_t)&mmc_ext_csd,
244ad71d45eSYann Gautier 				sizeof(mmc_ext_csd));
245ad71d45eSYann Gautier 		if (ret != 0) {
246ad71d45eSYann Gautier 			return ret;
247ad71d45eSYann Gautier 		}
248ad71d45eSYann Gautier 
24993768644SHaojian Zhuang 		do {
25093768644SHaojian Zhuang 			ret = mmc_device_state();
25193768644SHaojian Zhuang 			if (ret < 0) {
25293768644SHaojian Zhuang 				return ret;
25393768644SHaojian Zhuang 			}
25493768644SHaojian Zhuang 		} while (ret != MMC_STATE_TRAN);
25593768644SHaojian Zhuang 
256ad71d45eSYann Gautier 		nb_blocks = (mmc_ext_csd[CMD_EXTCSD_SEC_CNT] << 0) |
257ad71d45eSYann Gautier 			    (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 1] << 8) |
258ad71d45eSYann Gautier 			    (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 2] << 16) |
259ad71d45eSYann Gautier 			    (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 3] << 24);
260ad71d45eSYann Gautier 
261ad71d45eSYann Gautier 		mmc_dev_info->device_size = (unsigned long long)nb_blocks *
262ad71d45eSYann Gautier 			mmc_dev_info->block_size;
263ad71d45eSYann Gautier 
264ad71d45eSYann Gautier 		break;
265ad71d45eSYann Gautier 
266ad71d45eSYann Gautier 	case MMC_IS_SD:
267ad71d45eSYann Gautier 		/*
268ad71d45eSYann Gautier 		 * Use the same mmc_csd struct, as required fields here
269ad71d45eSYann Gautier 		 * (READ_BL_LEN, C_SIZE, CSIZE_MULT) are common with eMMC.
270ad71d45eSYann Gautier 		 */
271ad71d45eSYann Gautier 		mmc_dev_info->block_size = BIT_32(mmc_csd.read_bl_len);
272ad71d45eSYann Gautier 
273ad71d45eSYann Gautier 		c_size = ((unsigned long long)mmc_csd.c_size_high << 2U) |
274ad71d45eSYann Gautier 			 (unsigned long long)mmc_csd.c_size_low;
275ad71d45eSYann Gautier 		assert(c_size != 0xFFFU);
276ad71d45eSYann Gautier 
277ad71d45eSYann Gautier 		mmc_dev_info->device_size = (c_size + 1U) *
278ad71d45eSYann Gautier 					    BIT_64(mmc_csd.c_size_mult + 2U) *
279ad71d45eSYann Gautier 					    mmc_dev_info->block_size;
280ad71d45eSYann Gautier 
281ad71d45eSYann Gautier 		break;
282ad71d45eSYann Gautier 
283ad71d45eSYann Gautier 	case MMC_IS_SD_HC:
284ad71d45eSYann Gautier 		assert(mmc_csd.csd_structure == 1U);
285ad71d45eSYann Gautier 
286ad71d45eSYann Gautier 		mmc_dev_info->block_size = MMC_BLOCK_SIZE;
287ad71d45eSYann Gautier 
288ad71d45eSYann Gautier 		/* Need to use mmc_csd_sd_v2 struct */
289ad71d45eSYann Gautier 		csd_sd_v2 = (struct mmc_csd_sd_v2 *)&mmc_csd;
290ad71d45eSYann Gautier 		c_size = ((unsigned long long)csd_sd_v2->c_size_high << 16) |
291ad71d45eSYann Gautier 			 (unsigned long long)csd_sd_v2->c_size_low;
292ad71d45eSYann Gautier 
293ad71d45eSYann Gautier 		mmc_dev_info->device_size = (c_size + 1U) << MULT_BY_512K_SHIFT;
294ad71d45eSYann Gautier 
295ad71d45eSYann Gautier 		break;
296ad71d45eSYann Gautier 
297ad71d45eSYann Gautier 	default:
298ad71d45eSYann Gautier 		ret = -EINVAL;
299ad71d45eSYann Gautier 		break;
300ad71d45eSYann Gautier 	}
301ad71d45eSYann Gautier 
302ad71d45eSYann Gautier 	if (ret != 0) {
303ad71d45eSYann Gautier 		return ret;
304ad71d45eSYann Gautier 	}
305ad71d45eSYann Gautier 
306ad71d45eSYann Gautier 	speed_idx = (mmc_csd.tran_speed & CSD_TRAN_SPEED_MULT_MASK) >>
307ad71d45eSYann Gautier 			 CSD_TRAN_SPEED_MULT_SHIFT;
308ad71d45eSYann Gautier 
309ad71d45eSYann Gautier 	assert(speed_idx > 0U);
310ad71d45eSYann Gautier 
311ad71d45eSYann Gautier 	if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) {
312ad71d45eSYann Gautier 		mmc_dev_info->max_bus_freq = tran_speed_base[speed_idx];
313ad71d45eSYann Gautier 	} else {
314ad71d45eSYann Gautier 		mmc_dev_info->max_bus_freq = sd_tran_speed_base[speed_idx];
315ad71d45eSYann Gautier 	}
316ad71d45eSYann Gautier 
317ad71d45eSYann Gautier 	freq_unit = mmc_csd.tran_speed & CSD_TRAN_SPEED_UNIT_MASK;
318ad71d45eSYann Gautier 	while (freq_unit != 0U) {
319ad71d45eSYann Gautier 		mmc_dev_info->max_bus_freq *= 10U;
320ad71d45eSYann Gautier 		--freq_unit;
321ad71d45eSYann Gautier 	}
322ad71d45eSYann Gautier 
323ad71d45eSYann Gautier 	mmc_dev_info->max_bus_freq *= 10000U;
324ad71d45eSYann Gautier 
325ad71d45eSYann Gautier 	return 0;
326ad71d45eSYann Gautier }
327ad71d45eSYann Gautier 
328ad71d45eSYann Gautier static int sd_send_op_cond(void)
329ad71d45eSYann Gautier {
33015e913d4SYann Gautier 	int n;
331ad71d45eSYann Gautier 	unsigned int resp_data[4];
332ad71d45eSYann Gautier 
33315e913d4SYann Gautier 	for (n = 0; n < SEND_OP_COND_MAX_RETRIES; n++) {
334ad71d45eSYann Gautier 		int ret;
335ad71d45eSYann Gautier 
336ad71d45eSYann Gautier 		/* CMD55: Application Specific Command */
33797d5db8cSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(55), 0, MMC_RESPONSE_R1, NULL);
338ad71d45eSYann Gautier 		if (ret != 0) {
339ad71d45eSYann Gautier 			return ret;
340ad71d45eSYann Gautier 		}
341ad71d45eSYann Gautier 
342ad71d45eSYann Gautier 		/* ACMD41: SD_SEND_OP_COND */
34394522ff7SBryan O'Donoghue 		ret = mmc_send_cmd(MMC_ACMD(41), OCR_HCS, MMC_RESPONSE_R3,
344ad71d45eSYann Gautier 				   &resp_data[0]);
345ad71d45eSYann Gautier 		if (ret != 0) {
346ad71d45eSYann Gautier 			return ret;
347ad71d45eSYann Gautier 		}
348ad71d45eSYann Gautier 
34915e913d4SYann Gautier 		if ((resp_data[0] & OCR_POWERUP) != 0U) {
350ad71d45eSYann Gautier 			mmc_ocr_value = resp_data[0];
351ad71d45eSYann Gautier 
352ad71d45eSYann Gautier 			if ((mmc_ocr_value & OCR_HCS) != 0U) {
353ad71d45eSYann Gautier 				mmc_dev_info->mmc_dev_type = MMC_IS_SD_HC;
354ad71d45eSYann Gautier 			} else {
355ad71d45eSYann Gautier 				mmc_dev_info->mmc_dev_type = MMC_IS_SD;
356ad71d45eSYann Gautier 			}
357ad71d45eSYann Gautier 
358ad71d45eSYann Gautier 			return 0;
359ad71d45eSYann Gautier 		}
360ad71d45eSYann Gautier 
36115e913d4SYann Gautier 		mdelay(1);
36215e913d4SYann Gautier 	}
36315e913d4SYann Gautier 
36415e913d4SYann Gautier 	ERROR("ACMD41 failed after %d retries\n", SEND_OP_COND_MAX_RETRIES);
36515e913d4SYann Gautier 
36615e913d4SYann Gautier 	return -EIO;
36715e913d4SYann Gautier }
36815e913d4SYann Gautier 
36915e913d4SYann Gautier static int mmc_reset_to_idle(void)
370ad71d45eSYann Gautier {
371ad71d45eSYann Gautier 	int ret;
37215e913d4SYann Gautier 
373ad71d45eSYann Gautier 	/* CMD0: reset to IDLE */
374ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(0), 0, 0, NULL);
375ad71d45eSYann Gautier 	if (ret != 0) {
376ad71d45eSYann Gautier 		return ret;
377ad71d45eSYann Gautier 	}
378ad71d45eSYann Gautier 
37915e913d4SYann Gautier 	mdelay(2);
38015e913d4SYann Gautier 
38115e913d4SYann Gautier 	return 0;
382ad71d45eSYann Gautier }
383ad71d45eSYann Gautier 
38415e913d4SYann Gautier static int mmc_send_op_cond(void)
38515e913d4SYann Gautier {
38615e913d4SYann Gautier 	int ret, n;
38715e913d4SYann Gautier 	unsigned int resp_data[4];
38815e913d4SYann Gautier 
38977614a99SYann Gautier 	ret = mmc_reset_to_idle();
39077614a99SYann Gautier 	if (ret != 0) {
39177614a99SYann Gautier 		return ret;
39277614a99SYann Gautier 	};
39315e913d4SYann Gautier 
39415e913d4SYann Gautier 	for (n = 0; n < SEND_OP_COND_MAX_RETRIES; n++) {
395ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(1), OCR_SECTOR_MODE |
396ad71d45eSYann Gautier 				   OCR_VDD_MIN_2V7 | OCR_VDD_MIN_1V7,
39794522ff7SBryan O'Donoghue 				   MMC_RESPONSE_R3, &resp_data[0]);
398ad71d45eSYann Gautier 		if (ret != 0) {
399ad71d45eSYann Gautier 			return ret;
400ad71d45eSYann Gautier 		}
401ad71d45eSYann Gautier 
40215e913d4SYann Gautier 		if ((resp_data[0] & OCR_POWERUP) != 0U) {
403ad71d45eSYann Gautier 			mmc_ocr_value = resp_data[0];
404ad71d45eSYann Gautier 			return 0;
405ad71d45eSYann Gautier 		}
406ad71d45eSYann Gautier 
407*7d639429SJoakim Bech 		mdelay(10);
40815e913d4SYann Gautier 	}
40915e913d4SYann Gautier 
41015e913d4SYann Gautier 	ERROR("CMD1 failed after %d retries\n", SEND_OP_COND_MAX_RETRIES);
41115e913d4SYann Gautier 
41215e913d4SYann Gautier 	return -EIO;
41315e913d4SYann Gautier }
41415e913d4SYann Gautier 
415ad71d45eSYann Gautier static int mmc_enumerate(unsigned int clk, unsigned int bus_width)
416ad71d45eSYann Gautier {
417ad71d45eSYann Gautier 	int ret;
418ad71d45eSYann Gautier 	unsigned int resp_data[4];
419ad71d45eSYann Gautier 
420ad71d45eSYann Gautier 	ops->init();
421ad71d45eSYann Gautier 
42277614a99SYann Gautier 	ret = mmc_reset_to_idle();
42377614a99SYann Gautier 	if (ret != 0) {
42477614a99SYann Gautier 		return ret;
42577614a99SYann Gautier 	};
426ad71d45eSYann Gautier 
427e74dc940SHaojian Zhuang 	if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) {
428e74dc940SHaojian Zhuang 		ret = mmc_send_op_cond();
429e74dc940SHaojian Zhuang 	} else {
430ad71d45eSYann Gautier 		/* CMD8: Send Interface Condition Command */
431ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(8), VHS_2_7_3_6_V | CMD8_CHECK_PATTERN,
43297d5db8cSYann Gautier 				   MMC_RESPONSE_R5, &resp_data[0]);
433ad71d45eSYann Gautier 
434ad71d45eSYann Gautier 		if ((ret == 0) && ((resp_data[0] & 0xffU) == CMD8_CHECK_PATTERN)) {
435ad71d45eSYann Gautier 			ret = sd_send_op_cond();
436e74dc940SHaojian Zhuang 		}
437ad71d45eSYann Gautier 	}
438ad71d45eSYann Gautier 	if (ret != 0) {
439ad71d45eSYann Gautier 		return ret;
440ad71d45eSYann Gautier 	}
441ad71d45eSYann Gautier 
442ad71d45eSYann Gautier 	/* CMD2: Card Identification */
443a2a69bc8SShawn Guo 	ret = mmc_send_cmd(MMC_CMD(2), 0, MMC_RESPONSE_R2, NULL);
444ad71d45eSYann Gautier 	if (ret != 0) {
445ad71d45eSYann Gautier 		return ret;
446ad71d45eSYann Gautier 	}
447ad71d45eSYann Gautier 
448ad71d45eSYann Gautier 	/* CMD3: Set Relative Address */
449ad71d45eSYann Gautier 	if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) {
450ad71d45eSYann Gautier 		rca = MMC_FIX_RCA;
451ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(3), rca << RCA_SHIFT_OFFSET,
45297d5db8cSYann Gautier 				   MMC_RESPONSE_R1, NULL);
453ad71d45eSYann Gautier 		if (ret != 0) {
454ad71d45eSYann Gautier 			return ret;
455ad71d45eSYann Gautier 		}
456ad71d45eSYann Gautier 	} else {
457ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(3), 0,
45897d5db8cSYann Gautier 				   MMC_RESPONSE_R6, &resp_data[0]);
459ad71d45eSYann Gautier 		if (ret != 0) {
460ad71d45eSYann Gautier 			return ret;
461ad71d45eSYann Gautier 		}
462ad71d45eSYann Gautier 
463ad71d45eSYann Gautier 		rca = (resp_data[0] & 0xFFFF0000U) >> 16;
464ad71d45eSYann Gautier 	}
465ad71d45eSYann Gautier 
466ad71d45eSYann Gautier 	/* CMD9: CSD Register */
467ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(9), rca << RCA_SHIFT_OFFSET,
468a2a69bc8SShawn Guo 			   MMC_RESPONSE_R2, &resp_data[0]);
469ad71d45eSYann Gautier 	if (ret != 0) {
470ad71d45eSYann Gautier 		return ret;
471ad71d45eSYann Gautier 	}
472ad71d45eSYann Gautier 
473ad71d45eSYann Gautier 	memcpy(&mmc_csd, &resp_data, sizeof(resp_data));
474ad71d45eSYann Gautier 
475ad71d45eSYann Gautier 	/* CMD7: Select Card */
476ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(7), rca << RCA_SHIFT_OFFSET,
47797d5db8cSYann Gautier 			   MMC_RESPONSE_R1, NULL);
478ad71d45eSYann Gautier 	if (ret != 0) {
479ad71d45eSYann Gautier 		return ret;
480ad71d45eSYann Gautier 	}
481ad71d45eSYann Gautier 
482ad71d45eSYann Gautier 	do {
483ad71d45eSYann Gautier 		ret = mmc_device_state();
484ad71d45eSYann Gautier 		if (ret < 0) {
485ad71d45eSYann Gautier 			return ret;
486ad71d45eSYann Gautier 		}
487ad71d45eSYann Gautier 	} while (ret != MMC_STATE_TRAN);
488ad71d45eSYann Gautier 
489bd4e3deeSHaojian Zhuang 	ret = mmc_set_ios(clk, bus_width);
490ad71d45eSYann Gautier 	if (ret != 0) {
491ad71d45eSYann Gautier 		return ret;
492ad71d45eSYann Gautier 	}
493ad71d45eSYann Gautier 
494bd4e3deeSHaojian Zhuang 	return mmc_fill_device_info();
495ad71d45eSYann Gautier }
496ad71d45eSYann Gautier 
497ea315a69SHaojian Zhuang size_t mmc_read_blocks(int lba, uintptr_t buf, size_t size)
498ad71d45eSYann Gautier {
499ad71d45eSYann Gautier 	int ret;
500ad71d45eSYann Gautier 	unsigned int cmd_idx, cmd_arg;
501ad71d45eSYann Gautier 
502ad71d45eSYann Gautier 	assert((ops != NULL) &&
503ad71d45eSYann Gautier 	       (ops->read != NULL) &&
504ad71d45eSYann Gautier 	       (size != 0U) &&
505ad71d45eSYann Gautier 	       ((size & MMC_BLOCK_MASK) == 0U));
506ad71d45eSYann Gautier 
507ad71d45eSYann Gautier 	ret = ops->prepare(lba, buf, size);
508ad71d45eSYann Gautier 	if (ret != 0) {
509ad71d45eSYann Gautier 		return 0;
510ad71d45eSYann Gautier 	}
511ad71d45eSYann Gautier 
512ad71d45eSYann Gautier 	if (is_cmd23_enabled()) {
513ad71d45eSYann Gautier 		/* Set block count */
514ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE,
51597d5db8cSYann Gautier 				   MMC_RESPONSE_R1, NULL);
516ad71d45eSYann Gautier 		if (ret != 0) {
517ad71d45eSYann Gautier 			return 0;
518ad71d45eSYann Gautier 		}
519ad71d45eSYann Gautier 
520ad71d45eSYann Gautier 		cmd_idx = MMC_CMD(18);
521ad71d45eSYann Gautier 	} else {
522ad71d45eSYann Gautier 		if (size > MMC_BLOCK_SIZE) {
523ad71d45eSYann Gautier 			cmd_idx = MMC_CMD(18);
524ad71d45eSYann Gautier 		} else {
525ad71d45eSYann Gautier 			cmd_idx = MMC_CMD(17);
526ad71d45eSYann Gautier 		}
527ad71d45eSYann Gautier 	}
528ad71d45eSYann Gautier 
529ad71d45eSYann Gautier 	if (((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) &&
530ad71d45eSYann Gautier 	    (mmc_dev_info->mmc_dev_type != MMC_IS_SD_HC)) {
531ad71d45eSYann Gautier 		cmd_arg = lba * MMC_BLOCK_SIZE;
532ad71d45eSYann Gautier 	} else {
533ad71d45eSYann Gautier 		cmd_arg = lba;
534ad71d45eSYann Gautier 	}
535ad71d45eSYann Gautier 
53697d5db8cSYann Gautier 	ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R1, NULL);
537ad71d45eSYann Gautier 	if (ret != 0) {
538ad71d45eSYann Gautier 		return 0;
539ad71d45eSYann Gautier 	}
540ad71d45eSYann Gautier 
541ad71d45eSYann Gautier 	ret = ops->read(lba, buf, size);
542ad71d45eSYann Gautier 	if (ret != 0) {
543ad71d45eSYann Gautier 		return 0;
544ad71d45eSYann Gautier 	}
545ad71d45eSYann Gautier 
546ad71d45eSYann Gautier 	/* Wait buffer empty */
547ad71d45eSYann Gautier 	do {
548ad71d45eSYann Gautier 		ret = mmc_device_state();
549ad71d45eSYann Gautier 		if (ret < 0) {
550ad71d45eSYann Gautier 			return 0;
551ad71d45eSYann Gautier 		}
552ad71d45eSYann Gautier 	} while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_DATA));
553ad71d45eSYann Gautier 
554ad71d45eSYann Gautier 	if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) {
55561752898SBryan O'Donoghue 		ret = mmc_send_cmd(MMC_CMD(12), 0, MMC_RESPONSE_R1B, NULL);
556ad71d45eSYann Gautier 		if (ret != 0) {
557ad71d45eSYann Gautier 			return 0;
558ad71d45eSYann Gautier 		}
559ad71d45eSYann Gautier 	}
560ad71d45eSYann Gautier 
561ad71d45eSYann Gautier 	return size;
562ad71d45eSYann Gautier }
563ad71d45eSYann Gautier 
564ea315a69SHaojian Zhuang size_t mmc_write_blocks(int lba, const uintptr_t buf, size_t size)
565ad71d45eSYann Gautier {
566ad71d45eSYann Gautier 	int ret;
567ad71d45eSYann Gautier 	unsigned int cmd_idx, cmd_arg;
568ad71d45eSYann Gautier 
569ad71d45eSYann Gautier 	assert((ops != NULL) &&
570ad71d45eSYann Gautier 	       (ops->write != NULL) &&
571ad71d45eSYann Gautier 	       (size != 0U) &&
572ad71d45eSYann Gautier 	       ((buf & MMC_BLOCK_MASK) == 0U) &&
573ad71d45eSYann Gautier 	       ((size & MMC_BLOCK_MASK) == 0U));
574ad71d45eSYann Gautier 
575ad71d45eSYann Gautier 	ret = ops->prepare(lba, buf, size);
576ad71d45eSYann Gautier 	if (ret != 0) {
577ad71d45eSYann Gautier 		return 0;
578ad71d45eSYann Gautier 	}
579ad71d45eSYann Gautier 
580ad71d45eSYann Gautier 	if (is_cmd23_enabled()) {
581ad71d45eSYann Gautier 		/* Set block count */
582ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE,
58397d5db8cSYann Gautier 				   MMC_RESPONSE_R1, NULL);
584ad71d45eSYann Gautier 		if (ret != 0) {
585ad71d45eSYann Gautier 			return 0;
586ad71d45eSYann Gautier 		}
587ad71d45eSYann Gautier 
588ad71d45eSYann Gautier 		cmd_idx = MMC_CMD(25);
589ad71d45eSYann Gautier 	} else {
590ad71d45eSYann Gautier 		if (size > MMC_BLOCK_SIZE) {
591ad71d45eSYann Gautier 			cmd_idx = MMC_CMD(25);
592ad71d45eSYann Gautier 		} else {
593ad71d45eSYann Gautier 			cmd_idx = MMC_CMD(24);
594ad71d45eSYann Gautier 		}
595ad71d45eSYann Gautier 	}
596ad71d45eSYann Gautier 
597ad71d45eSYann Gautier 	if ((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) {
598ad71d45eSYann Gautier 		cmd_arg = lba * MMC_BLOCK_SIZE;
599ad71d45eSYann Gautier 	} else {
600ad71d45eSYann Gautier 		cmd_arg = lba;
601ad71d45eSYann Gautier 	}
602ad71d45eSYann Gautier 
60397d5db8cSYann Gautier 	ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R1, NULL);
604ad71d45eSYann Gautier 	if (ret != 0) {
605ad71d45eSYann Gautier 		return 0;
606ad71d45eSYann Gautier 	}
607ad71d45eSYann Gautier 
608ad71d45eSYann Gautier 	ret = ops->write(lba, buf, size);
609ad71d45eSYann Gautier 	if (ret != 0) {
610ad71d45eSYann Gautier 		return 0;
611ad71d45eSYann Gautier 	}
612ad71d45eSYann Gautier 
613ad71d45eSYann Gautier 	/* Wait buffer empty */
614ad71d45eSYann Gautier 	do {
615ad71d45eSYann Gautier 		ret = mmc_device_state();
616ad71d45eSYann Gautier 		if (ret < 0) {
617ad71d45eSYann Gautier 			return 0;
618ad71d45eSYann Gautier 		}
619ad71d45eSYann Gautier 	} while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_RCV));
620ad71d45eSYann Gautier 
621ad71d45eSYann Gautier 	if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) {
62261752898SBryan O'Donoghue 		ret = mmc_send_cmd(MMC_CMD(12), 0, MMC_RESPONSE_R1B, NULL);
623ad71d45eSYann Gautier 		if (ret != 0) {
624ad71d45eSYann Gautier 			return 0;
625ad71d45eSYann Gautier 		}
626ad71d45eSYann Gautier 	}
627ad71d45eSYann Gautier 
628ad71d45eSYann Gautier 	return size;
629ad71d45eSYann Gautier }
630ad71d45eSYann Gautier 
631ea315a69SHaojian Zhuang size_t mmc_erase_blocks(int lba, size_t size)
632ad71d45eSYann Gautier {
633ad71d45eSYann Gautier 	int ret;
634ad71d45eSYann Gautier 
635ad71d45eSYann Gautier 	assert(ops != NULL);
636ad71d45eSYann Gautier 	assert((size != 0U) && ((size & MMC_BLOCK_MASK) == 0U));
637ad71d45eSYann Gautier 
63897d5db8cSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(35), lba, MMC_RESPONSE_R1, NULL);
639ad71d45eSYann Gautier 	if (ret != 0) {
640ad71d45eSYann Gautier 		return 0;
641ad71d45eSYann Gautier 	}
642ad71d45eSYann Gautier 
643ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(36), lba + (size / MMC_BLOCK_SIZE) - 1U,
64497d5db8cSYann Gautier 			   MMC_RESPONSE_R1, NULL);
645ad71d45eSYann Gautier 	if (ret != 0) {
646ad71d45eSYann Gautier 		return 0;
647ad71d45eSYann Gautier 	}
648ad71d45eSYann Gautier 
64997d5db8cSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(38), lba, MMC_RESPONSE_R1B, NULL);
650ad71d45eSYann Gautier 	if (ret != 0) {
651ad71d45eSYann Gautier 		return 0;
652ad71d45eSYann Gautier 	}
653ad71d45eSYann Gautier 
654ad71d45eSYann Gautier 	do {
655ad71d45eSYann Gautier 		ret = mmc_device_state();
656ad71d45eSYann Gautier 		if (ret < 0) {
657ad71d45eSYann Gautier 			return 0;
658ad71d45eSYann Gautier 		}
659ad71d45eSYann Gautier 	} while (ret != MMC_STATE_TRAN);
660ad71d45eSYann Gautier 
661ad71d45eSYann Gautier 	return size;
662ad71d45eSYann Gautier }
663ad71d45eSYann Gautier 
664ad71d45eSYann Gautier static inline void mmc_rpmb_enable(void)
665ad71d45eSYann Gautier {
666ad71d45eSYann Gautier 	mmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG,
667ad71d45eSYann Gautier 			PART_CFG_BOOT_PARTITION1_ENABLE |
668ad71d45eSYann Gautier 			PART_CFG_PARTITION1_ACCESS);
669ad71d45eSYann Gautier }
670ad71d45eSYann Gautier 
671ad71d45eSYann Gautier static inline void mmc_rpmb_disable(void)
672ad71d45eSYann Gautier {
673ad71d45eSYann Gautier 	mmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG,
674ad71d45eSYann Gautier 			PART_CFG_BOOT_PARTITION1_ENABLE);
675ad71d45eSYann Gautier }
676ad71d45eSYann Gautier 
677ea315a69SHaojian Zhuang size_t mmc_rpmb_read_blocks(int lba, uintptr_t buf, size_t size)
678ad71d45eSYann Gautier {
679ad71d45eSYann Gautier 	size_t size_read;
680ad71d45eSYann Gautier 
681ad71d45eSYann Gautier 	mmc_rpmb_enable();
682ad71d45eSYann Gautier 	size_read = mmc_read_blocks(lba, buf, size);
683ad71d45eSYann Gautier 	mmc_rpmb_disable();
684ad71d45eSYann Gautier 
685ad71d45eSYann Gautier 	return size_read;
686ad71d45eSYann Gautier }
687ad71d45eSYann Gautier 
688ea315a69SHaojian Zhuang size_t mmc_rpmb_write_blocks(int lba, const uintptr_t buf, size_t size)
689ad71d45eSYann Gautier {
690ad71d45eSYann Gautier 	size_t size_written;
691ad71d45eSYann Gautier 
692ad71d45eSYann Gautier 	mmc_rpmb_enable();
693ad71d45eSYann Gautier 	size_written = mmc_write_blocks(lba, buf, size);
694ad71d45eSYann Gautier 	mmc_rpmb_disable();
695ad71d45eSYann Gautier 
696ad71d45eSYann Gautier 	return size_written;
697ad71d45eSYann Gautier }
698ad71d45eSYann Gautier 
699ea315a69SHaojian Zhuang size_t mmc_rpmb_erase_blocks(int lba, size_t size)
700ad71d45eSYann Gautier {
701ad71d45eSYann Gautier 	size_t size_erased;
702ad71d45eSYann Gautier 
703ad71d45eSYann Gautier 	mmc_rpmb_enable();
704ad71d45eSYann Gautier 	size_erased = mmc_erase_blocks(lba, size);
705ad71d45eSYann Gautier 	mmc_rpmb_disable();
706ad71d45eSYann Gautier 
707ad71d45eSYann Gautier 	return size_erased;
708ad71d45eSYann Gautier }
709ad71d45eSYann Gautier 
710ad71d45eSYann Gautier int mmc_init(const struct mmc_ops *ops_ptr, unsigned int clk,
711ad71d45eSYann Gautier 	     unsigned int width, unsigned int flags,
712ad71d45eSYann Gautier 	     struct mmc_device_info *device_info)
713ad71d45eSYann Gautier {
714ad71d45eSYann Gautier 	assert((ops_ptr != NULL) &&
715ad71d45eSYann Gautier 	       (ops_ptr->init != NULL) &&
716ad71d45eSYann Gautier 	       (ops_ptr->send_cmd != NULL) &&
717ad71d45eSYann Gautier 	       (ops_ptr->set_ios != NULL) &&
718ad71d45eSYann Gautier 	       (ops_ptr->prepare != NULL) &&
719ad71d45eSYann Gautier 	       (ops_ptr->read != NULL) &&
720ad71d45eSYann Gautier 	       (ops_ptr->write != NULL) &&
721ad71d45eSYann Gautier 	       (device_info != NULL) &&
722ad71d45eSYann Gautier 	       (clk != 0) &&
723ad71d45eSYann Gautier 	       ((width == MMC_BUS_WIDTH_1) ||
724ad71d45eSYann Gautier 		(width == MMC_BUS_WIDTH_4) ||
725ad71d45eSYann Gautier 		(width == MMC_BUS_WIDTH_8) ||
726ad71d45eSYann Gautier 		(width == MMC_BUS_WIDTH_DDR_4) ||
727ad71d45eSYann Gautier 		(width == MMC_BUS_WIDTH_DDR_8)));
728ad71d45eSYann Gautier 
729ad71d45eSYann Gautier 	ops = ops_ptr;
730ad71d45eSYann Gautier 	mmc_flags = flags;
731ad71d45eSYann Gautier 	mmc_dev_info = device_info;
732ad71d45eSYann Gautier 
733ad71d45eSYann Gautier 	return mmc_enumerate(clk, width);
734ad71d45eSYann Gautier }
735