• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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