blob: 3fae2a15b8b5713265d2414cc2f2ffeca0b47cfd [file] [log] [blame]
Haojian Zhuangfffe9e72016-03-18 22:08:26 +08001/*
2 * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * Neither the name of ARM nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without specific
16 * prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 *
30 * Defines a simple and generic interface to access eMMC device.
31 */
32
33#include <arch_helpers.h>
34#include <assert.h>
35#include <debug.h>
36#include <emmc.h>
37#include <errno.h>
38#include <string.h>
39
40static const emmc_ops_t *ops;
41static unsigned int emmc_ocr_value;
42static emmc_csd_t emmc_csd;
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +080043static unsigned int emmc_flags;
44
45static int is_cmd23_enabled(void)
46{
47 return (!!(emmc_flags & EMMC_FLAG_CMD23));
48}
Haojian Zhuangfffe9e72016-03-18 22:08:26 +080049
50static int emmc_device_state(void)
51{
52 emmc_cmd_t cmd;
53 int ret;
54
55 do {
56 memset(&cmd, 0, sizeof(emmc_cmd_t));
57 cmd.cmd_idx = EMMC_CMD13;
58 cmd.cmd_arg = EMMC_FIX_RCA << RCA_SHIFT_OFFSET;
59 cmd.resp_type = EMMC_RESPONSE_R1;
60 ret = ops->send_cmd(&cmd);
61 assert(ret == 0);
62 assert((cmd.resp_data[0] & STATUS_SWITCH_ERROR) == 0);
63 /* Ignore improbable errors in release builds */
64 (void)ret;
65 } while ((cmd.resp_data[0] & STATUS_READY_FOR_DATA) == 0);
66 return EMMC_GET_STATE(cmd.resp_data[0]);
67}
68
69static void emmc_set_ext_csd(unsigned int ext_cmd, unsigned int value)
70{
71 emmc_cmd_t cmd;
72 int ret, state;
73
74 memset(&cmd, 0, sizeof(emmc_cmd_t));
75 cmd.cmd_idx = EMMC_CMD6;
76 cmd.cmd_arg = EXTCSD_WRITE_BYTES | EXTCSD_CMD(ext_cmd) |
77 EXTCSD_VALUE(value) | 1;
78 ret = ops->send_cmd(&cmd);
79 assert(ret == 0);
80
81 /* wait to exit PRG state */
82 do {
83 state = emmc_device_state();
84 } while (state == EMMC_STATE_PRG);
85 /* Ignore improbable errors in release builds */
86 (void)ret;
87}
88
89static void emmc_set_ios(int clk, int bus_width)
90{
91 int ret;
92
93 /* set IO speed & IO bus width */
94 if (emmc_csd.spec_vers == 4)
95 emmc_set_ext_csd(CMD_EXTCSD_BUS_WIDTH, bus_width);
96 ret = ops->set_ios(clk, bus_width);
97 assert(ret == 0);
98 /* Ignore improbable errors in release builds */
99 (void)ret;
100}
101
102static int emmc_enumerate(int clk, int bus_width)
103{
104 emmc_cmd_t cmd;
105 int ret, state;
106
107 ops->init();
108
109 /* CMD0: reset to IDLE */
110 memset(&cmd, 0, sizeof(emmc_cmd_t));
111 cmd.cmd_idx = EMMC_CMD0;
112 ret = ops->send_cmd(&cmd);
113 assert(ret == 0);
114
115 while (1) {
116 /* CMD1: get OCR register */
117 memset(&cmd, 0, sizeof(emmc_cmd_t));
118 cmd.cmd_idx = EMMC_CMD1;
119 cmd.cmd_arg = OCR_SECTOR_MODE | OCR_VDD_MIN_2V7 |
120 OCR_VDD_MIN_1V7;
121 cmd.resp_type = EMMC_RESPONSE_R3;
122 ret = ops->send_cmd(&cmd);
123 assert(ret == 0);
124 emmc_ocr_value = cmd.resp_data[0];
125 if (emmc_ocr_value & OCR_POWERUP)
126 break;
127 }
128
129 /* CMD2: Card Identification */
130 memset(&cmd, 0, sizeof(emmc_cmd_t));
131 cmd.cmd_idx = EMMC_CMD2;
132 cmd.resp_type = EMMC_RESPONSE_R2;
133 ret = ops->send_cmd(&cmd);
134 assert(ret == 0);
135
136 /* CMD3: Set Relative Address */
137 memset(&cmd, 0, sizeof(emmc_cmd_t));
138 cmd.cmd_idx = EMMC_CMD3;
139 cmd.cmd_arg = EMMC_FIX_RCA << RCA_SHIFT_OFFSET;
140 cmd.resp_type = EMMC_RESPONSE_R1;
141 ret = ops->send_cmd(&cmd);
142 assert(ret == 0);
143
144 /* CMD9: CSD Register */
145 memset(&cmd, 0, sizeof(emmc_cmd_t));
146 cmd.cmd_idx = EMMC_CMD9;
147 cmd.cmd_arg = EMMC_FIX_RCA << RCA_SHIFT_OFFSET;
148 cmd.resp_type = EMMC_RESPONSE_R2;
149 ret = ops->send_cmd(&cmd);
150 assert(ret == 0);
151 memcpy(&emmc_csd, &cmd.resp_data, sizeof(cmd.resp_data));
152
153 /* CMD7: Select Card */
154 memset(&cmd, 0, sizeof(emmc_cmd_t));
155 cmd.cmd_idx = EMMC_CMD7;
156 cmd.cmd_arg = EMMC_FIX_RCA << RCA_SHIFT_OFFSET;
157 cmd.resp_type = EMMC_RESPONSE_R1;
158 ret = ops->send_cmd(&cmd);
159 assert(ret == 0);
160 /* wait to TRAN state */
161 do {
162 state = emmc_device_state();
163 } while (state != EMMC_STATE_TRAN);
164
165 emmc_set_ios(clk, bus_width);
166 return ret;
167}
168
169size_t emmc_read_blocks(int lba, uintptr_t buf, size_t size)
170{
171 emmc_cmd_t cmd;
172 int ret;
173
174 assert((ops != 0) &&
175 (ops->read != 0) &&
176 ((buf & EMMC_BLOCK_MASK) == 0) &&
177 ((size & EMMC_BLOCK_MASK) == 0));
178
179 inv_dcache_range(buf, size);
180 ret = ops->prepare(lba, buf, size);
181 assert(ret == 0);
182
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800183 if (is_cmd23_enabled()) {
184 memset(&cmd, 0, sizeof(emmc_cmd_t));
185 /* set block count */
186 cmd.cmd_idx = EMMC_CMD23;
187 cmd.cmd_arg = size / EMMC_BLOCK_SIZE;
188 cmd.resp_type = EMMC_RESPONSE_R1;
189 ret = ops->send_cmd(&cmd);
190 assert(ret == 0);
191
192 memset(&cmd, 0, sizeof(emmc_cmd_t));
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800193 cmd.cmd_idx = EMMC_CMD18;
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800194 } else {
195 if (size > EMMC_BLOCK_SIZE)
196 cmd.cmd_idx = EMMC_CMD18;
197 else
198 cmd.cmd_idx = EMMC_CMD17;
199 }
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800200 if ((emmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE)
201 cmd.cmd_arg = lba * EMMC_BLOCK_SIZE;
202 else
203 cmd.cmd_arg = lba;
204 cmd.resp_type = EMMC_RESPONSE_R1;
205 ret = ops->send_cmd(&cmd);
206 assert(ret == 0);
207
208 ret = ops->read(lba, buf, size);
209 assert(ret == 0);
210
211 /* wait buffer empty */
212 emmc_device_state();
213
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800214 if (is_cmd23_enabled() == 0) {
215 if (size > EMMC_BLOCK_SIZE) {
216 memset(&cmd, 0, sizeof(emmc_cmd_t));
217 cmd.cmd_idx = EMMC_CMD12;
218 ret = ops->send_cmd(&cmd);
219 assert(ret == 0);
220 }
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800221 }
222 /* Ignore improbable errors in release builds */
223 (void)ret;
224 return size;
225}
226
227size_t emmc_write_blocks(int lba, const uintptr_t buf, size_t size)
228{
229 emmc_cmd_t cmd;
230 int ret;
231
232 assert((ops != 0) &&
233 (ops->write != 0) &&
234 ((buf & EMMC_BLOCK_MASK) == 0) &&
235 ((size & EMMC_BLOCK_MASK) == 0));
236
237 clean_dcache_range(buf, size);
238 ret = ops->prepare(lba, buf, size);
239 assert(ret == 0);
240
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800241 if (is_cmd23_enabled()) {
242 /* set block count */
243 memset(&cmd, 0, sizeof(emmc_cmd_t));
244 cmd.cmd_idx = EMMC_CMD23;
245 cmd.cmd_arg = size / EMMC_BLOCK_SIZE;
246 cmd.resp_type = EMMC_RESPONSE_R1;
247 ret = ops->send_cmd(&cmd);
248 assert(ret == 0);
249
250 memset(&cmd, 0, sizeof(emmc_cmd_t));
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800251 cmd.cmd_idx = EMMC_CMD25;
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800252 } else {
253 memset(&cmd, 0, sizeof(emmc_cmd_t));
254 if (size > EMMC_BLOCK_SIZE)
255 cmd.cmd_idx = EMMC_CMD25;
256 else
257 cmd.cmd_idx = EMMC_CMD24;
258 }
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800259 if ((emmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE)
260 cmd.cmd_arg = lba * EMMC_BLOCK_SIZE;
261 else
262 cmd.cmd_arg = lba;
263 cmd.resp_type = EMMC_RESPONSE_R1;
264 ret = ops->send_cmd(&cmd);
265 assert(ret == 0);
266
267 ret = ops->write(lba, buf, size);
268 assert(ret == 0);
269
270 /* wait buffer empty */
271 emmc_device_state();
272
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800273 if (is_cmd23_enabled() == 0) {
274 if (size > EMMC_BLOCK_SIZE) {
275 memset(&cmd, 0, sizeof(emmc_cmd_t));
276 cmd.cmd_idx = EMMC_CMD12;
277 ret = ops->send_cmd(&cmd);
278 assert(ret == 0);
279 }
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800280 }
281 /* Ignore improbable errors in release builds */
282 (void)ret;
283 return size;
284}
285
286size_t emmc_erase_blocks(int lba, size_t size)
287{
288 emmc_cmd_t cmd;
289 int ret, state;
290
291 assert(ops != 0);
292 assert((size != 0) && ((size % EMMC_BLOCK_SIZE) == 0));
293
294 memset(&cmd, 0, sizeof(emmc_cmd_t));
295 cmd.cmd_idx = EMMC_CMD35;
296 cmd.cmd_arg = lba;
297 cmd.resp_type = EMMC_RESPONSE_R1;
298 ret = ops->send_cmd(&cmd);
299 assert(ret == 0);
300
301 memset(&cmd, 0, sizeof(emmc_cmd_t));
302 cmd.cmd_idx = EMMC_CMD36;
303 cmd.cmd_arg = lba + (size / EMMC_BLOCK_SIZE) - 1;
304 cmd.resp_type = EMMC_RESPONSE_R1;
305 ret = ops->send_cmd(&cmd);
306 assert(ret == 0);
307
308 memset(&cmd, 0, sizeof(emmc_cmd_t));
309 cmd.cmd_idx = EMMC_CMD38;
310 cmd.resp_type = EMMC_RESPONSE_R1B;
311 ret = ops->send_cmd(&cmd);
312 assert(ret == 0);
313
314 /* wait to TRAN state */
315 do {
316 state = emmc_device_state();
317 } while (state != EMMC_STATE_TRAN);
318 /* Ignore improbable errors in release builds */
319 (void)ret;
320 return size;
321}
322
323static inline void emmc_rpmb_enable(void)
324{
325 emmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG,
326 PART_CFG_BOOT_PARTITION1_ENABLE |
327 PART_CFG_PARTITION1_ACCESS);
328}
329
330static inline void emmc_rpmb_disable(void)
331{
332 emmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG,
333 PART_CFG_BOOT_PARTITION1_ENABLE);
334}
335
336size_t emmc_rpmb_read_blocks(int lba, uintptr_t buf, size_t size)
337{
338 size_t size_read;
339
340 emmc_rpmb_enable();
341 size_read = emmc_read_blocks(lba, buf, size);
342 emmc_rpmb_disable();
343 return size_read;
344}
345
346size_t emmc_rpmb_write_blocks(int lba, const uintptr_t buf, size_t size)
347{
348 size_t size_written;
349
350 emmc_rpmb_enable();
351 size_written = emmc_write_blocks(lba, buf, size);
352 emmc_rpmb_disable();
353 return size_written;
354}
355
356size_t emmc_rpmb_erase_blocks(int lba, size_t size)
357{
358 size_t size_erased;
359
360 emmc_rpmb_enable();
361 size_erased = emmc_erase_blocks(lba, size);
362 emmc_rpmb_disable();
363 return size_erased;
364}
365
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800366void emmc_init(const emmc_ops_t *ops_ptr, int clk, int width,
367 unsigned int flags)
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800368{
369 assert((ops_ptr != 0) &&
370 (ops_ptr->init != 0) &&
371 (ops_ptr->send_cmd != 0) &&
372 (ops_ptr->set_ios != 0) &&
373 (ops_ptr->prepare != 0) &&
374 (ops_ptr->read != 0) &&
375 (ops_ptr->write != 0) &&
376 (clk != 0) &&
377 ((width == EMMC_BUS_WIDTH_1) ||
378 (width == EMMC_BUS_WIDTH_4) ||
379 (width == EMMC_BUS_WIDTH_8)));
380 ops = ops_ptr;
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800381 emmc_flags = flags;
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800382
383 emmc_enumerate(clk, width);
384}