1 /*
2 * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 * Defines a simple and generic interface to access eMMC device.
7 */
8
9 #include <arch_helpers.h>
10 #include <assert.h>
11 #include <debug.h>
12 #include <emmc.h>
13 #include <errno.h>
14 #include <string.h>
15 #include <utils.h>
16
17 static const emmc_ops_t *ops;
18 static unsigned int emmc_ocr_value;
19 static emmc_csd_t emmc_csd;
20 static unsigned int emmc_flags;
21
is_cmd23_enabled(void)22 static int is_cmd23_enabled(void)
23 {
24 return (!!(emmc_flags & EMMC_FLAG_CMD23));
25 }
26
emmc_device_state(void)27 static int emmc_device_state(void)
28 {
29 emmc_cmd_t cmd;
30 int ret;
31
32 do {
33 zeromem(&cmd, sizeof(emmc_cmd_t));
34 cmd.cmd_idx = EMMC_CMD13;
35 cmd.cmd_arg = EMMC_FIX_RCA << RCA_SHIFT_OFFSET;
36 cmd.resp_type = EMMC_RESPONSE_R1;
37 ret = ops->send_cmd(&cmd);
38 assert(ret == 0);
39 assert((cmd.resp_data[0] & STATUS_SWITCH_ERROR) == 0);
40 /* Ignore improbable errors in release builds */
41 (void)ret;
42 } while ((cmd.resp_data[0] & STATUS_READY_FOR_DATA) == 0);
43 return EMMC_GET_STATE(cmd.resp_data[0]);
44 }
45
emmc_set_ext_csd(unsigned int ext_cmd,unsigned int value)46 static void emmc_set_ext_csd(unsigned int ext_cmd, unsigned int value)
47 {
48 emmc_cmd_t cmd;
49 int ret, state;
50
51 zeromem(&cmd, sizeof(emmc_cmd_t));
52 cmd.cmd_idx = EMMC_CMD6;
53 cmd.cmd_arg = EXTCSD_WRITE_BYTES | EXTCSD_CMD(ext_cmd) |
54 EXTCSD_VALUE(value) | 1;
55 ret = ops->send_cmd(&cmd);
56 assert(ret == 0);
57
58 /* wait to exit PRG state */
59 do {
60 state = emmc_device_state();
61 } while (state == EMMC_STATE_PRG);
62 /* Ignore improbable errors in release builds */
63 (void)ret;
64 }
65
emmc_set_ios(int clk,int bus_width)66 static void emmc_set_ios(int clk, int bus_width)
67 {
68 int ret;
69
70 /* set IO speed & IO bus width */
71 if (emmc_csd.spec_vers == 4)
72 emmc_set_ext_csd(CMD_EXTCSD_BUS_WIDTH, bus_width);
73 ret = ops->set_ios(clk, bus_width);
74 assert(ret == 0);
75 /* Ignore improbable errors in release builds */
76 (void)ret;
77 }
78
emmc_enumerate(int clk,int bus_width)79 static int emmc_enumerate(int clk, int bus_width)
80 {
81 emmc_cmd_t cmd;
82 int ret, state;
83
84 ops->init();
85
86 /* CMD0: reset to IDLE */
87 zeromem(&cmd, sizeof(emmc_cmd_t));
88 cmd.cmd_idx = EMMC_CMD0;
89 ret = ops->send_cmd(&cmd);
90 assert(ret == 0);
91
92 while (1) {
93 /* CMD1: get OCR register */
94 zeromem(&cmd, sizeof(emmc_cmd_t));
95 cmd.cmd_idx = EMMC_CMD1;
96 cmd.cmd_arg = OCR_SECTOR_MODE | OCR_VDD_MIN_2V7 |
97 OCR_VDD_MIN_1V7;
98 cmd.resp_type = EMMC_RESPONSE_R3;
99 ret = ops->send_cmd(&cmd);
100 assert(ret == 0);
101 emmc_ocr_value = cmd.resp_data[0];
102 if (emmc_ocr_value & OCR_POWERUP)
103 break;
104 }
105
106 /* CMD2: Card Identification */
107 zeromem(&cmd, sizeof(emmc_cmd_t));
108 cmd.cmd_idx = EMMC_CMD2;
109 cmd.resp_type = EMMC_RESPONSE_R2;
110 ret = ops->send_cmd(&cmd);
111 assert(ret == 0);
112
113 /* CMD3: Set Relative Address */
114 zeromem(&cmd, sizeof(emmc_cmd_t));
115 cmd.cmd_idx = EMMC_CMD3;
116 cmd.cmd_arg = EMMC_FIX_RCA << RCA_SHIFT_OFFSET;
117 cmd.resp_type = EMMC_RESPONSE_R1;
118 ret = ops->send_cmd(&cmd);
119 assert(ret == 0);
120
121 /* CMD9: CSD Register */
122 zeromem(&cmd, sizeof(emmc_cmd_t));
123 cmd.cmd_idx = EMMC_CMD9;
124 cmd.cmd_arg = EMMC_FIX_RCA << RCA_SHIFT_OFFSET;
125 cmd.resp_type = EMMC_RESPONSE_R2;
126 ret = ops->send_cmd(&cmd);
127 assert(ret == 0);
128 memcpy(&emmc_csd, &cmd.resp_data, sizeof(cmd.resp_data));
129
130 /* CMD7: Select Card */
131 zeromem(&cmd, sizeof(emmc_cmd_t));
132 cmd.cmd_idx = EMMC_CMD7;
133 cmd.cmd_arg = EMMC_FIX_RCA << RCA_SHIFT_OFFSET;
134 cmd.resp_type = EMMC_RESPONSE_R1;
135 ret = ops->send_cmd(&cmd);
136 assert(ret == 0);
137 /* wait to TRAN state */
138 do {
139 state = emmc_device_state();
140 } while (state != EMMC_STATE_TRAN);
141
142 emmc_set_ios(clk, bus_width);
143 return ret;
144 }
145
emmc_read_blocks(int lba,uintptr_t buf,size_t size)146 size_t emmc_read_blocks(int lba, uintptr_t buf, size_t size)
147 {
148 emmc_cmd_t cmd;
149 int ret;
150
151 assert((ops != 0) &&
152 (ops->read != 0) &&
153 ((buf & EMMC_BLOCK_MASK) == 0) &&
154 ((size & EMMC_BLOCK_MASK) == 0));
155
156 inv_dcache_range(buf, size);
157 ret = ops->prepare(lba, buf, size);
158 assert(ret == 0);
159
160 if (is_cmd23_enabled()) {
161 zeromem(&cmd, sizeof(emmc_cmd_t));
162 /* set block count */
163 cmd.cmd_idx = EMMC_CMD23;
164 cmd.cmd_arg = size / EMMC_BLOCK_SIZE;
165 cmd.resp_type = EMMC_RESPONSE_R1;
166 ret = ops->send_cmd(&cmd);
167 assert(ret == 0);
168
169 zeromem(&cmd, sizeof(emmc_cmd_t));
170 cmd.cmd_idx = EMMC_CMD18;
171 } else {
172 if (size > EMMC_BLOCK_SIZE)
173 cmd.cmd_idx = EMMC_CMD18;
174 else
175 cmd.cmd_idx = EMMC_CMD17;
176 }
177 if ((emmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE)
178 cmd.cmd_arg = lba * EMMC_BLOCK_SIZE;
179 else
180 cmd.cmd_arg = lba;
181 cmd.resp_type = EMMC_RESPONSE_R1;
182 ret = ops->send_cmd(&cmd);
183 assert(ret == 0);
184
185 ret = ops->read(lba, buf, size);
186 assert(ret == 0);
187
188 /* wait buffer empty */
189 emmc_device_state();
190
191 if (is_cmd23_enabled() == 0) {
192 if (size > EMMC_BLOCK_SIZE) {
193 zeromem(&cmd, sizeof(emmc_cmd_t));
194 cmd.cmd_idx = EMMC_CMD12;
195 ret = ops->send_cmd(&cmd);
196 assert(ret == 0);
197 }
198 }
199 /* Ignore improbable errors in release builds */
200 (void)ret;
201 return size;
202 }
203
emmc_write_blocks(int lba,const uintptr_t buf,size_t size)204 size_t emmc_write_blocks(int lba, const uintptr_t buf, size_t size)
205 {
206 emmc_cmd_t cmd;
207 int ret;
208
209 assert((ops != 0) &&
210 (ops->write != 0) &&
211 ((buf & EMMC_BLOCK_MASK) == 0) &&
212 ((size & EMMC_BLOCK_MASK) == 0));
213
214 clean_dcache_range(buf, size);
215 ret = ops->prepare(lba, buf, size);
216 assert(ret == 0);
217
218 if (is_cmd23_enabled()) {
219 /* set block count */
220 zeromem(&cmd, sizeof(emmc_cmd_t));
221 cmd.cmd_idx = EMMC_CMD23;
222 cmd.cmd_arg = size / EMMC_BLOCK_SIZE;
223 cmd.resp_type = EMMC_RESPONSE_R1;
224 ret = ops->send_cmd(&cmd);
225 assert(ret == 0);
226
227 zeromem(&cmd, sizeof(emmc_cmd_t));
228 cmd.cmd_idx = EMMC_CMD25;
229 } else {
230 zeromem(&cmd, sizeof(emmc_cmd_t));
231 if (size > EMMC_BLOCK_SIZE)
232 cmd.cmd_idx = EMMC_CMD25;
233 else
234 cmd.cmd_idx = EMMC_CMD24;
235 }
236 if ((emmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE)
237 cmd.cmd_arg = lba * EMMC_BLOCK_SIZE;
238 else
239 cmd.cmd_arg = lba;
240 cmd.resp_type = EMMC_RESPONSE_R1;
241 ret = ops->send_cmd(&cmd);
242 assert(ret == 0);
243
244 ret = ops->write(lba, buf, size);
245 assert(ret == 0);
246
247 /* wait buffer empty */
248 emmc_device_state();
249
250 if (is_cmd23_enabled() == 0) {
251 if (size > EMMC_BLOCK_SIZE) {
252 zeromem(&cmd, sizeof(emmc_cmd_t));
253 cmd.cmd_idx = EMMC_CMD12;
254 ret = ops->send_cmd(&cmd);
255 assert(ret == 0);
256 }
257 }
258 /* Ignore improbable errors in release builds */
259 (void)ret;
260 return size;
261 }
262
emmc_erase_blocks(int lba,size_t size)263 size_t emmc_erase_blocks(int lba, size_t size)
264 {
265 emmc_cmd_t cmd;
266 int ret, state;
267
268 assert(ops != 0);
269 assert((size != 0) && ((size % EMMC_BLOCK_SIZE) == 0));
270
271 zeromem(&cmd, sizeof(emmc_cmd_t));
272 cmd.cmd_idx = EMMC_CMD35;
273 cmd.cmd_arg = lba;
274 cmd.resp_type = EMMC_RESPONSE_R1;
275 ret = ops->send_cmd(&cmd);
276 assert(ret == 0);
277
278 zeromem(&cmd, sizeof(emmc_cmd_t));
279 cmd.cmd_idx = EMMC_CMD36;
280 cmd.cmd_arg = lba + (size / EMMC_BLOCK_SIZE) - 1;
281 cmd.resp_type = EMMC_RESPONSE_R1;
282 ret = ops->send_cmd(&cmd);
283 assert(ret == 0);
284
285 zeromem(&cmd, sizeof(emmc_cmd_t));
286 cmd.cmd_idx = EMMC_CMD38;
287 cmd.resp_type = EMMC_RESPONSE_R1B;
288 ret = ops->send_cmd(&cmd);
289 assert(ret == 0);
290
291 /* wait to TRAN state */
292 do {
293 state = emmc_device_state();
294 } while (state != EMMC_STATE_TRAN);
295 /* Ignore improbable errors in release builds */
296 (void)ret;
297 return size;
298 }
299
emmc_rpmb_enable(void)300 static inline void emmc_rpmb_enable(void)
301 {
302 emmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG,
303 PART_CFG_BOOT_PARTITION1_ENABLE |
304 PART_CFG_PARTITION1_ACCESS);
305 }
306
emmc_rpmb_disable(void)307 static inline void emmc_rpmb_disable(void)
308 {
309 emmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG,
310 PART_CFG_BOOT_PARTITION1_ENABLE);
311 }
312
emmc_rpmb_read_blocks(int lba,uintptr_t buf,size_t size)313 size_t emmc_rpmb_read_blocks(int lba, uintptr_t buf, size_t size)
314 {
315 size_t size_read;
316
317 emmc_rpmb_enable();
318 size_read = emmc_read_blocks(lba, buf, size);
319 emmc_rpmb_disable();
320 return size_read;
321 }
322
emmc_rpmb_write_blocks(int lba,const uintptr_t buf,size_t size)323 size_t emmc_rpmb_write_blocks(int lba, const uintptr_t buf, size_t size)
324 {
325 size_t size_written;
326
327 emmc_rpmb_enable();
328 size_written = emmc_write_blocks(lba, buf, size);
329 emmc_rpmb_disable();
330 return size_written;
331 }
332
emmc_rpmb_erase_blocks(int lba,size_t size)333 size_t emmc_rpmb_erase_blocks(int lba, size_t size)
334 {
335 size_t size_erased;
336
337 emmc_rpmb_enable();
338 size_erased = emmc_erase_blocks(lba, size);
339 emmc_rpmb_disable();
340 return size_erased;
341 }
342
emmc_init(const emmc_ops_t * ops_ptr,int clk,int width,unsigned int flags)343 void emmc_init(const emmc_ops_t *ops_ptr, int clk, int width,
344 unsigned int flags)
345 {
346 assert((ops_ptr != 0) &&
347 (ops_ptr->init != 0) &&
348 (ops_ptr->send_cmd != 0) &&
349 (ops_ptr->set_ios != 0) &&
350 (ops_ptr->prepare != 0) &&
351 (ops_ptr->read != 0) &&
352 (ops_ptr->write != 0) &&
353 (clk != 0) &&
354 ((width == EMMC_BUS_WIDTH_1) ||
355 (width == EMMC_BUS_WIDTH_4) ||
356 (width == EMMC_BUS_WIDTH_8)));
357 ops = ops_ptr;
358 emmc_flags = flags;
359
360 emmc_enumerate(clk, width);
361 }
362