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