1ad71d45eSYann Gautier /*
2f85041a6SAhmad Fatoum * Copyright (c) 2018-2022, ARM Limited and Contributors. All rights reserved.
3ad71d45eSYann Gautier *
4ad71d45eSYann Gautier * SPDX-License-Identifier: BSD-3-Clause
5ad71d45eSYann Gautier */
6ad71d45eSYann Gautier
7ad71d45eSYann Gautier /* Define a simple and generic interface to access eMMC and SD-card devices. */
8ad71d45eSYann Gautier
9ad71d45eSYann Gautier #include <assert.h>
10ad71d45eSYann Gautier #include <errno.h>
11ad71d45eSYann Gautier #include <stdbool.h>
12ad71d45eSYann Gautier #include <string.h>
1309d40e0eSAntonio Nino Diaz
1409d40e0eSAntonio Nino Diaz #include <arch_helpers.h>
1509d40e0eSAntonio Nino Diaz #include <common/debug.h>
1609d40e0eSAntonio Nino Diaz #include <drivers/delay_timer.h>
1709d40e0eSAntonio Nino Diaz #include <drivers/mmc.h>
1809d40e0eSAntonio Nino Diaz #include <lib/utils.h>
19ccf8392cSJayanth Dodderi Chidanand #include <plat/common/common_def.h>
20ad71d45eSYann Gautier
21ad71d45eSYann Gautier #define MMC_DEFAULT_MAX_RETRIES 5
22ad71d45eSYann Gautier #define SEND_OP_COND_MAX_RETRIES 100
23ad71d45eSYann Gautier
24ad71d45eSYann Gautier #define MULT_BY_512K_SHIFT 19
25ad71d45eSYann Gautier
26ad71d45eSYann Gautier static const struct mmc_ops *ops;
27ad71d45eSYann Gautier static unsigned int mmc_ocr_value;
28ad71d45eSYann Gautier static struct mmc_csd_emmc mmc_csd;
29e5b267bbSYann Gautier static struct sd_switch_status sd_switch_func_status;
3007858dd8SHaojian Zhuang static unsigned char mmc_ext_csd[512] __aligned(16);
31ad71d45eSYann Gautier static unsigned int mmc_flags;
3270eb88b7SYann Gautier static struct mmc_device_info *mmc_dev_info;
33ad71d45eSYann Gautier static unsigned int rca;
34a468e756STien Hock, Loh static unsigned int scr[2]__aligned(16) = { 0 };
35ad71d45eSYann Gautier
36ad71d45eSYann Gautier static const unsigned char tran_speed_base[16] = {
37ad71d45eSYann Gautier 0, 10, 12, 13, 15, 20, 26, 30, 35, 40, 45, 52, 55, 60, 70, 80
38ad71d45eSYann Gautier };
39ad71d45eSYann Gautier
40ad71d45eSYann Gautier static const unsigned char sd_tran_speed_base[16] = {
41ad71d45eSYann Gautier 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80
42ad71d45eSYann Gautier };
43ad71d45eSYann Gautier
is_cmd23_enabled(void)44ad71d45eSYann Gautier static bool is_cmd23_enabled(void)
45ad71d45eSYann Gautier {
46ad71d45eSYann Gautier return ((mmc_flags & MMC_FLAG_CMD23) != 0U);
47ad71d45eSYann Gautier }
48ad71d45eSYann Gautier
is_sd_cmd6_enabled(void)49e5b267bbSYann Gautier static bool is_sd_cmd6_enabled(void)
50e5b267bbSYann Gautier {
51e5b267bbSYann Gautier return ((mmc_flags & MMC_FLAG_SD_CMD6) != 0U);
52e5b267bbSYann Gautier }
53e5b267bbSYann Gautier
mmc_send_cmd(unsigned int idx,unsigned int arg,unsigned int r_type,unsigned int * r_data)54ad71d45eSYann Gautier static int mmc_send_cmd(unsigned int idx, unsigned int arg,
55ad71d45eSYann Gautier unsigned int r_type, unsigned int *r_data)
56ad71d45eSYann Gautier {
57ad71d45eSYann Gautier struct mmc_cmd cmd;
58ad71d45eSYann Gautier int ret;
59ad71d45eSYann Gautier
60ad71d45eSYann Gautier zeromem(&cmd, sizeof(struct mmc_cmd));
61ad71d45eSYann Gautier
62ad71d45eSYann Gautier cmd.cmd_idx = idx;
63ad71d45eSYann Gautier cmd.cmd_arg = arg;
64ad71d45eSYann Gautier cmd.resp_type = r_type;
65ad71d45eSYann Gautier
66ad71d45eSYann Gautier ret = ops->send_cmd(&cmd);
67ad71d45eSYann Gautier
68ad71d45eSYann Gautier if ((ret == 0) && (r_data != NULL)) {
69ad71d45eSYann Gautier int i;
70ad71d45eSYann Gautier
71ad71d45eSYann Gautier for (i = 0; i < 4; i++) {
72bf78a650SYann Gautier r_data[i] = cmd.resp_data[i];
73ad71d45eSYann Gautier }
74ad71d45eSYann Gautier }
75ad71d45eSYann Gautier
76ad71d45eSYann Gautier if (ret != 0) {
77ad71d45eSYann Gautier VERBOSE("Send command %u error: %d\n", idx, ret);
78ad71d45eSYann Gautier }
79ad71d45eSYann Gautier
80ad71d45eSYann Gautier return ret;
81ad71d45eSYann Gautier }
82ad71d45eSYann Gautier
mmc_device_state(void)83ad71d45eSYann Gautier static int mmc_device_state(void)
84ad71d45eSYann Gautier {
85ad71d45eSYann Gautier int retries = MMC_DEFAULT_MAX_RETRIES;
86b1a2c51aSAnand Saminathan unsigned int resp_data[4] = {0};
87ad71d45eSYann Gautier
88ad71d45eSYann Gautier do {
89ad71d45eSYann Gautier int ret;
90ad71d45eSYann Gautier
91ad71d45eSYann Gautier if (retries == 0) {
92ad71d45eSYann Gautier ERROR("CMD13 failed after %d retries\n",
93ad71d45eSYann Gautier MMC_DEFAULT_MAX_RETRIES);
94ad71d45eSYann Gautier return -EIO;
95ad71d45eSYann Gautier }
96ad71d45eSYann Gautier
97ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(13), rca << RCA_SHIFT_OFFSET,
9897d5db8cSYann Gautier MMC_RESPONSE_R1, &resp_data[0]);
99ad71d45eSYann Gautier if (ret != 0) {
100a468e756STien Hock, Loh retries--;
101a468e756STien Hock, Loh continue;
102ad71d45eSYann Gautier }
103ad71d45eSYann Gautier
104ad71d45eSYann Gautier if ((resp_data[0] & STATUS_SWITCH_ERROR) != 0U) {
105ad71d45eSYann Gautier return -EIO;
106ad71d45eSYann Gautier }
107ad71d45eSYann Gautier
108ad71d45eSYann Gautier retries--;
109ad71d45eSYann Gautier } while ((resp_data[0] & STATUS_READY_FOR_DATA) == 0U);
110ad71d45eSYann Gautier
111ad71d45eSYann Gautier return MMC_GET_STATE(resp_data[0]);
112ad71d45eSYann Gautier }
113ad71d45eSYann Gautier
mmc_send_part_switch_cmd(unsigned char part_config)11453cbc949SYann Gautier static int mmc_send_part_switch_cmd(unsigned char part_config)
1155014b52dSVyacheslav Yurkov {
1165014b52dSVyacheslav Yurkov int ret;
1175014b52dSVyacheslav Yurkov unsigned int part_time = 0;
1185014b52dSVyacheslav Yurkov
1195014b52dSVyacheslav Yurkov ret = mmc_send_cmd(MMC_CMD(6),
1205014b52dSVyacheslav Yurkov EXTCSD_WRITE_BYTES |
1215014b52dSVyacheslav Yurkov EXTCSD_CMD(CMD_EXTCSD_PARTITION_CONFIG) |
1225014b52dSVyacheslav Yurkov EXTCSD_VALUE(part_config) |
1235014b52dSVyacheslav Yurkov EXTCSD_CMD_SET_NORMAL,
1245014b52dSVyacheslav Yurkov MMC_RESPONSE_R1B, NULL);
1255014b52dSVyacheslav Yurkov if (ret != 0) {
1265014b52dSVyacheslav Yurkov return ret;
1275014b52dSVyacheslav Yurkov }
1285014b52dSVyacheslav Yurkov
1295014b52dSVyacheslav Yurkov /* Partition switch timing is in 10ms units */
1305014b52dSVyacheslav Yurkov part_time = mmc_ext_csd[CMD_EXTCSD_PART_SWITCH_TIME] * 10;
1315014b52dSVyacheslav Yurkov
1325014b52dSVyacheslav Yurkov mdelay(part_time);
1335014b52dSVyacheslav Yurkov
1345014b52dSVyacheslav Yurkov do {
1355014b52dSVyacheslav Yurkov ret = mmc_device_state();
1365014b52dSVyacheslav Yurkov if (ret < 0) {
1375014b52dSVyacheslav Yurkov return ret;
1385014b52dSVyacheslav Yurkov }
1395014b52dSVyacheslav Yurkov } while (ret == MMC_STATE_PRG);
1405014b52dSVyacheslav Yurkov
1415014b52dSVyacheslav Yurkov return 0;
1425014b52dSVyacheslav Yurkov }
1435014b52dSVyacheslav Yurkov
mmc_set_ext_csd(unsigned int ext_cmd,unsigned int value)144ad71d45eSYann Gautier static int mmc_set_ext_csd(unsigned int ext_cmd, unsigned int value)
145ad71d45eSYann Gautier {
146ad71d45eSYann Gautier int ret;
147ad71d45eSYann Gautier
148ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(6),
149ad71d45eSYann Gautier EXTCSD_WRITE_BYTES | EXTCSD_CMD(ext_cmd) |
150ad71d45eSYann Gautier EXTCSD_VALUE(value) | EXTCSD_CMD_SET_NORMAL,
15161752898SBryan O'Donoghue MMC_RESPONSE_R1B, NULL);
152ad71d45eSYann Gautier if (ret != 0) {
153ad71d45eSYann Gautier return ret;
154ad71d45eSYann Gautier }
155ad71d45eSYann Gautier
156ad71d45eSYann Gautier do {
157ad71d45eSYann Gautier ret = mmc_device_state();
158ad71d45eSYann Gautier if (ret < 0) {
159ad71d45eSYann Gautier return ret;
160ad71d45eSYann Gautier }
161ad71d45eSYann Gautier } while (ret == MMC_STATE_PRG);
162ad71d45eSYann Gautier
163ad71d45eSYann Gautier return 0;
164ad71d45eSYann Gautier }
165ad71d45eSYann Gautier
mmc_sd_switch(unsigned int bus_width)166ad71d45eSYann Gautier static int mmc_sd_switch(unsigned int bus_width)
167ad71d45eSYann Gautier {
168ad71d45eSYann Gautier int ret;
169ad71d45eSYann Gautier int retries = MMC_DEFAULT_MAX_RETRIES;
170ad71d45eSYann Gautier unsigned int bus_width_arg = 0;
171ad71d45eSYann Gautier
172ad71d45eSYann Gautier ret = ops->prepare(0, (uintptr_t)&scr, sizeof(scr));
173ad71d45eSYann Gautier if (ret != 0) {
174ad71d45eSYann Gautier return ret;
175ad71d45eSYann Gautier }
176ad71d45eSYann Gautier
177ad71d45eSYann Gautier /* CMD55: Application Specific Command */
178ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET,
17997d5db8cSYann Gautier MMC_RESPONSE_R5, NULL);
180ad71d45eSYann Gautier if (ret != 0) {
181ad71d45eSYann Gautier return ret;
182ad71d45eSYann Gautier }
183ad71d45eSYann Gautier
184ad71d45eSYann Gautier /* ACMD51: SEND_SCR */
185ad71d45eSYann Gautier do {
18697d5db8cSYann Gautier ret = mmc_send_cmd(MMC_ACMD(51), 0, MMC_RESPONSE_R1, NULL);
187ad71d45eSYann Gautier if ((ret != 0) && (retries == 0)) {
188ad71d45eSYann Gautier ERROR("ACMD51 failed after %d retries (ret=%d)\n",
189ad71d45eSYann Gautier MMC_DEFAULT_MAX_RETRIES, ret);
190ad71d45eSYann Gautier return ret;
191ad71d45eSYann Gautier }
192ad71d45eSYann Gautier
193ad71d45eSYann Gautier retries--;
194ad71d45eSYann Gautier } while (ret != 0);
195ad71d45eSYann Gautier
196ad71d45eSYann Gautier ret = ops->read(0, (uintptr_t)&scr, sizeof(scr));
197ad71d45eSYann Gautier if (ret != 0) {
198ad71d45eSYann Gautier return ret;
199ad71d45eSYann Gautier }
200ad71d45eSYann Gautier
201ad71d45eSYann Gautier if (((scr[0] & SD_SCR_BUS_WIDTH_4) != 0U) &&
202ad71d45eSYann Gautier (bus_width == MMC_BUS_WIDTH_4)) {
203ad71d45eSYann Gautier bus_width_arg = 2;
204ad71d45eSYann Gautier }
205ad71d45eSYann Gautier
206ad71d45eSYann Gautier /* CMD55: Application Specific Command */
207ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET,
20897d5db8cSYann Gautier MMC_RESPONSE_R5, NULL);
209ad71d45eSYann Gautier if (ret != 0) {
210ad71d45eSYann Gautier return ret;
211ad71d45eSYann Gautier }
212ad71d45eSYann Gautier
213ad71d45eSYann Gautier /* ACMD6: SET_BUS_WIDTH */
21497d5db8cSYann Gautier ret = mmc_send_cmd(MMC_ACMD(6), bus_width_arg, MMC_RESPONSE_R1, NULL);
215ad71d45eSYann Gautier if (ret != 0) {
216ad71d45eSYann Gautier return ret;
217ad71d45eSYann Gautier }
218ad71d45eSYann Gautier
219ad71d45eSYann Gautier do {
220ad71d45eSYann Gautier ret = mmc_device_state();
221ad71d45eSYann Gautier if (ret < 0) {
222ad71d45eSYann Gautier return ret;
223ad71d45eSYann Gautier }
224ad71d45eSYann Gautier } while (ret == MMC_STATE_PRG);
225ad71d45eSYann Gautier
226ad71d45eSYann Gautier return 0;
227ad71d45eSYann Gautier }
228ad71d45eSYann Gautier
mmc_set_ios(unsigned int clk,unsigned int bus_width)229ad71d45eSYann Gautier static int mmc_set_ios(unsigned int clk, unsigned int bus_width)
230ad71d45eSYann Gautier {
231ad71d45eSYann Gautier int ret;
232ad71d45eSYann Gautier unsigned int width = bus_width;
233ad71d45eSYann Gautier
23470eb88b7SYann Gautier if (mmc_dev_info->mmc_dev_type != MMC_IS_EMMC) {
235ad71d45eSYann Gautier if (width == MMC_BUS_WIDTH_8) {
236ad71d45eSYann Gautier WARN("Wrong bus config for SD-card, force to 4\n");
237ad71d45eSYann Gautier width = MMC_BUS_WIDTH_4;
238ad71d45eSYann Gautier }
239ad71d45eSYann Gautier ret = mmc_sd_switch(width);
240ad71d45eSYann Gautier if (ret != 0) {
241ad71d45eSYann Gautier return ret;
242ad71d45eSYann Gautier }
243ad71d45eSYann Gautier } else if (mmc_csd.spec_vers == 4U) {
244ad71d45eSYann Gautier ret = mmc_set_ext_csd(CMD_EXTCSD_BUS_WIDTH,
245ad71d45eSYann Gautier (unsigned int)width);
246ad71d45eSYann Gautier if (ret != 0) {
247ad71d45eSYann Gautier return ret;
248ad71d45eSYann Gautier }
249ad71d45eSYann Gautier } else {
250ad71d45eSYann Gautier VERBOSE("Wrong MMC type or spec version\n");
251ad71d45eSYann Gautier }
252ad71d45eSYann Gautier
253ad71d45eSYann Gautier return ops->set_ios(clk, width);
254ad71d45eSYann Gautier }
255ad71d45eSYann Gautier
mmc_fill_device_info(void)256ad71d45eSYann Gautier static int mmc_fill_device_info(void)
257ad71d45eSYann Gautier {
258ad71d45eSYann Gautier unsigned long long c_size;
259ad71d45eSYann Gautier unsigned int speed_idx;
260ad71d45eSYann Gautier unsigned int nb_blocks;
261ad71d45eSYann Gautier unsigned int freq_unit;
262cadb36cbSAntonio Nino Diaz int ret = 0;
263ad71d45eSYann Gautier struct mmc_csd_sd_v2 *csd_sd_v2;
264ad71d45eSYann Gautier
26570eb88b7SYann Gautier switch (mmc_dev_info->mmc_dev_type) {
266ad71d45eSYann Gautier case MMC_IS_EMMC:
26770eb88b7SYann Gautier mmc_dev_info->block_size = MMC_BLOCK_SIZE;
268ad71d45eSYann Gautier
269ad71d45eSYann Gautier ret = ops->prepare(0, (uintptr_t)&mmc_ext_csd,
270ad71d45eSYann Gautier sizeof(mmc_ext_csd));
271ad71d45eSYann Gautier if (ret != 0) {
272ad71d45eSYann Gautier return ret;
273ad71d45eSYann Gautier }
274ad71d45eSYann Gautier
275ad71d45eSYann Gautier /* MMC CMD8: SEND_EXT_CSD */
27697d5db8cSYann Gautier ret = mmc_send_cmd(MMC_CMD(8), 0, MMC_RESPONSE_R1, NULL);
277ad71d45eSYann Gautier if (ret != 0) {
278ad71d45eSYann Gautier return ret;
279ad71d45eSYann Gautier }
280ad71d45eSYann Gautier
281ad71d45eSYann Gautier ret = ops->read(0, (uintptr_t)&mmc_ext_csd,
282ad71d45eSYann Gautier sizeof(mmc_ext_csd));
283ad71d45eSYann Gautier if (ret != 0) {
284ad71d45eSYann Gautier return ret;
285ad71d45eSYann Gautier }
286ad71d45eSYann Gautier
28793768644SHaojian Zhuang do {
28893768644SHaojian Zhuang ret = mmc_device_state();
28993768644SHaojian Zhuang if (ret < 0) {
29093768644SHaojian Zhuang return ret;
29193768644SHaojian Zhuang }
29293768644SHaojian Zhuang } while (ret != MMC_STATE_TRAN);
29393768644SHaojian Zhuang
294ad71d45eSYann Gautier nb_blocks = (mmc_ext_csd[CMD_EXTCSD_SEC_CNT] << 0) |
295ad71d45eSYann Gautier (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 1] << 8) |
296ad71d45eSYann Gautier (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 2] << 16) |
297ad71d45eSYann Gautier (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 3] << 24);
298ad71d45eSYann Gautier
29970eb88b7SYann Gautier mmc_dev_info->device_size = (unsigned long long)nb_blocks *
30070eb88b7SYann Gautier mmc_dev_info->block_size;
301ad71d45eSYann Gautier
302ad71d45eSYann Gautier break;
303ad71d45eSYann Gautier
304ad71d45eSYann Gautier case MMC_IS_SD:
305ad71d45eSYann Gautier /*
306ad71d45eSYann Gautier * Use the same mmc_csd struct, as required fields here
307ad71d45eSYann Gautier * (READ_BL_LEN, C_SIZE, CSIZE_MULT) are common with eMMC.
308ad71d45eSYann Gautier */
30970eb88b7SYann Gautier mmc_dev_info->block_size = BIT_32(mmc_csd.read_bl_len);
310ad71d45eSYann Gautier
311ad71d45eSYann Gautier c_size = ((unsigned long long)mmc_csd.c_size_high << 2U) |
312ad71d45eSYann Gautier (unsigned long long)mmc_csd.c_size_low;
313ad71d45eSYann Gautier assert(c_size != 0xFFFU);
314ad71d45eSYann Gautier
31570eb88b7SYann Gautier mmc_dev_info->device_size = (c_size + 1U) *
316ad71d45eSYann Gautier BIT_64(mmc_csd.c_size_mult + 2U) *
31770eb88b7SYann Gautier mmc_dev_info->block_size;
318ad71d45eSYann Gautier
319ad71d45eSYann Gautier break;
320ad71d45eSYann Gautier
321ad71d45eSYann Gautier case MMC_IS_SD_HC:
322ad71d45eSYann Gautier assert(mmc_csd.csd_structure == 1U);
323ad71d45eSYann Gautier
32470eb88b7SYann Gautier mmc_dev_info->block_size = MMC_BLOCK_SIZE;
325ad71d45eSYann Gautier
326ad71d45eSYann Gautier /* Need to use mmc_csd_sd_v2 struct */
327ad71d45eSYann Gautier csd_sd_v2 = (struct mmc_csd_sd_v2 *)&mmc_csd;
328ad71d45eSYann Gautier c_size = ((unsigned long long)csd_sd_v2->c_size_high << 16) |
329ad71d45eSYann Gautier (unsigned long long)csd_sd_v2->c_size_low;
330ad71d45eSYann Gautier
33170eb88b7SYann Gautier mmc_dev_info->device_size = (c_size + 1U) << MULT_BY_512K_SHIFT;
332ad71d45eSYann Gautier
333ad71d45eSYann Gautier break;
334ad71d45eSYann Gautier
335ad71d45eSYann Gautier default:
336ad71d45eSYann Gautier ret = -EINVAL;
337ad71d45eSYann Gautier break;
338ad71d45eSYann Gautier }
339ad71d45eSYann Gautier
3405f9984efSYann Gautier if (ret < 0) {
341ad71d45eSYann Gautier return ret;
342ad71d45eSYann Gautier }
343ad71d45eSYann Gautier
344ad71d45eSYann Gautier speed_idx = (mmc_csd.tran_speed & CSD_TRAN_SPEED_MULT_MASK) >>
345ad71d45eSYann Gautier CSD_TRAN_SPEED_MULT_SHIFT;
346ad71d45eSYann Gautier
347ad71d45eSYann Gautier assert(speed_idx > 0U);
348ad71d45eSYann Gautier
34970eb88b7SYann Gautier if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) {
35070eb88b7SYann Gautier mmc_dev_info->max_bus_freq = tran_speed_base[speed_idx];
351ad71d45eSYann Gautier } else {
35270eb88b7SYann Gautier mmc_dev_info->max_bus_freq = sd_tran_speed_base[speed_idx];
353ad71d45eSYann Gautier }
354ad71d45eSYann Gautier
355ad71d45eSYann Gautier freq_unit = mmc_csd.tran_speed & CSD_TRAN_SPEED_UNIT_MASK;
356ad71d45eSYann Gautier while (freq_unit != 0U) {
35770eb88b7SYann Gautier mmc_dev_info->max_bus_freq *= 10U;
358ad71d45eSYann Gautier --freq_unit;
359ad71d45eSYann Gautier }
360ad71d45eSYann Gautier
36170eb88b7SYann Gautier mmc_dev_info->max_bus_freq *= 10000U;
362ad71d45eSYann Gautier
363ad71d45eSYann Gautier return 0;
364ad71d45eSYann Gautier }
365ad71d45eSYann Gautier
sd_switch(unsigned int mode,unsigned char group,unsigned char func)366e5b267bbSYann Gautier static int sd_switch(unsigned int mode, unsigned char group,
367e5b267bbSYann Gautier unsigned char func)
368e5b267bbSYann Gautier {
369e5b267bbSYann Gautier unsigned int group_shift = (group - 1U) * 4U;
370e5b267bbSYann Gautier unsigned int group_mask = GENMASK(group_shift + 3U, group_shift);
371e5b267bbSYann Gautier unsigned int arg;
372e5b267bbSYann Gautier int ret;
373e5b267bbSYann Gautier
374e5b267bbSYann Gautier ret = ops->prepare(0, (uintptr_t)&sd_switch_func_status,
375e5b267bbSYann Gautier sizeof(sd_switch_func_status));
376e5b267bbSYann Gautier if (ret != 0) {
377e5b267bbSYann Gautier return ret;
378e5b267bbSYann Gautier }
379e5b267bbSYann Gautier
380e5b267bbSYann Gautier /* MMC CMD6: SWITCH_FUNC */
381e5b267bbSYann Gautier arg = mode | SD_SWITCH_ALL_GROUPS_MASK;
382e5b267bbSYann Gautier arg &= ~group_mask;
383e5b267bbSYann Gautier arg |= func << group_shift;
384e5b267bbSYann Gautier ret = mmc_send_cmd(MMC_CMD(6), arg, MMC_RESPONSE_R1, NULL);
385e5b267bbSYann Gautier if (ret != 0) {
386e5b267bbSYann Gautier return ret;
387e5b267bbSYann Gautier }
388e5b267bbSYann Gautier
389e5b267bbSYann Gautier return ops->read(0, (uintptr_t)&sd_switch_func_status,
390e5b267bbSYann Gautier sizeof(sd_switch_func_status));
391e5b267bbSYann Gautier }
392e5b267bbSYann Gautier
sd_send_op_cond(void)393ad71d45eSYann Gautier static int sd_send_op_cond(void)
394ad71d45eSYann Gautier {
39515e913d4SYann Gautier int n;
396ad71d45eSYann Gautier unsigned int resp_data[4];
397ad71d45eSYann Gautier
39815e913d4SYann Gautier for (n = 0; n < SEND_OP_COND_MAX_RETRIES; n++) {
399ad71d45eSYann Gautier int ret;
400ad71d45eSYann Gautier
401ad71d45eSYann Gautier /* CMD55: Application Specific Command */
40297d5db8cSYann Gautier ret = mmc_send_cmd(MMC_CMD(55), 0, MMC_RESPONSE_R1, NULL);
403ad71d45eSYann Gautier if (ret != 0) {
404ad71d45eSYann Gautier return ret;
405ad71d45eSYann Gautier }
406ad71d45eSYann Gautier
407ad71d45eSYann Gautier /* ACMD41: SD_SEND_OP_COND */
408a468e756STien Hock, Loh ret = mmc_send_cmd(MMC_ACMD(41), OCR_HCS |
40970eb88b7SYann Gautier mmc_dev_info->ocr_voltage, MMC_RESPONSE_R3,
410ad71d45eSYann Gautier &resp_data[0]);
411ad71d45eSYann Gautier if (ret != 0) {
412ad71d45eSYann Gautier return ret;
413ad71d45eSYann Gautier }
414ad71d45eSYann Gautier
41515e913d4SYann Gautier if ((resp_data[0] & OCR_POWERUP) != 0U) {
416ad71d45eSYann Gautier mmc_ocr_value = resp_data[0];
417ad71d45eSYann Gautier
418ad71d45eSYann Gautier if ((mmc_ocr_value & OCR_HCS) != 0U) {
41970eb88b7SYann Gautier mmc_dev_info->mmc_dev_type = MMC_IS_SD_HC;
420ad71d45eSYann Gautier } else {
42170eb88b7SYann Gautier mmc_dev_info->mmc_dev_type = MMC_IS_SD;
422ad71d45eSYann Gautier }
423ad71d45eSYann Gautier
424ad71d45eSYann Gautier return 0;
425ad71d45eSYann Gautier }
426ad71d45eSYann Gautier
42757f4b6f8SYann Gautier mdelay(10);
42815e913d4SYann Gautier }
42915e913d4SYann Gautier
43015e913d4SYann Gautier ERROR("ACMD41 failed after %d retries\n", SEND_OP_COND_MAX_RETRIES);
43115e913d4SYann Gautier
43215e913d4SYann Gautier return -EIO;
43315e913d4SYann Gautier }
43415e913d4SYann Gautier
mmc_reset_to_idle(void)43515e913d4SYann Gautier static int mmc_reset_to_idle(void)
436ad71d45eSYann Gautier {
437ad71d45eSYann Gautier int ret;
43815e913d4SYann Gautier
439ad71d45eSYann Gautier /* CMD0: reset to IDLE */
440*4e4e6b50SGhennadi Procopciuc ret = mmc_send_cmd(MMC_CMD(0), 0, MMC_RESPONSE_NONE, NULL);
441ad71d45eSYann Gautier if (ret != 0) {
442ad71d45eSYann Gautier return ret;
443ad71d45eSYann Gautier }
444ad71d45eSYann Gautier
44515e913d4SYann Gautier mdelay(2);
44615e913d4SYann Gautier
44715e913d4SYann Gautier return 0;
448ad71d45eSYann Gautier }
449ad71d45eSYann Gautier
mmc_send_op_cond(void)45015e913d4SYann Gautier static int mmc_send_op_cond(void)
45115e913d4SYann Gautier {
45215e913d4SYann Gautier int ret, n;
45315e913d4SYann Gautier unsigned int resp_data[4];
45415e913d4SYann Gautier
45515e913d4SYann Gautier for (n = 0; n < SEND_OP_COND_MAX_RETRIES; n++) {
456ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(1), OCR_SECTOR_MODE |
457ad71d45eSYann Gautier OCR_VDD_MIN_2V7 | OCR_VDD_MIN_1V7,
45894522ff7SBryan O'Donoghue MMC_RESPONSE_R3, &resp_data[0]);
459ad71d45eSYann Gautier if (ret != 0) {
460ad71d45eSYann Gautier return ret;
461ad71d45eSYann Gautier }
462ad71d45eSYann Gautier
46315e913d4SYann Gautier if ((resp_data[0] & OCR_POWERUP) != 0U) {
464ad71d45eSYann Gautier mmc_ocr_value = resp_data[0];
465ad71d45eSYann Gautier return 0;
466ad71d45eSYann Gautier }
467ad71d45eSYann Gautier
4687d639429SJoakim Bech mdelay(10);
46915e913d4SYann Gautier }
47015e913d4SYann Gautier
47115e913d4SYann Gautier ERROR("CMD1 failed after %d retries\n", SEND_OP_COND_MAX_RETRIES);
47215e913d4SYann Gautier
47315e913d4SYann Gautier return -EIO;
47415e913d4SYann Gautier }
47515e913d4SYann Gautier
mmc_enumerate(unsigned int clk,unsigned int bus_width)476ad71d45eSYann Gautier static int mmc_enumerate(unsigned int clk, unsigned int bus_width)
477ad71d45eSYann Gautier {
478ad71d45eSYann Gautier int ret;
479ad71d45eSYann Gautier unsigned int resp_data[4];
480ad71d45eSYann Gautier
481ad71d45eSYann Gautier ops->init();
482ad71d45eSYann Gautier
48377614a99SYann Gautier ret = mmc_reset_to_idle();
48477614a99SYann Gautier if (ret != 0) {
48577614a99SYann Gautier return ret;
4864ecd2417SYann Gautier }
487ad71d45eSYann Gautier
48870eb88b7SYann Gautier if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) {
489e74dc940SHaojian Zhuang ret = mmc_send_op_cond();
490e74dc940SHaojian Zhuang } else {
491ad71d45eSYann Gautier /* CMD8: Send Interface Condition Command */
492ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(8), VHS_2_7_3_6_V | CMD8_CHECK_PATTERN,
49397d5db8cSYann Gautier MMC_RESPONSE_R5, &resp_data[0]);
494ad71d45eSYann Gautier
495ad71d45eSYann Gautier if ((ret == 0) && ((resp_data[0] & 0xffU) == CMD8_CHECK_PATTERN)) {
496ad71d45eSYann Gautier ret = sd_send_op_cond();
497e74dc940SHaojian Zhuang }
498ad71d45eSYann Gautier }
499ad71d45eSYann Gautier if (ret != 0) {
500ad71d45eSYann Gautier return ret;
501ad71d45eSYann Gautier }
502ad71d45eSYann Gautier
503ad71d45eSYann Gautier /* CMD2: Card Identification */
504a2a69bc8SShawn Guo ret = mmc_send_cmd(MMC_CMD(2), 0, MMC_RESPONSE_R2, NULL);
505ad71d45eSYann Gautier if (ret != 0) {
506ad71d45eSYann Gautier return ret;
507ad71d45eSYann Gautier }
508ad71d45eSYann Gautier
509ad71d45eSYann Gautier /* CMD3: Set Relative Address */
51070eb88b7SYann Gautier if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) {
511ad71d45eSYann Gautier rca = MMC_FIX_RCA;
512ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(3), rca << RCA_SHIFT_OFFSET,
51397d5db8cSYann Gautier MMC_RESPONSE_R1, NULL);
514ad71d45eSYann Gautier if (ret != 0) {
515ad71d45eSYann Gautier return ret;
516ad71d45eSYann Gautier }
517ad71d45eSYann Gautier } else {
518ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(3), 0,
51997d5db8cSYann Gautier MMC_RESPONSE_R6, &resp_data[0]);
520ad71d45eSYann Gautier if (ret != 0) {
521ad71d45eSYann Gautier return ret;
522ad71d45eSYann Gautier }
523ad71d45eSYann Gautier
524ad71d45eSYann Gautier rca = (resp_data[0] & 0xFFFF0000U) >> 16;
525ad71d45eSYann Gautier }
526ad71d45eSYann Gautier
527ad71d45eSYann Gautier /* CMD9: CSD Register */
528ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(9), rca << RCA_SHIFT_OFFSET,
529a2a69bc8SShawn Guo MMC_RESPONSE_R2, &resp_data[0]);
530ad71d45eSYann Gautier if (ret != 0) {
531ad71d45eSYann Gautier return ret;
532ad71d45eSYann Gautier }
533ad71d45eSYann Gautier
534ad71d45eSYann Gautier memcpy(&mmc_csd, &resp_data, sizeof(resp_data));
535ad71d45eSYann Gautier
536ad71d45eSYann Gautier /* CMD7: Select Card */
537ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(7), rca << RCA_SHIFT_OFFSET,
53897d5db8cSYann Gautier MMC_RESPONSE_R1, NULL);
539ad71d45eSYann Gautier if (ret != 0) {
540ad71d45eSYann Gautier return ret;
541ad71d45eSYann Gautier }
542ad71d45eSYann Gautier
543ad71d45eSYann Gautier do {
544ad71d45eSYann Gautier ret = mmc_device_state();
545ad71d45eSYann Gautier if (ret < 0) {
546ad71d45eSYann Gautier return ret;
547ad71d45eSYann Gautier }
548ad71d45eSYann Gautier } while (ret != MMC_STATE_TRAN);
549ad71d45eSYann Gautier
550bd4e3deeSHaojian Zhuang ret = mmc_set_ios(clk, bus_width);
551ad71d45eSYann Gautier if (ret != 0) {
552ad71d45eSYann Gautier return ret;
553ad71d45eSYann Gautier }
554ad71d45eSYann Gautier
555e5b267bbSYann Gautier ret = mmc_fill_device_info();
556e5b267bbSYann Gautier if (ret != 0) {
557e5b267bbSYann Gautier return ret;
558e5b267bbSYann Gautier }
559e5b267bbSYann Gautier
560e5b267bbSYann Gautier if (is_sd_cmd6_enabled() &&
561e5b267bbSYann Gautier (mmc_dev_info->mmc_dev_type == MMC_IS_SD_HC)) {
562e5b267bbSYann Gautier /* Try to switch to High Speed Mode */
563e5b267bbSYann Gautier ret = sd_switch(SD_SWITCH_FUNC_CHECK, 1U, 1U);
564e5b267bbSYann Gautier if (ret != 0) {
565e5b267bbSYann Gautier return ret;
566e5b267bbSYann Gautier }
567e5b267bbSYann Gautier
568e5b267bbSYann Gautier if ((sd_switch_func_status.support_g1 & BIT(9)) == 0U) {
569e5b267bbSYann Gautier /* High speed not supported, keep default speed */
570e5b267bbSYann Gautier return 0;
571e5b267bbSYann Gautier }
572e5b267bbSYann Gautier
573e5b267bbSYann Gautier ret = sd_switch(SD_SWITCH_FUNC_SWITCH, 1U, 1U);
574e5b267bbSYann Gautier if (ret != 0) {
575e5b267bbSYann Gautier return ret;
576e5b267bbSYann Gautier }
577e5b267bbSYann Gautier
578e5b267bbSYann Gautier if ((sd_switch_func_status.sel_g2_g1 & 0x1U) == 0U) {
579e5b267bbSYann Gautier /* Cannot switch to high speed, keep default speed */
580e5b267bbSYann Gautier return 0;
581e5b267bbSYann Gautier }
582e5b267bbSYann Gautier
583e5b267bbSYann Gautier mmc_dev_info->max_bus_freq = 50000000U;
584e5b267bbSYann Gautier ret = ops->set_ios(clk, bus_width);
585e5b267bbSYann Gautier }
586e5b267bbSYann Gautier
587e5b267bbSYann Gautier return ret;
588ad71d45eSYann Gautier }
589ad71d45eSYann Gautier
mmc_read_blocks(int lba,uintptr_t buf,size_t size)590ea315a69SHaojian Zhuang size_t mmc_read_blocks(int lba, uintptr_t buf, size_t size)
591ad71d45eSYann Gautier {
592ad71d45eSYann Gautier int ret;
593ad71d45eSYann Gautier unsigned int cmd_idx, cmd_arg;
594ad71d45eSYann Gautier
595ad71d45eSYann Gautier assert((ops != NULL) &&
596ad71d45eSYann Gautier (ops->read != NULL) &&
597ad71d45eSYann Gautier (size != 0U) &&
598ad71d45eSYann Gautier ((size & MMC_BLOCK_MASK) == 0U));
599ad71d45eSYann Gautier
600ad71d45eSYann Gautier ret = ops->prepare(lba, buf, size);
601ad71d45eSYann Gautier if (ret != 0) {
602ad71d45eSYann Gautier return 0;
603ad71d45eSYann Gautier }
604ad71d45eSYann Gautier
605ad71d45eSYann Gautier if (is_cmd23_enabled()) {
606ad71d45eSYann Gautier /* Set block count */
607ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE,
60897d5db8cSYann Gautier MMC_RESPONSE_R1, NULL);
609ad71d45eSYann Gautier if (ret != 0) {
610ad71d45eSYann Gautier return 0;
611ad71d45eSYann Gautier }
612ad71d45eSYann Gautier
613ad71d45eSYann Gautier cmd_idx = MMC_CMD(18);
614ad71d45eSYann Gautier } else {
615ad71d45eSYann Gautier if (size > MMC_BLOCK_SIZE) {
616ad71d45eSYann Gautier cmd_idx = MMC_CMD(18);
617ad71d45eSYann Gautier } else {
618ad71d45eSYann Gautier cmd_idx = MMC_CMD(17);
619ad71d45eSYann Gautier }
620ad71d45eSYann Gautier }
621ad71d45eSYann Gautier
622ad71d45eSYann Gautier if (((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) &&
62370eb88b7SYann Gautier (mmc_dev_info->mmc_dev_type != MMC_IS_SD_HC)) {
624ad71d45eSYann Gautier cmd_arg = lba * MMC_BLOCK_SIZE;
625ad71d45eSYann Gautier } else {
626ad71d45eSYann Gautier cmd_arg = lba;
627ad71d45eSYann Gautier }
628ad71d45eSYann Gautier
62997d5db8cSYann Gautier ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R1, NULL);
630ad71d45eSYann Gautier if (ret != 0) {
631ad71d45eSYann Gautier return 0;
632ad71d45eSYann Gautier }
633ad71d45eSYann Gautier
634ad71d45eSYann Gautier ret = ops->read(lba, buf, size);
635ad71d45eSYann Gautier if (ret != 0) {
636ad71d45eSYann Gautier return 0;
637ad71d45eSYann Gautier }
638ad71d45eSYann Gautier
639ad71d45eSYann Gautier /* Wait buffer empty */
640ad71d45eSYann Gautier do {
641ad71d45eSYann Gautier ret = mmc_device_state();
642ad71d45eSYann Gautier if (ret < 0) {
643ad71d45eSYann Gautier return 0;
644ad71d45eSYann Gautier }
645ad71d45eSYann Gautier } while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_DATA));
646ad71d45eSYann Gautier
647ad71d45eSYann Gautier if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) {
64861752898SBryan O'Donoghue ret = mmc_send_cmd(MMC_CMD(12), 0, MMC_RESPONSE_R1B, NULL);
649ad71d45eSYann Gautier if (ret != 0) {
650ad71d45eSYann Gautier return 0;
651ad71d45eSYann Gautier }
652ad71d45eSYann Gautier }
653ad71d45eSYann Gautier
654ad71d45eSYann Gautier return size;
655ad71d45eSYann Gautier }
656ad71d45eSYann Gautier
mmc_write_blocks(int lba,const uintptr_t buf,size_t size)657ea315a69SHaojian Zhuang size_t mmc_write_blocks(int lba, const uintptr_t buf, size_t size)
658ad71d45eSYann Gautier {
659ad71d45eSYann Gautier int ret;
660ad71d45eSYann Gautier unsigned int cmd_idx, cmd_arg;
661ad71d45eSYann Gautier
662ad71d45eSYann Gautier assert((ops != NULL) &&
663ad71d45eSYann Gautier (ops->write != NULL) &&
664ad71d45eSYann Gautier (size != 0U) &&
665ad71d45eSYann Gautier ((buf & MMC_BLOCK_MASK) == 0U) &&
666ad71d45eSYann Gautier ((size & MMC_BLOCK_MASK) == 0U));
667ad71d45eSYann Gautier
668ad71d45eSYann Gautier ret = ops->prepare(lba, buf, size);
669ad71d45eSYann Gautier if (ret != 0) {
670ad71d45eSYann Gautier return 0;
671ad71d45eSYann Gautier }
672ad71d45eSYann Gautier
673ad71d45eSYann Gautier if (is_cmd23_enabled()) {
674ad71d45eSYann Gautier /* Set block count */
675ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE,
67697d5db8cSYann Gautier MMC_RESPONSE_R1, NULL);
677ad71d45eSYann Gautier if (ret != 0) {
678ad71d45eSYann Gautier return 0;
679ad71d45eSYann Gautier }
680ad71d45eSYann Gautier
681ad71d45eSYann Gautier cmd_idx = MMC_CMD(25);
682ad71d45eSYann Gautier } else {
683ad71d45eSYann Gautier if (size > MMC_BLOCK_SIZE) {
684ad71d45eSYann Gautier cmd_idx = MMC_CMD(25);
685ad71d45eSYann Gautier } else {
686ad71d45eSYann Gautier cmd_idx = MMC_CMD(24);
687ad71d45eSYann Gautier }
688ad71d45eSYann Gautier }
689ad71d45eSYann Gautier
690ad71d45eSYann Gautier if ((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) {
691ad71d45eSYann Gautier cmd_arg = lba * MMC_BLOCK_SIZE;
692ad71d45eSYann Gautier } else {
693ad71d45eSYann Gautier cmd_arg = lba;
694ad71d45eSYann Gautier }
695ad71d45eSYann Gautier
69697d5db8cSYann Gautier ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R1, NULL);
697ad71d45eSYann Gautier if (ret != 0) {
698ad71d45eSYann Gautier return 0;
699ad71d45eSYann Gautier }
700ad71d45eSYann Gautier
701ad71d45eSYann Gautier ret = ops->write(lba, buf, size);
702ad71d45eSYann Gautier if (ret != 0) {
703ad71d45eSYann Gautier return 0;
704ad71d45eSYann Gautier }
705ad71d45eSYann Gautier
706ad71d45eSYann Gautier /* Wait buffer empty */
707ad71d45eSYann Gautier do {
708ad71d45eSYann Gautier ret = mmc_device_state();
709ad71d45eSYann Gautier if (ret < 0) {
710ad71d45eSYann Gautier return 0;
711ad71d45eSYann Gautier }
712ad71d45eSYann Gautier } while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_RCV));
713ad71d45eSYann Gautier
714ad71d45eSYann Gautier if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) {
71561752898SBryan O'Donoghue ret = mmc_send_cmd(MMC_CMD(12), 0, MMC_RESPONSE_R1B, NULL);
716ad71d45eSYann Gautier if (ret != 0) {
717ad71d45eSYann Gautier return 0;
718ad71d45eSYann Gautier }
719ad71d45eSYann Gautier }
720ad71d45eSYann Gautier
721ad71d45eSYann Gautier return size;
722ad71d45eSYann Gautier }
723ad71d45eSYann Gautier
mmc_erase_blocks(int lba,size_t size)724ea315a69SHaojian Zhuang size_t mmc_erase_blocks(int lba, size_t size)
725ad71d45eSYann Gautier {
726ad71d45eSYann Gautier int ret;
727ad71d45eSYann Gautier
728ad71d45eSYann Gautier assert(ops != NULL);
729ad71d45eSYann Gautier assert((size != 0U) && ((size & MMC_BLOCK_MASK) == 0U));
730ad71d45eSYann Gautier
73197d5db8cSYann Gautier ret = mmc_send_cmd(MMC_CMD(35), lba, MMC_RESPONSE_R1, NULL);
732ad71d45eSYann Gautier if (ret != 0) {
733ad71d45eSYann Gautier return 0;
734ad71d45eSYann Gautier }
735ad71d45eSYann Gautier
736ad71d45eSYann Gautier ret = mmc_send_cmd(MMC_CMD(36), lba + (size / MMC_BLOCK_SIZE) - 1U,
73797d5db8cSYann Gautier MMC_RESPONSE_R1, NULL);
738ad71d45eSYann Gautier if (ret != 0) {
739ad71d45eSYann Gautier return 0;
740ad71d45eSYann Gautier }
741ad71d45eSYann Gautier
74297d5db8cSYann Gautier ret = mmc_send_cmd(MMC_CMD(38), lba, MMC_RESPONSE_R1B, NULL);
743ad71d45eSYann Gautier if (ret != 0) {
744ad71d45eSYann Gautier return 0;
745ad71d45eSYann Gautier }
746ad71d45eSYann Gautier
747ad71d45eSYann Gautier do {
748ad71d45eSYann Gautier ret = mmc_device_state();
749ad71d45eSYann Gautier if (ret < 0) {
750ad71d45eSYann Gautier return 0;
751ad71d45eSYann Gautier }
752ad71d45eSYann Gautier } while (ret != MMC_STATE_TRAN);
753ad71d45eSYann Gautier
754ad71d45eSYann Gautier return size;
755ad71d45eSYann Gautier }
756ad71d45eSYann Gautier
mmc_part_switch(unsigned char part_type)75753cbc949SYann Gautier static int mmc_part_switch(unsigned char part_type)
7585014b52dSVyacheslav Yurkov {
75953cbc949SYann Gautier unsigned char part_config = mmc_ext_csd[CMD_EXTCSD_PARTITION_CONFIG];
7605014b52dSVyacheslav Yurkov
7615014b52dSVyacheslav Yurkov part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
7625014b52dSVyacheslav Yurkov part_config |= part_type;
7635014b52dSVyacheslav Yurkov
7645014b52dSVyacheslav Yurkov return mmc_send_part_switch_cmd(part_config);
7655014b52dSVyacheslav Yurkov }
7665014b52dSVyacheslav Yurkov
mmc_current_boot_part(void)7675014b52dSVyacheslav Yurkov static unsigned char mmc_current_boot_part(void)
7685014b52dSVyacheslav Yurkov {
7695014b52dSVyacheslav Yurkov return PART_CFG_CURRENT_BOOT_PARTITION(mmc_ext_csd[CMD_EXTCSD_PARTITION_CONFIG]);
7705014b52dSVyacheslav Yurkov }
7715014b52dSVyacheslav Yurkov
mmc_part_switch_current_boot(void)772f85041a6SAhmad Fatoum int mmc_part_switch_current_boot(void)
7735014b52dSVyacheslav Yurkov {
7745014b52dSVyacheslav Yurkov unsigned char current_boot_part = mmc_current_boot_part();
775f85041a6SAhmad Fatoum int ret;
7765014b52dSVyacheslav Yurkov
77714cda516SYann Gautier if ((current_boot_part != 1U) && (current_boot_part != 2U)) {
7785014b52dSVyacheslav Yurkov ERROR("Got unexpected value for active boot partition, %u\n", current_boot_part);
779f85041a6SAhmad Fatoum return -EIO;
7805014b52dSVyacheslav Yurkov }
7815014b52dSVyacheslav Yurkov
7825014b52dSVyacheslav Yurkov ret = mmc_part_switch(current_boot_part);
7835014b52dSVyacheslav Yurkov if (ret < 0) {
7845014b52dSVyacheslav Yurkov ERROR("Failed to switch to boot partition, %d\n", ret);
785f85041a6SAhmad Fatoum }
786f85041a6SAhmad Fatoum
787f85041a6SAhmad Fatoum return ret;
788f85041a6SAhmad Fatoum }
789f85041a6SAhmad Fatoum
mmc_part_switch_user(void)790f85041a6SAhmad Fatoum int mmc_part_switch_user(void)
791f85041a6SAhmad Fatoum {
792f85041a6SAhmad Fatoum int ret;
793f85041a6SAhmad Fatoum
79401c5dd5eSAhmad Fatoum ret = mmc_part_switch(PART_CFG_BOOT_PARTITION_NO_ACCESS);
795f85041a6SAhmad Fatoum if (ret < 0) {
796f85041a6SAhmad Fatoum ERROR("Failed to switch to user partition, %d\n", ret);
797f85041a6SAhmad Fatoum }
798f85041a6SAhmad Fatoum
799f85041a6SAhmad Fatoum return ret;
800f85041a6SAhmad Fatoum }
801f85041a6SAhmad Fatoum
mmc_boot_part_size(void)802f462c124SYann Gautier size_t mmc_boot_part_size(void)
803f462c124SYann Gautier {
804f462c124SYann Gautier return mmc_ext_csd[CMD_EXTCSD_BOOT_SIZE_MULT] * SZ_128K;
805f462c124SYann Gautier }
806f462c124SYann Gautier
mmc_boot_part_read_blocks(int lba,uintptr_t buf,size_t size)807f85041a6SAhmad Fatoum size_t mmc_boot_part_read_blocks(int lba, uintptr_t buf, size_t size)
808f85041a6SAhmad Fatoum {
809f85041a6SAhmad Fatoum size_t size_read;
810f85041a6SAhmad Fatoum int ret;
811f85041a6SAhmad Fatoum
812f85041a6SAhmad Fatoum ret = mmc_part_switch_current_boot();
813f85041a6SAhmad Fatoum if (ret < 0) {
8145014b52dSVyacheslav Yurkov return 0;
8155014b52dSVyacheslav Yurkov }
8165014b52dSVyacheslav Yurkov
8175014b52dSVyacheslav Yurkov size_read = mmc_read_blocks(lba, buf, size);
8185014b52dSVyacheslav Yurkov
819f85041a6SAhmad Fatoum ret = mmc_part_switch_user();
8205014b52dSVyacheslav Yurkov if (ret < 0) {
8215014b52dSVyacheslav Yurkov return 0;
8225014b52dSVyacheslav Yurkov }
8235014b52dSVyacheslav Yurkov
8245014b52dSVyacheslav Yurkov return size_read;
8255014b52dSVyacheslav Yurkov }
8265014b52dSVyacheslav Yurkov
mmc_init(const struct mmc_ops * ops_ptr,unsigned int clk,unsigned int width,unsigned int flags,struct mmc_device_info * device_info)827ad71d45eSYann Gautier int mmc_init(const struct mmc_ops *ops_ptr, unsigned int clk,
828ad71d45eSYann Gautier unsigned int width, unsigned int flags,
829ad71d45eSYann Gautier struct mmc_device_info *device_info)
830ad71d45eSYann Gautier {
831ad71d45eSYann Gautier assert((ops_ptr != NULL) &&
832ad71d45eSYann Gautier (ops_ptr->init != NULL) &&
833ad71d45eSYann Gautier (ops_ptr->send_cmd != NULL) &&
834ad71d45eSYann Gautier (ops_ptr->set_ios != NULL) &&
835ad71d45eSYann Gautier (ops_ptr->prepare != NULL) &&
836ad71d45eSYann Gautier (ops_ptr->read != NULL) &&
837ad71d45eSYann Gautier (ops_ptr->write != NULL) &&
838ad71d45eSYann Gautier (device_info != NULL) &&
839ad71d45eSYann Gautier (clk != 0) &&
840ad71d45eSYann Gautier ((width == MMC_BUS_WIDTH_1) ||
841ad71d45eSYann Gautier (width == MMC_BUS_WIDTH_4) ||
842ad71d45eSYann Gautier (width == MMC_BUS_WIDTH_8) ||
843ad71d45eSYann Gautier (width == MMC_BUS_WIDTH_DDR_4) ||
844ad71d45eSYann Gautier (width == MMC_BUS_WIDTH_DDR_8)));
845ad71d45eSYann Gautier
846ad71d45eSYann Gautier ops = ops_ptr;
847ad71d45eSYann Gautier mmc_flags = flags;
84870eb88b7SYann Gautier mmc_dev_info = device_info;
849ad71d45eSYann Gautier
850ad71d45eSYann Gautier return mmc_enumerate(clk, width);
851ad71d45eSYann Gautier }
852