1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * (C) Copyright 2003
3*4882a593Smuzhiyun * Kyle Harris, kharris@nexus-tech.net
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * SPDX-License-Identifier: GPL-2.0+
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun #include <common.h>
8*4882a593Smuzhiyun #include <command.h>
9*4882a593Smuzhiyun #include <console.h>
10*4882a593Smuzhiyun #include <mmc.h>
11*4882a593Smuzhiyun #include <optee_include/OpteeClientInterface.h>
12*4882a593Smuzhiyun #include <optee_include/OpteeClientApiLib.h>
13*4882a593Smuzhiyun #include <optee_test.h>
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun static int curr_device = -1;
16*4882a593Smuzhiyun
print_mmcinfo(struct mmc * mmc)17*4882a593Smuzhiyun static void print_mmcinfo(struct mmc *mmc)
18*4882a593Smuzhiyun {
19*4882a593Smuzhiyun int i;
20*4882a593Smuzhiyun const char *timing[] = {
21*4882a593Smuzhiyun "Legacy", "High Speed", "High Speed", "SDR12",
22*4882a593Smuzhiyun "SDR25", "SDR50", "SDR104", "DDR50",
23*4882a593Smuzhiyun "DDR52", "HS200", "HS400", "HS400 Enhanced Strobe"};
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun printf("Device: %s\n", mmc->cfg->name);
26*4882a593Smuzhiyun printf("Manufacturer ID: %x\n", mmc->cid[0] >> 24);
27*4882a593Smuzhiyun printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff);
28*4882a593Smuzhiyun printf("Name: %c%c%c%c%c \n", mmc->cid[0] & 0xff,
29*4882a593Smuzhiyun (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
30*4882a593Smuzhiyun (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff);
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun printf("Timing Interface: %s\n", timing[mmc->timing]);
33*4882a593Smuzhiyun printf("Tran Speed: %d\n", mmc->clock);
34*4882a593Smuzhiyun printf("Rd Block Len: %d\n", mmc->read_bl_len);
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun printf("%s version %d.%d", IS_SD(mmc) ? "SD" : "MMC",
37*4882a593Smuzhiyun EXTRACT_SDMMC_MAJOR_VERSION(mmc->version),
38*4882a593Smuzhiyun EXTRACT_SDMMC_MINOR_VERSION(mmc->version));
39*4882a593Smuzhiyun if (EXTRACT_SDMMC_CHANGE_VERSION(mmc->version) != 0)
40*4882a593Smuzhiyun printf(".%d", EXTRACT_SDMMC_CHANGE_VERSION(mmc->version));
41*4882a593Smuzhiyun printf("\n");
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun printf("High Capacity: %s\n", mmc->high_capacity ? "Yes" : "No");
44*4882a593Smuzhiyun puts("Capacity: ");
45*4882a593Smuzhiyun print_size(mmc->capacity, "\n");
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun printf("Bus Width: %d-bit%s\n", mmc->bus_width,
48*4882a593Smuzhiyun mmc_card_ddr(mmc) ? " DDR" : "");
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun puts("Erase Group Size: ");
51*4882a593Smuzhiyun print_size(((u64)mmc->erase_grp_size) << 9, "\n");
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun if (!IS_SD(mmc) && mmc->version >= MMC_VERSION_4_41) {
54*4882a593Smuzhiyun bool has_enh = (mmc->part_support & ENHNCD_SUPPORT) != 0;
55*4882a593Smuzhiyun bool usr_enh = has_enh && (mmc->part_attr & EXT_CSD_ENH_USR);
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun puts("HC WP Group Size: ");
58*4882a593Smuzhiyun print_size(((u64)mmc->hc_wp_grp_size) << 9, "\n");
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun puts("User Capacity: ");
61*4882a593Smuzhiyun print_size(mmc->capacity_user, usr_enh ? " ENH" : "");
62*4882a593Smuzhiyun if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_USR)
63*4882a593Smuzhiyun puts(" WRREL\n");
64*4882a593Smuzhiyun else
65*4882a593Smuzhiyun putc('\n');
66*4882a593Smuzhiyun if (usr_enh) {
67*4882a593Smuzhiyun puts("User Enhanced Start: ");
68*4882a593Smuzhiyun print_size(mmc->enh_user_start, "\n");
69*4882a593Smuzhiyun puts("User Enhanced Size: ");
70*4882a593Smuzhiyun print_size(mmc->enh_user_size, "\n");
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun puts("Boot Capacity: ");
73*4882a593Smuzhiyun print_size(mmc->capacity_boot, has_enh ? " ENH\n" : "\n");
74*4882a593Smuzhiyun puts("RPMB Capacity: ");
75*4882a593Smuzhiyun print_size(mmc->capacity_rpmb, has_enh ? " ENH\n" : "\n");
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(mmc->capacity_gp); i++) {
78*4882a593Smuzhiyun bool is_enh = has_enh &&
79*4882a593Smuzhiyun (mmc->part_attr & EXT_CSD_ENH_GP(i));
80*4882a593Smuzhiyun if (mmc->capacity_gp[i]) {
81*4882a593Smuzhiyun printf("GP%i Capacity: ", i+1);
82*4882a593Smuzhiyun print_size(mmc->capacity_gp[i],
83*4882a593Smuzhiyun is_enh ? " ENH" : "");
84*4882a593Smuzhiyun if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_GP(i))
85*4882a593Smuzhiyun puts(" WRREL\n");
86*4882a593Smuzhiyun else
87*4882a593Smuzhiyun putc('\n');
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun }
init_mmc_device(int dev,bool force_init)92*4882a593Smuzhiyun static struct mmc *init_mmc_device(int dev, bool force_init)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun struct mmc *mmc;
95*4882a593Smuzhiyun mmc = find_mmc_device(dev);
96*4882a593Smuzhiyun if (!mmc) {
97*4882a593Smuzhiyun printf("no mmc device at slot %x\n", dev);
98*4882a593Smuzhiyun return NULL;
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun if (force_init)
102*4882a593Smuzhiyun mmc->has_init = 0;
103*4882a593Smuzhiyun if (mmc_init(mmc))
104*4882a593Smuzhiyun return NULL;
105*4882a593Smuzhiyun return mmc;
106*4882a593Smuzhiyun }
do_mmcinfo(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])107*4882a593Smuzhiyun static int do_mmcinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
108*4882a593Smuzhiyun {
109*4882a593Smuzhiyun struct mmc *mmc;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun if (curr_device < 0) {
112*4882a593Smuzhiyun if (get_mmc_num() > 0)
113*4882a593Smuzhiyun curr_device = 0;
114*4882a593Smuzhiyun else {
115*4882a593Smuzhiyun puts("No MMC device available\n");
116*4882a593Smuzhiyun return 1;
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun mmc = init_mmc_device(curr_device, false);
121*4882a593Smuzhiyun if (!mmc)
122*4882a593Smuzhiyun return CMD_RET_FAILURE;
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun print_mmcinfo(mmc);
125*4882a593Smuzhiyun return CMD_RET_SUCCESS;
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun #ifdef CONFIG_OPTEE_CLIENT
do_mmc_test_secure_storage(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])129*4882a593Smuzhiyun static int do_mmc_test_secure_storage(cmd_tbl_t *cmdtp,
130*4882a593Smuzhiyun int flag, int argc, char * const argv[])
131*4882a593Smuzhiyun {
132*4882a593Smuzhiyun #ifdef CONFIG_MMC
133*4882a593Smuzhiyun struct mmc *mmc;
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun if (curr_device < 0) {
136*4882a593Smuzhiyun if (get_mmc_num() > 0) {
137*4882a593Smuzhiyun puts("MMC device available\n");
138*4882a593Smuzhiyun curr_device = 0;
139*4882a593Smuzhiyun } else {
140*4882a593Smuzhiyun puts("No MMC device available\n");
141*4882a593Smuzhiyun return 1;
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun mmc = init_mmc_device(curr_device, false);
146*4882a593Smuzhiyun if (!mmc)
147*4882a593Smuzhiyun printf("No mmc device\n");
148*4882a593Smuzhiyun #endif
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun int i, count = 100;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun for (i = 1; i <= count; i++) {
153*4882a593Smuzhiyun if (test_secure_storage_default() == 0) {
154*4882a593Smuzhiyun printf("test_secure_storage_default success! %d/%d\n", i, count);
155*4882a593Smuzhiyun } else {
156*4882a593Smuzhiyun printf("test_secure_storage_default fail! %d/%d\n", i, count);
157*4882a593Smuzhiyun break;
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun if (test_secure_storage_security_partition() == 0) {
160*4882a593Smuzhiyun printf("test_secure_storage_security_partition success! %d/%d\n", i, count);
161*4882a593Smuzhiyun } else {
162*4882a593Smuzhiyun printf("test_secure_storage_security_partition fail! %d/%d\n", i, count);
163*4882a593Smuzhiyun break;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun return CMD_RET_SUCCESS;
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun
do_mmc_testefuse(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])170*4882a593Smuzhiyun static int do_mmc_testefuse(cmd_tbl_t *cmdtp,
171*4882a593Smuzhiyun int flag, int argc, char * const argv[])
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun uint32_t outbuf32[8];
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun trusty_read_attribute_hash(outbuf32, 8);
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun printf(" 0x%x 0x%x 0x%x 0x%x \n",
178*4882a593Smuzhiyun outbuf32[0], outbuf32[1], outbuf32[2], outbuf32[3]);
179*4882a593Smuzhiyun printf(" 0x%x 0x%x 0x%x 0x%x \n",
180*4882a593Smuzhiyun outbuf32[4], outbuf32[5], outbuf32[6], outbuf32[7]);
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun return CMD_RET_SUCCESS;
183*4882a593Smuzhiyun }
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun #endif
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun #ifdef CONFIG_SUPPORT_EMMC_RPMB
188*4882a593Smuzhiyun char temp_original_part;
init_rpmb(void)189*4882a593Smuzhiyun int init_rpmb(void)
190*4882a593Smuzhiyun {
191*4882a593Smuzhiyun struct mmc *mmc;
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun if (curr_device < 0) {
194*4882a593Smuzhiyun if (get_mmc_num() > 0) {
195*4882a593Smuzhiyun curr_device = 0;
196*4882a593Smuzhiyun } else {
197*4882a593Smuzhiyun printf("No MMC device available\n");
198*4882a593Smuzhiyun return CMD_RET_FAILURE;
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun mmc = init_mmc_device(curr_device, false);
203*4882a593Smuzhiyun if (!mmc)
204*4882a593Smuzhiyun return CMD_RET_FAILURE;
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun if (!(mmc->version & MMC_VERSION_MMC)) {
207*4882a593Smuzhiyun printf("It is not a EMMC device\n");
208*4882a593Smuzhiyun return CMD_RET_FAILURE;
209*4882a593Smuzhiyun }
210*4882a593Smuzhiyun if (mmc->version < MMC_VERSION_4_41) {
211*4882a593Smuzhiyun printf("RPMB not supported before version 4.41\n");
212*4882a593Smuzhiyun return CMD_RET_FAILURE;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun /* Switch to the RPMB partition */
216*4882a593Smuzhiyun #ifndef CONFIG_BLK
217*4882a593Smuzhiyun temp_original_part = mmc->block_dev.hwpart;
218*4882a593Smuzhiyun debug("mmc->block_dev.hwpart\n");
219*4882a593Smuzhiyun #else
220*4882a593Smuzhiyun temp_original_part = mmc_get_blk_desc(mmc)->hwpart;
221*4882a593Smuzhiyun debug("mmc_get_blk_desc(mmc)->hwpart\n");
222*4882a593Smuzhiyun #endif
223*4882a593Smuzhiyun debug("init_rpmb temp_original_part = 0x%X\n", temp_original_part);
224*4882a593Smuzhiyun if (blk_select_hwpart_devnum
225*4882a593Smuzhiyun (IF_TYPE_MMC, curr_device, MMC_PART_RPMB) != 0)
226*4882a593Smuzhiyun return CMD_RET_FAILURE;
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun return CMD_RET_SUCCESS;
229*4882a593Smuzhiyun }
230*4882a593Smuzhiyun
finish_rpmb(void)231*4882a593Smuzhiyun int finish_rpmb(void)
232*4882a593Smuzhiyun {
233*4882a593Smuzhiyun /* Return to original partition */
234*4882a593Smuzhiyun debug("finish_rpmb temp_original_part = 0x%X\n", temp_original_part);
235*4882a593Smuzhiyun if (blk_select_hwpart_devnum
236*4882a593Smuzhiyun (IF_TYPE_MMC, curr_device, temp_original_part) != 0)
237*4882a593Smuzhiyun return CMD_RET_FAILURE;
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun return CMD_RET_SUCCESS;
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun
do_readcounter(struct s_rpmb * requestpackets)242*4882a593Smuzhiyun int do_readcounter(struct s_rpmb *requestpackets)
243*4882a593Smuzhiyun {
244*4882a593Smuzhiyun struct mmc *mmc = find_mmc_device(curr_device);
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun return read_counter(mmc, requestpackets);
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun
do_programkey(struct s_rpmb * requestpackets)249*4882a593Smuzhiyun int do_programkey(struct s_rpmb *requestpackets)
250*4882a593Smuzhiyun {
251*4882a593Smuzhiyun struct mmc *mmc = find_mmc_device(curr_device);
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun return program_key(mmc, requestpackets);
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun
do_authenticatedread(struct s_rpmb * requestpackets,uint16_t block_count)256*4882a593Smuzhiyun int do_authenticatedread(struct s_rpmb *requestpackets, uint16_t block_count)
257*4882a593Smuzhiyun {
258*4882a593Smuzhiyun struct mmc *mmc = find_mmc_device(curr_device);
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun return authenticated_read(mmc, requestpackets, block_count);
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun
do_authenticatedwrite(struct s_rpmb * requestpackets)263*4882a593Smuzhiyun int do_authenticatedwrite(struct s_rpmb *requestpackets)
264*4882a593Smuzhiyun {
265*4882a593Smuzhiyun struct mmc *mmc = find_mmc_device(curr_device);
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun return authenticated_write(mmc, requestpackets);
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun
do_returnmmc(void)270*4882a593Smuzhiyun struct mmc *do_returnmmc(void)
271*4882a593Smuzhiyun {
272*4882a593Smuzhiyun struct mmc *mmc = find_mmc_device(curr_device);
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun return mmc;
275*4882a593Smuzhiyun }
276*4882a593Smuzhiyun
confirm_key_prog(void)277*4882a593Smuzhiyun static int confirm_key_prog(void)
278*4882a593Smuzhiyun {
279*4882a593Smuzhiyun puts("Warning: Programming authentication key can be done only once !\n"
280*4882a593Smuzhiyun " Use this command only if you are sure of what you are doing,\n"
281*4882a593Smuzhiyun "Really perform the key programming? <y/N> ");
282*4882a593Smuzhiyun if (confirm_yesno())
283*4882a593Smuzhiyun return 1;
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun puts("Authentication key programming aborted\n");
286*4882a593Smuzhiyun return 0;
287*4882a593Smuzhiyun }
do_mmcrpmb_key(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])288*4882a593Smuzhiyun static int do_mmcrpmb_key(cmd_tbl_t *cmdtp, int flag,
289*4882a593Smuzhiyun int argc, char * const argv[])
290*4882a593Smuzhiyun {
291*4882a593Smuzhiyun void *key_addr;
292*4882a593Smuzhiyun struct mmc *mmc = find_mmc_device(curr_device);
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun if (argc != 2)
295*4882a593Smuzhiyun return CMD_RET_USAGE;
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun key_addr = (void *)simple_strtoul(argv[1], NULL, 16);
298*4882a593Smuzhiyun if (!confirm_key_prog())
299*4882a593Smuzhiyun return CMD_RET_FAILURE;
300*4882a593Smuzhiyun if (mmc_rpmb_set_key(mmc, key_addr)) {
301*4882a593Smuzhiyun printf("ERROR - Key already programmed ?\n");
302*4882a593Smuzhiyun return CMD_RET_FAILURE;
303*4882a593Smuzhiyun }
304*4882a593Smuzhiyun return CMD_RET_SUCCESS;
305*4882a593Smuzhiyun }
do_mmcrpmb_read(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])306*4882a593Smuzhiyun static int do_mmcrpmb_read(cmd_tbl_t *cmdtp, int flag,
307*4882a593Smuzhiyun int argc, char * const argv[])
308*4882a593Smuzhiyun {
309*4882a593Smuzhiyun u16 blk, cnt;
310*4882a593Smuzhiyun void *addr;
311*4882a593Smuzhiyun int n;
312*4882a593Smuzhiyun void *key_addr = NULL;
313*4882a593Smuzhiyun struct mmc *mmc = find_mmc_device(curr_device);
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun if (argc < 4)
316*4882a593Smuzhiyun return CMD_RET_USAGE;
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun addr = (void *)simple_strtoul(argv[1], NULL, 16);
319*4882a593Smuzhiyun blk = simple_strtoul(argv[2], NULL, 16);
320*4882a593Smuzhiyun cnt = simple_strtoul(argv[3], NULL, 16);
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun if (argc == 5)
323*4882a593Smuzhiyun key_addr = (void *)simple_strtoul(argv[4], NULL, 16);
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun printf("\nMMC RPMB read: dev # %d, block # %d, count %d ... ",
326*4882a593Smuzhiyun curr_device, blk, cnt);
327*4882a593Smuzhiyun n = mmc_rpmb_read(mmc, addr, blk, cnt, key_addr);
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun printf("%d RPMB blocks read: %s\n", n, (n == cnt) ? "OK" : "ERROR");
330*4882a593Smuzhiyun if (n != cnt)
331*4882a593Smuzhiyun return CMD_RET_FAILURE;
332*4882a593Smuzhiyun return CMD_RET_SUCCESS;
333*4882a593Smuzhiyun }
do_mmcrpmb_write(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])334*4882a593Smuzhiyun static int do_mmcrpmb_write(cmd_tbl_t *cmdtp, int flag,
335*4882a593Smuzhiyun int argc, char * const argv[])
336*4882a593Smuzhiyun {
337*4882a593Smuzhiyun u16 blk, cnt;
338*4882a593Smuzhiyun void *addr;
339*4882a593Smuzhiyun int n;
340*4882a593Smuzhiyun void *key_addr;
341*4882a593Smuzhiyun struct mmc *mmc = find_mmc_device(curr_device);
342*4882a593Smuzhiyun
343*4882a593Smuzhiyun if (argc != 5)
344*4882a593Smuzhiyun return CMD_RET_USAGE;
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun addr = (void *)simple_strtoul(argv[1], NULL, 16);
347*4882a593Smuzhiyun blk = simple_strtoul(argv[2], NULL, 16);
348*4882a593Smuzhiyun cnt = simple_strtoul(argv[3], NULL, 16);
349*4882a593Smuzhiyun key_addr = (void *)simple_strtoul(argv[4], NULL, 16);
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun printf("\nMMC RPMB write: dev # %d, block # %d, count %d ... ",
352*4882a593Smuzhiyun curr_device, blk, cnt);
353*4882a593Smuzhiyun n = mmc_rpmb_write(mmc, addr, blk, cnt, key_addr);
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun printf("%d RPMB blocks written: %s\n", n, (n == cnt) ? "OK" : "ERROR");
356*4882a593Smuzhiyun if (n != cnt)
357*4882a593Smuzhiyun return CMD_RET_FAILURE;
358*4882a593Smuzhiyun return CMD_RET_SUCCESS;
359*4882a593Smuzhiyun }
do_mmcrpmb_counter(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])360*4882a593Smuzhiyun static int do_mmcrpmb_counter(cmd_tbl_t *cmdtp, int flag,
361*4882a593Smuzhiyun int argc, char * const argv[])
362*4882a593Smuzhiyun {
363*4882a593Smuzhiyun unsigned long counter;
364*4882a593Smuzhiyun struct mmc *mmc = find_mmc_device(curr_device);
365*4882a593Smuzhiyun
366*4882a593Smuzhiyun if (mmc_rpmb_get_counter(mmc, &counter))
367*4882a593Smuzhiyun return CMD_RET_FAILURE;
368*4882a593Smuzhiyun printf("RPMB Write counter= %lx\n", counter);
369*4882a593Smuzhiyun return CMD_RET_SUCCESS;
370*4882a593Smuzhiyun }
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun static cmd_tbl_t cmd_rpmb[] = {
373*4882a593Smuzhiyun U_BOOT_CMD_MKENT(key, 2, 0, do_mmcrpmb_key, "", ""),
374*4882a593Smuzhiyun U_BOOT_CMD_MKENT(read, 5, 1, do_mmcrpmb_read, "", ""),
375*4882a593Smuzhiyun U_BOOT_CMD_MKENT(write, 5, 0, do_mmcrpmb_write, "", ""),
376*4882a593Smuzhiyun U_BOOT_CMD_MKENT(counter, 1, 1, do_mmcrpmb_counter, "", ""),
377*4882a593Smuzhiyun };
378*4882a593Smuzhiyun
do_mmcrpmb(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])379*4882a593Smuzhiyun static int do_mmcrpmb(cmd_tbl_t *cmdtp, int flag,
380*4882a593Smuzhiyun int argc, char * const argv[])
381*4882a593Smuzhiyun {
382*4882a593Smuzhiyun cmd_tbl_t *cp;
383*4882a593Smuzhiyun struct mmc *mmc;
384*4882a593Smuzhiyun char original_part;
385*4882a593Smuzhiyun int ret;
386*4882a593Smuzhiyun
387*4882a593Smuzhiyun cp = find_cmd_tbl(argv[1], cmd_rpmb, ARRAY_SIZE(cmd_rpmb));
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun /* Drop the rpmb subcommand */
390*4882a593Smuzhiyun argc--;
391*4882a593Smuzhiyun argv++;
392*4882a593Smuzhiyun
393*4882a593Smuzhiyun if (cp == NULL || argc > cp->maxargs)
394*4882a593Smuzhiyun return CMD_RET_USAGE;
395*4882a593Smuzhiyun if (flag == CMD_FLAG_REPEAT && !cp->repeatable)
396*4882a593Smuzhiyun return CMD_RET_SUCCESS;
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun mmc = init_mmc_device(curr_device, false);
399*4882a593Smuzhiyun if (!mmc)
400*4882a593Smuzhiyun return CMD_RET_FAILURE;
401*4882a593Smuzhiyun
402*4882a593Smuzhiyun if (!(mmc->version & MMC_VERSION_MMC)) {
403*4882a593Smuzhiyun printf("It is not a EMMC device\n");
404*4882a593Smuzhiyun return CMD_RET_FAILURE;
405*4882a593Smuzhiyun }
406*4882a593Smuzhiyun if (mmc->version < MMC_VERSION_4_41) {
407*4882a593Smuzhiyun printf("RPMB not supported before version 4.41\n");
408*4882a593Smuzhiyun return CMD_RET_FAILURE;
409*4882a593Smuzhiyun }
410*4882a593Smuzhiyun /* Switch to the RPMB partition */
411*4882a593Smuzhiyun #ifndef CONFIG_BLK
412*4882a593Smuzhiyun original_part = mmc->block_dev.hwpart;
413*4882a593Smuzhiyun #else
414*4882a593Smuzhiyun original_part = mmc_get_blk_desc(mmc)->hwpart;
415*4882a593Smuzhiyun #endif
416*4882a593Smuzhiyun if (blk_select_hwpart_devnum(IF_TYPE_MMC, curr_device, MMC_PART_RPMB) !=
417*4882a593Smuzhiyun 0)
418*4882a593Smuzhiyun return CMD_RET_FAILURE;
419*4882a593Smuzhiyun ret = cp->cmd(cmdtp, flag, argc, argv);
420*4882a593Smuzhiyun
421*4882a593Smuzhiyun /* Return to original partition */
422*4882a593Smuzhiyun if (blk_select_hwpart_devnum(IF_TYPE_MMC, curr_device, original_part) !=
423*4882a593Smuzhiyun 0)
424*4882a593Smuzhiyun return CMD_RET_FAILURE;
425*4882a593Smuzhiyun return ret;
426*4882a593Smuzhiyun }
427*4882a593Smuzhiyun #endif
428*4882a593Smuzhiyun
do_mmc_read(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])429*4882a593Smuzhiyun static int do_mmc_read(cmd_tbl_t *cmdtp, int flag,
430*4882a593Smuzhiyun int argc, char * const argv[])
431*4882a593Smuzhiyun {
432*4882a593Smuzhiyun struct mmc *mmc;
433*4882a593Smuzhiyun u32 blk, cnt, n;
434*4882a593Smuzhiyun void *addr;
435*4882a593Smuzhiyun
436*4882a593Smuzhiyun if (argc != 4)
437*4882a593Smuzhiyun return CMD_RET_USAGE;
438*4882a593Smuzhiyun
439*4882a593Smuzhiyun addr = (void *)simple_strtoul(argv[1], NULL, 16);
440*4882a593Smuzhiyun blk = simple_strtoul(argv[2], NULL, 16);
441*4882a593Smuzhiyun cnt = simple_strtoul(argv[3], NULL, 16);
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun mmc = init_mmc_device(curr_device, false);
444*4882a593Smuzhiyun if (!mmc)
445*4882a593Smuzhiyun return CMD_RET_FAILURE;
446*4882a593Smuzhiyun
447*4882a593Smuzhiyun printf("\nMMC read: dev # %d, block # %d, count %d ... ",
448*4882a593Smuzhiyun curr_device, blk, cnt);
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun n = blk_dread(mmc_get_blk_desc(mmc), blk, cnt, addr);
451*4882a593Smuzhiyun printf("%d blocks read: %s\n", n, (n == cnt) ? "OK" : "ERROR");
452*4882a593Smuzhiyun
453*4882a593Smuzhiyun return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
454*4882a593Smuzhiyun }
do_mmc_write(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])455*4882a593Smuzhiyun static int do_mmc_write(cmd_tbl_t *cmdtp, int flag,
456*4882a593Smuzhiyun int argc, char * const argv[])
457*4882a593Smuzhiyun {
458*4882a593Smuzhiyun struct mmc *mmc;
459*4882a593Smuzhiyun u32 blk, cnt, n;
460*4882a593Smuzhiyun void *addr;
461*4882a593Smuzhiyun
462*4882a593Smuzhiyun if (argc != 4)
463*4882a593Smuzhiyun return CMD_RET_USAGE;
464*4882a593Smuzhiyun
465*4882a593Smuzhiyun addr = (void *)simple_strtoul(argv[1], NULL, 16);
466*4882a593Smuzhiyun blk = simple_strtoul(argv[2], NULL, 16);
467*4882a593Smuzhiyun cnt = simple_strtoul(argv[3], NULL, 16);
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun mmc = init_mmc_device(curr_device, false);
470*4882a593Smuzhiyun if (!mmc)
471*4882a593Smuzhiyun return CMD_RET_FAILURE;
472*4882a593Smuzhiyun
473*4882a593Smuzhiyun printf("\nMMC write: dev # %d, block # %d, count %d ... ",
474*4882a593Smuzhiyun curr_device, blk, cnt);
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun if (mmc_getwp(mmc) == 1) {
477*4882a593Smuzhiyun printf("Error: card is write protected!\n");
478*4882a593Smuzhiyun return CMD_RET_FAILURE;
479*4882a593Smuzhiyun }
480*4882a593Smuzhiyun n = blk_dwrite(mmc_get_blk_desc(mmc), blk, cnt, addr);
481*4882a593Smuzhiyun printf("%d blocks written: %s\n", n, (n == cnt) ? "OK" : "ERROR");
482*4882a593Smuzhiyun
483*4882a593Smuzhiyun return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
484*4882a593Smuzhiyun }
do_mmc_erase(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])485*4882a593Smuzhiyun static int do_mmc_erase(cmd_tbl_t *cmdtp, int flag,
486*4882a593Smuzhiyun int argc, char * const argv[])
487*4882a593Smuzhiyun {
488*4882a593Smuzhiyun struct mmc *mmc;
489*4882a593Smuzhiyun u32 blk, cnt, n;
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun if (argc != 3)
492*4882a593Smuzhiyun return CMD_RET_USAGE;
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun blk = simple_strtoul(argv[1], NULL, 16);
495*4882a593Smuzhiyun cnt = simple_strtoul(argv[2], NULL, 16);
496*4882a593Smuzhiyun
497*4882a593Smuzhiyun mmc = init_mmc_device(curr_device, false);
498*4882a593Smuzhiyun if (!mmc)
499*4882a593Smuzhiyun return CMD_RET_FAILURE;
500*4882a593Smuzhiyun
501*4882a593Smuzhiyun printf("\nMMC erase: dev # %d, block # %d, count %d ... ",
502*4882a593Smuzhiyun curr_device, blk, cnt);
503*4882a593Smuzhiyun
504*4882a593Smuzhiyun if (mmc_getwp(mmc) == 1) {
505*4882a593Smuzhiyun printf("Error: card is write protected!\n");
506*4882a593Smuzhiyun return CMD_RET_FAILURE;
507*4882a593Smuzhiyun }
508*4882a593Smuzhiyun n = blk_derase(mmc_get_blk_desc(mmc), blk, cnt);
509*4882a593Smuzhiyun printf("%d blocks erased: %s\n", n, (n == cnt) ? "OK" : "ERROR");
510*4882a593Smuzhiyun
511*4882a593Smuzhiyun return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
512*4882a593Smuzhiyun }
do_mmc_rescan(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])513*4882a593Smuzhiyun static int do_mmc_rescan(cmd_tbl_t *cmdtp, int flag,
514*4882a593Smuzhiyun int argc, char * const argv[])
515*4882a593Smuzhiyun {
516*4882a593Smuzhiyun struct mmc *mmc;
517*4882a593Smuzhiyun
518*4882a593Smuzhiyun mmc = init_mmc_device(curr_device, true);
519*4882a593Smuzhiyun if (!mmc)
520*4882a593Smuzhiyun return CMD_RET_FAILURE;
521*4882a593Smuzhiyun
522*4882a593Smuzhiyun return CMD_RET_SUCCESS;
523*4882a593Smuzhiyun }
do_mmc_part(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])524*4882a593Smuzhiyun static int do_mmc_part(cmd_tbl_t *cmdtp, int flag,
525*4882a593Smuzhiyun int argc, char * const argv[])
526*4882a593Smuzhiyun {
527*4882a593Smuzhiyun struct blk_desc *mmc_dev;
528*4882a593Smuzhiyun struct mmc *mmc;
529*4882a593Smuzhiyun
530*4882a593Smuzhiyun mmc = init_mmc_device(curr_device, false);
531*4882a593Smuzhiyun if (!mmc)
532*4882a593Smuzhiyun return CMD_RET_FAILURE;
533*4882a593Smuzhiyun
534*4882a593Smuzhiyun mmc_dev = blk_get_devnum_by_type(IF_TYPE_MMC, curr_device);
535*4882a593Smuzhiyun if (mmc_dev != NULL && mmc_dev->type != DEV_TYPE_UNKNOWN) {
536*4882a593Smuzhiyun part_print(mmc_dev);
537*4882a593Smuzhiyun return CMD_RET_SUCCESS;
538*4882a593Smuzhiyun }
539*4882a593Smuzhiyun
540*4882a593Smuzhiyun puts("get mmc type error!\n");
541*4882a593Smuzhiyun return CMD_RET_FAILURE;
542*4882a593Smuzhiyun }
do_mmc_dev(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])543*4882a593Smuzhiyun static int do_mmc_dev(cmd_tbl_t *cmdtp, int flag,
544*4882a593Smuzhiyun int argc, char * const argv[])
545*4882a593Smuzhiyun {
546*4882a593Smuzhiyun int dev, part = 0, ret;
547*4882a593Smuzhiyun struct mmc *mmc;
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun if (argc == 1) {
550*4882a593Smuzhiyun dev = curr_device;
551*4882a593Smuzhiyun } else if (argc == 2) {
552*4882a593Smuzhiyun dev = simple_strtoul(argv[1], NULL, 10);
553*4882a593Smuzhiyun } else if (argc == 3) {
554*4882a593Smuzhiyun dev = (int)simple_strtoul(argv[1], NULL, 10);
555*4882a593Smuzhiyun part = (int)simple_strtoul(argv[2], NULL, 10);
556*4882a593Smuzhiyun if (part > PART_ACCESS_MASK) {
557*4882a593Smuzhiyun printf("#part_num shouldn't be larger than %d\n",
558*4882a593Smuzhiyun PART_ACCESS_MASK);
559*4882a593Smuzhiyun return CMD_RET_FAILURE;
560*4882a593Smuzhiyun }
561*4882a593Smuzhiyun } else {
562*4882a593Smuzhiyun return CMD_RET_USAGE;
563*4882a593Smuzhiyun }
564*4882a593Smuzhiyun
565*4882a593Smuzhiyun mmc = init_mmc_device(dev, false);
566*4882a593Smuzhiyun if (!mmc)
567*4882a593Smuzhiyun return CMD_RET_FAILURE;
568*4882a593Smuzhiyun
569*4882a593Smuzhiyun ret = blk_select_hwpart_devnum(IF_TYPE_MMC, dev, part);
570*4882a593Smuzhiyun printf("switch to partitions #%d, %s\n",
571*4882a593Smuzhiyun part, (!ret) ? "OK" : "ERROR");
572*4882a593Smuzhiyun if (ret)
573*4882a593Smuzhiyun return 1;
574*4882a593Smuzhiyun
575*4882a593Smuzhiyun curr_device = dev;
576*4882a593Smuzhiyun if (mmc->part_config == MMCPART_NOAVAILABLE)
577*4882a593Smuzhiyun printf("mmc%d is current device\n", curr_device);
578*4882a593Smuzhiyun else
579*4882a593Smuzhiyun printf("mmc%d(part %d) is current device\n",
580*4882a593Smuzhiyun curr_device, mmc_get_blk_desc(mmc)->hwpart);
581*4882a593Smuzhiyun
582*4882a593Smuzhiyun return CMD_RET_SUCCESS;
583*4882a593Smuzhiyun }
do_mmc_list(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])584*4882a593Smuzhiyun static int do_mmc_list(cmd_tbl_t *cmdtp, int flag,
585*4882a593Smuzhiyun int argc, char * const argv[])
586*4882a593Smuzhiyun {
587*4882a593Smuzhiyun print_mmc_devices('\n');
588*4882a593Smuzhiyun return CMD_RET_SUCCESS;
589*4882a593Smuzhiyun }
590*4882a593Smuzhiyun
parse_hwpart_user(struct mmc_hwpart_conf * pconf,int argc,char * const argv[])591*4882a593Smuzhiyun static int parse_hwpart_user(struct mmc_hwpart_conf *pconf,
592*4882a593Smuzhiyun int argc, char * const argv[])
593*4882a593Smuzhiyun {
594*4882a593Smuzhiyun int i = 0;
595*4882a593Smuzhiyun
596*4882a593Smuzhiyun memset(&pconf->user, 0, sizeof(pconf->user));
597*4882a593Smuzhiyun
598*4882a593Smuzhiyun while (i < argc) {
599*4882a593Smuzhiyun if (!strcmp(argv[i], "enh")) {
600*4882a593Smuzhiyun if (i + 2 >= argc)
601*4882a593Smuzhiyun return -1;
602*4882a593Smuzhiyun pconf->user.enh_start =
603*4882a593Smuzhiyun simple_strtoul(argv[i+1], NULL, 10);
604*4882a593Smuzhiyun pconf->user.enh_size =
605*4882a593Smuzhiyun simple_strtoul(argv[i+2], NULL, 10);
606*4882a593Smuzhiyun i += 3;
607*4882a593Smuzhiyun } else if (!strcmp(argv[i], "wrrel")) {
608*4882a593Smuzhiyun if (i + 1 >= argc)
609*4882a593Smuzhiyun return -1;
610*4882a593Smuzhiyun pconf->user.wr_rel_change = 1;
611*4882a593Smuzhiyun if (!strcmp(argv[i+1], "on"))
612*4882a593Smuzhiyun pconf->user.wr_rel_set = 1;
613*4882a593Smuzhiyun else if (!strcmp(argv[i+1], "off"))
614*4882a593Smuzhiyun pconf->user.wr_rel_set = 0;
615*4882a593Smuzhiyun else
616*4882a593Smuzhiyun return -1;
617*4882a593Smuzhiyun i += 2;
618*4882a593Smuzhiyun } else {
619*4882a593Smuzhiyun break;
620*4882a593Smuzhiyun }
621*4882a593Smuzhiyun }
622*4882a593Smuzhiyun return i;
623*4882a593Smuzhiyun }
624*4882a593Smuzhiyun
parse_hwpart_gp(struct mmc_hwpart_conf * pconf,int pidx,int argc,char * const argv[])625*4882a593Smuzhiyun static int parse_hwpart_gp(struct mmc_hwpart_conf *pconf, int pidx,
626*4882a593Smuzhiyun int argc, char * const argv[])
627*4882a593Smuzhiyun {
628*4882a593Smuzhiyun int i;
629*4882a593Smuzhiyun
630*4882a593Smuzhiyun memset(&pconf->gp_part[pidx], 0, sizeof(pconf->gp_part[pidx]));
631*4882a593Smuzhiyun
632*4882a593Smuzhiyun if (1 >= argc)
633*4882a593Smuzhiyun return -1;
634*4882a593Smuzhiyun pconf->gp_part[pidx].size = simple_strtoul(argv[0], NULL, 10);
635*4882a593Smuzhiyun
636*4882a593Smuzhiyun i = 1;
637*4882a593Smuzhiyun while (i < argc) {
638*4882a593Smuzhiyun if (!strcmp(argv[i], "enh")) {
639*4882a593Smuzhiyun pconf->gp_part[pidx].enhanced = 1;
640*4882a593Smuzhiyun i += 1;
641*4882a593Smuzhiyun } else if (!strcmp(argv[i], "wrrel")) {
642*4882a593Smuzhiyun if (i + 1 >= argc)
643*4882a593Smuzhiyun return -1;
644*4882a593Smuzhiyun pconf->gp_part[pidx].wr_rel_change = 1;
645*4882a593Smuzhiyun if (!strcmp(argv[i+1], "on"))
646*4882a593Smuzhiyun pconf->gp_part[pidx].wr_rel_set = 1;
647*4882a593Smuzhiyun else if (!strcmp(argv[i+1], "off"))
648*4882a593Smuzhiyun pconf->gp_part[pidx].wr_rel_set = 0;
649*4882a593Smuzhiyun else
650*4882a593Smuzhiyun return -1;
651*4882a593Smuzhiyun i += 2;
652*4882a593Smuzhiyun } else {
653*4882a593Smuzhiyun break;
654*4882a593Smuzhiyun }
655*4882a593Smuzhiyun }
656*4882a593Smuzhiyun return i;
657*4882a593Smuzhiyun }
658*4882a593Smuzhiyun
do_mmc_hwpartition(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])659*4882a593Smuzhiyun static int do_mmc_hwpartition(cmd_tbl_t *cmdtp, int flag,
660*4882a593Smuzhiyun int argc, char * const argv[])
661*4882a593Smuzhiyun {
662*4882a593Smuzhiyun struct mmc *mmc;
663*4882a593Smuzhiyun struct mmc_hwpart_conf pconf = { };
664*4882a593Smuzhiyun enum mmc_hwpart_conf_mode mode = MMC_HWPART_CONF_CHECK;
665*4882a593Smuzhiyun int i, r, pidx;
666*4882a593Smuzhiyun
667*4882a593Smuzhiyun mmc = init_mmc_device(curr_device, false);
668*4882a593Smuzhiyun if (!mmc)
669*4882a593Smuzhiyun return CMD_RET_FAILURE;
670*4882a593Smuzhiyun
671*4882a593Smuzhiyun if (argc < 1)
672*4882a593Smuzhiyun return CMD_RET_USAGE;
673*4882a593Smuzhiyun i = 1;
674*4882a593Smuzhiyun while (i < argc) {
675*4882a593Smuzhiyun if (!strcmp(argv[i], "user")) {
676*4882a593Smuzhiyun i++;
677*4882a593Smuzhiyun r = parse_hwpart_user(&pconf, argc-i, &argv[i]);
678*4882a593Smuzhiyun if (r < 0)
679*4882a593Smuzhiyun return CMD_RET_USAGE;
680*4882a593Smuzhiyun i += r;
681*4882a593Smuzhiyun } else if (!strncmp(argv[i], "gp", 2) &&
682*4882a593Smuzhiyun strlen(argv[i]) == 3 &&
683*4882a593Smuzhiyun argv[i][2] >= '1' && argv[i][2] <= '4') {
684*4882a593Smuzhiyun pidx = argv[i][2] - '1';
685*4882a593Smuzhiyun i++;
686*4882a593Smuzhiyun r = parse_hwpart_gp(&pconf, pidx, argc-i, &argv[i]);
687*4882a593Smuzhiyun if (r < 0)
688*4882a593Smuzhiyun return CMD_RET_USAGE;
689*4882a593Smuzhiyun i += r;
690*4882a593Smuzhiyun } else if (!strcmp(argv[i], "check")) {
691*4882a593Smuzhiyun mode = MMC_HWPART_CONF_CHECK;
692*4882a593Smuzhiyun i++;
693*4882a593Smuzhiyun } else if (!strcmp(argv[i], "set")) {
694*4882a593Smuzhiyun mode = MMC_HWPART_CONF_SET;
695*4882a593Smuzhiyun i++;
696*4882a593Smuzhiyun } else if (!strcmp(argv[i], "complete")) {
697*4882a593Smuzhiyun mode = MMC_HWPART_CONF_COMPLETE;
698*4882a593Smuzhiyun i++;
699*4882a593Smuzhiyun } else {
700*4882a593Smuzhiyun return CMD_RET_USAGE;
701*4882a593Smuzhiyun }
702*4882a593Smuzhiyun }
703*4882a593Smuzhiyun
704*4882a593Smuzhiyun puts("Partition configuration:\n");
705*4882a593Smuzhiyun if (pconf.user.enh_size) {
706*4882a593Smuzhiyun puts("\tUser Enhanced Start: ");
707*4882a593Smuzhiyun print_size(((u64)pconf.user.enh_start) << 9, "\n");
708*4882a593Smuzhiyun puts("\tUser Enhanced Size: ");
709*4882a593Smuzhiyun print_size(((u64)pconf.user.enh_size) << 9, "\n");
710*4882a593Smuzhiyun } else {
711*4882a593Smuzhiyun puts("\tNo enhanced user data area\n");
712*4882a593Smuzhiyun }
713*4882a593Smuzhiyun if (pconf.user.wr_rel_change)
714*4882a593Smuzhiyun printf("\tUser partition write reliability: %s\n",
715*4882a593Smuzhiyun pconf.user.wr_rel_set ? "on" : "off");
716*4882a593Smuzhiyun for (pidx = 0; pidx < 4; pidx++) {
717*4882a593Smuzhiyun if (pconf.gp_part[pidx].size) {
718*4882a593Smuzhiyun printf("\tGP%i Capacity: ", pidx+1);
719*4882a593Smuzhiyun print_size(((u64)pconf.gp_part[pidx].size) << 9,
720*4882a593Smuzhiyun pconf.gp_part[pidx].enhanced ?
721*4882a593Smuzhiyun " ENH\n" : "\n");
722*4882a593Smuzhiyun } else {
723*4882a593Smuzhiyun printf("\tNo GP%i partition\n", pidx+1);
724*4882a593Smuzhiyun }
725*4882a593Smuzhiyun if (pconf.gp_part[pidx].wr_rel_change)
726*4882a593Smuzhiyun printf("\tGP%i write reliability: %s\n", pidx+1,
727*4882a593Smuzhiyun pconf.gp_part[pidx].wr_rel_set ? "on" : "off");
728*4882a593Smuzhiyun }
729*4882a593Smuzhiyun
730*4882a593Smuzhiyun if (!mmc_hwpart_config(mmc, &pconf, mode)) {
731*4882a593Smuzhiyun if (mode == MMC_HWPART_CONF_COMPLETE)
732*4882a593Smuzhiyun puts("Partitioning successful, "
733*4882a593Smuzhiyun "power-cycle to make effective\n");
734*4882a593Smuzhiyun return CMD_RET_SUCCESS;
735*4882a593Smuzhiyun } else {
736*4882a593Smuzhiyun puts("Failed!\n");
737*4882a593Smuzhiyun return CMD_RET_FAILURE;
738*4882a593Smuzhiyun }
739*4882a593Smuzhiyun }
740*4882a593Smuzhiyun
741*4882a593Smuzhiyun #ifdef CONFIG_SUPPORT_EMMC_BOOT
do_mmc_bootbus(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])742*4882a593Smuzhiyun static int do_mmc_bootbus(cmd_tbl_t *cmdtp, int flag,
743*4882a593Smuzhiyun int argc, char * const argv[])
744*4882a593Smuzhiyun {
745*4882a593Smuzhiyun int dev;
746*4882a593Smuzhiyun struct mmc *mmc;
747*4882a593Smuzhiyun u8 width, reset, mode;
748*4882a593Smuzhiyun
749*4882a593Smuzhiyun if (argc != 5)
750*4882a593Smuzhiyun return CMD_RET_USAGE;
751*4882a593Smuzhiyun dev = simple_strtoul(argv[1], NULL, 10);
752*4882a593Smuzhiyun width = simple_strtoul(argv[2], NULL, 10);
753*4882a593Smuzhiyun reset = simple_strtoul(argv[3], NULL, 10);
754*4882a593Smuzhiyun mode = simple_strtoul(argv[4], NULL, 10);
755*4882a593Smuzhiyun
756*4882a593Smuzhiyun mmc = init_mmc_device(dev, false);
757*4882a593Smuzhiyun if (!mmc)
758*4882a593Smuzhiyun return CMD_RET_FAILURE;
759*4882a593Smuzhiyun
760*4882a593Smuzhiyun if (IS_SD(mmc)) {
761*4882a593Smuzhiyun puts("BOOT_BUS_WIDTH only exists on eMMC\n");
762*4882a593Smuzhiyun return CMD_RET_FAILURE;
763*4882a593Smuzhiyun }
764*4882a593Smuzhiyun
765*4882a593Smuzhiyun /* acknowledge to be sent during boot operation */
766*4882a593Smuzhiyun return mmc_set_boot_bus_width(mmc, width, reset, mode);
767*4882a593Smuzhiyun }
do_mmc_boot_resize(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])768*4882a593Smuzhiyun static int do_mmc_boot_resize(cmd_tbl_t *cmdtp, int flag,
769*4882a593Smuzhiyun int argc, char * const argv[])
770*4882a593Smuzhiyun {
771*4882a593Smuzhiyun int dev;
772*4882a593Smuzhiyun struct mmc *mmc;
773*4882a593Smuzhiyun u32 bootsize, rpmbsize;
774*4882a593Smuzhiyun
775*4882a593Smuzhiyun if (argc != 4)
776*4882a593Smuzhiyun return CMD_RET_USAGE;
777*4882a593Smuzhiyun dev = simple_strtoul(argv[1], NULL, 10);
778*4882a593Smuzhiyun bootsize = simple_strtoul(argv[2], NULL, 10);
779*4882a593Smuzhiyun rpmbsize = simple_strtoul(argv[3], NULL, 10);
780*4882a593Smuzhiyun
781*4882a593Smuzhiyun mmc = init_mmc_device(dev, false);
782*4882a593Smuzhiyun if (!mmc)
783*4882a593Smuzhiyun return CMD_RET_FAILURE;
784*4882a593Smuzhiyun
785*4882a593Smuzhiyun if (IS_SD(mmc)) {
786*4882a593Smuzhiyun printf("It is not a EMMC device\n");
787*4882a593Smuzhiyun return CMD_RET_FAILURE;
788*4882a593Smuzhiyun }
789*4882a593Smuzhiyun
790*4882a593Smuzhiyun if (mmc_boot_partition_size_change(mmc, bootsize, rpmbsize)) {
791*4882a593Smuzhiyun printf("EMMC boot partition Size change Failed.\n");
792*4882a593Smuzhiyun return CMD_RET_FAILURE;
793*4882a593Smuzhiyun }
794*4882a593Smuzhiyun
795*4882a593Smuzhiyun printf("EMMC boot partition Size %d MB\n", bootsize);
796*4882a593Smuzhiyun printf("EMMC RPMB partition Size %d MB\n", rpmbsize);
797*4882a593Smuzhiyun return CMD_RET_SUCCESS;
798*4882a593Smuzhiyun }
799*4882a593Smuzhiyun
mmc_partconf_print(struct mmc * mmc)800*4882a593Smuzhiyun static int mmc_partconf_print(struct mmc *mmc)
801*4882a593Smuzhiyun {
802*4882a593Smuzhiyun u8 ack, access, part;
803*4882a593Smuzhiyun
804*4882a593Smuzhiyun if (mmc->part_config == MMCPART_NOAVAILABLE) {
805*4882a593Smuzhiyun printf("No part_config info for ver. 0x%x\n", mmc->version);
806*4882a593Smuzhiyun return CMD_RET_FAILURE;
807*4882a593Smuzhiyun }
808*4882a593Smuzhiyun
809*4882a593Smuzhiyun access = EXT_CSD_EXTRACT_PARTITION_ACCESS(mmc->part_config);
810*4882a593Smuzhiyun ack = EXT_CSD_EXTRACT_BOOT_ACK(mmc->part_config);
811*4882a593Smuzhiyun part = EXT_CSD_EXTRACT_BOOT_PART(mmc->part_config);
812*4882a593Smuzhiyun
813*4882a593Smuzhiyun printf("EXT_CSD[179], PARTITION_CONFIG:\n"
814*4882a593Smuzhiyun "BOOT_ACK: 0x%x\n"
815*4882a593Smuzhiyun "BOOT_PARTITION_ENABLE: 0x%x\n"
816*4882a593Smuzhiyun "PARTITION_ACCESS: 0x%x\n", ack, part, access);
817*4882a593Smuzhiyun
818*4882a593Smuzhiyun return CMD_RET_SUCCESS;
819*4882a593Smuzhiyun }
820*4882a593Smuzhiyun
do_mmc_partconf(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])821*4882a593Smuzhiyun static int do_mmc_partconf(cmd_tbl_t *cmdtp, int flag,
822*4882a593Smuzhiyun int argc, char * const argv[])
823*4882a593Smuzhiyun {
824*4882a593Smuzhiyun int dev;
825*4882a593Smuzhiyun struct mmc *mmc;
826*4882a593Smuzhiyun u8 ack, part_num, access;
827*4882a593Smuzhiyun
828*4882a593Smuzhiyun if (argc != 2 && argc != 5)
829*4882a593Smuzhiyun return CMD_RET_USAGE;
830*4882a593Smuzhiyun
831*4882a593Smuzhiyun dev = simple_strtoul(argv[1], NULL, 10);
832*4882a593Smuzhiyun
833*4882a593Smuzhiyun mmc = init_mmc_device(dev, false);
834*4882a593Smuzhiyun if (!mmc)
835*4882a593Smuzhiyun return CMD_RET_FAILURE;
836*4882a593Smuzhiyun
837*4882a593Smuzhiyun if (IS_SD(mmc)) {
838*4882a593Smuzhiyun puts("PARTITION_CONFIG only exists on eMMC\n");
839*4882a593Smuzhiyun return CMD_RET_FAILURE;
840*4882a593Smuzhiyun }
841*4882a593Smuzhiyun
842*4882a593Smuzhiyun if (argc == 2)
843*4882a593Smuzhiyun return mmc_partconf_print(mmc);
844*4882a593Smuzhiyun
845*4882a593Smuzhiyun ack = simple_strtoul(argv[2], NULL, 10);
846*4882a593Smuzhiyun part_num = simple_strtoul(argv[3], NULL, 10);
847*4882a593Smuzhiyun access = simple_strtoul(argv[4], NULL, 10);
848*4882a593Smuzhiyun
849*4882a593Smuzhiyun /* acknowledge to be sent during boot operation */
850*4882a593Smuzhiyun return mmc_set_part_conf(mmc, ack, part_num, access);
851*4882a593Smuzhiyun }
do_mmc_rst_func(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])852*4882a593Smuzhiyun static int do_mmc_rst_func(cmd_tbl_t *cmdtp, int flag,
853*4882a593Smuzhiyun int argc, char * const argv[])
854*4882a593Smuzhiyun {
855*4882a593Smuzhiyun int dev;
856*4882a593Smuzhiyun struct mmc *mmc;
857*4882a593Smuzhiyun u8 enable;
858*4882a593Smuzhiyun
859*4882a593Smuzhiyun /*
860*4882a593Smuzhiyun * Set the RST_n_ENABLE bit of RST_n_FUNCTION
861*4882a593Smuzhiyun * The only valid values are 0x0, 0x1 and 0x2 and writing
862*4882a593Smuzhiyun * a value of 0x1 or 0x2 sets the value permanently.
863*4882a593Smuzhiyun */
864*4882a593Smuzhiyun if (argc != 3)
865*4882a593Smuzhiyun return CMD_RET_USAGE;
866*4882a593Smuzhiyun
867*4882a593Smuzhiyun dev = simple_strtoul(argv[1], NULL, 10);
868*4882a593Smuzhiyun enable = simple_strtoul(argv[2], NULL, 10);
869*4882a593Smuzhiyun
870*4882a593Smuzhiyun if (enable > 2) {
871*4882a593Smuzhiyun puts("Invalid RST_n_ENABLE value\n");
872*4882a593Smuzhiyun return CMD_RET_USAGE;
873*4882a593Smuzhiyun }
874*4882a593Smuzhiyun
875*4882a593Smuzhiyun mmc = init_mmc_device(dev, false);
876*4882a593Smuzhiyun if (!mmc)
877*4882a593Smuzhiyun return CMD_RET_FAILURE;
878*4882a593Smuzhiyun
879*4882a593Smuzhiyun if (IS_SD(mmc)) {
880*4882a593Smuzhiyun puts("RST_n_FUNCTION only exists on eMMC\n");
881*4882a593Smuzhiyun return CMD_RET_FAILURE;
882*4882a593Smuzhiyun }
883*4882a593Smuzhiyun
884*4882a593Smuzhiyun return mmc_set_rst_n_function(mmc, enable);
885*4882a593Smuzhiyun }
886*4882a593Smuzhiyun #endif
do_mmc_setdsr(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])887*4882a593Smuzhiyun static int do_mmc_setdsr(cmd_tbl_t *cmdtp, int flag,
888*4882a593Smuzhiyun int argc, char * const argv[])
889*4882a593Smuzhiyun {
890*4882a593Smuzhiyun struct mmc *mmc;
891*4882a593Smuzhiyun u32 val;
892*4882a593Smuzhiyun int ret;
893*4882a593Smuzhiyun
894*4882a593Smuzhiyun if (argc != 2)
895*4882a593Smuzhiyun return CMD_RET_USAGE;
896*4882a593Smuzhiyun val = simple_strtoul(argv[1], NULL, 16);
897*4882a593Smuzhiyun
898*4882a593Smuzhiyun mmc = find_mmc_device(curr_device);
899*4882a593Smuzhiyun if (!mmc) {
900*4882a593Smuzhiyun printf("no mmc device at slot %x\n", curr_device);
901*4882a593Smuzhiyun return CMD_RET_FAILURE;
902*4882a593Smuzhiyun }
903*4882a593Smuzhiyun ret = mmc_set_dsr(mmc, val);
904*4882a593Smuzhiyun printf("set dsr %s\n", (!ret) ? "OK, force rescan" : "ERROR");
905*4882a593Smuzhiyun if (!ret) {
906*4882a593Smuzhiyun mmc->has_init = 0;
907*4882a593Smuzhiyun if (mmc_init(mmc))
908*4882a593Smuzhiyun return CMD_RET_FAILURE;
909*4882a593Smuzhiyun else
910*4882a593Smuzhiyun return CMD_RET_SUCCESS;
911*4882a593Smuzhiyun }
912*4882a593Smuzhiyun return ret;
913*4882a593Smuzhiyun }
914*4882a593Smuzhiyun
915*4882a593Smuzhiyun #ifdef CONFIG_CMD_BKOPS_ENABLE
do_mmc_bkops_enable(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])916*4882a593Smuzhiyun static int do_mmc_bkops_enable(cmd_tbl_t *cmdtp, int flag,
917*4882a593Smuzhiyun int argc, char * const argv[])
918*4882a593Smuzhiyun {
919*4882a593Smuzhiyun int dev;
920*4882a593Smuzhiyun struct mmc *mmc;
921*4882a593Smuzhiyun
922*4882a593Smuzhiyun if (argc != 2)
923*4882a593Smuzhiyun return CMD_RET_USAGE;
924*4882a593Smuzhiyun
925*4882a593Smuzhiyun dev = simple_strtoul(argv[1], NULL, 10);
926*4882a593Smuzhiyun
927*4882a593Smuzhiyun mmc = init_mmc_device(dev, false);
928*4882a593Smuzhiyun if (!mmc)
929*4882a593Smuzhiyun return CMD_RET_FAILURE;
930*4882a593Smuzhiyun
931*4882a593Smuzhiyun if (IS_SD(mmc)) {
932*4882a593Smuzhiyun puts("BKOPS_EN only exists on eMMC\n");
933*4882a593Smuzhiyun return CMD_RET_FAILURE;
934*4882a593Smuzhiyun }
935*4882a593Smuzhiyun
936*4882a593Smuzhiyun return mmc_set_bkops_enable(mmc);
937*4882a593Smuzhiyun }
938*4882a593Smuzhiyun #endif
939*4882a593Smuzhiyun
940*4882a593Smuzhiyun static cmd_tbl_t cmd_mmc[] = {
941*4882a593Smuzhiyun U_BOOT_CMD_MKENT(info, 1, 0, do_mmcinfo, "", ""),
942*4882a593Smuzhiyun U_BOOT_CMD_MKENT(read, 4, 1, do_mmc_read, "", ""),
943*4882a593Smuzhiyun U_BOOT_CMD_MKENT(write, 4, 0, do_mmc_write, "", ""),
944*4882a593Smuzhiyun U_BOOT_CMD_MKENT(erase, 3, 0, do_mmc_erase, "", ""),
945*4882a593Smuzhiyun U_BOOT_CMD_MKENT(rescan, 1, 1, do_mmc_rescan, "", ""),
946*4882a593Smuzhiyun U_BOOT_CMD_MKENT(part, 1, 1, do_mmc_part, "", ""),
947*4882a593Smuzhiyun U_BOOT_CMD_MKENT(dev, 3, 0, do_mmc_dev, "", ""),
948*4882a593Smuzhiyun U_BOOT_CMD_MKENT(list, 1, 1, do_mmc_list, "", ""),
949*4882a593Smuzhiyun U_BOOT_CMD_MKENT(hwpartition, 28, 0, do_mmc_hwpartition, "", ""),
950*4882a593Smuzhiyun #ifdef CONFIG_SUPPORT_EMMC_BOOT
951*4882a593Smuzhiyun U_BOOT_CMD_MKENT(bootbus, 5, 0, do_mmc_bootbus, "", ""),
952*4882a593Smuzhiyun U_BOOT_CMD_MKENT(bootpart-resize, 4, 0, do_mmc_boot_resize, "", ""),
953*4882a593Smuzhiyun U_BOOT_CMD_MKENT(partconf, 5, 0, do_mmc_partconf, "", ""),
954*4882a593Smuzhiyun U_BOOT_CMD_MKENT(rst-function, 3, 0, do_mmc_rst_func, "", ""),
955*4882a593Smuzhiyun #endif
956*4882a593Smuzhiyun #ifdef CONFIG_OPTEE_CLIENT
957*4882a593Smuzhiyun U_BOOT_CMD_MKENT(testsecurestorage, 1, 0, do_mmc_test_secure_storage, "", ""),
958*4882a593Smuzhiyun U_BOOT_CMD_MKENT(testefuse, 1, 0, do_mmc_testefuse, "", ""),
959*4882a593Smuzhiyun #endif
960*4882a593Smuzhiyun #ifdef CONFIG_SUPPORT_EMMC_RPMB
961*4882a593Smuzhiyun U_BOOT_CMD_MKENT(rpmb, CONFIG_SYS_MAXARGS, 1, do_mmcrpmb, "", ""),
962*4882a593Smuzhiyun #endif
963*4882a593Smuzhiyun U_BOOT_CMD_MKENT(setdsr, 2, 0, do_mmc_setdsr, "", ""),
964*4882a593Smuzhiyun #ifdef CONFIG_CMD_BKOPS_ENABLE
965*4882a593Smuzhiyun U_BOOT_CMD_MKENT(bkops-enable, 2, 0, do_mmc_bkops_enable, "", ""),
966*4882a593Smuzhiyun #endif
967*4882a593Smuzhiyun };
968*4882a593Smuzhiyun
do_mmcops(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])969*4882a593Smuzhiyun static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
970*4882a593Smuzhiyun {
971*4882a593Smuzhiyun cmd_tbl_t *cp;
972*4882a593Smuzhiyun
973*4882a593Smuzhiyun cp = find_cmd_tbl(argv[1], cmd_mmc, ARRAY_SIZE(cmd_mmc));
974*4882a593Smuzhiyun
975*4882a593Smuzhiyun /* Drop the mmc command */
976*4882a593Smuzhiyun argc--;
977*4882a593Smuzhiyun argv++;
978*4882a593Smuzhiyun
979*4882a593Smuzhiyun if (cp == NULL || argc > cp->maxargs)
980*4882a593Smuzhiyun return CMD_RET_USAGE;
981*4882a593Smuzhiyun if (flag == CMD_FLAG_REPEAT && !cp->repeatable)
982*4882a593Smuzhiyun return CMD_RET_SUCCESS;
983*4882a593Smuzhiyun
984*4882a593Smuzhiyun if (curr_device < 0) {
985*4882a593Smuzhiyun if (get_mmc_num() > 0) {
986*4882a593Smuzhiyun curr_device = 0;
987*4882a593Smuzhiyun } else {
988*4882a593Smuzhiyun puts("No MMC device available\n");
989*4882a593Smuzhiyun return CMD_RET_FAILURE;
990*4882a593Smuzhiyun }
991*4882a593Smuzhiyun }
992*4882a593Smuzhiyun return cp->cmd(cmdtp, flag, argc, argv);
993*4882a593Smuzhiyun }
994*4882a593Smuzhiyun
995*4882a593Smuzhiyun U_BOOT_CMD(
996*4882a593Smuzhiyun mmc, 29, 1, do_mmcops,
997*4882a593Smuzhiyun "MMC sub system",
998*4882a593Smuzhiyun "info - display info of the current MMC device\n"
999*4882a593Smuzhiyun "mmc read addr blk# cnt\n"
1000*4882a593Smuzhiyun "mmc write addr blk# cnt\n"
1001*4882a593Smuzhiyun "mmc erase blk# cnt\n"
1002*4882a593Smuzhiyun "mmc rescan\n"
1003*4882a593Smuzhiyun "mmc part - lists available partition on current mmc device\n"
1004*4882a593Smuzhiyun "mmc dev [dev] [part] - show or set current mmc device [partition]\n"
1005*4882a593Smuzhiyun "mmc list - lists available devices\n"
1006*4882a593Smuzhiyun "mmc hwpartition [args...] - does hardware partitioning\n"
1007*4882a593Smuzhiyun " arguments (sizes in 512-byte blocks):\n"
1008*4882a593Smuzhiyun " [user [enh start cnt] [wrrel {on|off}]] - sets user data area attributes\n"
1009*4882a593Smuzhiyun " [gp1|gp2|gp3|gp4 cnt [enh] [wrrel {on|off}]] - general purpose partition\n"
1010*4882a593Smuzhiyun " [check|set|complete] - mode, complete set partitioning completed\n"
1011*4882a593Smuzhiyun " WARNING: Partitioning is a write-once setting once it is set to complete.\n"
1012*4882a593Smuzhiyun " Power cycling is required to initialize partitions after set to complete.\n"
1013*4882a593Smuzhiyun #ifdef CONFIG_SUPPORT_EMMC_BOOT
1014*4882a593Smuzhiyun "mmc bootbus dev boot_bus_width reset_boot_bus_width boot_mode\n"
1015*4882a593Smuzhiyun " - Set the BOOT_BUS_WIDTH field of the specified device\n"
1016*4882a593Smuzhiyun "mmc bootpart-resize <dev> <boot part size MB> <RPMB part size MB>\n"
1017*4882a593Smuzhiyun " - Change sizes of boot and RPMB partitions of specified device\n"
1018*4882a593Smuzhiyun "mmc partconf dev [boot_ack boot_partition partition_access]\n"
1019*4882a593Smuzhiyun " - Show or change the bits of the PARTITION_CONFIG field of the specified device\n"
1020*4882a593Smuzhiyun "mmc rst-function dev value\n"
1021*4882a593Smuzhiyun " - Change the RST_n_FUNCTION field of the specified device\n"
1022*4882a593Smuzhiyun " WARNING: This is a write-once field and 0 / 1 / 2 are the only valid values.\n"
1023*4882a593Smuzhiyun #endif
1024*4882a593Smuzhiyun #ifdef CONFIG_OPTEE_CLIENT
1025*4882a593Smuzhiyun "mmc testsecurestorage - test CA call static TA to store data in security\n"
1026*4882a593Smuzhiyun "mmc testefuse - test CA call static TA,and TA read or write efuse\n"
1027*4882a593Smuzhiyun #endif
1028*4882a593Smuzhiyun #ifdef CONFIG_SUPPORT_EMMC_RPMB
1029*4882a593Smuzhiyun "mmc rpmb read addr blk# cnt [address of auth-key] - block size is 256 bytes\n"
1030*4882a593Smuzhiyun "mmc rpmb write addr blk# cnt <address of auth-key> - block size is 256 bytes\n"
1031*4882a593Smuzhiyun "mmc rpmb key <address of auth-key> - program the RPMB authentication key.\n"
1032*4882a593Smuzhiyun "mmc rpmb counter - read the value of the write counter\n"
1033*4882a593Smuzhiyun #endif
1034*4882a593Smuzhiyun "mmc setdsr <value> - set DSR register value\n"
1035*4882a593Smuzhiyun #ifdef CONFIG_CMD_BKOPS_ENABLE
1036*4882a593Smuzhiyun "mmc bkops-enable <dev> - enable background operations handshake on device\n"
1037*4882a593Smuzhiyun " WARNING: This is a write-once setting.\n"
1038*4882a593Smuzhiyun #endif
1039*4882a593Smuzhiyun );
1040*4882a593Smuzhiyun
1041*4882a593Smuzhiyun /* Old command kept for compatibility. Same as 'mmc info' */
1042*4882a593Smuzhiyun U_BOOT_CMD(
1043*4882a593Smuzhiyun mmcinfo, 1, 0, do_mmcinfo,
1044*4882a593Smuzhiyun "display MMC info",
1045*4882a593Smuzhiyun "- display info of the current MMC device"
1046*4882a593Smuzhiyun );
1047*4882a593Smuzhiyun
1048