blob: 99ad1475e3deb709bb2f4c66d5572a9d85b0e089 [file] [log] [blame]
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001/*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public
4 * License v2 as published by the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 * General Public License for more details.
10 *
11 * You should have received a copy of the GNU General Public
12 * License along with this program; if not, write to the
13 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14 * Boston, MA 021110-1307, USA.
15 */
16
Gwendal Grignou771984c2014-07-01 12:46:18 -070017#include <errno.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050018#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <sys/ioctl.h>
Gwendal Grignou771984c2014-07-01 12:46:18 -070022#include <sys/param.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050023#include <sys/types.h>
24#include <dirent.h>
25#include <sys/stat.h>
26#include <unistd.h>
27#include <fcntl.h>
28#include <libgen.h>
29#include <limits.h>
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050030#include <ctype.h>
31
32#include "mmc.h"
33#include "mmc_cmds.h"
34
Nick Sanders9d57aa72014-03-05 21:38:54 -080035#define EXT_CSD_SIZE 512
Gwendal Grignou771984c2014-07-01 12:46:18 -070036#define FFU_PATH_SIZE (512 - 1)
Nick Sanders9d57aa72014-03-05 21:38:54 -080037#define CID_SIZE 16
38
39
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050040int read_extcsd(int fd, __u8 *ext_csd)
41{
42 int ret = 0;
43 struct mmc_ioc_cmd idata;
44 memset(&idata, 0, sizeof(idata));
Nick Sanders9d57aa72014-03-05 21:38:54 -080045 memset(ext_csd, 0, sizeof(__u8) * EXT_CSD_SIZE);
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050046 idata.write_flag = 0;
47 idata.opcode = MMC_SEND_EXT_CSD;
48 idata.arg = 0;
49 idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
Nick Sanders9d57aa72014-03-05 21:38:54 -080050 idata.blksz = EXT_CSD_SIZE;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050051 idata.blocks = 1;
52 mmc_ioc_cmd_set_data(idata, ext_csd);
53
54 ret = ioctl(fd, MMC_IOC_CMD, &idata);
55 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -080056 perror("ioctl SEND_EXT_CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050057
58 return ret;
59}
60
61int write_extcsd_value(int fd, __u8 index, __u8 value)
62{
63 int ret = 0;
64 struct mmc_ioc_cmd idata;
65
66 memset(&idata, 0, sizeof(idata));
67 idata.write_flag = 1;
68 idata.opcode = MMC_SWITCH;
69 idata.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
70 (index << 16) |
71 (value << 8) |
72 EXT_CSD_CMD_SET_NORMAL;
73 idata.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
74
75 ret = ioctl(fd, MMC_IOC_CMD, &idata);
76 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -080077 perror("ioctl Write EXT CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050078
79 return ret;
80}
81
Ben Gardiner27c357d2013-05-30 17:12:47 -040082int send_status(int fd, __u32 *response)
83{
84 int ret = 0;
85 struct mmc_ioc_cmd idata;
86
87 memset(&idata, 0, sizeof(idata));
88 idata.opcode = MMC_SEND_STATUS;
89 idata.arg = (1 << 16);
90 idata.flags = MMC_RSP_R1 | MMC_CMD_AC;
91
92 ret = ioctl(fd, MMC_IOC_CMD, &idata);
93 if (ret)
94 perror("ioctl");
95
96 *response = idata.response[0];
97
98 return ret;
99}
100
Chris Ballb9c7a172012-02-20 12:34:25 -0500101void print_writeprotect_status(__u8 *ext_csd)
102{
103 __u8 reg;
104 __u8 ext_csd_rev = ext_csd[192];
105
106 /* A43: reserved [174:0] */
107 if (ext_csd_rev >= 5) {
108 printf("Boot write protection status registers"
109 " [BOOT_WP_STATUS]: 0x%02x\n", ext_csd[174]);
110
111 reg = ext_csd[EXT_CSD_BOOT_WP];
112 printf("Boot Area Write protection [BOOT_WP]: 0x%02x\n", reg);
113 printf(" Power ro locking: ");
114 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_DIS)
115 printf("not possible\n");
116 else
117 printf("possible\n");
118
119 printf(" Permanent ro locking: ");
120 if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_DIS)
121 printf("not possible\n");
122 else
123 printf("possible\n");
124
125 printf(" ro lock status: ");
126 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_EN)
127 printf("locked until next power on\n");
128 else if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_EN)
129 printf("locked permanently\n");
130 else
131 printf("not locked\n");
132 }
133}
134
135int do_writeprotect_get(int nargs, char **argv)
136{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800137 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballb9c7a172012-02-20 12:34:25 -0500138 int fd, ret;
139 char *device;
140
Chris Ball8ba44662012-04-19 13:22:54 -0400141 CHECK(nargs != 2, "Usage: mmc writeprotect get </path/to/mmcblkX>\n",
142 exit(1));
Chris Ballb9c7a172012-02-20 12:34:25 -0500143
144 device = argv[1];
145
146 fd = open(device, O_RDWR);
147 if (fd < 0) {
148 perror("open");
149 exit(1);
150 }
151
152 ret = read_extcsd(fd, ext_csd);
153 if (ret) {
154 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
155 exit(1);
156 }
157
158 print_writeprotect_status(ext_csd);
159
160 return ret;
161}
162
163int do_writeprotect_set(int nargs, char **argv)
164{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800165 __u8 ext_csd[EXT_CSD_SIZE], value;
Chris Ballb9c7a172012-02-20 12:34:25 -0500166 int fd, ret;
167 char *device;
168
Chris Ball8ba44662012-04-19 13:22:54 -0400169 CHECK(nargs != 2, "Usage: mmc writeprotect set </path/to/mmcblkX>\n",
170 exit(1));
Chris Ballb9c7a172012-02-20 12:34:25 -0500171
172 device = argv[1];
173
174 fd = open(device, O_RDWR);
175 if (fd < 0) {
176 perror("open");
177 exit(1);
178 }
179
180 ret = read_extcsd(fd, ext_csd);
181 if (ret) {
182 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
183 exit(1);
184 }
185
186 value = ext_csd[EXT_CSD_BOOT_WP] |
187 EXT_CSD_BOOT_WP_B_PWR_WP_EN;
188 ret = write_extcsd_value(fd, EXT_CSD_BOOT_WP, value);
189 if (ret) {
190 fprintf(stderr, "Could not write 0x%02x to "
191 "EXT_CSD[%d] in %s\n",
192 value, EXT_CSD_BOOT_WP, device);
193 exit(1);
194 }
195
196 return ret;
197}
198
Saugata Dasb7e25992012-05-17 09:26:34 -0400199int do_disable_512B_emulation(int nargs, char **argv)
200{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800201 __u8 ext_csd[EXT_CSD_SIZE], native_sector_size, data_sector_size, wr_rel_param;
Saugata Dasb7e25992012-05-17 09:26:34 -0400202 int fd, ret;
203 char *device;
204
205 CHECK(nargs != 2, "Usage: mmc disable 512B emulation </path/to/mmcblkX>\n", exit(1));
206 device = argv[1];
207
208 fd = open(device, O_RDWR);
209 if (fd < 0) {
210 perror("open");
211 exit(1);
212 }
213
214 ret = read_extcsd(fd, ext_csd);
215 if (ret) {
216 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
217 exit(1);
218 }
219
220 wr_rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
221 native_sector_size = ext_csd[EXT_CSD_NATIVE_SECTOR_SIZE];
222 data_sector_size = ext_csd[EXT_CSD_DATA_SECTOR_SIZE];
223
224 if (native_sector_size && !data_sector_size &&
225 (wr_rel_param & EN_REL_WR)) {
226 ret = write_extcsd_value(fd, EXT_CSD_USE_NATIVE_SECTOR, 1);
227
228 if (ret) {
229 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
230 1, EXT_CSD_BOOT_WP, device);
231 exit(1);
232 }
233 printf("MMC disable 512B emulation successful. Now reset the device to switch to 4KB native sector mode.\n");
234 } else if (native_sector_size && data_sector_size) {
235 printf("MMC 512B emulation mode is already disabled; doing nothing.\n");
236 } else {
237 printf("MMC does not support disabling 512B emulation mode.\n");
238 }
239
240 return ret;
241}
242
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200243int do_write_boot_en(int nargs, char **argv)
244{
245 __u8 ext_csd[512];
246 __u8 value = 0;
247 int fd, ret;
248 char *device;
249 int boot_area, send_ack;
250
251 CHECK(nargs != 4, "Usage: mmc bootpart enable <partition_number> "
252 "<send_ack> </path/to/mmcblkX>\n", exit(1));
253
254 /*
255 * If <send_ack> is 1, the device will send acknowledgment
256 * pattern "010" to the host when boot operation begins.
257 * If <send_ack> is 0, it won't.
258 */
259 boot_area = strtol(argv[1], NULL, 10);
260 send_ack = strtol(argv[2], NULL, 10);
261 device = argv[3];
262
263 fd = open(device, O_RDWR);
264 if (fd < 0) {
265 perror("open");
266 exit(1);
267 }
268
269 ret = read_extcsd(fd, ext_csd);
270 if (ret) {
271 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
272 exit(1);
273 }
274
275 value = ext_csd[EXT_CSD_PART_CONFIG];
276
277 switch (boot_area) {
278 case EXT_CSD_PART_CONFIG_ACC_BOOT0:
279 value |= (1 << 3);
280 value &= ~(3 << 4);
281 break;
282 case EXT_CSD_PART_CONFIG_ACC_BOOT1:
283 value |= (1 << 4);
284 value &= ~(1 << 3);
285 value &= ~(1 << 5);
286 break;
287 case EXT_CSD_PART_CONFIG_ACC_USER_AREA:
288 value |= (boot_area << 3);
289 break;
290 default:
291 fprintf(stderr, "Cannot enable the boot area\n");
292 exit(1);
293 }
294 if (send_ack)
295 value |= EXT_CSD_PART_CONFIG_ACC_ACK;
296 else
297 value &= ~EXT_CSD_PART_CONFIG_ACC_ACK;
298
299 ret = write_extcsd_value(fd, EXT_CSD_PART_CONFIG, value);
300 if (ret) {
301 fprintf(stderr, "Could not write 0x%02x to "
302 "EXT_CSD[%d] in %s\n",
303 value, EXT_CSD_PART_CONFIG, device);
304 exit(1);
305 }
306 return ret;
307}
308
Chris Ballf74dfe22012-10-19 16:49:55 -0400309int do_hwreset(int value, int nargs, char **argv)
310{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800311 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballf74dfe22012-10-19 16:49:55 -0400312 int fd, ret;
313 char *device;
314
315 CHECK(nargs != 2, "Usage: mmc hwreset enable </path/to/mmcblkX>\n",
316 exit(1));
317
318 device = argv[1];
319
320 fd = open(device, O_RDWR);
321 if (fd < 0) {
322 perror("open");
323 exit(1);
324 }
325
326 ret = read_extcsd(fd, ext_csd);
327 if (ret) {
328 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
329 exit(1);
330 }
331
332 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
333 EXT_CSD_HW_RESET_EN) {
334 fprintf(stderr,
335 "H/W Reset is already permanently enabled on %s\n",
336 device);
337 exit(1);
338 }
339 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
340 EXT_CSD_HW_RESET_DIS) {
341 fprintf(stderr,
342 "H/W Reset is already permanently disabled on %s\n",
343 device);
344 exit(1);
345 }
346
347 ret = write_extcsd_value(fd, EXT_CSD_RST_N_FUNCTION, value);
348 if (ret) {
349 fprintf(stderr,
350 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
351 value, EXT_CSD_RST_N_FUNCTION, device);
352 exit(1);
353 }
354
355 return ret;
356}
357
358int do_hwreset_en(int nargs, char **argv)
359{
360 return do_hwreset(EXT_CSD_HW_RESET_EN, nargs, argv);
361}
362
363int do_hwreset_dis(int nargs, char **argv)
364{
365 return do_hwreset(EXT_CSD_HW_RESET_DIS, nargs, argv);
366}
367
Jaehoon Chung86496512012-09-21 10:08:05 +0000368int do_write_bkops_en(int nargs, char **argv)
369{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800370 __u8 ext_csd[EXT_CSD_SIZE], value = 0;
Jaehoon Chung86496512012-09-21 10:08:05 +0000371 int fd, ret;
372 char *device;
373
374 CHECK(nargs != 2, "Usage: mmc bkops enable </path/to/mmcblkX>\n",
375 exit(1));
376
377 device = argv[1];
378
379 fd = open(device, O_RDWR);
380 if (fd < 0) {
381 perror("open");
382 exit(1);
383 }
384
385 ret = read_extcsd(fd, ext_csd);
386 if (ret) {
387 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
388 exit(1);
389 }
390
391 if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) {
392 fprintf(stderr, "%s doesn't support BKOPS\n", device);
393 exit(1);
394 }
395
396 ret = write_extcsd_value(fd, EXT_CSD_BKOPS_EN, BKOPS_ENABLE);
397 if (ret) {
398 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
399 value, EXT_CSD_BKOPS_EN, device);
400 exit(1);
401 }
402
403 return ret;
404}
405
Ben Gardiner27c357d2013-05-30 17:12:47 -0400406int do_status_get(int nargs, char **argv)
407{
408 __u32 response;
409 int fd, ret;
410 char *device;
411
412 CHECK(nargs != 2, "Usage: mmc status get </path/to/mmcblkX>\n",
413 exit(1));
414
415 device = argv[1];
416
417 fd = open(device, O_RDWR);
418 if (fd < 0) {
419 perror("open");
420 exit(1);
421 }
422
423 ret = send_status(fd, &response);
424 if (ret) {
425 fprintf(stderr, "Could not read response to SEND_STATUS from %s\n", device);
426 exit(1);
427 }
428
429 printf("SEND_STATUS response: 0x%08x\n", response);
430
431 return ret;
432}
433
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800434__u32 get_word_from_ext_csd(__u8 *ext_csd_loc)
435{
436 return (ext_csd_loc[3] << 24) |
437 (ext_csd_loc[2] << 16) |
438 (ext_csd_loc[1] << 8) |
439 ext_csd_loc[0];
440}
441
Ben Gardiner4e850232013-05-30 17:12:49 -0400442unsigned int get_sector_count(__u8 *ext_csd)
443{
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800444 return get_word_from_ext_csd(&ext_csd[EXT_CSD_SEC_COUNT_0]);
Ben Gardiner4e850232013-05-30 17:12:49 -0400445}
446
447int is_blockaddresed(__u8 *ext_csd)
448{
449 unsigned int sectors = get_sector_count(ext_csd);
450
451 return (sectors > (2u * 1024 * 1024 * 1024) / 512);
452}
453
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400454unsigned int get_hc_wp_grp_size(__u8 *ext_csd)
455{
456 return ext_csd[221];
457}
458
459unsigned int get_hc_erase_grp_size(__u8 *ext_csd)
460{
461 return ext_csd[224];
462}
463
Ben Gardinere6e84e92013-09-19 11:14:27 -0400464int set_partitioning_setting_completed(int dry_run, const char * const device,
465 int fd)
466{
467 int ret;
468
469 if (dry_run) {
470 fprintf(stderr, "NOT setting PARTITION_SETTING_COMPLETED\n");
471 fprintf(stderr, "These changes will not take effect neither "
472 "now nor after a power cycle\n");
473 return 1;
474 }
475
476 fprintf(stderr, "setting OTP PARTITION_SETTING_COMPLETED!\n");
477 ret = write_extcsd_value(fd, EXT_CSD_PARTITION_SETTING_COMPLETED, 0x1);
478 if (ret) {
479 fprintf(stderr, "Could not write 0x1 to "
480 "EXT_CSD[%d] in %s\n",
481 EXT_CSD_PARTITION_SETTING_COMPLETED, device);
482 return 1;
483 }
484
485 __u32 response;
486 ret = send_status(fd, &response);
487 if (ret) {
488 fprintf(stderr, "Could not get response to SEND_STATUS "
489 "from %s\n", device);
490 return 1;
491 }
492
493 if (response & R1_SWITCH_ERROR) {
494 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED "
495 "failed on %s\n", device);
496 return 1;
497 }
498
499 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED on "
500 "%s SUCCESS\n", device);
501 fprintf(stderr, "Device power cycle needed for settings to "
502 "take effect.\n"
503 "Confirm that PARTITION_SETTING_COMPLETED bit is set "
504 "using 'extcsd read' after power cycle\n");
505
506 return 0;
507}
508
Ben Gardinerd91d3692013-05-30 17:12:51 -0400509int do_enh_area_set(int nargs, char **argv)
510{
511 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -0800512 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardinerd91d3692013-05-30 17:12:51 -0400513 int fd, ret;
514 char *device;
515 int dry_run = 1;
516 unsigned int start_kib, length_kib, enh_start_addr, enh_size_mult;
517 unsigned long align;
518
519 CHECK(nargs != 5, "Usage: mmc enh_area set <-y|-n> <start KiB> <length KiB> "
520 "</path/to/mmcblkX>\n", exit(1));
521
522 if (!strcmp("-y", argv[1]))
523 dry_run = 0;
524
525 start_kib = strtol(argv[2], NULL, 10);
526 length_kib = strtol(argv[3], NULL, 10);
527 device = argv[4];
528
529 fd = open(device, O_RDWR);
530 if (fd < 0) {
531 perror("open");
532 exit(1);
533 }
534
535 ret = read_extcsd(fd, ext_csd);
536 if (ret) {
537 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
538 exit(1);
539 }
540
541 /* assert ENH_ATTRIBUTE_EN */
542 if (!(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & EXT_CSD_ENH_ATTRIBUTE_EN))
543 {
544 printf(" Device cannot have enhanced tech.\n");
545 exit(1);
546 }
547
548 /* assert not PARTITION_SETTING_COMPLETED */
549 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
550 {
551 printf(" Device is already partitioned\n");
552 exit(1);
553 }
554
555 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
556
557 enh_size_mult = (length_kib + align/2l) / align;
558
559 enh_start_addr = start_kib * 1024 / (is_blockaddresed(ext_csd) ? 512 : 1);
560 enh_start_addr /= align;
561 enh_start_addr *= align;
562
563 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
564 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
565 if (ret) {
566 fprintf(stderr, "Could not write 0x1 to "
567 "EXT_CSD[%d] in %s\n",
568 EXT_CSD_ERASE_GROUP_DEF, device);
569 exit(1);
570 }
571
572 /* write to ENH_START_ADDR and ENH_SIZE_MULT and PARTITIONS_ATTRIBUTE's ENH_USR bit */
573 value = (enh_start_addr >> 24) & 0xff;
574 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_3, value);
575 if (ret) {
576 fprintf(stderr, "Could not write 0x%02x to "
577 "EXT_CSD[%d] in %s\n", value,
578 EXT_CSD_ENH_START_ADDR_3, device);
579 exit(1);
580 }
581 value = (enh_start_addr >> 16) & 0xff;
582 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_2, value);
583 if (ret) {
584 fprintf(stderr, "Could not write 0x%02x to "
585 "EXT_CSD[%d] in %s\n", value,
586 EXT_CSD_ENH_START_ADDR_2, device);
587 exit(1);
588 }
589 value = (enh_start_addr >> 8) & 0xff;
590 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_1, value);
591 if (ret) {
592 fprintf(stderr, "Could not write 0x%02x to "
593 "EXT_CSD[%d] in %s\n", value,
594 EXT_CSD_ENH_START_ADDR_1, device);
595 exit(1);
596 }
597 value = enh_start_addr & 0xff;
598 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_0, value);
599 if (ret) {
600 fprintf(stderr, "Could not write 0x%02x to "
601 "EXT_CSD[%d] in %s\n", value,
602 EXT_CSD_ENH_START_ADDR_0, device);
603 exit(1);
604 }
605
606 value = (enh_size_mult >> 16) & 0xff;
607 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_2, value);
608 if (ret) {
609 fprintf(stderr, "Could not write 0x%02x to "
610 "EXT_CSD[%d] in %s\n", value,
611 EXT_CSD_ENH_SIZE_MULT_2, device);
612 exit(1);
613 }
614 value = (enh_size_mult >> 8) & 0xff;
615 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_1, value);
616 if (ret) {
617 fprintf(stderr, "Could not write 0x%02x to "
618 "EXT_CSD[%d] in %s\n", value,
619 EXT_CSD_ENH_SIZE_MULT_1, device);
620 exit(1);
621 }
622 value = enh_size_mult & 0xff;
623 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_0, value);
624 if (ret) {
625 fprintf(stderr, "Could not write 0x%02x to "
626 "EXT_CSD[%d] in %s\n", value,
627 EXT_CSD_ENH_SIZE_MULT_0, device);
628 exit(1);
629 }
630
631 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, EXT_CSD_ENH_USR);
632 if (ret) {
633 fprintf(stderr, "Could not write EXT_CSD_ENH_USR to "
634 "EXT_CSD[%d] in %s\n",
635 EXT_CSD_PARTITIONS_ATTRIBUTE, device);
636 exit(1);
637 }
638
Ben Gardinere6e84e92013-09-19 11:14:27 -0400639 printf("Done setting ENH_USR area on %s\n", device);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400640
Ben Gardinere6e84e92013-09-19 11:14:27 -0400641 if (!set_partitioning_setting_completed(dry_run, device, fd))
Ben Gardinerd91d3692013-05-30 17:12:51 -0400642 exit(1);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400643
644 return 0;
645}
646
Ben Gardiner196d0d22013-09-19 11:14:29 -0400647int do_write_reliability_set(int nargs, char **argv)
648{
649 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -0800650 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardiner196d0d22013-09-19 11:14:29 -0400651 int fd, ret;
652
653 int dry_run = 1;
654 int partition;
655 char *device;
656
657 CHECK(nargs != 4, "Usage: mmc write_reliability set <-y|-n> "
658 "<partition> </path/to/mmcblkX>\n", exit(1));
659
660 if (!strcmp("-y", argv[1]))
661 dry_run = 0;
662
663 partition = strtol(argv[2], NULL, 10);
664 device = argv[3];
665
666 fd = open(device, O_RDWR);
667 if (fd < 0) {
668 perror("open");
669 exit(1);
670 }
671
672 ret = read_extcsd(fd, ext_csd);
673 if (ret) {
674 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
675 exit(1);
676 }
677
678 /* assert not PARTITION_SETTING_COMPLETED */
679 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
680 {
681 printf(" Device is already partitioned\n");
682 exit(1);
683 }
684
685 /* assert HS_CTRL_REL */
686 if (!(ext_csd[EXT_CSD_WR_REL_PARAM] & HS_CTRL_REL)) {
687 printf("Cannot set write reliability parameters, WR_REL_SET is "
688 "read-only\n");
689 exit(1);
690 }
691
692 value = ext_csd[EXT_CSD_WR_REL_SET] | (1<<partition);
693 ret = write_extcsd_value(fd, EXT_CSD_WR_REL_SET, value);
694 if (ret) {
695 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
696 value, EXT_CSD_WR_REL_SET, device);
697 exit(1);
698 }
699
700 printf("Done setting EXT_CSD_WR_REL_SET to 0x%02x on %s\n",
701 value, device);
702
703 if (!set_partitioning_setting_completed(dry_run, device, fd))
704 exit(1);
705
706 return 0;
707}
708
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500709int do_read_extcsd(int nargs, char **argv)
710{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800711 __u8 ext_csd[EXT_CSD_SIZE], ext_csd_rev, reg;
Oliver Metz11f2cea2013-09-23 08:40:52 +0200712 __u32 regl;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500713 int fd, ret;
714 char *device;
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100715 const char *str;
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800716 const char *ver_str[] = {
717 "4.0", /* 0 */
718 "4.1", /* 1 */
719 "4.2", /* 2 */
720 "4.3", /* 3 */
721 "Obsolete", /* 4 */
722 "4.41", /* 5 */
723 "4.5", /* 6 */
724 "5.0", /* 7 */
725 };
726 int boot_access;
727 const char* boot_access_str[] = {
728 "No access to boot partition", /* 0 */
729 "R/W Boot Partition 1", /* 1 */
730 "R/W Boot Partition 2", /* 2 */
731 "R/W Replay Protected Memory Block (RPMB)", /* 3 */
732 };
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500733
Chris Ball8ba44662012-04-19 13:22:54 -0400734 CHECK(nargs != 2, "Usage: mmc extcsd read </path/to/mmcblkX>\n",
735 exit(1));
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500736
737 device = argv[1];
738
739 fd = open(device, O_RDWR);
740 if (fd < 0) {
741 perror("open");
742 exit(1);
743 }
744
745 ret = read_extcsd(fd, ext_csd);
746 if (ret) {
747 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
748 exit(1);
749 }
750
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100751 ext_csd_rev = ext_csd[192];
752
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800753 if ((ext_csd_rev < sizeof(ver_str)/sizeof(char*)) &&
754 (ext_csd_rev != 4))
755 str = ver_str[ext_csd_rev];
756 else
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100757 goto out_free;
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800758
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100759 printf("=============================================\n");
760 printf(" Extended CSD rev 1.%d (MMC %s)\n", ext_csd_rev, str);
761 printf("=============================================\n\n");
762
763 if (ext_csd_rev < 3)
764 goto out_free; /* No ext_csd */
765
766 /* Parse the Extended CSD registers.
767 * Reserved bit should be read as "0" in case of spec older
768 * than A441.
769 */
770 reg = ext_csd[EXT_CSD_S_CMD_SET];
771 printf("Card Supported Command sets [S_CMD_SET: 0x%02x]\n", reg);
772 if (!reg)
Chris Ballb9c7a172012-02-20 12:34:25 -0500773 printf(" - Standard MMC command sets\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100774
775 reg = ext_csd[EXT_CSD_HPI_FEATURE];
776 printf("HPI Features [HPI_FEATURE: 0x%02x]: ", reg);
777 if (reg & EXT_CSD_HPI_SUPP) {
778 if (reg & EXT_CSD_HPI_IMPL)
Chris Ballb9c7a172012-02-20 12:34:25 -0500779 printf("implementation based on CMD12\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100780 else
781 printf("implementation based on CMD13\n");
782 }
783
784 printf("Background operations support [BKOPS_SUPPORT: 0x%02x]\n",
785 ext_csd[502]);
786
787 if (ext_csd_rev >= 6) {
788 printf("Max Packet Read Cmd [MAX_PACKED_READS: 0x%02x]\n",
789 ext_csd[501]);
790 printf("Max Packet Write Cmd [MAX_PACKED_WRITES: 0x%02x]\n",
791 ext_csd[500]);
792 printf("Data TAG support [DATA_TAG_SUPPORT: 0x%02x]\n",
793 ext_csd[499]);
794
795 printf("Data TAG Unit Size [TAG_UNIT_SIZE: 0x%02x]\n",
796 ext_csd[498]);
797 printf("Tag Resources Size [TAG_RES_SIZE: 0x%02x]\n",
798 ext_csd[497]);
799 printf("Context Management Capabilities"
800 " [CONTEXT_CAPABILITIES: 0x%02x]\n", ext_csd[496]);
801 printf("Large Unit Size [LARGE_UNIT_SIZE_M1: 0x%02x]\n",
802 ext_csd[495]);
803 printf("Extended partition attribute support"
804 " [EXT_SUPPORT: 0x%02x]\n", ext_csd[494]);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800805 }
806 if (ext_csd_rev >= 7) {
807 int j;
808 int eol_info;
809 char* eol_info_str[] = {
810 "Not Defined", /* 0 */
811 "Normal", /* 1 */
812 "Warning", /* 2 */
813 "Urgent", /* 3 */
814 };
815
816 printf("Supported modes [SUPPORTED_MODES: 0x%02x]\n",
817 ext_csd[493]);
818 printf("FFU features [FFU_FEATURES: 0x%02x]\n",
819 ext_csd[492]);
820 printf("Operation codes timeout"
821 " [OPERATION_CODE_TIMEOUT: 0x%02x]\n",
822 ext_csd[491]);
823 printf("FFU Argument [FFU_ARG: 0x%08x]\n",
824 get_word_from_ext_csd(&ext_csd[487]));
825 printf("Number of FW sectors correctly programmed"
826 " [NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED: %d]\n",
827 get_word_from_ext_csd(&ext_csd[302]));
828 printf("Vendor proprietary health report:\n");
829 for (j = 301; j >= 270; j--)
830 printf("[VENDOR_PROPRIETARY_HEALTH_REPORT[%d]]:"
831 " 0x%02x\n", j, ext_csd[j]);
832 for (j = 269; j >= 268; j--) {
833 __u8 life_used=ext_csd[j];
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -0800834 char est_type = 'B' + (j - 269);
835 printf("Device life time estimation type %c"
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800836 " [DEVICE_LIFE_TIME_EST_TYP_%c: 0x%02x]\n",
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -0800837 est_type, est_type, life_used);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800838 if (life_used >= 0x1 && life_used <= 0xa)
839 printf(" i.e. %d%% - %d%% device life time"
840 " used\n",
841 (life_used - 1) * 10, life_used * 10);
842 else if (life_used == 0xb)
843 printf(" i.e. Exceeded its maximum estimated"
844 " device life time\n");
845 }
846 eol_info = ext_csd[267];
847 printf("Pre EOL information [PRE_EOL_INFO: 0x%02x]\n",
848 eol_info);
849 if (eol_info < sizeof(eol_info_str)/sizeof(char*))
850 printf(" i.e. %s\n", eol_info_str[eol_info]);
851 else
852 printf(" i.e. Reserved\n");
853
854 printf("Optimal read size [OPTIMAL_READ_SIZE: 0x%02x]\n",
855 ext_csd[266]);
856 printf("Optimal write size [OPTIMAL_WRITE_SIZE: 0x%02x]\n",
857 ext_csd[265]);
858 printf("Optimal trim unit size"
859 " [OPTIMAL_TRIM_UNIT_SIZE: 0x%02x]\n", ext_csd[264]);
860 printf("Device version [DEVICE_VERSION: 0x%02x - 0x%02x]\n",
861 ext_csd[263], ext_csd[262]);
862 printf("Firmware version:\n");
863 for (j = 261; j >= 254; j--)
864 printf("[FIRMWARE_VERSION[%d]]:"
865 " 0x%02x\n", j, ext_csd[j]);
866
867 printf("Power class for 200MHz, DDR at VCC= 3.6V"
868 " [PWR_CL_DDR_200_360: 0x%02x]\n", ext_csd[253]);
869 }
870 if (ext_csd_rev >= 6) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100871 printf("Generic CMD6 Timer [GENERIC_CMD6_TIME: 0x%02x]\n",
872 ext_csd[248]);
873 printf("Power off notification [POWER_OFF_LONG_TIME: 0x%02x]\n",
874 ext_csd[247]);
875 printf("Cache Size [CACHE_SIZE] is %d KiB\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800876 get_word_from_ext_csd(&ext_csd[249]));
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100877 }
878
879 /* A441: Reserved [501:247]
880 A43: reserved [246:229] */
881 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100882 printf("Background operations status"
Chris Ballb9c7a172012-02-20 12:34:25 -0500883 " [BKOPS_STATUS: 0x%02x]\n", ext_csd[246]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100884
885 /* CORRECTLY_PRG_SECTORS_NUM [245:242] TODO */
886
887 printf("1st Initialisation Time after programmed sector"
888 " [INI_TIMEOUT_AP: 0x%02x]\n", ext_csd[241]);
889
890 /* A441: reserved [240] */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100891 printf("Power class for 52MHz, DDR at 3.6V"
892 " [PWR_CL_DDR_52_360: 0x%02x]\n", ext_csd[239]);
893 printf("Power class for 52MHz, DDR at 1.95V"
894 " [PWR_CL_DDR_52_195: 0x%02x]\n", ext_csd[238]);
895
896 /* A441: reserved [237-236] */
897
898 if (ext_csd_rev >= 6) {
899 printf("Power class for 200MHz at 3.6V"
900 " [PWR_CL_200_360: 0x%02x]\n", ext_csd[237]);
901 printf("Power class for 200MHz, at 1.95V"
902 " [PWR_CL_200_195: 0x%02x]\n", ext_csd[236]);
903 }
Chris Ballb9c7a172012-02-20 12:34:25 -0500904 printf("Minimum Performance for 8bit at 52MHz in DDR mode:\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100905 printf(" [MIN_PERF_DDR_W_8_52: 0x%02x]\n", ext_csd[235]);
906 printf(" [MIN_PERF_DDR_R_8_52: 0x%02x]\n", ext_csd[234]);
907 /* A441: reserved [233] */
908 printf("TRIM Multiplier [TRIM_MULT: 0x%02x]\n", ext_csd[232]);
909 printf("Secure Feature support [SEC_FEATURE_SUPPORT: 0x%02x]\n",
910 ext_csd[231]);
911 }
912 if (ext_csd_rev == 5) { /* Obsolete in 4.5 */
913 printf("Secure Erase Multiplier [SEC_ERASE_MULT: 0x%02x]\n",
914 ext_csd[230]);
915 printf("Secure TRIM Multiplier [SEC_TRIM_MULT: 0x%02x]\n",
916 ext_csd[229]);
917 }
918 reg = ext_csd[EXT_CSD_BOOT_INFO];
919 printf("Boot Information [BOOT_INFO: 0x%02x]\n", reg);
920 if (reg & EXT_CSD_BOOT_INFO_ALT)
921 printf(" Device supports alternative boot method\n");
922 if (reg & EXT_CSD_BOOT_INFO_DDR_DDR)
923 printf(" Device supports dual data rate during boot\n");
924 if (reg & EXT_CSD_BOOT_INFO_HS_MODE)
925 printf(" Device supports high speed timing during boot\n");
926
927 /* A441/A43: reserved [227] */
928 printf("Boot partition size [BOOT_SIZE_MULTI: 0x%02x]\n", ext_csd[226]);
929 printf("Access size [ACC_SIZE: 0x%02x]\n", ext_csd[225]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400930
931 reg = get_hc_erase_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100932 printf("High-capacity erase unit size [HC_ERASE_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400933 reg);
934 printf(" i.e. %u KiB\n", 512 * reg);
935
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100936 printf("High-capacity erase timeout [ERASE_TIMEOUT_MULT: 0x%02x]\n",
937 ext_csd[223]);
938 printf("Reliable write sector count [REL_WR_SEC_C: 0x%02x]\n",
939 ext_csd[222]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400940
941 reg = get_hc_wp_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100942 printf("High-capacity W protect group size [HC_WP_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400943 reg);
944 printf(" i.e. %lu KiB\n", 512l * get_hc_erase_grp_size(ext_csd) * reg);
945
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100946 printf("Sleep current (VCC) [S_C_VCC: 0x%02x]\n", ext_csd[220]);
947 printf("Sleep current (VCCQ) [S_C_VCCQ: 0x%02x]\n", ext_csd[219]);
948 /* A441/A43: reserved [218] */
949 printf("Sleep/awake timeout [S_A_TIMEOUT: 0x%02x]\n", ext_csd[217]);
950 /* A441/A43: reserved [216] */
Ben Gardiner4e850232013-05-30 17:12:49 -0400951
952 unsigned int sectors = get_sector_count(ext_csd);
953 printf("Sector Count [SEC_COUNT: 0x%08x]\n", sectors);
954 if (is_blockaddresed(ext_csd))
955 printf(" Device is block-addressed\n");
956 else
957 printf(" Device is NOT block-addressed\n");
958
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100959 /* A441/A43: reserved [211] */
960 printf("Minimum Write Performance for 8bit:\n");
961 printf(" [MIN_PERF_W_8_52: 0x%02x]\n", ext_csd[210]);
962 printf(" [MIN_PERF_R_8_52: 0x%02x]\n", ext_csd[209]);
963 printf(" [MIN_PERF_W_8_26_4_52: 0x%02x]\n", ext_csd[208]);
964 printf(" [MIN_PERF_R_8_26_4_52: 0x%02x]\n", ext_csd[207]);
965 printf("Minimum Write Performance for 4bit:\n");
966 printf(" [MIN_PERF_W_4_26: 0x%02x]\n", ext_csd[206]);
967 printf(" [MIN_PERF_R_4_26: 0x%02x]\n", ext_csd[205]);
968 /* A441/A43: reserved [204] */
969 printf("Power classes registers:\n");
970 printf(" [PWR_CL_26_360: 0x%02x]\n", ext_csd[203]);
971 printf(" [PWR_CL_52_360: 0x%02x]\n", ext_csd[202]);
972 printf(" [PWR_CL_26_195: 0x%02x]\n", ext_csd[201]);
973 printf(" [PWR_CL_52_195: 0x%02x]\n", ext_csd[200]);
974
975 /* A43: reserved [199:198] */
976 if (ext_csd_rev >= 5) {
977 printf("Partition switching timing "
978 "[PARTITION_SWITCH_TIME: 0x%02x]\n", ext_csd[199]);
979 printf("Out-of-interrupt busy timing"
980 " [OUT_OF_INTERRUPT_TIME: 0x%02x]\n", ext_csd[198]);
981 }
982
983 /* A441/A43: reserved [197] [195] [193] [190] [188]
984 * [186] [184] [182] [180] [176] */
985
986 if (ext_csd_rev >= 6)
987 printf("I/O Driver Strength [DRIVER_STRENGTH: 0x%02x]\n",
988 ext_csd[197]);
989
Oleg Matcovschi64f63a32013-05-23 17:11:07 -0700990 /* DEVICE_TYPE in A45, CARD_TYPE in A441 */
991 reg = ext_csd[196];
992 printf("Card Type [CARD_TYPE: 0x%02x]\n", reg);
993 if (reg & 0x20) printf(" HS200 Single Data Rate eMMC @200MHz 1.2VI/O\n");
994 if (reg & 0x10) printf(" HS200 Single Data Rate eMMC @200MHz 1.8VI/O\n");
995 if (reg & 0x08) printf(" HS Dual Data Rate eMMC @52MHz 1.2VI/O\n");
996 if (reg & 0x04) printf(" HS Dual Data Rate eMMC @52MHz 1.8V or 3VI/O\n");
997 if (reg & 0x02) printf(" HS eMMC @52MHz - at rated device voltage(s)\n");
998 if (reg & 0x01) printf(" HS eMMC @26MHz - at rated device voltage(s)\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100999
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001000 printf("CSD structure version [CSD_STRUCTURE: 0x%02x]\n", ext_csd[194]);
1001 /* ext_csd_rev = ext_csd[192] (already done!!!) */
1002 printf("Command set [CMD_SET: 0x%02x]\n", ext_csd[191]);
1003 printf("Command set revision [CMD_SET_REV: 0x%02x]\n", ext_csd[189]);
1004 printf("Power class [POWER_CLASS: 0x%02x]\n", ext_csd[187]);
1005 printf("High-speed interface timing [HS_TIMING: 0x%02x]\n",
1006 ext_csd[185]);
1007 /* bus_width: ext_csd[183] not readable */
1008 printf("Erased memory content [ERASED_MEM_CONT: 0x%02x]\n",
1009 ext_csd[181]);
1010 reg = ext_csd[EXT_CSD_BOOT_CFG];
1011 printf("Boot configuration bytes [PARTITION_CONFIG: 0x%02x]\n", reg);
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001012 switch ((reg & EXT_CSD_BOOT_CFG_EN)>>3) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001013 case 0x0:
1014 printf(" Not boot enable\n");
1015 break;
1016 case 0x1:
1017 printf(" Boot Partition 1 enabled\n");
1018 break;
1019 case 0x2:
1020 printf(" Boot Partition 2 enabled\n");
1021 break;
1022 case 0x7:
1023 printf(" User Area Enabled for boot\n");
1024 break;
1025 }
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001026 boot_access = reg & EXT_CSD_BOOT_CFG_ACC;
1027 if (boot_access < sizeof(boot_access_str) / sizeof(char*))
1028 printf(" %s\n", boot_access_str[boot_access]);
1029 else
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001030 printf(" Access to General Purpose partition %d\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001031 boot_access - 3);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001032
1033 printf("Boot config protection [BOOT_CONFIG_PROT: 0x%02x]\n",
1034 ext_csd[178]);
1035 printf("Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x%02x]\n",
1036 ext_csd[177]);
1037 printf("High-density erase group definition"
Ben Gardinerd91d3692013-05-30 17:12:51 -04001038 " [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[EXT_CSD_ERASE_GROUP_DEF]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001039
Chris Ballb9c7a172012-02-20 12:34:25 -05001040 print_writeprotect_status(ext_csd);
1041
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001042 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001043 /* A441]: reserved [172] */
1044 printf("User area write protection register"
1045 " [USER_WP]: 0x%02x\n", ext_csd[171]);
1046 /* A441]: reserved [170] */
1047 printf("FW configuration [FW_CONFIG]: 0x%02x\n", ext_csd[169]);
1048 printf("RPMB Size [RPMB_SIZE_MULT]: 0x%02x\n", ext_csd[168]);
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001049
1050 reg = ext_csd[EXT_CSD_WR_REL_SET];
1051 const char * const fast = "existing data is at risk if a power "
1052 "failure occurs during a write operation";
1053 const char * const reliable = "the device protects existing "
1054 "data if a power failure occurs during a write "
1055 "operation";
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001056 printf("Write reliability setting register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001057 " [WR_REL_SET]: 0x%02x\n", reg);
1058
1059 printf(" user area: %s\n", reg & (1<<0) ? reliable : fast);
1060 int i;
1061 for (i = 1; i <= 4; i++) {
1062 printf(" partition %d: %s\n", i,
1063 reg & (1<<i) ? reliable : fast);
1064 }
1065
1066 reg = ext_csd[EXT_CSD_WR_REL_PARAM];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001067 printf("Write reliability parameter register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001068 " [WR_REL_PARAM]: 0x%02x\n", reg);
1069 if (reg & 0x01)
1070 printf(" Device supports writing EXT_CSD_WR_REL_SET\n");
1071 if (reg & 0x04)
1072 printf(" Device supports the enhanced def. of reliable "
1073 "write\n");
1074
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001075 /* sanitize_start ext_csd[165]]: not readable
1076 * bkops_start ext_csd[164]]: only writable */
1077 printf("Enable background operations handshake"
1078 " [BKOPS_EN]: 0x%02x\n", ext_csd[163]);
1079 printf("H/W reset function"
1080 " [RST_N_FUNCTION]: 0x%02x\n", ext_csd[162]);
1081 printf("HPI management [HPI_MGMT]: 0x%02x\n", ext_csd[161]);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001082 reg = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001083 printf("Partitioning Support [PARTITIONING_SUPPORT]: 0x%02x\n",
1084 reg);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001085 if (reg & EXT_CSD_PARTITIONING_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001086 printf(" Device support partitioning feature\n");
1087 else
1088 printf(" Device NOT support partitioning feature\n");
Ben Gardiner82bd9502013-06-27 11:04:10 -04001089 if (reg & EXT_CSD_ENH_ATTRIBUTE_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001090 printf(" Device can have enhanced tech.\n");
1091 else
1092 printf(" Device cannot have enhanced tech.\n");
1093
Oliver Metz11f2cea2013-09-23 08:40:52 +02001094 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
Oliver Metz22f26412013-09-23 08:40:51 +02001095 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
1096 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
1097
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001098 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n",
Oliver Metz11f2cea2013-09-23 08:40:52 +02001099 regl);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001100 unsigned int wp_sz = get_hc_wp_grp_size(ext_csd);
1101 unsigned int erase_sz = get_hc_erase_grp_size(ext_csd);
Oliver Metz11f2cea2013-09-23 08:40:52 +02001102 printf(" i.e. %lu KiB\n", 512l * regl * wp_sz * erase_sz);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001103
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001104 printf("Partitions attribute [PARTITIONS_ATTRIBUTE]: 0x%02x\n",
Ben Gardinerd91d3692013-05-30 17:12:51 -04001105 ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]);
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001106 reg = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001107 printf("Partitioning Setting"
1108 " [PARTITION_SETTING_COMPLETED]: 0x%02x\n",
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001109 reg);
1110 if (reg)
1111 printf(" Device partition setting complete\n");
1112 else
1113 printf(" Device partition setting NOT complete\n");
1114
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001115 printf("General Purpose Partition Size\n"
1116 " [GP_SIZE_MULT_4]: 0x%06x\n", (ext_csd[154] << 16) |
1117 (ext_csd[153] << 8) | ext_csd[152]);
1118 printf(" [GP_SIZE_MULT_3]: 0x%06x\n", (ext_csd[151] << 16) |
1119 (ext_csd[150] << 8) | ext_csd[149]);
1120 printf(" [GP_SIZE_MULT_2]: 0x%06x\n", (ext_csd[148] << 16) |
1121 (ext_csd[147] << 8) | ext_csd[146]);
1122 printf(" [GP_SIZE_MULT_1]: 0x%06x\n", (ext_csd[145] << 16) |
1123 (ext_csd[144] << 8) | ext_csd[143]);
1124
Oliver Metz11f2cea2013-09-23 08:40:52 +02001125 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001126 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
1127 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001128 printf("Enhanced User Data Area Size"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001129 " [ENH_SIZE_MULT]: 0x%06x\n", regl);
1130 printf(" i.e. %lu KiB\n", 512l * regl *
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001131 get_hc_erase_grp_size(ext_csd) *
1132 get_hc_wp_grp_size(ext_csd));
Ben Gardiner68f490b2013-05-30 17:12:48 -04001133
Oliver Metz11f2cea2013-09-23 08:40:52 +02001134 regl = (ext_csd[EXT_CSD_ENH_START_ADDR_3] << 24) |
Ben Gardiner68f490b2013-05-30 17:12:48 -04001135 (ext_csd[EXT_CSD_ENH_START_ADDR_2] << 16) |
1136 (ext_csd[EXT_CSD_ENH_START_ADDR_1] << 8) |
1137 ext_csd[EXT_CSD_ENH_START_ADDR_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001138 printf("Enhanced User Data Start Address"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001139 " [ENH_START_ADDR]: 0x%06x\n", regl);
Ben Gardiner4e850232013-05-30 17:12:49 -04001140 printf(" i.e. %lu bytes offset\n", (is_blockaddresed(ext_csd) ?
Oliver Metz11f2cea2013-09-23 08:40:52 +02001141 1l : 512l) * regl);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001142
1143 /* A441]: reserved [135] */
1144 printf("Bad Block Management mode"
1145 " [SEC_BAD_BLK_MGMNT]: 0x%02x\n", ext_csd[134]);
1146 /* A441: reserved [133:0] */
1147 }
1148 /* B45 */
1149 if (ext_csd_rev >= 6) {
1150 int j;
1151 /* tcase_support ext_csd[132] not readable */
1152 printf("Periodic Wake-up [PERIODIC_WAKEUP]: 0x%02x\n",
1153 ext_csd[131]);
1154 printf("Program CID/CSD in DDR mode support"
1155 " [PROGRAM_CID_CSD_DDR_SUPPORT]: 0x%02x\n",
1156 ext_csd[130]);
1157
1158 for (j = 127; j >= 64; j--)
1159 printf("Vendor Specific Fields"
1160 " [VENDOR_SPECIFIC_FIELD[%d]]: 0x%02x\n",
1161 j, ext_csd[j]);
1162
Gwendal Grignoue966e672014-07-07 14:03:13 -07001163 reg = ext_csd[63];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001164 printf("Native sector size [NATIVE_SECTOR_SIZE]: 0x%02x\n",
Gwendal Grignoue966e672014-07-07 14:03:13 -07001165 reg);
1166 if (reg == 0x00)
1167 printf(" i.e. 512 B\n");
1168 else if (reg == 0x01)
1169 printf(" i.e. 4 KiB\n");
1170 else
1171 printf(" i.e. Reserved\n");
1172
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001173 printf("Sector size emulation [USE_NATIVE_SECTOR]: 0x%02x\n",
1174 ext_csd[62]);
Gwendal Grignoue966e672014-07-07 14:03:13 -07001175 reg = ext_csd[61];
1176 printf("Sector size [DATA_SECTOR_SIZE]: 0x%02x\n", reg);
1177 if (reg == 0x00)
1178 printf(" i.e. 512 B\n");
1179 else if (reg == 0x01)
1180 printf(" i.e. 4 KiB\n");
1181 else
1182 printf(" i.e. Reserved\n");
1183
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001184 printf("1st initialization after disabling sector"
1185 " size emulation [INI_TIMEOUT_EMU]: 0x%02x\n",
1186 ext_csd[60]);
1187 printf("Class 6 commands control [CLASS_6_CTRL]: 0x%02x\n",
1188 ext_csd[59]);
1189 printf("Number of addressed group to be Released"
1190 "[DYNCAP_NEEDED]: 0x%02x\n", ext_csd[58]);
1191 printf("Exception events control"
1192 " [EXCEPTION_EVENTS_CTRL]: 0x%04x\n",
1193 (ext_csd[57] << 8) | ext_csd[56]);
1194 printf("Exception events status"
1195 "[EXCEPTION_EVENTS_STATUS]: 0x%04x\n",
1196 (ext_csd[55] << 8) | ext_csd[54]);
1197 printf("Extended Partitions Attribute"
1198 " [EXT_PARTITIONS_ATTRIBUTE]: 0x%04x\n",
1199 (ext_csd[53] << 8) | ext_csd[52]);
1200
1201 for (j = 51; j >= 37; j--)
1202 printf("Context configuration"
1203 " [CONTEXT_CONF[%d]]: 0x%02x\n", j, ext_csd[j]);
1204
1205 printf("Packed command status"
1206 " [PACKED_COMMAND_STATUS]: 0x%02x\n", ext_csd[36]);
1207 printf("Packed command failure index"
1208 " [PACKED_FAILURE_INDEX]: 0x%02x\n", ext_csd[35]);
1209 printf("Power Off Notification"
1210 " [POWER_OFF_NOTIFICATION]: 0x%02x\n", ext_csd[34]);
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001211 printf("Control to turn the Cache ON/OFF"
1212 " [CACHE_CTRL]: 0x%02x\n", ext_csd[33]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001213 /* flush_cache ext_csd[32] not readable */
1214 /*Reserved [31:0] */
1215 }
Gwendal Grignoue966e672014-07-07 14:03:13 -07001216 if (ext_csd_rev >= 7) {
1217 printf("Mode config [MODE_CONFIG: 0x%02x]\n", ext_csd[30]);
1218 printf("Mode operation codes [MODE_OPERATION_CODES: 0x%02x]\n",
1219 ext_csd[29]);
1220
1221 reg = ext_csd[26];
1222 printf("FFU status [FFU_STATUS: 0x%02x]\n", reg);
1223 switch (reg) {
1224 case 0x00:
1225 printf(" Success\n");
1226 break;
1227 case 0x10:
1228 printf(" General error\n");
1229 break;
1230 case 0x11:
1231 printf(" Firmware install error\n");
1232 break;
1233 case 0x12:
1234 printf(" Error in downloading firmware\n");
1235 break;
1236 default:
1237 printf(" Reserved\n");
1238 }
1239 printf("Pre loading data size [PRE_LOADING_DATA_SIZE] is"
1240 " %d sector size\n",
1241 get_word_from_ext_csd(&ext_csd[22]));
1242 printf("Max pre loading data size [MAX_PRE_LOADING_DATA_SIZE] is"
1243 " %d sector size\n",
1244 get_word_from_ext_csd(&ext_csd[18]));
1245 printf("Product state awareness enablement"
1246 " [PRODUCT_STATE_AWARENESS_ENABLEMENT: 0x%02x]\n",
1247 ext_csd[17]);
1248 printf("Secure Removal Type [SECURE_REMOVAL_TYPE: 0x%02x]\n",
1249 ext_csd[16]);
1250 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001251
1252out_free:
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001253 return ret;
1254}
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001255
Nick Sanders9d57aa72014-03-05 21:38:54 -08001256int do_dump_extcsd(int nargs, char **argv)
1257{
1258 __u8 ext_csd[EXT_CSD_SIZE];
1259 int fd, ret;
1260 char *device;
1261 int i, j;
1262
1263 CHECK(nargs != 2, "Usage: mmc extcsd dump </path/to/mmcblkX>\n",
1264 exit(1));
1265
1266 device = argv[1];
1267
1268 fd = open(device, O_RDWR);
1269 if (fd < 0) {
1270 perror("Failed to open mmc device");
1271 exit(1);
1272 }
1273
1274 ret = read_extcsd(fd, ext_csd);
1275 if (ret) {
1276 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1277 exit(1);
1278 }
1279
1280 /* Dump all bytes so that any undecoded or proprietary registers */
1281 /* can be acessed. */
1282 printf("EXT_CSD binary dump:\n");
1283 for (i = 0; i < EXT_CSD_SIZE; i+= 16) {
1284 printf(" %3d: %3x: ", i, i);
1285 for (j = 0; (j < 16) && (i + j < EXT_CSD_SIZE); j++) {
1286 printf(" %02x", ext_csd[i+j]);
1287 }
1288 printf("\n");
1289 }
1290
1291 return ret;
1292}
1293
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001294int do_sanitize(int nargs, char **argv)
1295{
1296 int fd, ret;
1297 char *device;
1298
1299 CHECK(nargs != 2, "Usage: mmc sanitize </path/to/mmcblkX>\n",
1300 exit(1));
1301
1302 device = argv[1];
1303
1304 fd = open(device, O_RDWR);
1305 if (fd < 0) {
1306 perror("open");
1307 exit(1);
1308 }
1309
1310 ret = write_extcsd_value(fd, EXT_CSD_SANITIZE_START, 1);
1311 if (ret) {
1312 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1313 1, EXT_CSD_SANITIZE_START, device);
1314 exit(1);
1315 }
1316
1317 return ret;
1318
1319}
1320
Gwendal Grignou771984c2014-07-01 12:46:18 -07001321int do_emmc50_ffu (int nargs, char **argv)
1322{
1323 int fd, ret;
1324 char *device;
1325 char *path;
Gwendal Grignou0f757342014-10-16 16:52:46 -07001326 struct mmc_ioc_cmd mmc_ioc_cmd;
Gwendal Grignou771984c2014-07-01 12:46:18 -07001327
1328 CHECK(nargs != 3, "Usage: ffu <image name> </path/to/mmcblkX> \n",
1329 exit(1));
1330
1331 path = argv[1];
1332 if (strlen(path) > FFU_PATH_SIZE) {
1333 fprintf(stderr, "Filename \"%.20s\" too long\n", path);
1334 exit(1);
1335 }
1336 device = argv[2];
1337 fd = open(device, O_RDWR);
1338 if (fd < 0) {
1339 perror("open");
1340 exit(1);
1341 }
1342
Gwendal Grignou0f757342014-10-16 16:52:46 -07001343 /* prepare and send ioctl */
1344 memset(&mmc_ioc_cmd, 0, sizeof(mmc_ioc_cmd));
1345 mmc_ioc_cmd.opcode = MMC_FFU_INVOKE_OP;
1346 mmc_ioc_cmd.blksz = MIN(strlen(path), FFU_PATH_SIZE);
1347 mmc_ioc_cmd.blocks = 1;
1348 mmc_ioc_cmd.arg = 0;
1349 mmc_ioc_cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
1350 mmc_ioc_cmd.write_flag = 1;
1351 mmc_ioc_cmd_set_data(mmc_ioc_cmd, path);
1352 ret = ioctl(fd, MMC_IOC_CMD, &mmc_ioc_cmd);
Gwendal Grignou771984c2014-07-01 12:46:18 -07001353 if (ret) {
1354 fprintf(stderr, "FFU install failed : %s\n", strerror(errno));
1355 exit(1);
1356 }
1357
1358 close(fd);
1359 return 0;
1360}
1361