]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
71f95118 WD |
2 | /* |
3 | * (C) Copyright 2003 | |
4 | * Kyle Harris, [email protected] | |
71f95118 WD |
5 | */ |
6 | ||
7 | #include <common.h> | |
e6f6f9e6 | 8 | #include <blk.h> |
71f95118 | 9 | #include <command.h> |
24b852a7 | 10 | #include <console.h> |
d581076a | 11 | #include <memalign.h> |
71f95118 | 12 | #include <mmc.h> |
e6f6f9e6 | 13 | #include <part.h> |
732bc7ce JB |
14 | #include <sparse_format.h> |
15 | #include <image-sparse.h> | |
71f95118 | 16 | |
02e22c2d | 17 | static int curr_device = -1; |
272cc70b AF |
18 | |
19 | static void print_mmcinfo(struct mmc *mmc) | |
20 | { | |
c5f0d3f1 DSC |
21 | int i; |
22 | ||
93bfd616 | 23 | printf("Device: %s\n", mmc->cfg->name); |
272cc70b | 24 | printf("Manufacturer ID: %x\n", mmc->cid[0] >> 24); |
84191f73 MM |
25 | if (IS_SD(mmc)) { |
26 | printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff); | |
27 | printf("Name: %c%c%c%c%c \n", mmc->cid[0] & 0xff, | |
28 | (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, | |
29 | (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff); | |
30 | } else { | |
31 | printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xff); | |
32 | printf("Name: %c%c%c%c%c%c \n", mmc->cid[0] & 0xff, | |
33 | (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, | |
34 | (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff, | |
35 | (mmc->cid[2] >> 24)); | |
36 | } | |
272cc70b | 37 | |
7a96ec74 | 38 | printf("Bus Speed: %d\n", mmc->clock); |
52d241df | 39 | #if CONFIG_IS_ENABLED(MMC_VERBOSE) |
41e30dcf | 40 | printf("Mode: %s\n", mmc_mode_name(mmc->selected_mode)); |
52d241df JJH |
41 | mmc_dump_capabilities("card capabilities", mmc->card_caps); |
42 | mmc_dump_capabilities("host capabilities", mmc->host_caps); | |
43 | #endif | |
272cc70b AF |
44 | printf("Rd Block Len: %d\n", mmc->read_bl_len); |
45 | ||
4b7cee53 PA |
46 | printf("%s version %d.%d", IS_SD(mmc) ? "SD" : "MMC", |
47 | EXTRACT_SDMMC_MAJOR_VERSION(mmc->version), | |
48 | EXTRACT_SDMMC_MINOR_VERSION(mmc->version)); | |
49 | if (EXTRACT_SDMMC_CHANGE_VERSION(mmc->version) != 0) | |
50 | printf(".%d", EXTRACT_SDMMC_CHANGE_VERSION(mmc->version)); | |
51 | printf("\n"); | |
272cc70b AF |
52 | |
53 | printf("High Capacity: %s\n", mmc->high_capacity ? "Yes" : "No"); | |
940e0782 MK |
54 | puts("Capacity: "); |
55 | print_size(mmc->capacity, "\n"); | |
272cc70b | 56 | |
786e8f81 AG |
57 | printf("Bus Width: %d-bit%s\n", mmc->bus_width, |
58 | mmc->ddr_mode ? " DDR" : ""); | |
c5f0d3f1 | 59 | |
e6fa5a54 | 60 | #if CONFIG_IS_ENABLED(MMC_WRITE) |
b0361526 DSC |
61 | puts("Erase Group Size: "); |
62 | print_size(((u64)mmc->erase_grp_size) << 9, "\n"); | |
e6fa5a54 | 63 | #endif |
b0361526 | 64 | |
525ada21 | 65 | if (!IS_SD(mmc) && mmc->version >= MMC_VERSION_4_41) { |
c3dbb4f9 | 66 | bool has_enh = (mmc->part_support & ENHNCD_SUPPORT) != 0; |
beb98a14 | 67 | bool usr_enh = has_enh && (mmc->part_attr & EXT_CSD_ENH_USR); |
d581076a MV |
68 | ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); |
69 | u8 wp; | |
d5210e45 | 70 | int ret; |
b0361526 | 71 | |
b7a6e2c9 | 72 | #if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) |
b0361526 DSC |
73 | puts("HC WP Group Size: "); |
74 | print_size(((u64)mmc->hc_wp_grp_size) << 9, "\n"); | |
b7a6e2c9 | 75 | #endif |
b0361526 | 76 | |
c5f0d3f1 | 77 | puts("User Capacity: "); |
9e41a00b DSC |
78 | print_size(mmc->capacity_user, usr_enh ? " ENH" : ""); |
79 | if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_USR) | |
80 | puts(" WRREL\n"); | |
81 | else | |
82 | putc('\n'); | |
beb98a14 DSC |
83 | if (usr_enh) { |
84 | puts("User Enhanced Start: "); | |
85 | print_size(mmc->enh_user_start, "\n"); | |
86 | puts("User Enhanced Size: "); | |
87 | print_size(mmc->enh_user_size, "\n"); | |
88 | } | |
c5f0d3f1 | 89 | puts("Boot Capacity: "); |
c3dbb4f9 | 90 | print_size(mmc->capacity_boot, has_enh ? " ENH\n" : "\n"); |
c5f0d3f1 | 91 | puts("RPMB Capacity: "); |
c3dbb4f9 | 92 | print_size(mmc->capacity_rpmb, has_enh ? " ENH\n" : "\n"); |
b0361526 | 93 | |
c5f0d3f1 | 94 | for (i = 0; i < ARRAY_SIZE(mmc->capacity_gp); i++) { |
c3dbb4f9 DSC |
95 | bool is_enh = has_enh && |
96 | (mmc->part_attr & EXT_CSD_ENH_GP(i)); | |
c5f0d3f1 | 97 | if (mmc->capacity_gp[i]) { |
f289fd73 | 98 | printf("GP%i Capacity: ", i+1); |
c3dbb4f9 | 99 | print_size(mmc->capacity_gp[i], |
9e41a00b DSC |
100 | is_enh ? " ENH" : ""); |
101 | if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_GP(i)) | |
102 | puts(" WRREL\n"); | |
103 | else | |
104 | putc('\n'); | |
c5f0d3f1 DSC |
105 | } |
106 | } | |
d5210e45 HS |
107 | ret = mmc_send_ext_csd(mmc, ext_csd); |
108 | if (ret) | |
109 | return; | |
110 | wp = ext_csd[EXT_CSD_BOOT_WP_STATUS]; | |
111 | for (i = 0; i < 2; ++i) { | |
112 | printf("Boot area %d is ", i); | |
113 | switch (wp & 3) { | |
114 | case 0: | |
115 | printf("not write protected\n"); | |
116 | break; | |
117 | case 1: | |
118 | printf("power on protected\n"); | |
119 | break; | |
120 | case 2: | |
121 | printf("permanently protected\n"); | |
122 | break; | |
123 | default: | |
124 | printf("in reserved protection state\n"); | |
125 | break; | |
126 | } | |
127 | wp >>= 2; | |
128 | } | |
c5f0d3f1 | 129 | } |
272cc70b | 130 | } |
19f7a34a AG |
131 | |
132 | static struct mmc *__init_mmc_device(int dev, bool force_init, | |
133 | enum bus_mode speed_mode) | |
1fd93c6e PA |
134 | { |
135 | struct mmc *mmc; | |
136 | mmc = find_mmc_device(dev); | |
137 | if (!mmc) { | |
138 | printf("no mmc device at slot %x\n", dev); | |
139 | return NULL; | |
140 | } | |
bcfde7ff | 141 | |
d2a08369 MV |
142 | if (!mmc_getcd(mmc)) |
143 | force_init = true; | |
144 | ||
1ae24a50 SW |
145 | if (force_init) |
146 | mmc->has_init = 0; | |
19f7a34a AG |
147 | |
148 | if (IS_ENABLED(CONFIG_MMC_SPEED_MODE_SET)) | |
149 | mmc->user_speed_mode = speed_mode; | |
150 | ||
1fd93c6e PA |
151 | if (mmc_init(mmc)) |
152 | return NULL; | |
1d044d32 MV |
153 | |
154 | #ifdef CONFIG_BLOCK_CACHE | |
155 | struct blk_desc *bd = mmc_get_blk_desc(mmc); | |
156 | blkcache_invalidate(bd->if_type, bd->devnum); | |
157 | #endif | |
158 | ||
1fd93c6e PA |
159 | return mmc; |
160 | } | |
09140113 | 161 | |
19f7a34a AG |
162 | static struct mmc *init_mmc_device(int dev, bool force_init) |
163 | { | |
164 | return __init_mmc_device(dev, force_init, MMC_MODES_END); | |
165 | } | |
166 | ||
09140113 SG |
167 | static int do_mmcinfo(struct cmd_tbl *cmdtp, int flag, int argc, |
168 | char *const argv[]) | |
272cc70b AF |
169 | { |
170 | struct mmc *mmc; | |
272cc70b | 171 | |
ea6ebe21 LW |
172 | if (curr_device < 0) { |
173 | if (get_mmc_num() > 0) | |
174 | curr_device = 0; | |
175 | else { | |
176 | puts("No MMC device available\n"); | |
177 | return 1; | |
178 | } | |
179 | } | |
272cc70b | 180 | |
1ae24a50 | 181 | mmc = init_mmc_device(curr_device, false); |
1fd93c6e PA |
182 | if (!mmc) |
183 | return CMD_RET_FAILURE; | |
272cc70b | 184 | |
1fd93c6e PA |
185 | print_mmcinfo(mmc); |
186 | return CMD_RET_SUCCESS; | |
187 | } | |
272cc70b | 188 | |
5a7b11e6 | 189 | #if CONFIG_IS_ENABLED(CMD_MMC_RPMB) |
1fd93c6e PA |
190 | static int confirm_key_prog(void) |
191 | { | |
192 | puts("Warning: Programming authentication key can be done only once !\n" | |
193 | " Use this command only if you are sure of what you are doing,\n" | |
194 | "Really perform the key programming? <y/N> "); | |
195 | if (confirm_yesno()) | |
ea6ebe21 | 196 | return 1; |
1fd93c6e PA |
197 | |
198 | puts("Authentication key programming aborted\n"); | |
199 | return 0; | |
200 | } | |
09140113 SG |
201 | |
202 | static int do_mmcrpmb_key(struct cmd_tbl *cmdtp, int flag, | |
203 | int argc, char *const argv[]) | |
1fd93c6e PA |
204 | { |
205 | void *key_addr; | |
206 | struct mmc *mmc = find_mmc_device(curr_device); | |
207 | ||
208 | if (argc != 2) | |
209 | return CMD_RET_USAGE; | |
210 | ||
7e5f460e | 211 | key_addr = (void *)hextoul(argv[1], NULL); |
1fd93c6e PA |
212 | if (!confirm_key_prog()) |
213 | return CMD_RET_FAILURE; | |
214 | if (mmc_rpmb_set_key(mmc, key_addr)) { | |
215 | printf("ERROR - Key already programmed ?\n"); | |
216 | return CMD_RET_FAILURE; | |
272cc70b | 217 | } |
1fd93c6e | 218 | return CMD_RET_SUCCESS; |
272cc70b | 219 | } |
09140113 SG |
220 | |
221 | static int do_mmcrpmb_read(struct cmd_tbl *cmdtp, int flag, | |
222 | int argc, char *const argv[]) | |
1fd93c6e PA |
223 | { |
224 | u16 blk, cnt; | |
225 | void *addr; | |
226 | int n; | |
227 | void *key_addr = NULL; | |
228 | struct mmc *mmc = find_mmc_device(curr_device); | |
272cc70b | 229 | |
1fd93c6e PA |
230 | if (argc < 4) |
231 | return CMD_RET_USAGE; | |
272cc70b | 232 | |
7e5f460e SG |
233 | addr = (void *)hextoul(argv[1], NULL); |
234 | blk = hextoul(argv[2], NULL); | |
235 | cnt = hextoul(argv[3], NULL); | |
1fd93c6e PA |
236 | |
237 | if (argc == 5) | |
7e5f460e | 238 | key_addr = (void *)hextoul(argv[4], NULL); |
1fd93c6e PA |
239 | |
240 | printf("\nMMC RPMB read: dev # %d, block # %d, count %d ... ", | |
241 | curr_device, blk, cnt); | |
242 | n = mmc_rpmb_read(mmc, addr, blk, cnt, key_addr); | |
243 | ||
244 | printf("%d RPMB blocks read: %s\n", n, (n == cnt) ? "OK" : "ERROR"); | |
245 | if (n != cnt) | |
246 | return CMD_RET_FAILURE; | |
247 | return CMD_RET_SUCCESS; | |
248 | } | |
09140113 SG |
249 | |
250 | static int do_mmcrpmb_write(struct cmd_tbl *cmdtp, int flag, | |
251 | int argc, char *const argv[]) | |
272cc70b | 252 | { |
1fd93c6e PA |
253 | u16 blk, cnt; |
254 | void *addr; | |
255 | int n; | |
256 | void *key_addr; | |
257 | struct mmc *mmc = find_mmc_device(curr_device); | |
6be95ccf | 258 | |
1fd93c6e | 259 | if (argc != 5) |
4c12eeb8 | 260 | return CMD_RET_USAGE; |
272cc70b | 261 | |
7e5f460e SG |
262 | addr = (void *)hextoul(argv[1], NULL); |
263 | blk = hextoul(argv[2], NULL); | |
264 | cnt = hextoul(argv[3], NULL); | |
265 | key_addr = (void *)hextoul(argv[4], NULL); | |
1fd93c6e PA |
266 | |
267 | printf("\nMMC RPMB write: dev # %d, block # %d, count %d ... ", | |
268 | curr_device, blk, cnt); | |
269 | n = mmc_rpmb_write(mmc, addr, blk, cnt, key_addr); | |
270 | ||
271 | printf("%d RPMB blocks written: %s\n", n, (n == cnt) ? "OK" : "ERROR"); | |
272 | if (n != cnt) | |
273 | return CMD_RET_FAILURE; | |
274 | return CMD_RET_SUCCESS; | |
275 | } | |
09140113 SG |
276 | |
277 | static int do_mmcrpmb_counter(struct cmd_tbl *cmdtp, int flag, | |
278 | int argc, char *const argv[]) | |
1fd93c6e PA |
279 | { |
280 | unsigned long counter; | |
281 | struct mmc *mmc = find_mmc_device(curr_device); | |
282 | ||
283 | if (mmc_rpmb_get_counter(mmc, &counter)) | |
284 | return CMD_RET_FAILURE; | |
285 | printf("RPMB Write counter= %lx\n", counter); | |
286 | return CMD_RET_SUCCESS; | |
287 | } | |
288 | ||
09140113 | 289 | static struct cmd_tbl cmd_rpmb[] = { |
1fd93c6e PA |
290 | U_BOOT_CMD_MKENT(key, 2, 0, do_mmcrpmb_key, "", ""), |
291 | U_BOOT_CMD_MKENT(read, 5, 1, do_mmcrpmb_read, "", ""), | |
292 | U_BOOT_CMD_MKENT(write, 5, 0, do_mmcrpmb_write, "", ""), | |
293 | U_BOOT_CMD_MKENT(counter, 1, 1, do_mmcrpmb_counter, "", ""), | |
294 | }; | |
295 | ||
09140113 SG |
296 | static int do_mmcrpmb(struct cmd_tbl *cmdtp, int flag, |
297 | int argc, char *const argv[]) | |
1fd93c6e | 298 | { |
09140113 | 299 | struct cmd_tbl *cp; |
1fd93c6e PA |
300 | struct mmc *mmc; |
301 | char original_part; | |
302 | int ret; | |
303 | ||
304 | cp = find_cmd_tbl(argv[1], cmd_rpmb, ARRAY_SIZE(cmd_rpmb)); | |
305 | ||
306 | /* Drop the rpmb subcommand */ | |
307 | argc--; | |
308 | argv++; | |
309 | ||
310 | if (cp == NULL || argc > cp->maxargs) | |
311 | return CMD_RET_USAGE; | |
80a48dd4 | 312 | if (flag == CMD_FLAG_REPEAT && !cmd_is_repeatable(cp)) |
1fd93c6e PA |
313 | return CMD_RET_SUCCESS; |
314 | ||
1ae24a50 | 315 | mmc = init_mmc_device(curr_device, false); |
1fd93c6e PA |
316 | if (!mmc) |
317 | return CMD_RET_FAILURE; | |
318 | ||
319 | if (!(mmc->version & MMC_VERSION_MMC)) { | |
71a3e5c5 | 320 | printf("It is not an eMMC device\n"); |
1fd93c6e PA |
321 | return CMD_RET_FAILURE; |
322 | } | |
323 | if (mmc->version < MMC_VERSION_4_41) { | |
324 | printf("RPMB not supported before version 4.41\n"); | |
325 | return CMD_RET_FAILURE; | |
ea6ebe21 | 326 | } |
1fd93c6e | 327 | /* Switch to the RPMB partition */ |
4dc80c87 | 328 | #ifndef CONFIG_BLK |
b955e42b | 329 | original_part = mmc->block_dev.hwpart; |
4dc80c87 KY |
330 | #else |
331 | original_part = mmc_get_blk_desc(mmc)->hwpart; | |
332 | #endif | |
69f45cd5 SG |
333 | if (blk_select_hwpart_devnum(IF_TYPE_MMC, curr_device, MMC_PART_RPMB) != |
334 | 0) | |
873cc1d7 | 335 | return CMD_RET_FAILURE; |
1fd93c6e | 336 | ret = cp->cmd(cmdtp, flag, argc, argv); |
272cc70b | 337 | |
1fd93c6e | 338 | /* Return to original partition */ |
69f45cd5 SG |
339 | if (blk_select_hwpart_devnum(IF_TYPE_MMC, curr_device, original_part) != |
340 | 0) | |
873cc1d7 | 341 | return CMD_RET_FAILURE; |
1fd93c6e PA |
342 | return ret; |
343 | } | |
344 | #endif | |
9fd38372 | 345 | |
09140113 SG |
346 | static int do_mmc_read(struct cmd_tbl *cmdtp, int flag, |
347 | int argc, char *const argv[]) | |
1fd93c6e PA |
348 | { |
349 | struct mmc *mmc; | |
350 | u32 blk, cnt, n; | |
351 | void *addr; | |
e85649c7 | 352 | |
1fd93c6e PA |
353 | if (argc != 4) |
354 | return CMD_RET_USAGE; | |
ea6ebe21 | 355 | |
7e5f460e SG |
356 | addr = (void *)hextoul(argv[1], NULL); |
357 | blk = hextoul(argv[2], NULL); | |
358 | cnt = hextoul(argv[3], NULL); | |
272cc70b | 359 | |
1ae24a50 | 360 | mmc = init_mmc_device(curr_device, false); |
1fd93c6e PA |
361 | if (!mmc) |
362 | return CMD_RET_FAILURE; | |
ea6ebe21 | 363 | |
1fd93c6e PA |
364 | printf("\nMMC read: dev # %d, block # %d, count %d ... ", |
365 | curr_device, blk, cnt); | |
9fd38372 | 366 | |
c40fdca6 | 367 | n = blk_dread(mmc_get_blk_desc(mmc), blk, cnt, addr); |
1fd93c6e | 368 | printf("%d blocks read: %s\n", n, (n == cnt) ? "OK" : "ERROR"); |
8f3b9642 | 369 | |
1fd93c6e PA |
370 | return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE; |
371 | } | |
e6fa5a54 | 372 | |
c232d14d | 373 | #if CONFIG_IS_ENABLED(CMD_MMC_SWRITE) |
732bc7ce JB |
374 | static lbaint_t mmc_sparse_write(struct sparse_storage *info, lbaint_t blk, |
375 | lbaint_t blkcnt, const void *buffer) | |
376 | { | |
377 | struct blk_desc *dev_desc = info->priv; | |
378 | ||
379 | return blk_dwrite(dev_desc, blk, blkcnt, buffer); | |
380 | } | |
381 | ||
382 | static lbaint_t mmc_sparse_reserve(struct sparse_storage *info, | |
383 | lbaint_t blk, lbaint_t blkcnt) | |
384 | { | |
385 | return blkcnt; | |
386 | } | |
387 | ||
09140113 SG |
388 | static int do_mmc_sparse_write(struct cmd_tbl *cmdtp, int flag, |
389 | int argc, char *const argv[]) | |
732bc7ce JB |
390 | { |
391 | struct sparse_storage sparse; | |
392 | struct blk_desc *dev_desc; | |
393 | struct mmc *mmc; | |
394 | char dest[11]; | |
395 | void *addr; | |
396 | u32 blk; | |
397 | ||
398 | if (argc != 3) | |
399 | return CMD_RET_USAGE; | |
400 | ||
7e5f460e SG |
401 | addr = (void *)hextoul(argv[1], NULL); |
402 | blk = hextoul(argv[2], NULL); | |
732bc7ce JB |
403 | |
404 | if (!is_sparse_image(addr)) { | |
405 | printf("Not a sparse image\n"); | |
406 | return CMD_RET_FAILURE; | |
407 | } | |
408 | ||
409 | mmc = init_mmc_device(curr_device, false); | |
410 | if (!mmc) | |
411 | return CMD_RET_FAILURE; | |
412 | ||
413 | printf("\nMMC Sparse write: dev # %d, block # %d ... ", | |
414 | curr_device, blk); | |
415 | ||
416 | if (mmc_getwp(mmc) == 1) { | |
417 | printf("Error: card is write protected!\n"); | |
418 | return CMD_RET_FAILURE; | |
419 | } | |
420 | ||
421 | dev_desc = mmc_get_blk_desc(mmc); | |
422 | sparse.priv = dev_desc; | |
423 | sparse.blksz = 512; | |
424 | sparse.start = blk; | |
425 | sparse.size = dev_desc->lba - blk; | |
426 | sparse.write = mmc_sparse_write; | |
427 | sparse.reserve = mmc_sparse_reserve; | |
428 | sparse.mssg = NULL; | |
429 | sprintf(dest, "0x" LBAF, sparse.start * sparse.blksz); | |
430 | ||
c4ded03e | 431 | if (write_sparse_image(&sparse, dest, addr, NULL)) |
732bc7ce JB |
432 | return CMD_RET_FAILURE; |
433 | else | |
434 | return CMD_RET_SUCCESS; | |
435 | } | |
436 | #endif | |
437 | ||
c232d14d | 438 | #if CONFIG_IS_ENABLED(MMC_WRITE) |
09140113 SG |
439 | static int do_mmc_write(struct cmd_tbl *cmdtp, int flag, |
440 | int argc, char *const argv[]) | |
1fd93c6e PA |
441 | { |
442 | struct mmc *mmc; | |
443 | u32 blk, cnt, n; | |
444 | void *addr; | |
8f3b9642 | 445 | |
1fd93c6e PA |
446 | if (argc != 4) |
447 | return CMD_RET_USAGE; | |
272cc70b | 448 | |
7e5f460e SG |
449 | addr = (void *)hextoul(argv[1], NULL); |
450 | blk = hextoul(argv[2], NULL); | |
451 | cnt = hextoul(argv[3], NULL); | |
bc897b1d | 452 | |
1ae24a50 | 453 | mmc = init_mmc_device(curr_device, false); |
1fd93c6e PA |
454 | if (!mmc) |
455 | return CMD_RET_FAILURE; | |
bc897b1d | 456 | |
1fd93c6e PA |
457 | printf("\nMMC write: dev # %d, block # %d, count %d ... ", |
458 | curr_device, blk, cnt); | |
272cc70b | 459 | |
1fd93c6e PA |
460 | if (mmc_getwp(mmc) == 1) { |
461 | printf("Error: card is write protected!\n"); | |
462 | return CMD_RET_FAILURE; | |
463 | } | |
c40fdca6 | 464 | n = blk_dwrite(mmc_get_blk_desc(mmc), blk, cnt, addr); |
1fd93c6e | 465 | printf("%d blocks written: %s\n", n, (n == cnt) ? "OK" : "ERROR"); |
792970b0 | 466 | |
1fd93c6e PA |
467 | return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE; |
468 | } | |
09140113 SG |
469 | |
470 | static int do_mmc_erase(struct cmd_tbl *cmdtp, int flag, | |
471 | int argc, char *const argv[]) | |
1fd93c6e PA |
472 | { |
473 | struct mmc *mmc; | |
474 | u32 blk, cnt, n; | |
2a91c913 | 475 | |
1fd93c6e PA |
476 | if (argc != 3) |
477 | return CMD_RET_USAGE; | |
792970b0 | 478 | |
7e5f460e SG |
479 | blk = hextoul(argv[1], NULL); |
480 | cnt = hextoul(argv[2], NULL); | |
5a99b9de | 481 | |
1ae24a50 | 482 | mmc = init_mmc_device(curr_device, false); |
1fd93c6e PA |
483 | if (!mmc) |
484 | return CMD_RET_FAILURE; | |
5a99b9de | 485 | |
1fd93c6e PA |
486 | printf("\nMMC erase: dev # %d, block # %d, count %d ... ", |
487 | curr_device, blk, cnt); | |
5a99b9de | 488 | |
1fd93c6e PA |
489 | if (mmc_getwp(mmc) == 1) { |
490 | printf("Error: card is write protected!\n"); | |
491 | return CMD_RET_FAILURE; | |
492 | } | |
c40fdca6 | 493 | n = blk_derase(mmc_get_blk_desc(mmc), blk, cnt); |
1fd93c6e | 494 | printf("%d blocks erased: %s\n", n, (n == cnt) ? "OK" : "ERROR"); |
b01e6fe6 | 495 | |
1fd93c6e PA |
496 | return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE; |
497 | } | |
e6fa5a54 JJH |
498 | #endif |
499 | ||
09140113 SG |
500 | static int do_mmc_rescan(struct cmd_tbl *cmdtp, int flag, |
501 | int argc, char *const argv[]) | |
1fd93c6e PA |
502 | { |
503 | struct mmc *mmc; | |
19f7a34a AG |
504 | |
505 | if (argc == 1) { | |
506 | mmc = init_mmc_device(curr_device, true); | |
507 | } else if (argc == 2) { | |
27434703 HS |
508 | enum bus_mode speed_mode; |
509 | ||
19f7a34a AG |
510 | speed_mode = (int)dectoul(argv[1], NULL); |
511 | mmc = __init_mmc_device(curr_device, true, speed_mode); | |
512 | } else { | |
513 | return CMD_RET_USAGE; | |
514 | } | |
2a91c913 | 515 | |
941944e4 | 516 | if (!mmc) |
1fd93c6e | 517 | return CMD_RET_FAILURE; |
2a91c913 | 518 | |
1fd93c6e PA |
519 | return CMD_RET_SUCCESS; |
520 | } | |
09140113 SG |
521 | |
522 | static int do_mmc_part(struct cmd_tbl *cmdtp, int flag, | |
523 | int argc, char *const argv[]) | |
1fd93c6e | 524 | { |
4101f687 | 525 | struct blk_desc *mmc_dev; |
1fd93c6e PA |
526 | struct mmc *mmc; |
527 | ||
1ae24a50 | 528 | mmc = init_mmc_device(curr_device, false); |
1fd93c6e PA |
529 | if (!mmc) |
530 | return CMD_RET_FAILURE; | |
531 | ||
3c457f4d | 532 | mmc_dev = blk_get_devnum_by_type(IF_TYPE_MMC, curr_device); |
1fd93c6e | 533 | if (mmc_dev != NULL && mmc_dev->type != DEV_TYPE_UNKNOWN) { |
3e8bd469 | 534 | part_print(mmc_dev); |
1fd93c6e PA |
535 | return CMD_RET_SUCCESS; |
536 | } | |
537 | ||
538 | puts("get mmc type error!\n"); | |
539 | return CMD_RET_FAILURE; | |
540 | } | |
09140113 SG |
541 | |
542 | static int do_mmc_dev(struct cmd_tbl *cmdtp, int flag, | |
543 | int argc, char *const argv[]) | |
1fd93c6e | 544 | { |
60dc58f7 | 545 | int dev, part = 0, ret; |
1fd93c6e PA |
546 | struct mmc *mmc; |
547 | ||
548 | if (argc == 1) { | |
549 | dev = curr_device; | |
19f7a34a | 550 | mmc = init_mmc_device(dev, true); |
1fd93c6e | 551 | } else if (argc == 2) { |
19f7a34a AG |
552 | dev = (int)dectoul(argv[1], NULL); |
553 | mmc = init_mmc_device(dev, true); | |
1fd93c6e | 554 | } else if (argc == 3) { |
0b1284eb SG |
555 | dev = (int)dectoul(argv[1], NULL); |
556 | part = (int)dectoul(argv[2], NULL); | |
1fd93c6e PA |
557 | if (part > PART_ACCESS_MASK) { |
558 | printf("#part_num shouldn't be larger than %d\n", | |
559 | PART_ACCESS_MASK); | |
560 | return CMD_RET_FAILURE; | |
33ace362 | 561 | } |
19f7a34a AG |
562 | mmc = init_mmc_device(dev, true); |
563 | } else if (argc == 4) { | |
27434703 HS |
564 | enum bus_mode speed_mode; |
565 | ||
19f7a34a AG |
566 | dev = (int)dectoul(argv[1], NULL); |
567 | part = (int)dectoul(argv[2], NULL); | |
568 | if (part > PART_ACCESS_MASK) { | |
569 | printf("#part_num shouldn't be larger than %d\n", | |
570 | PART_ACCESS_MASK); | |
571 | return CMD_RET_FAILURE; | |
572 | } | |
573 | speed_mode = (int)dectoul(argv[3], NULL); | |
574 | mmc = __init_mmc_device(dev, true, speed_mode); | |
1fd93c6e PA |
575 | } else { |
576 | return CMD_RET_USAGE; | |
577 | } | |
33ace362 | 578 | |
1fd93c6e PA |
579 | if (!mmc) |
580 | return CMD_RET_FAILURE; | |
581 | ||
69f45cd5 | 582 | ret = blk_select_hwpart_devnum(IF_TYPE_MMC, dev, part); |
60dc58f7 SW |
583 | printf("switch to partitions #%d, %s\n", |
584 | part, (!ret) ? "OK" : "ERROR"); | |
585 | if (ret) | |
586 | return 1; | |
587 | ||
1fd93c6e PA |
588 | curr_device = dev; |
589 | if (mmc->part_config == MMCPART_NOAVAILABLE) | |
590 | printf("mmc%d is current device\n", curr_device); | |
591 | else | |
592 | printf("mmc%d(part %d) is current device\n", | |
c40fdca6 | 593 | curr_device, mmc_get_blk_desc(mmc)->hwpart); |
33ace362 | 594 | |
1fd93c6e PA |
595 | return CMD_RET_SUCCESS; |
596 | } | |
09140113 SG |
597 | |
598 | static int do_mmc_list(struct cmd_tbl *cmdtp, int flag, | |
599 | int argc, char *const argv[]) | |
1fd93c6e PA |
600 | { |
601 | print_mmc_devices('\n'); | |
602 | return CMD_RET_SUCCESS; | |
603 | } | |
c599f53b | 604 | |
cf17789e | 605 | #if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) |
f702dc1e MV |
606 | static void parse_hwpart_user_enh_size(struct mmc *mmc, |
607 | struct mmc_hwpart_conf *pconf, | |
608 | char *argv) | |
609 | { | |
1d4b3b2f | 610 | int i, ret; |
f702dc1e MV |
611 | |
612 | pconf->user.enh_size = 0; | |
613 | ||
614 | if (!strcmp(argv, "-")) { /* The rest of eMMC */ | |
615 | ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); | |
616 | ret = mmc_send_ext_csd(mmc, ext_csd); | |
617 | if (ret) | |
618 | return; | |
1d4b3b2f | 619 | /* The enh_size value is in 512B block units */ |
f702dc1e MV |
620 | pconf->user.enh_size = |
621 | ((ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT + 2] << 16) + | |
622 | (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT + 1] << 8) + | |
623 | ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT]) * 1024 * | |
624 | ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * | |
625 | ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; | |
626 | pconf->user.enh_size -= pconf->user.enh_start; | |
1d4b3b2f MV |
627 | for (i = 0; i < ARRAY_SIZE(mmc->capacity_gp); i++) { |
628 | /* | |
629 | * If the eMMC already has GP partitions set, | |
630 | * subtract their size from the maximum USER | |
631 | * partition size. | |
632 | * | |
633 | * Else, if the command was used to configure new | |
634 | * GP partitions, subtract their size from maximum | |
635 | * USER partition size. | |
636 | */ | |
637 | if (mmc->capacity_gp[i]) { | |
638 | /* The capacity_gp is in 1B units */ | |
639 | pconf->user.enh_size -= mmc->capacity_gp[i] >> 9; | |
640 | } else if (pconf->gp_part[i].size) { | |
641 | /* The gp_part[].size is in 512B units */ | |
642 | pconf->user.enh_size -= pconf->gp_part[i].size; | |
643 | } | |
644 | } | |
f702dc1e MV |
645 | } else { |
646 | pconf->user.enh_size = dectoul(argv, NULL); | |
647 | } | |
648 | } | |
649 | ||
650 | static int parse_hwpart_user(struct mmc *mmc, struct mmc_hwpart_conf *pconf, | |
09140113 | 651 | int argc, char *const argv[]) |
189f963a DSC |
652 | { |
653 | int i = 0; | |
654 | ||
655 | memset(&pconf->user, 0, sizeof(pconf->user)); | |
656 | ||
657 | while (i < argc) { | |
658 | if (!strcmp(argv[i], "enh")) { | |
659 | if (i + 2 >= argc) | |
660 | return -1; | |
661 | pconf->user.enh_start = | |
0b1284eb | 662 | dectoul(argv[i + 1], NULL); |
f702dc1e | 663 | parse_hwpart_user_enh_size(mmc, pconf, argv[i + 2]); |
189f963a DSC |
664 | i += 3; |
665 | } else if (!strcmp(argv[i], "wrrel")) { | |
666 | if (i + 1 >= argc) | |
667 | return -1; | |
668 | pconf->user.wr_rel_change = 1; | |
669 | if (!strcmp(argv[i+1], "on")) | |
670 | pconf->user.wr_rel_set = 1; | |
671 | else if (!strcmp(argv[i+1], "off")) | |
672 | pconf->user.wr_rel_set = 0; | |
673 | else | |
674 | return -1; | |
675 | i += 2; | |
676 | } else { | |
677 | break; | |
678 | } | |
679 | } | |
680 | return i; | |
681 | } | |
682 | ||
683 | static int parse_hwpart_gp(struct mmc_hwpart_conf *pconf, int pidx, | |
09140113 | 684 | int argc, char *const argv[]) |
189f963a DSC |
685 | { |
686 | int i; | |
687 | ||
688 | memset(&pconf->gp_part[pidx], 0, sizeof(pconf->gp_part[pidx])); | |
689 | ||
690 | if (1 >= argc) | |
691 | return -1; | |
0b1284eb | 692 | pconf->gp_part[pidx].size = dectoul(argv[0], NULL); |
189f963a DSC |
693 | |
694 | i = 1; | |
695 | while (i < argc) { | |
696 | if (!strcmp(argv[i], "enh")) { | |
697 | pconf->gp_part[pidx].enhanced = 1; | |
698 | i += 1; | |
699 | } else if (!strcmp(argv[i], "wrrel")) { | |
700 | if (i + 1 >= argc) | |
701 | return -1; | |
702 | pconf->gp_part[pidx].wr_rel_change = 1; | |
703 | if (!strcmp(argv[i+1], "on")) | |
704 | pconf->gp_part[pidx].wr_rel_set = 1; | |
705 | else if (!strcmp(argv[i+1], "off")) | |
706 | pconf->gp_part[pidx].wr_rel_set = 0; | |
707 | else | |
708 | return -1; | |
709 | i += 2; | |
710 | } else { | |
711 | break; | |
712 | } | |
713 | } | |
714 | return i; | |
715 | } | |
716 | ||
09140113 SG |
717 | static int do_mmc_hwpartition(struct cmd_tbl *cmdtp, int flag, |
718 | int argc, char *const argv[]) | |
c599f53b DSC |
719 | { |
720 | struct mmc *mmc; | |
721 | struct mmc_hwpart_conf pconf = { }; | |
722 | enum mmc_hwpart_conf_mode mode = MMC_HWPART_CONF_CHECK; | |
189f963a | 723 | int i, r, pidx; |
c599f53b DSC |
724 | |
725 | mmc = init_mmc_device(curr_device, false); | |
726 | if (!mmc) | |
727 | return CMD_RET_FAILURE; | |
728 | ||
0d453c84 JC |
729 | if (IS_SD(mmc)) { |
730 | puts("SD doesn't support partitioning\n"); | |
731 | return CMD_RET_FAILURE; | |
732 | } | |
733 | ||
c599f53b DSC |
734 | if (argc < 1) |
735 | return CMD_RET_USAGE; | |
736 | i = 1; | |
737 | while (i < argc) { | |
189f963a DSC |
738 | if (!strcmp(argv[i], "user")) { |
739 | i++; | |
f702dc1e | 740 | r = parse_hwpart_user(mmc, &pconf, argc - i, &argv[i]); |
189f963a | 741 | if (r < 0) |
c599f53b | 742 | return CMD_RET_USAGE; |
189f963a | 743 | i += r; |
c599f53b DSC |
744 | } else if (!strncmp(argv[i], "gp", 2) && |
745 | strlen(argv[i]) == 3 && | |
746 | argv[i][2] >= '1' && argv[i][2] <= '4') { | |
c599f53b | 747 | pidx = argv[i][2] - '1'; |
189f963a DSC |
748 | i++; |
749 | r = parse_hwpart_gp(&pconf, pidx, argc-i, &argv[i]); | |
750 | if (r < 0) | |
751 | return CMD_RET_USAGE; | |
752 | i += r; | |
c599f53b DSC |
753 | } else if (!strcmp(argv[i], "check")) { |
754 | mode = MMC_HWPART_CONF_CHECK; | |
755 | i++; | |
756 | } else if (!strcmp(argv[i], "set")) { | |
757 | mode = MMC_HWPART_CONF_SET; | |
758 | i++; | |
759 | } else if (!strcmp(argv[i], "complete")) { | |
760 | mode = MMC_HWPART_CONF_COMPLETE; | |
761 | i++; | |
762 | } else { | |
763 | return CMD_RET_USAGE; | |
764 | } | |
765 | } | |
766 | ||
767 | puts("Partition configuration:\n"); | |
768 | if (pconf.user.enh_size) { | |
769 | puts("\tUser Enhanced Start: "); | |
770 | print_size(((u64)pconf.user.enh_start) << 9, "\n"); | |
771 | puts("\tUser Enhanced Size: "); | |
772 | print_size(((u64)pconf.user.enh_size) << 9, "\n"); | |
773 | } else { | |
774 | puts("\tNo enhanced user data area\n"); | |
775 | } | |
189f963a DSC |
776 | if (pconf.user.wr_rel_change) |
777 | printf("\tUser partition write reliability: %s\n", | |
778 | pconf.user.wr_rel_set ? "on" : "off"); | |
c599f53b DSC |
779 | for (pidx = 0; pidx < 4; pidx++) { |
780 | if (pconf.gp_part[pidx].size) { | |
781 | printf("\tGP%i Capacity: ", pidx+1); | |
782 | print_size(((u64)pconf.gp_part[pidx].size) << 9, | |
783 | pconf.gp_part[pidx].enhanced ? | |
784 | " ENH\n" : "\n"); | |
785 | } else { | |
786 | printf("\tNo GP%i partition\n", pidx+1); | |
787 | } | |
189f963a DSC |
788 | if (pconf.gp_part[pidx].wr_rel_change) |
789 | printf("\tGP%i write reliability: %s\n", pidx+1, | |
790 | pconf.gp_part[pidx].wr_rel_set ? "on" : "off"); | |
c599f53b DSC |
791 | } |
792 | ||
793 | if (!mmc_hwpart_config(mmc, &pconf, mode)) { | |
794 | if (mode == MMC_HWPART_CONF_COMPLETE) | |
795 | puts("Partitioning successful, " | |
796 | "power-cycle to make effective\n"); | |
797 | return CMD_RET_SUCCESS; | |
798 | } else { | |
189f963a | 799 | puts("Failed!\n"); |
c599f53b DSC |
800 | return CMD_RET_FAILURE; |
801 | } | |
802 | } | |
cf17789e | 803 | #endif |
c599f53b | 804 | |
1fd93c6e | 805 | #ifdef CONFIG_SUPPORT_EMMC_BOOT |
09140113 SG |
806 | static int do_mmc_bootbus(struct cmd_tbl *cmdtp, int flag, |
807 | int argc, char *const argv[]) | |
1fd93c6e PA |
808 | { |
809 | int dev; | |
810 | struct mmc *mmc; | |
811 | u8 width, reset, mode; | |
812 | ||
813 | if (argc != 5) | |
814 | return CMD_RET_USAGE; | |
0b1284eb SG |
815 | dev = dectoul(argv[1], NULL); |
816 | width = dectoul(argv[2], NULL); | |
817 | reset = dectoul(argv[3], NULL); | |
818 | mode = dectoul(argv[4], NULL); | |
1fd93c6e | 819 | |
1ae24a50 | 820 | mmc = init_mmc_device(dev, false); |
1fd93c6e PA |
821 | if (!mmc) |
822 | return CMD_RET_FAILURE; | |
823 | ||
824 | if (IS_SD(mmc)) { | |
825 | puts("BOOT_BUS_WIDTH only exists on eMMC\n"); | |
826 | return CMD_RET_FAILURE; | |
2a91c913 | 827 | } |
ab71188c | 828 | |
e9978b17 JC |
829 | /* |
830 | * BOOT_BUS_CONDITIONS[177] | |
831 | * BOOT_MODE[4:3] | |
832 | * 0x0 : Use SDR + Backward compatible timing in boot operation | |
833 | * 0x1 : Use SDR + High Speed Timing in boot operation mode | |
834 | * 0x2 : Use DDR in boot operation | |
835 | * RESET_BOOT_BUS_CONDITIONS | |
836 | * 0x0 : Reset bus width to x1, SDR, Backward compatible | |
837 | * 0x1 : Retain BOOT_BUS_WIDTH and BOOT_MODE | |
838 | * BOOT_BUS_WIDTH | |
839 | * 0x0 : x1(sdr) or x4 (ddr) buswidth | |
840 | * 0x1 : x4(sdr/ddr) buswith | |
841 | * 0x2 : x8(sdr/ddr) buswith | |
842 | * | |
843 | */ | |
844 | if (width >= 0x3) { | |
845 | printf("boot_bus_width %d is invalid\n", width); | |
846 | return CMD_RET_FAILURE; | |
847 | } | |
848 | ||
849 | if (reset >= 0x2) { | |
850 | printf("reset_boot_bus_width %d is invalid\n", reset); | |
851 | return CMD_RET_FAILURE; | |
852 | } | |
853 | ||
854 | if (mode >= 0x3) { | |
855 | printf("reset_boot_bus_width %d is invalid\n", mode); | |
856 | return CMD_RET_FAILURE; | |
857 | } | |
858 | ||
1fd93c6e | 859 | /* acknowledge to be sent during boot operation */ |
e9978b17 JC |
860 | if (mmc_set_boot_bus_width(mmc, width, reset, mode)) { |
861 | puts("BOOT_BUS_WIDTH is failed to change.\n"); | |
862 | return CMD_RET_FAILURE; | |
863 | } | |
864 | ||
865 | printf("Set to BOOT_BUS_WIDTH = 0x%x, RESET = 0x%x, BOOT_MODE = 0x%x\n", | |
866 | width, reset, mode); | |
867 | return CMD_RET_SUCCESS; | |
1fd93c6e | 868 | } |
09140113 SG |
869 | |
870 | static int do_mmc_boot_resize(struct cmd_tbl *cmdtp, int flag, | |
871 | int argc, char *const argv[]) | |
1fd93c6e PA |
872 | { |
873 | int dev; | |
874 | struct mmc *mmc; | |
875 | u32 bootsize, rpmbsize; | |
ab71188c | 876 | |
1fd93c6e PA |
877 | if (argc != 4) |
878 | return CMD_RET_USAGE; | |
0b1284eb SG |
879 | dev = dectoul(argv[1], NULL); |
880 | bootsize = dectoul(argv[2], NULL); | |
881 | rpmbsize = dectoul(argv[3], NULL); | |
1fd93c6e | 882 | |
1ae24a50 | 883 | mmc = init_mmc_device(dev, false); |
1fd93c6e PA |
884 | if (!mmc) |
885 | return CMD_RET_FAILURE; | |
886 | ||
887 | if (IS_SD(mmc)) { | |
71a3e5c5 | 888 | printf("It is not an eMMC device\n"); |
1fd93c6e | 889 | return CMD_RET_FAILURE; |
ab71188c MN |
890 | } |
891 | ||
1fd93c6e PA |
892 | if (mmc_boot_partition_size_change(mmc, bootsize, rpmbsize)) { |
893 | printf("EMMC boot partition Size change Failed.\n"); | |
894 | return CMD_RET_FAILURE; | |
895 | } | |
e85649c7 | 896 | |
1fd93c6e PA |
897 | printf("EMMC boot partition Size %d MB\n", bootsize); |
898 | printf("EMMC RPMB partition Size %d MB\n", rpmbsize); | |
899 | return CMD_RET_SUCCESS; | |
900 | } | |
bdb60996 | 901 | |
5c2beda5 | 902 | static int mmc_partconf_print(struct mmc *mmc, const char *varname) |
bdb60996 AD |
903 | { |
904 | u8 ack, access, part; | |
905 | ||
906 | if (mmc->part_config == MMCPART_NOAVAILABLE) { | |
907 | printf("No part_config info for ver. 0x%x\n", mmc->version); | |
908 | return CMD_RET_FAILURE; | |
909 | } | |
910 | ||
911 | access = EXT_CSD_EXTRACT_PARTITION_ACCESS(mmc->part_config); | |
912 | ack = EXT_CSD_EXTRACT_BOOT_ACK(mmc->part_config); | |
913 | part = EXT_CSD_EXTRACT_BOOT_PART(mmc->part_config); | |
914 | ||
5c2beda5 RD |
915 | if(varname) |
916 | env_set_hex(varname, part); | |
917 | ||
bdb60996 AD |
918 | printf("EXT_CSD[179], PARTITION_CONFIG:\n" |
919 | "BOOT_ACK: 0x%x\n" | |
920 | "BOOT_PARTITION_ENABLE: 0x%x\n" | |
921 | "PARTITION_ACCESS: 0x%x\n", ack, part, access); | |
922 | ||
923 | return CMD_RET_SUCCESS; | |
924 | } | |
925 | ||
09140113 SG |
926 | static int do_mmc_partconf(struct cmd_tbl *cmdtp, int flag, |
927 | int argc, char *const argv[]) | |
1fd93c6e PA |
928 | { |
929 | int dev; | |
930 | struct mmc *mmc; | |
931 | u8 ack, part_num, access; | |
272cc70b | 932 | |
5c2beda5 | 933 | if (argc != 2 && argc != 3 && argc != 5) |
1fd93c6e | 934 | return CMD_RET_USAGE; |
272cc70b | 935 | |
0b1284eb | 936 | dev = dectoul(argv[1], NULL); |
d23d8d7e | 937 | |
1ae24a50 | 938 | mmc = init_mmc_device(dev, false); |
1fd93c6e PA |
939 | if (!mmc) |
940 | return CMD_RET_FAILURE; | |
941 | ||
942 | if (IS_SD(mmc)) { | |
943 | puts("PARTITION_CONFIG only exists on eMMC\n"); | |
944 | return CMD_RET_FAILURE; | |
945 | } | |
946 | ||
5c2beda5 RD |
947 | if (argc == 2 || argc == 3) |
948 | return mmc_partconf_print(mmc, argc == 3 ? argv[2] : NULL); | |
bdb60996 | 949 | |
0b1284eb SG |
950 | ack = dectoul(argv[2], NULL); |
951 | part_num = dectoul(argv[3], NULL); | |
952 | access = dectoul(argv[4], NULL); | |
bdb60996 | 953 | |
1fd93c6e PA |
954 | /* acknowledge to be sent during boot operation */ |
955 | return mmc_set_part_conf(mmc, ack, part_num, access); | |
956 | } | |
09140113 SG |
957 | |
958 | static int do_mmc_rst_func(struct cmd_tbl *cmdtp, int flag, | |
959 | int argc, char *const argv[]) | |
1fd93c6e PA |
960 | { |
961 | int dev; | |
962 | struct mmc *mmc; | |
963 | u8 enable; | |
964 | ||
965 | /* | |
966 | * Set the RST_n_ENABLE bit of RST_n_FUNCTION | |
967 | * The only valid values are 0x0, 0x1 and 0x2 and writing | |
968 | * a value of 0x1 or 0x2 sets the value permanently. | |
969 | */ | |
970 | if (argc != 3) | |
971 | return CMD_RET_USAGE; | |
972 | ||
0b1284eb SG |
973 | dev = dectoul(argv[1], NULL); |
974 | enable = dectoul(argv[2], NULL); | |
1fd93c6e | 975 | |
678e9316 | 976 | if (enable > 2) { |
1fd93c6e PA |
977 | puts("Invalid RST_n_ENABLE value\n"); |
978 | return CMD_RET_USAGE; | |
979 | } | |
980 | ||
1ae24a50 | 981 | mmc = init_mmc_device(dev, false); |
1fd93c6e PA |
982 | if (!mmc) |
983 | return CMD_RET_FAILURE; | |
984 | ||
985 | if (IS_SD(mmc)) { | |
986 | puts("RST_n_FUNCTION only exists on eMMC\n"); | |
987 | return CMD_RET_FAILURE; | |
988 | } | |
272cc70b | 989 | |
1fd93c6e PA |
990 | return mmc_set_rst_n_function(mmc, enable); |
991 | } | |
992 | #endif | |
09140113 SG |
993 | static int do_mmc_setdsr(struct cmd_tbl *cmdtp, int flag, |
994 | int argc, char *const argv[]) | |
1fd93c6e PA |
995 | { |
996 | struct mmc *mmc; | |
997 | u32 val; | |
998 | int ret; | |
999 | ||
1000 | if (argc != 2) | |
1001 | return CMD_RET_USAGE; | |
7e5f460e | 1002 | val = hextoul(argv[1], NULL); |
1fd93c6e PA |
1003 | |
1004 | mmc = find_mmc_device(curr_device); | |
1005 | if (!mmc) { | |
1006 | printf("no mmc device at slot %x\n", curr_device); | |
1007 | return CMD_RET_FAILURE; | |
1008 | } | |
1009 | ret = mmc_set_dsr(mmc, val); | |
1010 | printf("set dsr %s\n", (!ret) ? "OK, force rescan" : "ERROR"); | |
1011 | if (!ret) { | |
1012 | mmc->has_init = 0; | |
1013 | if (mmc_init(mmc)) | |
1014 | return CMD_RET_FAILURE; | |
1015 | else | |
1016 | return CMD_RET_SUCCESS; | |
272cc70b | 1017 | } |
1fd93c6e PA |
1018 | return ret; |
1019 | } | |
1020 | ||
cd3d4880 | 1021 | #ifdef CONFIG_CMD_BKOPS_ENABLE |
09140113 SG |
1022 | static int do_mmc_bkops_enable(struct cmd_tbl *cmdtp, int flag, |
1023 | int argc, char *const argv[]) | |
cd3d4880 TM |
1024 | { |
1025 | int dev; | |
1026 | struct mmc *mmc; | |
1027 | ||
1028 | if (argc != 2) | |
1029 | return CMD_RET_USAGE; | |
1030 | ||
0b1284eb | 1031 | dev = dectoul(argv[1], NULL); |
cd3d4880 TM |
1032 | |
1033 | mmc = init_mmc_device(dev, false); | |
1034 | if (!mmc) | |
1035 | return CMD_RET_FAILURE; | |
1036 | ||
1037 | if (IS_SD(mmc)) { | |
1038 | puts("BKOPS_EN only exists on eMMC\n"); | |
1039 | return CMD_RET_FAILURE; | |
1040 | } | |
1041 | ||
1042 | return mmc_set_bkops_enable(mmc); | |
1043 | } | |
1044 | #endif | |
1045 | ||
09140113 | 1046 | static int do_mmc_boot_wp(struct cmd_tbl *cmdtp, int flag, |
0469d846 HS |
1047 | int argc, char * const argv[]) |
1048 | { | |
1049 | int err; | |
1050 | struct mmc *mmc; | |
1051 | ||
1052 | mmc = init_mmc_device(curr_device, false); | |
1053 | if (!mmc) | |
1054 | return CMD_RET_FAILURE; | |
1055 | if (IS_SD(mmc)) { | |
1056 | printf("It is not an eMMC device\n"); | |
1057 | return CMD_RET_FAILURE; | |
1058 | } | |
1059 | err = mmc_boot_wp(mmc); | |
1060 | if (err) | |
1061 | return CMD_RET_FAILURE; | |
1062 | printf("boot areas protected\n"); | |
1063 | return CMD_RET_SUCCESS; | |
1064 | } | |
1065 | ||
09140113 | 1066 | static struct cmd_tbl cmd_mmc[] = { |
1fd93c6e PA |
1067 | U_BOOT_CMD_MKENT(info, 1, 0, do_mmcinfo, "", ""), |
1068 | U_BOOT_CMD_MKENT(read, 4, 1, do_mmc_read, "", ""), | |
0469d846 | 1069 | U_BOOT_CMD_MKENT(wp, 1, 0, do_mmc_boot_wp, "", ""), |
e6fa5a54 | 1070 | #if CONFIG_IS_ENABLED(MMC_WRITE) |
1fd93c6e PA |
1071 | U_BOOT_CMD_MKENT(write, 4, 0, do_mmc_write, "", ""), |
1072 | U_BOOT_CMD_MKENT(erase, 3, 0, do_mmc_erase, "", ""), | |
c232d14d AK |
1073 | #endif |
1074 | #if CONFIG_IS_ENABLED(CMD_MMC_SWRITE) | |
1075 | U_BOOT_CMD_MKENT(swrite, 3, 0, do_mmc_sparse_write, "", ""), | |
e6fa5a54 | 1076 | #endif |
19f7a34a | 1077 | U_BOOT_CMD_MKENT(rescan, 2, 1, do_mmc_rescan, "", ""), |
1fd93c6e | 1078 | U_BOOT_CMD_MKENT(part, 1, 1, do_mmc_part, "", ""), |
19f7a34a | 1079 | U_BOOT_CMD_MKENT(dev, 4, 0, do_mmc_dev, "", ""), |
1fd93c6e | 1080 | U_BOOT_CMD_MKENT(list, 1, 1, do_mmc_list, "", ""), |
cf17789e | 1081 | #if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) |
189f963a | 1082 | U_BOOT_CMD_MKENT(hwpartition, 28, 0, do_mmc_hwpartition, "", ""), |
cf17789e | 1083 | #endif |
1fd93c6e PA |
1084 | #ifdef CONFIG_SUPPORT_EMMC_BOOT |
1085 | U_BOOT_CMD_MKENT(bootbus, 5, 0, do_mmc_bootbus, "", ""), | |
fa7b8851 | 1086 | U_BOOT_CMD_MKENT(bootpart-resize, 4, 0, do_mmc_boot_resize, "", ""), |
1fd93c6e PA |
1087 | U_BOOT_CMD_MKENT(partconf, 5, 0, do_mmc_partconf, "", ""), |
1088 | U_BOOT_CMD_MKENT(rst-function, 3, 0, do_mmc_rst_func, "", ""), | |
1089 | #endif | |
5a7b11e6 | 1090 | #if CONFIG_IS_ENABLED(CMD_MMC_RPMB) |
1fd93c6e PA |
1091 | U_BOOT_CMD_MKENT(rpmb, CONFIG_SYS_MAXARGS, 1, do_mmcrpmb, "", ""), |
1092 | #endif | |
1093 | U_BOOT_CMD_MKENT(setdsr, 2, 0, do_mmc_setdsr, "", ""), | |
cd3d4880 TM |
1094 | #ifdef CONFIG_CMD_BKOPS_ENABLE |
1095 | U_BOOT_CMD_MKENT(bkops-enable, 2, 0, do_mmc_bkops_enable, "", ""), | |
1096 | #endif | |
1fd93c6e PA |
1097 | }; |
1098 | ||
09140113 SG |
1099 | static int do_mmcops(struct cmd_tbl *cmdtp, int flag, int argc, |
1100 | char *const argv[]) | |
1fd93c6e | 1101 | { |
09140113 | 1102 | struct cmd_tbl *cp; |
1fd93c6e PA |
1103 | |
1104 | cp = find_cmd_tbl(argv[1], cmd_mmc, ARRAY_SIZE(cmd_mmc)); | |
1105 | ||
1106 | /* Drop the mmc command */ | |
1107 | argc--; | |
1108 | argv++; | |
1109 | ||
1110 | if (cp == NULL || argc > cp->maxargs) | |
1111 | return CMD_RET_USAGE; | |
80a48dd4 | 1112 | if (flag == CMD_FLAG_REPEAT && !cmd_is_repeatable(cp)) |
1fd93c6e | 1113 | return CMD_RET_SUCCESS; |
ea6ebe21 | 1114 | |
1fd93c6e PA |
1115 | if (curr_device < 0) { |
1116 | if (get_mmc_num() > 0) { | |
1117 | curr_device = 0; | |
1118 | } else { | |
1119 | puts("No MMC device available\n"); | |
1120 | return CMD_RET_FAILURE; | |
1121 | } | |
1122 | } | |
1123 | return cp->cmd(cmdtp, flag, argc, argv); | |
272cc70b AF |
1124 | } |
1125 | ||
1126 | U_BOOT_CMD( | |
189f963a | 1127 | mmc, 29, 1, do_mmcops, |
852dbfdd | 1128 | "MMC sub system", |
1fd93c6e PA |
1129 | "info - display info of the current MMC device\n" |
1130 | "mmc read addr blk# cnt\n" | |
ea6ebe21 | 1131 | "mmc write addr blk# cnt\n" |
c232d14d | 1132 | #if CONFIG_IS_ENABLED(CMD_MMC_SWRITE) |
732bc7ce JB |
1133 | "mmc swrite addr blk#\n" |
1134 | #endif | |
e6f99a56 | 1135 | "mmc erase blk# cnt\n" |
19f7a34a | 1136 | "mmc rescan [mode]\n" |
ea6ebe21 | 1137 | "mmc part - lists available partition on current mmc device\n" |
19f7a34a AG |
1138 | "mmc dev [dev] [part] [mode] - show or set current mmc device [partition] and set mode\n" |
1139 | " - the required speed mode is passed as the index from the following list\n" | |
1140 | " [MMC_LEGACY, MMC_HS, SD_HS, MMC_HS_52, MMC_DDR_52, UHS_SDR12, UHS_SDR25,\n" | |
1141 | " UHS_SDR50, UHS_DDR50, UHS_SDR104, MMC_HS_200, MMC_HS_400, MMC_HS_400_ES]\n" | |
2a91c913 | 1142 | "mmc list - lists available devices\n" |
a633a804 | 1143 | "mmc wp - power on write protect boot partitions\n" |
84593679 | 1144 | #if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) |
8ae82c4b | 1145 | "mmc hwpartition <USER> <GP> <MODE> - does hardware partitioning\n" |
c599f53b | 1146 | " arguments (sizes in 512-byte blocks):\n" |
8ae82c4b JC |
1147 | " USER - <user> <enh> <start> <cnt> <wrrel> <{on|off}>\n" |
1148 | " : sets user data area attributes\n" | |
1149 | " GP - <{gp1|gp2|gp3|gp4}> <cnt> <enh> <wrrel> <{on|off}>\n" | |
1150 | " : general purpose partition\n" | |
1151 | " MODE - <{check|set|complete}>\n" | |
1152 | " : mode, complete set partitioning completed\n" | |
189f963a DSC |
1153 | " WARNING: Partitioning is a write-once setting once it is set to complete.\n" |
1154 | " Power cycling is required to initialize partitions after set to complete.\n" | |
84593679 | 1155 | #endif |
2a91c913 | 1156 | #ifdef CONFIG_SUPPORT_EMMC_BOOT |
1019b196 | 1157 | "mmc bootbus <dev> <boot_bus_width> <reset_boot_bus_width> <boot_mode>\n" |
5a99b9de | 1158 | " - Set the BOOT_BUS_WIDTH field of the specified device\n" |
f1fd957e TR |
1159 | "mmc bootpart-resize <dev> <boot part size MB> <RPMB part size MB>\n" |
1160 | " - Change sizes of boot and RPMB partitions of specified device\n" | |
5c2beda5 | 1161 | "mmc partconf <dev> [[varname] | [<boot_ack> <boot_partition> <partition_access>]]\n" |
bdb60996 | 1162 | " - Show or change the bits of the PARTITION_CONFIG field of the specified device\n" |
5c2beda5 | 1163 | " If showing the bits, optionally store the boot_partition field into varname\n" |
1019b196 | 1164 | "mmc rst-function <dev> <value>\n" |
33ace362 TR |
1165 | " - Change the RST_n_FUNCTION field of the specified device\n" |
1166 | " WARNING: This is a write-once field and 0 / 1 / 2 are the only valid values.\n" | |
3511b4e2 | 1167 | #endif |
5a7b11e6 | 1168 | #if CONFIG_IS_ENABLED(CMD_MMC_RPMB) |
1fd93c6e PA |
1169 | "mmc rpmb read addr blk# cnt [address of auth-key] - block size is 256 bytes\n" |
1170 | "mmc rpmb write addr blk# cnt <address of auth-key> - block size is 256 bytes\n" | |
1171 | "mmc rpmb key <address of auth-key> - program the RPMB authentication key.\n" | |
1172 | "mmc rpmb counter - read the value of the write counter\n" | |
1173 | #endif | |
1174 | "mmc setdsr <value> - set DSR register value\n" | |
cd3d4880 TM |
1175 | #ifdef CONFIG_CMD_BKOPS_ENABLE |
1176 | "mmc bkops-enable <dev> - enable background operations handshake on device\n" | |
1177 | " WARNING: This is a write-once setting.\n" | |
1178 | #endif | |
2a91c913 | 1179 | ); |
1fd93c6e PA |
1180 | |
1181 | /* Old command kept for compatibility. Same as 'mmc info' */ | |
1182 | U_BOOT_CMD( | |
1183 | mmcinfo, 1, 0, do_mmcinfo, | |
1184 | "display MMC info", | |
1185 | "- display info of the current MMC device" | |
1186 | ); |