blob: 1c1ea82ac9ff72631208d5b89c36648747cbacb5 [file] [log] [blame]
Haojian Zhuangfffe9e72016-03-18 22:08:26 +08001/*
Douglas Raillarda8954fc2017-01-26 15:54:44 +00002 * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved.
Haojian Zhuangfffe9e72016-03-18 22:08:26 +08003 *
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>
Douglas Raillarda8954fc2017-01-26 15:54:44 +000039#include <utils.h>
Haojian Zhuangfffe9e72016-03-18 22:08:26 +080040
41static const emmc_ops_t *ops;
42static unsigned int emmc_ocr_value;
43static emmc_csd_t emmc_csd;
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +080044static unsigned int emmc_flags;
45
46static int is_cmd23_enabled(void)
47{
48 return (!!(emmc_flags & EMMC_FLAG_CMD23));
49}
Haojian Zhuangfffe9e72016-03-18 22:08:26 +080050
51static int emmc_device_state(void)
52{
53 emmc_cmd_t cmd;
54 int ret;
55
56 do {
Douglas Raillarda8954fc2017-01-26 15:54:44 +000057 zeromem(&cmd, sizeof(emmc_cmd_t));
Haojian Zhuangfffe9e72016-03-18 22:08:26 +080058 cmd.cmd_idx = EMMC_CMD13;
59 cmd.cmd_arg = EMMC_FIX_RCA << RCA_SHIFT_OFFSET;
60 cmd.resp_type = EMMC_RESPONSE_R1;
61 ret = ops->send_cmd(&cmd);
62 assert(ret == 0);
63 assert((cmd.resp_data[0] & STATUS_SWITCH_ERROR) == 0);
64 /* Ignore improbable errors in release builds */
65 (void)ret;
66 } while ((cmd.resp_data[0] & STATUS_READY_FOR_DATA) == 0);
67 return EMMC_GET_STATE(cmd.resp_data[0]);
68}
69
70static void emmc_set_ext_csd(unsigned int ext_cmd, unsigned int value)
71{
72 emmc_cmd_t cmd;
73 int ret, state;
74
Douglas Raillarda8954fc2017-01-26 15:54:44 +000075 zeromem(&cmd, sizeof(emmc_cmd_t));
Haojian Zhuangfffe9e72016-03-18 22:08:26 +080076 cmd.cmd_idx = EMMC_CMD6;
77 cmd.cmd_arg = EXTCSD_WRITE_BYTES | EXTCSD_CMD(ext_cmd) |
78 EXTCSD_VALUE(value) | 1;
79 ret = ops->send_cmd(&cmd);
80 assert(ret == 0);
81
82 /* wait to exit PRG state */
83 do {
84 state = emmc_device_state();
85 } while (state == EMMC_STATE_PRG);
86 /* Ignore improbable errors in release builds */
87 (void)ret;
88}
89
90static void emmc_set_ios(int clk, int bus_width)
91{
92 int ret;
93
94 /* set IO speed & IO bus width */
95 if (emmc_csd.spec_vers == 4)
96 emmc_set_ext_csd(CMD_EXTCSD_BUS_WIDTH, bus_width);
97 ret = ops->set_ios(clk, bus_width);
98 assert(ret == 0);
99 /* Ignore improbable errors in release builds */
100 (void)ret;
101}
102
103static int emmc_enumerate(int clk, int bus_width)
104{
105 emmc_cmd_t cmd;
106 int ret, state;
107
108 ops->init();
109
110 /* CMD0: reset to IDLE */
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000111 zeromem(&cmd, sizeof(emmc_cmd_t));
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800112 cmd.cmd_idx = EMMC_CMD0;
113 ret = ops->send_cmd(&cmd);
114 assert(ret == 0);
115
116 while (1) {
117 /* CMD1: get OCR register */
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000118 zeromem(&cmd, sizeof(emmc_cmd_t));
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800119 cmd.cmd_idx = EMMC_CMD1;
120 cmd.cmd_arg = OCR_SECTOR_MODE | OCR_VDD_MIN_2V7 |
121 OCR_VDD_MIN_1V7;
122 cmd.resp_type = EMMC_RESPONSE_R3;
123 ret = ops->send_cmd(&cmd);
124 assert(ret == 0);
125 emmc_ocr_value = cmd.resp_data[0];
126 if (emmc_ocr_value & OCR_POWERUP)
127 break;
128 }
129
130 /* CMD2: Card Identification */
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000131 zeromem(&cmd, sizeof(emmc_cmd_t));
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800132 cmd.cmd_idx = EMMC_CMD2;
133 cmd.resp_type = EMMC_RESPONSE_R2;
134 ret = ops->send_cmd(&cmd);
135 assert(ret == 0);
136
137 /* CMD3: Set Relative Address */
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000138 zeromem(&cmd, sizeof(emmc_cmd_t));
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800139 cmd.cmd_idx = EMMC_CMD3;
140 cmd.cmd_arg = EMMC_FIX_RCA << RCA_SHIFT_OFFSET;
141 cmd.resp_type = EMMC_RESPONSE_R1;
142 ret = ops->send_cmd(&cmd);
143 assert(ret == 0);
144
145 /* CMD9: CSD Register */
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000146 zeromem(&cmd, sizeof(emmc_cmd_t));
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800147 cmd.cmd_idx = EMMC_CMD9;
148 cmd.cmd_arg = EMMC_FIX_RCA << RCA_SHIFT_OFFSET;
149 cmd.resp_type = EMMC_RESPONSE_R2;
150 ret = ops->send_cmd(&cmd);
151 assert(ret == 0);
152 memcpy(&emmc_csd, &cmd.resp_data, sizeof(cmd.resp_data));
153
154 /* CMD7: Select Card */
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000155 zeromem(&cmd, sizeof(emmc_cmd_t));
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800156 cmd.cmd_idx = EMMC_CMD7;
157 cmd.cmd_arg = EMMC_FIX_RCA << RCA_SHIFT_OFFSET;
158 cmd.resp_type = EMMC_RESPONSE_R1;
159 ret = ops->send_cmd(&cmd);
160 assert(ret == 0);
161 /* wait to TRAN state */
162 do {
163 state = emmc_device_state();
164 } while (state != EMMC_STATE_TRAN);
165
166 emmc_set_ios(clk, bus_width);
167 return ret;
168}
169
170size_t emmc_read_blocks(int lba, uintptr_t buf, size_t size)
171{
172 emmc_cmd_t cmd;
173 int ret;
174
175 assert((ops != 0) &&
176 (ops->read != 0) &&
177 ((buf & EMMC_BLOCK_MASK) == 0) &&
178 ((size & EMMC_BLOCK_MASK) == 0));
179
180 inv_dcache_range(buf, size);
181 ret = ops->prepare(lba, buf, size);
182 assert(ret == 0);
183
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800184 if (is_cmd23_enabled()) {
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000185 zeromem(&cmd, sizeof(emmc_cmd_t));
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800186 /* set block count */
187 cmd.cmd_idx = EMMC_CMD23;
188 cmd.cmd_arg = size / EMMC_BLOCK_SIZE;
189 cmd.resp_type = EMMC_RESPONSE_R1;
190 ret = ops->send_cmd(&cmd);
191 assert(ret == 0);
192
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000193 zeromem(&cmd, sizeof(emmc_cmd_t));
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800194 cmd.cmd_idx = EMMC_CMD18;
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800195 } else {
196 if (size > EMMC_BLOCK_SIZE)
197 cmd.cmd_idx = EMMC_CMD18;
198 else
199 cmd.cmd_idx = EMMC_CMD17;
200 }
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800201 if ((emmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE)
202 cmd.cmd_arg = lba * EMMC_BLOCK_SIZE;
203 else
204 cmd.cmd_arg = lba;
205 cmd.resp_type = EMMC_RESPONSE_R1;
206 ret = ops->send_cmd(&cmd);
207 assert(ret == 0);
208
209 ret = ops->read(lba, buf, size);
210 assert(ret == 0);
211
212 /* wait buffer empty */
213 emmc_device_state();
214
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800215 if (is_cmd23_enabled() == 0) {
216 if (size > EMMC_BLOCK_SIZE) {
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000217 zeromem(&cmd, sizeof(emmc_cmd_t));
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800218 cmd.cmd_idx = EMMC_CMD12;
219 ret = ops->send_cmd(&cmd);
220 assert(ret == 0);
221 }
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800222 }
223 /* Ignore improbable errors in release builds */
224 (void)ret;
225 return size;
226}
227
228size_t emmc_write_blocks(int lba, const uintptr_t buf, size_t size)
229{
230 emmc_cmd_t cmd;
231 int ret;
232
233 assert((ops != 0) &&
234 (ops->write != 0) &&
235 ((buf & EMMC_BLOCK_MASK) == 0) &&
236 ((size & EMMC_BLOCK_MASK) == 0));
237
238 clean_dcache_range(buf, size);
239 ret = ops->prepare(lba, buf, size);
240 assert(ret == 0);
241
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800242 if (is_cmd23_enabled()) {
243 /* set block count */
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000244 zeromem(&cmd, sizeof(emmc_cmd_t));
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800245 cmd.cmd_idx = EMMC_CMD23;
246 cmd.cmd_arg = size / EMMC_BLOCK_SIZE;
247 cmd.resp_type = EMMC_RESPONSE_R1;
248 ret = ops->send_cmd(&cmd);
249 assert(ret == 0);
250
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000251 zeromem(&cmd, sizeof(emmc_cmd_t));
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800252 cmd.cmd_idx = EMMC_CMD25;
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800253 } else {
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000254 zeromem(&cmd, sizeof(emmc_cmd_t));
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800255 if (size > EMMC_BLOCK_SIZE)
256 cmd.cmd_idx = EMMC_CMD25;
257 else
258 cmd.cmd_idx = EMMC_CMD24;
259 }
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800260 if ((emmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE)
261 cmd.cmd_arg = lba * EMMC_BLOCK_SIZE;
262 else
263 cmd.cmd_arg = lba;
264 cmd.resp_type = EMMC_RESPONSE_R1;
265 ret = ops->send_cmd(&cmd);
266 assert(ret == 0);
267
268 ret = ops->write(lba, buf, size);
269 assert(ret == 0);
270
271 /* wait buffer empty */
272 emmc_device_state();
273
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800274 if (is_cmd23_enabled() == 0) {
275 if (size > EMMC_BLOCK_SIZE) {
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000276 zeromem(&cmd, sizeof(emmc_cmd_t));
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800277 cmd.cmd_idx = EMMC_CMD12;
278 ret = ops->send_cmd(&cmd);
279 assert(ret == 0);
280 }
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800281 }
282 /* Ignore improbable errors in release builds */
283 (void)ret;
284 return size;
285}
286
287size_t emmc_erase_blocks(int lba, size_t size)
288{
289 emmc_cmd_t cmd;
290 int ret, state;
291
292 assert(ops != 0);
293 assert((size != 0) && ((size % EMMC_BLOCK_SIZE) == 0));
294
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000295 zeromem(&cmd, sizeof(emmc_cmd_t));
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800296 cmd.cmd_idx = EMMC_CMD35;
297 cmd.cmd_arg = lba;
298 cmd.resp_type = EMMC_RESPONSE_R1;
299 ret = ops->send_cmd(&cmd);
300 assert(ret == 0);
301
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000302 zeromem(&cmd, sizeof(emmc_cmd_t));
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800303 cmd.cmd_idx = EMMC_CMD36;
304 cmd.cmd_arg = lba + (size / EMMC_BLOCK_SIZE) - 1;
305 cmd.resp_type = EMMC_RESPONSE_R1;
306 ret = ops->send_cmd(&cmd);
307 assert(ret == 0);
308
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000309 zeromem(&cmd, sizeof(emmc_cmd_t));
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800310 cmd.cmd_idx = EMMC_CMD38;
311 cmd.resp_type = EMMC_RESPONSE_R1B;
312 ret = ops->send_cmd(&cmd);
313 assert(ret == 0);
314
315 /* wait to TRAN state */
316 do {
317 state = emmc_device_state();
318 } while (state != EMMC_STATE_TRAN);
319 /* Ignore improbable errors in release builds */
320 (void)ret;
321 return size;
322}
323
324static inline void emmc_rpmb_enable(void)
325{
326 emmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG,
327 PART_CFG_BOOT_PARTITION1_ENABLE |
328 PART_CFG_PARTITION1_ACCESS);
329}
330
331static inline void emmc_rpmb_disable(void)
332{
333 emmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG,
334 PART_CFG_BOOT_PARTITION1_ENABLE);
335}
336
337size_t emmc_rpmb_read_blocks(int lba, uintptr_t buf, size_t size)
338{
339 size_t size_read;
340
341 emmc_rpmb_enable();
342 size_read = emmc_read_blocks(lba, buf, size);
343 emmc_rpmb_disable();
344 return size_read;
345}
346
347size_t emmc_rpmb_write_blocks(int lba, const uintptr_t buf, size_t size)
348{
349 size_t size_written;
350
351 emmc_rpmb_enable();
352 size_written = emmc_write_blocks(lba, buf, size);
353 emmc_rpmb_disable();
354 return size_written;
355}
356
357size_t emmc_rpmb_erase_blocks(int lba, size_t size)
358{
359 size_t size_erased;
360
361 emmc_rpmb_enable();
362 size_erased = emmc_erase_blocks(lba, size);
363 emmc_rpmb_disable();
364 return size_erased;
365}
366
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800367void emmc_init(const emmc_ops_t *ops_ptr, int clk, int width,
368 unsigned int flags)
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800369{
370 assert((ops_ptr != 0) &&
371 (ops_ptr->init != 0) &&
372 (ops_ptr->send_cmd != 0) &&
373 (ops_ptr->set_ios != 0) &&
374 (ops_ptr->prepare != 0) &&
375 (ops_ptr->read != 0) &&
376 (ops_ptr->write != 0) &&
377 (clk != 0) &&
378 ((width == EMMC_BUS_WIDTH_1) ||
379 (width == EMMC_BUS_WIDTH_4) ||
380 (width == EMMC_BUS_WIDTH_8)));
381 ops = ops_ptr;
Haojian Zhuangbd5cf9c2016-08-02 20:51:27 +0800382 emmc_flags = flags;
Haojian Zhuangfffe9e72016-03-18 22:08:26 +0800383
384 emmc_enumerate(clk, width);
385}