]>
Commit | Line | Data |
---|---|---|
71f95118 WD |
1 | /* |
2 | * (C) Copyright 2003 | |
3 | * Kyle Harris, [email protected] | |
4 | * | |
1a459660 | 5 | * SPDX-License-Identifier: GPL-2.0+ |
71f95118 WD |
6 | */ |
7 | ||
8 | #include <common.h> | |
9 | #include <command.h> | |
71f95118 WD |
10 | #include <mmc.h> |
11 | ||
02e22c2d | 12 | static int curr_device = -1; |
ea6ebe21 | 13 | #ifndef CONFIG_GENERIC_MMC |
54841ab5 | 14 | int do_mmc (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
71f95118 | 15 | { |
869f6bf4 MK |
16 | int dev; |
17 | ||
47e26b1b | 18 | if (argc < 2) |
4c12eeb8 | 19 | return CMD_RET_USAGE; |
869f6bf4 MK |
20 | |
21 | if (strcmp(argv[1], "init") == 0) { | |
22 | if (argc == 2) { | |
23 | if (curr_device < 0) | |
24 | dev = 1; | |
25 | else | |
26 | dev = curr_device; | |
27 | } else if (argc == 3) { | |
28 | dev = (int)simple_strtoul(argv[2], NULL, 10); | |
29 | } else { | |
4c12eeb8 | 30 | return CMD_RET_USAGE; |
869f6bf4 MK |
31 | } |
32 | ||
33 | if (mmc_legacy_init(dev) != 0) { | |
34 | puts("No MMC card found\n"); | |
35 | return 1; | |
36 | } | |
37 | ||
38 | curr_device = dev; | |
39 | printf("mmc%d is available\n", curr_device); | |
40 | } else if (strcmp(argv[1], "device") == 0) { | |
41 | if (argc == 2) { | |
42 | if (curr_device < 0) { | |
43 | puts("No MMC device available\n"); | |
44 | return 1; | |
45 | } | |
46 | } else if (argc == 3) { | |
47 | dev = (int)simple_strtoul(argv[2], NULL, 10); | |
48 | ||
49 | #ifdef CONFIG_SYS_MMC_SET_DEV | |
50 | if (mmc_set_dev(dev) != 0) | |
51 | return 1; | |
52 | #endif | |
53 | curr_device = dev; | |
54 | } else { | |
4c12eeb8 | 55 | return CMD_RET_USAGE; |
869f6bf4 MK |
56 | } |
57 | ||
58 | printf("mmc%d is current device\n", curr_device); | |
59 | } else { | |
4c12eeb8 | 60 | return CMD_RET_USAGE; |
71f95118 | 61 | } |
869f6bf4 | 62 | |
71f95118 WD |
63 | return 0; |
64 | } | |
65 | ||
0d498393 | 66 | U_BOOT_CMD( |
869f6bf4 MK |
67 | mmc, 3, 1, do_mmc, |
68 | "MMC sub-system", | |
69 | "init [dev] - init MMC sub system\n" | |
a89c33db | 70 | "mmc device [dev] - show or set current device" |
b0fce99b | 71 | ); |
3511b4e2 | 72 | #else /* !CONFIG_GENERIC_MMC */ |
272cc70b | 73 | |
6be95ccf LW |
74 | enum mmc_state { |
75 | MMC_INVALID, | |
76 | MMC_READ, | |
77 | MMC_WRITE, | |
e6f99a56 | 78 | MMC_ERASE, |
6be95ccf | 79 | }; |
272cc70b AF |
80 | static void print_mmcinfo(struct mmc *mmc) |
81 | { | |
82 | printf("Device: %s\n", mmc->name); | |
83 | printf("Manufacturer ID: %x\n", mmc->cid[0] >> 24); | |
84 | printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff); | |
85 | printf("Name: %c%c%c%c%c \n", mmc->cid[0] & 0xff, | |
86 | (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, | |
87 | (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff); | |
88 | ||
89 | printf("Tran Speed: %d\n", mmc->tran_speed); | |
90 | printf("Rd Block Len: %d\n", mmc->read_bl_len); | |
91 | ||
92 | printf("%s version %d.%d\n", IS_SD(mmc) ? "SD" : "MMC", | |
64f4a619 | 93 | (mmc->version >> 8) & 0xf, mmc->version & 0xff); |
272cc70b AF |
94 | |
95 | printf("High Capacity: %s\n", mmc->high_capacity ? "Yes" : "No"); | |
940e0782 MK |
96 | puts("Capacity: "); |
97 | print_size(mmc->capacity, "\n"); | |
272cc70b AF |
98 | |
99 | printf("Bus Width: %d-bit\n", mmc->bus_width); | |
100 | } | |
101 | ||
088f1b19 | 102 | static int do_mmcinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
272cc70b AF |
103 | { |
104 | struct mmc *mmc; | |
272cc70b | 105 | |
ea6ebe21 LW |
106 | if (curr_device < 0) { |
107 | if (get_mmc_num() > 0) | |
108 | curr_device = 0; | |
109 | else { | |
110 | puts("No MMC device available\n"); | |
111 | return 1; | |
112 | } | |
113 | } | |
272cc70b | 114 | |
ea6ebe21 | 115 | mmc = find_mmc_device(curr_device); |
272cc70b AF |
116 | |
117 | if (mmc) { | |
118 | mmc_init(mmc); | |
119 | ||
120 | print_mmcinfo(mmc); | |
ea6ebe21 LW |
121 | return 0; |
122 | } else { | |
123 | printf("no mmc device at slot %x\n", curr_device); | |
124 | return 1; | |
272cc70b | 125 | } |
272cc70b AF |
126 | } |
127 | ||
388a29d0 | 128 | U_BOOT_CMD( |
ea6ebe21 | 129 | mmcinfo, 1, 0, do_mmcinfo, |
cc9f607b | 130 | "display MMC info", |
59ddead1 | 131 | "- display info of the current MMC device" |
a89c33db | 132 | ); |
272cc70b | 133 | |
088f1b19 | 134 | static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
272cc70b | 135 | { |
6be95ccf LW |
136 | enum mmc_state state; |
137 | ||
ea6ebe21 | 138 | if (argc < 2) |
4c12eeb8 | 139 | return CMD_RET_USAGE; |
272cc70b | 140 | |
ea6ebe21 LW |
141 | if (curr_device < 0) { |
142 | if (get_mmc_num() > 0) | |
143 | curr_device = 0; | |
144 | else { | |
145 | puts("No MMC device available\n"); | |
146 | return 1; | |
147 | } | |
148 | } | |
272cc70b | 149 | |
ea6ebe21 | 150 | if (strcmp(argv[1], "rescan") == 0) { |
9fd38372 SW |
151 | struct mmc *mmc; |
152 | ||
153 | if (argc != 2) | |
154 | return CMD_RET_USAGE; | |
e85649c7 | 155 | |
9fd38372 | 156 | mmc = find_mmc_device(curr_device); |
ea6ebe21 LW |
157 | if (!mmc) { |
158 | printf("no mmc device at slot %x\n", curr_device); | |
159 | return 1; | |
160 | } | |
161 | ||
bc897b1d | 162 | mmc->has_init = 0; |
272cc70b | 163 | |
8fd01b8f MJ |
164 | if (mmc_init(mmc)) |
165 | return 1; | |
166 | else | |
167 | return 0; | |
792970b0 | 168 | } else if (strcmp(argv[1], "part") == 0) { |
ea6ebe21 | 169 | block_dev_desc_t *mmc_dev; |
9fd38372 | 170 | struct mmc *mmc; |
ea6ebe21 | 171 | |
9fd38372 SW |
172 | if (argc != 2) |
173 | return CMD_RET_USAGE; | |
174 | ||
175 | mmc = find_mmc_device(curr_device); | |
ea6ebe21 LW |
176 | if (!mmc) { |
177 | printf("no mmc device at slot %x\n", curr_device); | |
178 | return 1; | |
179 | } | |
180 | mmc_init(mmc); | |
181 | mmc_dev = mmc_get_dev(curr_device); | |
182 | if (mmc_dev != NULL && | |
183 | mmc_dev->type != DEV_TYPE_UNKNOWN) { | |
184 | print_part(mmc_dev); | |
272cc70b | 185 | return 0; |
ea6ebe21 | 186 | } |
8f3b9642 | 187 | |
ea6ebe21 LW |
188 | puts("get mmc type error!\n"); |
189 | return 1; | |
190 | } else if (strcmp(argv[1], "list") == 0) { | |
9fd38372 SW |
191 | if (argc != 2) |
192 | return CMD_RET_USAGE; | |
ea6ebe21 LW |
193 | print_mmc_devices('\n'); |
194 | return 0; | |
195 | } else if (strcmp(argv[1], "dev") == 0) { | |
bc897b1d | 196 | int dev, part = -1; |
ea6ebe21 LW |
197 | struct mmc *mmc; |
198 | ||
199 | if (argc == 2) | |
200 | dev = curr_device; | |
201 | else if (argc == 3) | |
202 | dev = simple_strtoul(argv[2], NULL, 10); | |
bc897b1d LW |
203 | else if (argc == 4) { |
204 | dev = (int)simple_strtoul(argv[2], NULL, 10); | |
205 | part = (int)simple_strtoul(argv[3], NULL, 10); | |
206 | if (part > PART_ACCESS_MASK) { | |
207 | printf("#part_num shouldn't be larger" | |
208 | " than %d\n", PART_ACCESS_MASK); | |
209 | return 1; | |
210 | } | |
211 | } else | |
4c12eeb8 | 212 | return CMD_RET_USAGE; |
8f3b9642 | 213 | |
ea6ebe21 LW |
214 | mmc = find_mmc_device(dev); |
215 | if (!mmc) { | |
216 | printf("no mmc device at slot %x\n", dev); | |
8f3b9642 | 217 | return 1; |
272cc70b AF |
218 | } |
219 | ||
bc897b1d LW |
220 | mmc_init(mmc); |
221 | if (part != -1) { | |
222 | int ret; | |
223 | if (mmc->part_config == MMCPART_NOAVAILABLE) { | |
224 | printf("Card doesn't support part_switch\n"); | |
225 | return 1; | |
226 | } | |
227 | ||
228 | if (part != mmc->part_num) { | |
229 | ret = mmc_switch_part(dev, part); | |
230 | if (!ret) | |
231 | mmc->part_num = part; | |
232 | ||
1f8b546f | 233 | printf("switch to partitions #%d, %s\n", |
bc897b1d LW |
234 | part, (!ret) ? "OK" : "ERROR"); |
235 | } | |
236 | } | |
ea6ebe21 | 237 | curr_device = dev; |
bc897b1d LW |
238 | if (mmc->part_config == MMCPART_NOAVAILABLE) |
239 | printf("mmc%d is current device\n", curr_device); | |
240 | else | |
241 | printf("mmc%d(part %d) is current device\n", | |
242 | curr_device, mmc->part_num); | |
272cc70b | 243 | |
ea6ebe21 | 244 | return 0; |
2a91c913 | 245 | #ifdef CONFIG_SUPPORT_EMMC_BOOT |
792970b0 TR |
246 | } else if (strcmp(argv[1], "partconf") == 0) { |
247 | int dev; | |
248 | struct mmc *mmc; | |
249 | u8 ack, part_num, access; | |
250 | ||
251 | if (argc == 6) { | |
252 | dev = simple_strtoul(argv[2], NULL, 10); | |
253 | ack = simple_strtoul(argv[3], NULL, 10); | |
254 | part_num = simple_strtoul(argv[4], NULL, 10); | |
255 | access = simple_strtoul(argv[5], NULL, 10); | |
256 | } else { | |
257 | return CMD_RET_USAGE; | |
258 | } | |
259 | ||
260 | mmc = find_mmc_device(dev); | |
261 | if (!mmc) { | |
262 | printf("no mmc device at slot %x\n", dev); | |
263 | return 1; | |
264 | } | |
2a91c913 | 265 | |
792970b0 TR |
266 | if (IS_SD(mmc)) { |
267 | puts("PARTITION_CONFIG only exists on eMMC\n"); | |
268 | return 1; | |
269 | } | |
270 | ||
271 | /* acknowledge to be sent during boot operation */ | |
272 | return mmc_set_part_conf(mmc, ack, part_num, access); | |
5a99b9de TR |
273 | } else if (strcmp(argv[1], "bootbus") == 0) { |
274 | int dev; | |
275 | struct mmc *mmc; | |
276 | u8 width, reset, mode; | |
277 | ||
278 | if (argc == 6) { | |
279 | dev = simple_strtoul(argv[2], NULL, 10); | |
280 | width = simple_strtoul(argv[3], NULL, 10); | |
281 | reset = simple_strtoul(argv[4], NULL, 10); | |
282 | mode = simple_strtoul(argv[5], NULL, 10); | |
283 | } else { | |
284 | return CMD_RET_USAGE; | |
285 | } | |
286 | ||
287 | mmc = find_mmc_device(dev); | |
288 | if (!mmc) { | |
289 | printf("no mmc device at slot %x\n", dev); | |
290 | return 1; | |
291 | } | |
292 | ||
293 | if (IS_SD(mmc)) { | |
294 | puts("BOOT_BUS_WIDTH only exists on eMMC\n"); | |
295 | return 1; | |
296 | } | |
297 | ||
298 | /* acknowledge to be sent during boot operation */ | |
299 | return mmc_set_boot_bus_width(mmc, width, reset, mode); | |
f1fd957e | 300 | } else if (strcmp(argv[1], "bootpart-resize") == 0) { |
2a91c913 | 301 | int dev; |
f1fd957e | 302 | struct mmc *mmc; |
b01e6fe6 | 303 | u32 bootsize, rpmbsize; |
2a91c913 | 304 | |
b01e6fe6 TR |
305 | if (argc == 5) { |
306 | dev = simple_strtoul(argv[2], NULL, 10); | |
307 | bootsize = simple_strtoul(argv[3], NULL, 10); | |
308 | rpmbsize = simple_strtoul(argv[4], NULL, 10); | |
309 | } else { | |
310 | return CMD_RET_USAGE; | |
311 | } | |
312 | ||
313 | mmc = find_mmc_device(dev); | |
2a91c913 A |
314 | if (!mmc) { |
315 | printf("no mmc device at slot %x\n", dev); | |
316 | return 1; | |
317 | } | |
318 | ||
319 | if (IS_SD(mmc)) { | |
320 | printf("It is not a EMMC device\n"); | |
321 | return 1; | |
322 | } | |
323 | ||
324 | if (0 == mmc_boot_partition_size_change(mmc, | |
325 | bootsize, rpmbsize)) { | |
326 | printf("EMMC boot partition Size %d MB\n", bootsize); | |
327 | printf("EMMC RPMB partition Size %d MB\n", rpmbsize); | |
328 | return 0; | |
329 | } else { | |
330 | printf("EMMC boot partition Size change Failed.\n"); | |
331 | return 1; | |
332 | } | |
333 | #endif /* CONFIG_SUPPORT_EMMC_BOOT */ | |
334 | } | |
ab71188c MN |
335 | |
336 | else if (argc == 3 && strcmp(argv[1], "setdsr") == 0) { | |
337 | struct mmc *mmc = find_mmc_device(curr_device); | |
338 | u32 val = simple_strtoul(argv[2], NULL, 16); | |
339 | int ret; | |
340 | ||
341 | if (!mmc) { | |
342 | printf("no mmc device at slot %x\n", curr_device); | |
343 | return 1; | |
344 | } | |
345 | ret = mmc_set_dsr(mmc, val); | |
346 | printf("set dsr %s\n", (!ret) ? "OK, force rescan" : "ERROR"); | |
347 | if (!ret) { | |
348 | mmc->has_init = 0; | |
349 | if (mmc_init(mmc)) | |
350 | return 1; | |
351 | else | |
352 | return 0; | |
353 | } | |
354 | return ret; | |
355 | } | |
356 | ||
ed80c931 TH |
357 | state = MMC_INVALID; |
358 | if (argc == 5 && strcmp(argv[1], "read") == 0) | |
6be95ccf | 359 | state = MMC_READ; |
ed80c931 | 360 | else if (argc == 5 && strcmp(argv[1], "write") == 0) |
6be95ccf | 361 | state = MMC_WRITE; |
ed80c931 | 362 | else if (argc == 4 && strcmp(argv[1], "erase") == 0) |
e6f99a56 | 363 | state = MMC_ERASE; |
272cc70b | 364 | |
6be95ccf LW |
365 | if (state != MMC_INVALID) { |
366 | struct mmc *mmc = find_mmc_device(curr_device); | |
e6f99a56 LW |
367 | int idx = 2; |
368 | u32 blk, cnt, n; | |
369 | void *addr; | |
370 | ||
371 | if (state != MMC_ERASE) { | |
372 | addr = (void *)simple_strtoul(argv[idx], NULL, 16); | |
373 | ++idx; | |
374 | } else | |
088f1b19 | 375 | addr = NULL; |
e6f99a56 LW |
376 | blk = simple_strtoul(argv[idx], NULL, 16); |
377 | cnt = simple_strtoul(argv[idx + 1], NULL, 16); | |
272cc70b | 378 | |
ea6ebe21 LW |
379 | if (!mmc) { |
380 | printf("no mmc device at slot %x\n", curr_device); | |
381 | return 1; | |
382 | } | |
e85649c7 | 383 | |
6be95ccf LW |
384 | printf("\nMMC %s: dev # %d, block # %d, count %d ... ", |
385 | argv[1], curr_device, blk, cnt); | |
272cc70b | 386 | |
ea6ebe21 | 387 | mmc_init(mmc); |
272cc70b | 388 | |
d23d8d7e NK |
389 | if ((state == MMC_WRITE || state == MMC_ERASE)) { |
390 | if (mmc_getwp(mmc) == 1) { | |
391 | printf("Error: card is write protected!\n"); | |
392 | return 1; | |
393 | } | |
394 | } | |
395 | ||
6be95ccf LW |
396 | switch (state) { |
397 | case MMC_READ: | |
398 | n = mmc->block_dev.block_read(curr_device, blk, | |
399 | cnt, addr); | |
400 | /* flush cache after read */ | |
401 | flush_cache((ulong)addr, cnt * 512); /* FIXME */ | |
402 | break; | |
403 | case MMC_WRITE: | |
404 | n = mmc->block_dev.block_write(curr_device, blk, | |
405 | cnt, addr); | |
406 | break; | |
e6f99a56 LW |
407 | case MMC_ERASE: |
408 | n = mmc->block_dev.block_erase(curr_device, blk, cnt); | |
409 | break; | |
6be95ccf LW |
410 | default: |
411 | BUG(); | |
412 | } | |
272cc70b | 413 | |
6be95ccf LW |
414 | printf("%d blocks %s: %s\n", |
415 | n, argv[1], (n == cnt) ? "OK" : "ERROR"); | |
ea6ebe21 | 416 | return (n == cnt) ? 0 : 1; |
272cc70b | 417 | } |
ea6ebe21 | 418 | |
4c12eeb8 | 419 | return CMD_RET_USAGE; |
272cc70b AF |
420 | } |
421 | ||
422 | U_BOOT_CMD( | |
423 | mmc, 6, 1, do_mmcops, | |
852dbfdd | 424 | "MMC sub system", |
ea6ebe21 LW |
425 | "read addr blk# cnt\n" |
426 | "mmc write addr blk# cnt\n" | |
e6f99a56 | 427 | "mmc erase blk# cnt\n" |
ea6ebe21 LW |
428 | "mmc rescan\n" |
429 | "mmc part - lists available partition on current mmc device\n" | |
bc897b1d | 430 | "mmc dev [dev] [part] - show or set current mmc device [partition]\n" |
2a91c913 A |
431 | "mmc list - lists available devices\n" |
432 | #ifdef CONFIG_SUPPORT_EMMC_BOOT | |
5a99b9de TR |
433 | "mmc bootbus dev boot_bus_width reset_boot_bus_width boot_mode\n" |
434 | " - Set the BOOT_BUS_WIDTH field of the specified device\n" | |
f1fd957e TR |
435 | "mmc bootpart-resize <dev> <boot part size MB> <RPMB part size MB>\n" |
436 | " - Change sizes of boot and RPMB partitions of specified device\n" | |
792970b0 TR |
437 | "mmc partconf dev boot_ack boot_partition partition_access\n" |
438 | " - Change the bits of the PARTITION_CONFIG field of the specified device\n" | |
3511b4e2 | 439 | #endif |
ab71188c | 440 | "mmc setdsr - set DSR register value\n" |
2a91c913 A |
441 | ); |
442 | #endif /* !CONFIG_GENERIC_MMC */ |