]>
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 | |
2a91c913 A |
134 | #ifdef CONFIG_SUPPORT_EMMC_BOOT |
135 | static int boot_part_access(struct mmc *mmc, u8 ack, u8 part_num, u8 access) | |
136 | { | |
137 | int err; | |
138 | err = mmc_boot_part_access(mmc, ack, part_num, access); | |
139 | ||
140 | if ((err == 0) && (access != 0)) { | |
141 | printf("\t\t\t!!!Notice!!!\n"); | |
142 | ||
143 | printf("!You must close EMMC boot Partition"); | |
144 | printf("after all images are written\n"); | |
145 | ||
146 | printf("!EMMC boot partition has continuity"); | |
147 | printf("at image writing time.\n"); | |
148 | ||
149 | printf("!So, do not close the boot partition"); | |
150 | printf("before all images are written.\n"); | |
151 | return 0; | |
152 | } else if ((err == 0) && (access == 0)) | |
153 | return 0; | |
154 | else if ((err != 0) && (access != 0)) { | |
155 | printf("EMMC boot partition-%d OPEN Failed.\n", part_num); | |
156 | return 1; | |
157 | } else { | |
158 | printf("EMMC boot partition-%d CLOSE Failed.\n", part_num); | |
159 | return 1; | |
160 | } | |
161 | } | |
162 | #endif | |
163 | ||
088f1b19 | 164 | static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
272cc70b | 165 | { |
6be95ccf LW |
166 | enum mmc_state state; |
167 | ||
ea6ebe21 | 168 | if (argc < 2) |
4c12eeb8 | 169 | return CMD_RET_USAGE; |
272cc70b | 170 | |
ea6ebe21 LW |
171 | if (curr_device < 0) { |
172 | if (get_mmc_num() > 0) | |
173 | curr_device = 0; | |
174 | else { | |
175 | puts("No MMC device available\n"); | |
176 | return 1; | |
177 | } | |
178 | } | |
272cc70b | 179 | |
ea6ebe21 | 180 | if (strcmp(argv[1], "rescan") == 0) { |
9fd38372 SW |
181 | struct mmc *mmc; |
182 | ||
183 | if (argc != 2) | |
184 | return CMD_RET_USAGE; | |
e85649c7 | 185 | |
9fd38372 | 186 | mmc = find_mmc_device(curr_device); |
ea6ebe21 LW |
187 | if (!mmc) { |
188 | printf("no mmc device at slot %x\n", curr_device); | |
189 | return 1; | |
190 | } | |
191 | ||
bc897b1d | 192 | mmc->has_init = 0; |
272cc70b | 193 | |
8fd01b8f MJ |
194 | if (mmc_init(mmc)) |
195 | return 1; | |
196 | else | |
197 | return 0; | |
ea6ebe21 LW |
198 | } else if (strncmp(argv[1], "part", 4) == 0) { |
199 | block_dev_desc_t *mmc_dev; | |
9fd38372 | 200 | struct mmc *mmc; |
ea6ebe21 | 201 | |
9fd38372 SW |
202 | if (argc != 2) |
203 | return CMD_RET_USAGE; | |
204 | ||
205 | mmc = find_mmc_device(curr_device); | |
ea6ebe21 LW |
206 | if (!mmc) { |
207 | printf("no mmc device at slot %x\n", curr_device); | |
208 | return 1; | |
209 | } | |
210 | mmc_init(mmc); | |
211 | mmc_dev = mmc_get_dev(curr_device); | |
212 | if (mmc_dev != NULL && | |
213 | mmc_dev->type != DEV_TYPE_UNKNOWN) { | |
214 | print_part(mmc_dev); | |
272cc70b | 215 | return 0; |
ea6ebe21 | 216 | } |
8f3b9642 | 217 | |
ea6ebe21 LW |
218 | puts("get mmc type error!\n"); |
219 | return 1; | |
220 | } else if (strcmp(argv[1], "list") == 0) { | |
9fd38372 SW |
221 | if (argc != 2) |
222 | return CMD_RET_USAGE; | |
ea6ebe21 LW |
223 | print_mmc_devices('\n'); |
224 | return 0; | |
225 | } else if (strcmp(argv[1], "dev") == 0) { | |
bc897b1d | 226 | int dev, part = -1; |
ea6ebe21 LW |
227 | struct mmc *mmc; |
228 | ||
229 | if (argc == 2) | |
230 | dev = curr_device; | |
231 | else if (argc == 3) | |
232 | dev = simple_strtoul(argv[2], NULL, 10); | |
bc897b1d LW |
233 | else if (argc == 4) { |
234 | dev = (int)simple_strtoul(argv[2], NULL, 10); | |
235 | part = (int)simple_strtoul(argv[3], NULL, 10); | |
236 | if (part > PART_ACCESS_MASK) { | |
237 | printf("#part_num shouldn't be larger" | |
238 | " than %d\n", PART_ACCESS_MASK); | |
239 | return 1; | |
240 | } | |
241 | } else | |
4c12eeb8 | 242 | return CMD_RET_USAGE; |
8f3b9642 | 243 | |
ea6ebe21 LW |
244 | mmc = find_mmc_device(dev); |
245 | if (!mmc) { | |
246 | printf("no mmc device at slot %x\n", dev); | |
8f3b9642 | 247 | return 1; |
272cc70b AF |
248 | } |
249 | ||
bc897b1d LW |
250 | mmc_init(mmc); |
251 | if (part != -1) { | |
252 | int ret; | |
253 | if (mmc->part_config == MMCPART_NOAVAILABLE) { | |
254 | printf("Card doesn't support part_switch\n"); | |
255 | return 1; | |
256 | } | |
257 | ||
258 | if (part != mmc->part_num) { | |
259 | ret = mmc_switch_part(dev, part); | |
260 | if (!ret) | |
261 | mmc->part_num = part; | |
262 | ||
1f8b546f | 263 | printf("switch to partitions #%d, %s\n", |
bc897b1d LW |
264 | part, (!ret) ? "OK" : "ERROR"); |
265 | } | |
266 | } | |
ea6ebe21 | 267 | curr_device = dev; |
bc897b1d LW |
268 | if (mmc->part_config == MMCPART_NOAVAILABLE) |
269 | printf("mmc%d is current device\n", curr_device); | |
270 | else | |
271 | printf("mmc%d(part %d) is current device\n", | |
272 | curr_device, mmc->part_num); | |
272cc70b | 273 | |
ea6ebe21 | 274 | return 0; |
2a91c913 A |
275 | #ifdef CONFIG_SUPPORT_EMMC_BOOT |
276 | } else if ((strcmp(argv[1], "open") == 0) || | |
277 | (strcmp(argv[1], "close") == 0)) { | |
278 | int dev; | |
279 | struct mmc *mmc; | |
280 | u8 part_num, access = 0; | |
281 | ||
282 | if (argc == 4) { | |
283 | dev = simple_strtoul(argv[2], NULL, 10); | |
284 | part_num = simple_strtoul(argv[3], NULL, 10); | |
285 | } else { | |
286 | return CMD_RET_USAGE; | |
287 | } | |
288 | ||
289 | mmc = find_mmc_device(dev); | |
290 | if (!mmc) { | |
291 | printf("no mmc device at slot %x\n", dev); | |
292 | return 1; | |
293 | } | |
272cc70b | 294 | |
2a91c913 A |
295 | if (IS_SD(mmc)) { |
296 | printf("SD device cannot be opened/closed\n"); | |
297 | return 1; | |
298 | } | |
299 | ||
300 | if ((part_num <= 0) || (part_num > MMC_NUM_BOOT_PARTITION)) { | |
301 | printf("Invalid boot partition number:\n"); | |
302 | printf("Boot partition number cannot be <= 0\n"); | |
303 | printf("EMMC44 supports only 2 boot partitions\n"); | |
304 | return 1; | |
305 | } | |
306 | ||
307 | if (strcmp(argv[1], "open") == 0) | |
308 | access = part_num; /* enable R/W access to boot part*/ | |
309 | else | |
310 | access = 0; /* No access to boot partition */ | |
311 | ||
312 | /* acknowledge to be sent during boot operation */ | |
313 | return boot_part_access(mmc, 1, part_num, access); | |
314 | ||
315 | } else if (strcmp(argv[1], "bootpart") == 0) { | |
316 | int dev; | |
317 | dev = simple_strtoul(argv[2], NULL, 10); | |
318 | ||
319 | u32 bootsize = simple_strtoul(argv[3], NULL, 10); | |
320 | u32 rpmbsize = simple_strtoul(argv[4], NULL, 10); | |
321 | struct mmc *mmc = find_mmc_device(dev); | |
322 | if (!mmc) { | |
323 | printf("no mmc device at slot %x\n", dev); | |
324 | return 1; | |
325 | } | |
326 | ||
327 | if (IS_SD(mmc)) { | |
328 | printf("It is not a EMMC device\n"); | |
329 | return 1; | |
330 | } | |
331 | ||
332 | if (0 == mmc_boot_partition_size_change(mmc, | |
333 | bootsize, rpmbsize)) { | |
334 | printf("EMMC boot partition Size %d MB\n", bootsize); | |
335 | printf("EMMC RPMB partition Size %d MB\n", rpmbsize); | |
336 | return 0; | |
337 | } else { | |
338 | printf("EMMC boot partition Size change Failed.\n"); | |
339 | return 1; | |
340 | } | |
341 | #endif /* CONFIG_SUPPORT_EMMC_BOOT */ | |
342 | } | |
ed80c931 TH |
343 | state = MMC_INVALID; |
344 | if (argc == 5 && strcmp(argv[1], "read") == 0) | |
6be95ccf | 345 | state = MMC_READ; |
ed80c931 | 346 | else if (argc == 5 && strcmp(argv[1], "write") == 0) |
6be95ccf | 347 | state = MMC_WRITE; |
ed80c931 | 348 | else if (argc == 4 && strcmp(argv[1], "erase") == 0) |
e6f99a56 | 349 | state = MMC_ERASE; |
272cc70b | 350 | |
6be95ccf LW |
351 | if (state != MMC_INVALID) { |
352 | struct mmc *mmc = find_mmc_device(curr_device); | |
e6f99a56 LW |
353 | int idx = 2; |
354 | u32 blk, cnt, n; | |
355 | void *addr; | |
356 | ||
357 | if (state != MMC_ERASE) { | |
358 | addr = (void *)simple_strtoul(argv[idx], NULL, 16); | |
359 | ++idx; | |
360 | } else | |
088f1b19 | 361 | addr = NULL; |
e6f99a56 LW |
362 | blk = simple_strtoul(argv[idx], NULL, 16); |
363 | cnt = simple_strtoul(argv[idx + 1], NULL, 16); | |
272cc70b | 364 | |
ea6ebe21 LW |
365 | if (!mmc) { |
366 | printf("no mmc device at slot %x\n", curr_device); | |
367 | return 1; | |
368 | } | |
e85649c7 | 369 | |
6be95ccf LW |
370 | printf("\nMMC %s: dev # %d, block # %d, count %d ... ", |
371 | argv[1], curr_device, blk, cnt); | |
272cc70b | 372 | |
ea6ebe21 | 373 | mmc_init(mmc); |
272cc70b | 374 | |
d23d8d7e NK |
375 | if ((state == MMC_WRITE || state == MMC_ERASE)) { |
376 | if (mmc_getwp(mmc) == 1) { | |
377 | printf("Error: card is write protected!\n"); | |
378 | return 1; | |
379 | } | |
380 | } | |
381 | ||
6be95ccf LW |
382 | switch (state) { |
383 | case MMC_READ: | |
384 | n = mmc->block_dev.block_read(curr_device, blk, | |
385 | cnt, addr); | |
386 | /* flush cache after read */ | |
387 | flush_cache((ulong)addr, cnt * 512); /* FIXME */ | |
388 | break; | |
389 | case MMC_WRITE: | |
390 | n = mmc->block_dev.block_write(curr_device, blk, | |
391 | cnt, addr); | |
392 | break; | |
e6f99a56 LW |
393 | case MMC_ERASE: |
394 | n = mmc->block_dev.block_erase(curr_device, blk, cnt); | |
395 | break; | |
6be95ccf LW |
396 | default: |
397 | BUG(); | |
398 | } | |
272cc70b | 399 | |
6be95ccf LW |
400 | printf("%d blocks %s: %s\n", |
401 | n, argv[1], (n == cnt) ? "OK" : "ERROR"); | |
ea6ebe21 | 402 | return (n == cnt) ? 0 : 1; |
272cc70b | 403 | } |
ea6ebe21 | 404 | |
4c12eeb8 | 405 | return CMD_RET_USAGE; |
272cc70b AF |
406 | } |
407 | ||
408 | U_BOOT_CMD( | |
409 | mmc, 6, 1, do_mmcops, | |
852dbfdd | 410 | "MMC sub system", |
ea6ebe21 LW |
411 | "read addr blk# cnt\n" |
412 | "mmc write addr blk# cnt\n" | |
e6f99a56 | 413 | "mmc erase blk# cnt\n" |
ea6ebe21 LW |
414 | "mmc rescan\n" |
415 | "mmc part - lists available partition on current mmc device\n" | |
bc897b1d | 416 | "mmc dev [dev] [part] - show or set current mmc device [partition]\n" |
2a91c913 A |
417 | "mmc list - lists available devices\n" |
418 | #ifdef CONFIG_SUPPORT_EMMC_BOOT | |
419 | "mmc open <dev> <boot_partition>\n" | |
420 | " - Enable boot_part for booting and enable R/W access of boot_part\n" | |
421 | "mmc close <dev> <boot_partition>\n" | |
422 | " - Enable boot_part for booting and disable access to boot_part\n" | |
423 | "mmc bootpart <device num> <boot part size MB> <RPMB part size MB>\n" | |
1f8b546f | 424 | " - change sizes of boot and RPMB partitions of specified device\n" |
3511b4e2 | 425 | #endif |
2a91c913 A |
426 | ); |
427 | #endif /* !CONFIG_GENERIC_MMC */ |