blob: 5ec94c86374a2fb01c7cfc827575a107ca6c7aa9 [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"
Gwendal Grignou0da2c512015-01-08 15:36:03 -080034#include "ffu.h"
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050035
Nick Sanders9d57aa72014-03-05 21:38:54 -080036#define EXT_CSD_SIZE 512
Gwendal Grignou0da2c512015-01-08 15:36:03 -080037#define FFU_DATA_SIZE 512
Nick Sanders9d57aa72014-03-05 21:38:54 -080038#define CID_SIZE 16
39
40
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050041int read_extcsd(int fd, __u8 *ext_csd)
42{
43 int ret = 0;
44 struct mmc_ioc_cmd idata;
45 memset(&idata, 0, sizeof(idata));
Nick Sanders9d57aa72014-03-05 21:38:54 -080046 memset(ext_csd, 0, sizeof(__u8) * EXT_CSD_SIZE);
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050047 idata.write_flag = 0;
48 idata.opcode = MMC_SEND_EXT_CSD;
49 idata.arg = 0;
50 idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
Nick Sanders9d57aa72014-03-05 21:38:54 -080051 idata.blksz = EXT_CSD_SIZE;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050052 idata.blocks = 1;
53 mmc_ioc_cmd_set_data(idata, ext_csd);
54
55 ret = ioctl(fd, MMC_IOC_CMD, &idata);
56 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -080057 perror("ioctl SEND_EXT_CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050058
59 return ret;
60}
61
62int write_extcsd_value(int fd, __u8 index, __u8 value)
63{
64 int ret = 0;
65 struct mmc_ioc_cmd idata;
66
67 memset(&idata, 0, sizeof(idata));
68 idata.write_flag = 1;
69 idata.opcode = MMC_SWITCH;
70 idata.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
71 (index << 16) |
72 (value << 8) |
73 EXT_CSD_CMD_SET_NORMAL;
74 idata.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
75
76 ret = ioctl(fd, MMC_IOC_CMD, &idata);
77 if (ret)
Nick Sanders9d57aa72014-03-05 21:38:54 -080078 perror("ioctl Write EXT CSD");
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -050079
80 return ret;
81}
82
Ben Gardiner27c357d2013-05-30 17:12:47 -040083int send_status(int fd, __u32 *response)
84{
85 int ret = 0;
86 struct mmc_ioc_cmd idata;
87
88 memset(&idata, 0, sizeof(idata));
89 idata.opcode = MMC_SEND_STATUS;
90 idata.arg = (1 << 16);
91 idata.flags = MMC_RSP_R1 | MMC_CMD_AC;
92
93 ret = ioctl(fd, MMC_IOC_CMD, &idata);
94 if (ret)
95 perror("ioctl");
96
97 *response = idata.response[0];
98
99 return ret;
100}
101
Chris Ballb9c7a172012-02-20 12:34:25 -0500102void print_writeprotect_status(__u8 *ext_csd)
103{
104 __u8 reg;
105 __u8 ext_csd_rev = ext_csd[192];
106
107 /* A43: reserved [174:0] */
108 if (ext_csd_rev >= 5) {
109 printf("Boot write protection status registers"
110 " [BOOT_WP_STATUS]: 0x%02x\n", ext_csd[174]);
111
112 reg = ext_csd[EXT_CSD_BOOT_WP];
113 printf("Boot Area Write protection [BOOT_WP]: 0x%02x\n", reg);
114 printf(" Power ro locking: ");
115 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_DIS)
116 printf("not possible\n");
117 else
118 printf("possible\n");
119
120 printf(" Permanent ro locking: ");
121 if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_DIS)
122 printf("not possible\n");
123 else
124 printf("possible\n");
125
126 printf(" ro lock status: ");
127 if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_EN)
128 printf("locked until next power on\n");
129 else if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_EN)
130 printf("locked permanently\n");
131 else
132 printf("not locked\n");
133 }
134}
135
136int do_writeprotect_get(int nargs, char **argv)
137{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800138 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballb9c7a172012-02-20 12:34:25 -0500139 int fd, ret;
140 char *device;
141
Chris Ball8ba44662012-04-19 13:22:54 -0400142 CHECK(nargs != 2, "Usage: mmc writeprotect get </path/to/mmcblkX>\n",
143 exit(1));
Chris Ballb9c7a172012-02-20 12:34:25 -0500144
145 device = argv[1];
146
147 fd = open(device, O_RDWR);
148 if (fd < 0) {
149 perror("open");
150 exit(1);
151 }
152
153 ret = read_extcsd(fd, ext_csd);
154 if (ret) {
155 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
156 exit(1);
157 }
158
159 print_writeprotect_status(ext_csd);
160
161 return ret;
162}
163
164int do_writeprotect_set(int nargs, char **argv)
165{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800166 __u8 ext_csd[EXT_CSD_SIZE], value;
Chris Ballb9c7a172012-02-20 12:34:25 -0500167 int fd, ret;
168 char *device;
169
Chris Ball8ba44662012-04-19 13:22:54 -0400170 CHECK(nargs != 2, "Usage: mmc writeprotect set </path/to/mmcblkX>\n",
171 exit(1));
Chris Ballb9c7a172012-02-20 12:34:25 -0500172
173 device = argv[1];
174
175 fd = open(device, O_RDWR);
176 if (fd < 0) {
177 perror("open");
178 exit(1);
179 }
180
181 ret = read_extcsd(fd, ext_csd);
182 if (ret) {
183 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
184 exit(1);
185 }
186
187 value = ext_csd[EXT_CSD_BOOT_WP] |
188 EXT_CSD_BOOT_WP_B_PWR_WP_EN;
189 ret = write_extcsd_value(fd, EXT_CSD_BOOT_WP, value);
190 if (ret) {
191 fprintf(stderr, "Could not write 0x%02x to "
192 "EXT_CSD[%d] in %s\n",
193 value, EXT_CSD_BOOT_WP, device);
194 exit(1);
195 }
196
197 return ret;
198}
199
Saugata Dasb7e25992012-05-17 09:26:34 -0400200int do_disable_512B_emulation(int nargs, char **argv)
201{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800202 __u8 ext_csd[EXT_CSD_SIZE], native_sector_size, data_sector_size, wr_rel_param;
Saugata Dasb7e25992012-05-17 09:26:34 -0400203 int fd, ret;
204 char *device;
205
206 CHECK(nargs != 2, "Usage: mmc disable 512B emulation </path/to/mmcblkX>\n", exit(1));
207 device = argv[1];
208
209 fd = open(device, O_RDWR);
210 if (fd < 0) {
211 perror("open");
212 exit(1);
213 }
214
215 ret = read_extcsd(fd, ext_csd);
216 if (ret) {
217 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
218 exit(1);
219 }
220
221 wr_rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
222 native_sector_size = ext_csd[EXT_CSD_NATIVE_SECTOR_SIZE];
223 data_sector_size = ext_csd[EXT_CSD_DATA_SECTOR_SIZE];
224
225 if (native_sector_size && !data_sector_size &&
226 (wr_rel_param & EN_REL_WR)) {
227 ret = write_extcsd_value(fd, EXT_CSD_USE_NATIVE_SECTOR, 1);
228
229 if (ret) {
230 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
231 1, EXT_CSD_BOOT_WP, device);
232 exit(1);
233 }
234 printf("MMC disable 512B emulation successful. Now reset the device to switch to 4KB native sector mode.\n");
235 } else if (native_sector_size && data_sector_size) {
236 printf("MMC 512B emulation mode is already disabled; doing nothing.\n");
237 } else {
238 printf("MMC does not support disabling 512B emulation mode.\n");
239 }
240
241 return ret;
242}
243
Giuseppe CAVALLARO7bd13202012-04-19 10:58:37 +0200244int do_write_boot_en(int nargs, char **argv)
245{
246 __u8 ext_csd[512];
247 __u8 value = 0;
248 int fd, ret;
249 char *device;
250 int boot_area, send_ack;
251
252 CHECK(nargs != 4, "Usage: mmc bootpart enable <partition_number> "
253 "<send_ack> </path/to/mmcblkX>\n", exit(1));
254
255 /*
256 * If <send_ack> is 1, the device will send acknowledgment
257 * pattern "010" to the host when boot operation begins.
258 * If <send_ack> is 0, it won't.
259 */
260 boot_area = strtol(argv[1], NULL, 10);
261 send_ack = strtol(argv[2], NULL, 10);
262 device = argv[3];
263
264 fd = open(device, O_RDWR);
265 if (fd < 0) {
266 perror("open");
267 exit(1);
268 }
269
270 ret = read_extcsd(fd, ext_csd);
271 if (ret) {
272 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
273 exit(1);
274 }
275
276 value = ext_csd[EXT_CSD_PART_CONFIG];
277
278 switch (boot_area) {
279 case EXT_CSD_PART_CONFIG_ACC_BOOT0:
280 value |= (1 << 3);
281 value &= ~(3 << 4);
282 break;
283 case EXT_CSD_PART_CONFIG_ACC_BOOT1:
284 value |= (1 << 4);
285 value &= ~(1 << 3);
286 value &= ~(1 << 5);
287 break;
288 case EXT_CSD_PART_CONFIG_ACC_USER_AREA:
289 value |= (boot_area << 3);
290 break;
291 default:
292 fprintf(stderr, "Cannot enable the boot area\n");
293 exit(1);
294 }
295 if (send_ack)
296 value |= EXT_CSD_PART_CONFIG_ACC_ACK;
297 else
298 value &= ~EXT_CSD_PART_CONFIG_ACC_ACK;
299
300 ret = write_extcsd_value(fd, EXT_CSD_PART_CONFIG, value);
301 if (ret) {
302 fprintf(stderr, "Could not write 0x%02x to "
303 "EXT_CSD[%d] in %s\n",
304 value, EXT_CSD_PART_CONFIG, device);
305 exit(1);
306 }
307 return ret;
308}
309
Chris Ballf74dfe22012-10-19 16:49:55 -0400310int do_hwreset(int value, int nargs, char **argv)
311{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800312 __u8 ext_csd[EXT_CSD_SIZE];
Chris Ballf74dfe22012-10-19 16:49:55 -0400313 int fd, ret;
314 char *device;
315
316 CHECK(nargs != 2, "Usage: mmc hwreset enable </path/to/mmcblkX>\n",
317 exit(1));
318
319 device = argv[1];
320
321 fd = open(device, O_RDWR);
322 if (fd < 0) {
323 perror("open");
324 exit(1);
325 }
326
327 ret = read_extcsd(fd, ext_csd);
328 if (ret) {
329 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
330 exit(1);
331 }
332
333 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
334 EXT_CSD_HW_RESET_EN) {
335 fprintf(stderr,
336 "H/W Reset is already permanently enabled on %s\n",
337 device);
338 exit(1);
339 }
340 if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) ==
341 EXT_CSD_HW_RESET_DIS) {
342 fprintf(stderr,
343 "H/W Reset is already permanently disabled on %s\n",
344 device);
345 exit(1);
346 }
347
348 ret = write_extcsd_value(fd, EXT_CSD_RST_N_FUNCTION, value);
349 if (ret) {
350 fprintf(stderr,
351 "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
352 value, EXT_CSD_RST_N_FUNCTION, device);
353 exit(1);
354 }
355
356 return ret;
357}
358
359int do_hwreset_en(int nargs, char **argv)
360{
361 return do_hwreset(EXT_CSD_HW_RESET_EN, nargs, argv);
362}
363
364int do_hwreset_dis(int nargs, char **argv)
365{
366 return do_hwreset(EXT_CSD_HW_RESET_DIS, nargs, argv);
367}
368
Jaehoon Chung86496512012-09-21 10:08:05 +0000369int do_write_bkops_en(int nargs, char **argv)
370{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800371 __u8 ext_csd[EXT_CSD_SIZE], value = 0;
Jaehoon Chung86496512012-09-21 10:08:05 +0000372 int fd, ret;
373 char *device;
374
375 CHECK(nargs != 2, "Usage: mmc bkops enable </path/to/mmcblkX>\n",
376 exit(1));
377
378 device = argv[1];
379
380 fd = open(device, O_RDWR);
381 if (fd < 0) {
382 perror("open");
383 exit(1);
384 }
385
386 ret = read_extcsd(fd, ext_csd);
387 if (ret) {
388 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
389 exit(1);
390 }
391
392 if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) {
393 fprintf(stderr, "%s doesn't support BKOPS\n", device);
394 exit(1);
395 }
396
397 ret = write_extcsd_value(fd, EXT_CSD_BKOPS_EN, BKOPS_ENABLE);
398 if (ret) {
399 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
400 value, EXT_CSD_BKOPS_EN, device);
401 exit(1);
402 }
403
404 return ret;
405}
406
Ben Gardiner27c357d2013-05-30 17:12:47 -0400407int do_status_get(int nargs, char **argv)
408{
409 __u32 response;
410 int fd, ret;
411 char *device;
412
413 CHECK(nargs != 2, "Usage: mmc status get </path/to/mmcblkX>\n",
414 exit(1));
415
416 device = argv[1];
417
418 fd = open(device, O_RDWR);
419 if (fd < 0) {
420 perror("open");
421 exit(1);
422 }
423
424 ret = send_status(fd, &response);
425 if (ret) {
426 fprintf(stderr, "Could not read response to SEND_STATUS from %s\n", device);
427 exit(1);
428 }
429
430 printf("SEND_STATUS response: 0x%08x\n", response);
431
432 return ret;
433}
434
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800435__u32 get_word_from_ext_csd(__u8 *ext_csd_loc)
436{
437 return (ext_csd_loc[3] << 24) |
438 (ext_csd_loc[2] << 16) |
439 (ext_csd_loc[1] << 8) |
440 ext_csd_loc[0];
441}
442
Ben Gardiner4e850232013-05-30 17:12:49 -0400443unsigned int get_sector_count(__u8 *ext_csd)
444{
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800445 return get_word_from_ext_csd(&ext_csd[EXT_CSD_SEC_COUNT_0]);
Ben Gardiner4e850232013-05-30 17:12:49 -0400446}
447
448int is_blockaddresed(__u8 *ext_csd)
449{
450 unsigned int sectors = get_sector_count(ext_csd);
451
452 return (sectors > (2u * 1024 * 1024 * 1024) / 512);
453}
454
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400455unsigned int get_hc_wp_grp_size(__u8 *ext_csd)
456{
457 return ext_csd[221];
458}
459
460unsigned int get_hc_erase_grp_size(__u8 *ext_csd)
461{
462 return ext_csd[224];
463}
464
Ben Gardinere6e84e92013-09-19 11:14:27 -0400465int set_partitioning_setting_completed(int dry_run, const char * const device,
466 int fd)
467{
468 int ret;
469
470 if (dry_run) {
471 fprintf(stderr, "NOT setting PARTITION_SETTING_COMPLETED\n");
472 fprintf(stderr, "These changes will not take effect neither "
473 "now nor after a power cycle\n");
474 return 1;
475 }
476
477 fprintf(stderr, "setting OTP PARTITION_SETTING_COMPLETED!\n");
478 ret = write_extcsd_value(fd, EXT_CSD_PARTITION_SETTING_COMPLETED, 0x1);
479 if (ret) {
480 fprintf(stderr, "Could not write 0x1 to "
481 "EXT_CSD[%d] in %s\n",
482 EXT_CSD_PARTITION_SETTING_COMPLETED, device);
483 return 1;
484 }
485
486 __u32 response;
487 ret = send_status(fd, &response);
488 if (ret) {
489 fprintf(stderr, "Could not get response to SEND_STATUS "
490 "from %s\n", device);
491 return 1;
492 }
493
494 if (response & R1_SWITCH_ERROR) {
495 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED "
496 "failed on %s\n", device);
497 return 1;
498 }
499
500 fprintf(stderr, "Setting OTP PARTITION_SETTING_COMPLETED on "
501 "%s SUCCESS\n", device);
502 fprintf(stderr, "Device power cycle needed for settings to "
503 "take effect.\n"
504 "Confirm that PARTITION_SETTING_COMPLETED bit is set "
505 "using 'extcsd read' after power cycle\n");
506
507 return 0;
508}
509
Ben Gardinerd91d3692013-05-30 17:12:51 -0400510int do_enh_area_set(int nargs, char **argv)
511{
512 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -0800513 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardinerd91d3692013-05-30 17:12:51 -0400514 int fd, ret;
515 char *device;
516 int dry_run = 1;
517 unsigned int start_kib, length_kib, enh_start_addr, enh_size_mult;
518 unsigned long align;
519
520 CHECK(nargs != 5, "Usage: mmc enh_area set <-y|-n> <start KiB> <length KiB> "
521 "</path/to/mmcblkX>\n", exit(1));
522
523 if (!strcmp("-y", argv[1]))
524 dry_run = 0;
525
526 start_kib = strtol(argv[2], NULL, 10);
527 length_kib = strtol(argv[3], NULL, 10);
528 device = argv[4];
529
530 fd = open(device, O_RDWR);
531 if (fd < 0) {
532 perror("open");
533 exit(1);
534 }
535
536 ret = read_extcsd(fd, ext_csd);
537 if (ret) {
538 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
539 exit(1);
540 }
541
542 /* assert ENH_ATTRIBUTE_EN */
543 if (!(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & EXT_CSD_ENH_ATTRIBUTE_EN))
544 {
545 printf(" Device cannot have enhanced tech.\n");
546 exit(1);
547 }
548
549 /* assert not PARTITION_SETTING_COMPLETED */
550 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
551 {
552 printf(" Device is already partitioned\n");
553 exit(1);
554 }
555
556 align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
557
558 enh_size_mult = (length_kib + align/2l) / align;
559
560 enh_start_addr = start_kib * 1024 / (is_blockaddresed(ext_csd) ? 512 : 1);
561 enh_start_addr /= align;
562 enh_start_addr *= align;
563
564 /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
565 ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
566 if (ret) {
567 fprintf(stderr, "Could not write 0x1 to "
568 "EXT_CSD[%d] in %s\n",
569 EXT_CSD_ERASE_GROUP_DEF, device);
570 exit(1);
571 }
572
573 /* write to ENH_START_ADDR and ENH_SIZE_MULT and PARTITIONS_ATTRIBUTE's ENH_USR bit */
574 value = (enh_start_addr >> 24) & 0xff;
575 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_3, value);
576 if (ret) {
577 fprintf(stderr, "Could not write 0x%02x to "
578 "EXT_CSD[%d] in %s\n", value,
579 EXT_CSD_ENH_START_ADDR_3, device);
580 exit(1);
581 }
582 value = (enh_start_addr >> 16) & 0xff;
583 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_2, value);
584 if (ret) {
585 fprintf(stderr, "Could not write 0x%02x to "
586 "EXT_CSD[%d] in %s\n", value,
587 EXT_CSD_ENH_START_ADDR_2, device);
588 exit(1);
589 }
590 value = (enh_start_addr >> 8) & 0xff;
591 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_1, value);
592 if (ret) {
593 fprintf(stderr, "Could not write 0x%02x to "
594 "EXT_CSD[%d] in %s\n", value,
595 EXT_CSD_ENH_START_ADDR_1, device);
596 exit(1);
597 }
598 value = enh_start_addr & 0xff;
599 ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_0, value);
600 if (ret) {
601 fprintf(stderr, "Could not write 0x%02x to "
602 "EXT_CSD[%d] in %s\n", value,
603 EXT_CSD_ENH_START_ADDR_0, device);
604 exit(1);
605 }
606
607 value = (enh_size_mult >> 16) & 0xff;
608 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_2, value);
609 if (ret) {
610 fprintf(stderr, "Could not write 0x%02x to "
611 "EXT_CSD[%d] in %s\n", value,
612 EXT_CSD_ENH_SIZE_MULT_2, device);
613 exit(1);
614 }
615 value = (enh_size_mult >> 8) & 0xff;
616 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_1, value);
617 if (ret) {
618 fprintf(stderr, "Could not write 0x%02x to "
619 "EXT_CSD[%d] in %s\n", value,
620 EXT_CSD_ENH_SIZE_MULT_1, device);
621 exit(1);
622 }
623 value = enh_size_mult & 0xff;
624 ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_0, value);
625 if (ret) {
626 fprintf(stderr, "Could not write 0x%02x to "
627 "EXT_CSD[%d] in %s\n", value,
628 EXT_CSD_ENH_SIZE_MULT_0, device);
629 exit(1);
630 }
631
632 ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, EXT_CSD_ENH_USR);
633 if (ret) {
634 fprintf(stderr, "Could not write EXT_CSD_ENH_USR to "
635 "EXT_CSD[%d] in %s\n",
636 EXT_CSD_PARTITIONS_ATTRIBUTE, device);
637 exit(1);
638 }
639
Ben Gardinere6e84e92013-09-19 11:14:27 -0400640 printf("Done setting ENH_USR area on %s\n", device);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400641
Ben Gardinere6e84e92013-09-19 11:14:27 -0400642 if (!set_partitioning_setting_completed(dry_run, device, fd))
Ben Gardinerd91d3692013-05-30 17:12:51 -0400643 exit(1);
Ben Gardinerd91d3692013-05-30 17:12:51 -0400644
645 return 0;
646}
647
Ben Gardiner196d0d22013-09-19 11:14:29 -0400648int do_write_reliability_set(int nargs, char **argv)
649{
650 __u8 value;
Nick Sanders9d57aa72014-03-05 21:38:54 -0800651 __u8 ext_csd[EXT_CSD_SIZE];
Ben Gardiner196d0d22013-09-19 11:14:29 -0400652 int fd, ret;
653
654 int dry_run = 1;
655 int partition;
656 char *device;
657
658 CHECK(nargs != 4, "Usage: mmc write_reliability set <-y|-n> "
659 "<partition> </path/to/mmcblkX>\n", exit(1));
660
661 if (!strcmp("-y", argv[1]))
662 dry_run = 0;
663
664 partition = strtol(argv[2], NULL, 10);
665 device = argv[3];
666
667 fd = open(device, O_RDWR);
668 if (fd < 0) {
669 perror("open");
670 exit(1);
671 }
672
673 ret = read_extcsd(fd, ext_csd);
674 if (ret) {
675 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
676 exit(1);
677 }
678
679 /* assert not PARTITION_SETTING_COMPLETED */
680 if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
681 {
682 printf(" Device is already partitioned\n");
683 exit(1);
684 }
685
686 /* assert HS_CTRL_REL */
687 if (!(ext_csd[EXT_CSD_WR_REL_PARAM] & HS_CTRL_REL)) {
688 printf("Cannot set write reliability parameters, WR_REL_SET is "
689 "read-only\n");
690 exit(1);
691 }
692
693 value = ext_csd[EXT_CSD_WR_REL_SET] | (1<<partition);
694 ret = write_extcsd_value(fd, EXT_CSD_WR_REL_SET, value);
695 if (ret) {
696 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
697 value, EXT_CSD_WR_REL_SET, device);
698 exit(1);
699 }
700
701 printf("Done setting EXT_CSD_WR_REL_SET to 0x%02x on %s\n",
702 value, device);
703
704 if (!set_partitioning_setting_completed(dry_run, device, fd))
705 exit(1);
706
707 return 0;
708}
709
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500710int do_read_extcsd(int nargs, char **argv)
711{
Nick Sanders9d57aa72014-03-05 21:38:54 -0800712 __u8 ext_csd[EXT_CSD_SIZE], ext_csd_rev, reg;
Oliver Metz11f2cea2013-09-23 08:40:52 +0200713 __u32 regl;
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500714 int fd, ret;
715 char *device;
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100716 const char *str;
Gwendal Grignoueb1cd012015-01-08 15:34:55 -0800717 const char *ver_str[] = {
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800718 "4.0", /* 0 */
719 "4.1", /* 1 */
720 "4.2", /* 2 */
721 "4.3", /* 3 */
722 "Obsolete", /* 4 */
723 "4.41", /* 5 */
724 "4.5", /* 6 */
725 "5.0", /* 7 */
Puthikorn Voravootivatc384aec2015-04-28 11:28:41 -0700726 "5.1", /* 8 */
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800727 };
728 int boot_access;
729 const char* boot_access_str[] = {
730 "No access to boot partition", /* 0 */
731 "R/W Boot Partition 1", /* 1 */
732 "R/W Boot Partition 2", /* 2 */
733 "R/W Replay Protected Memory Block (RPMB)", /* 3 */
734 };
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500735
Chris Ball8ba44662012-04-19 13:22:54 -0400736 CHECK(nargs != 2, "Usage: mmc extcsd read </path/to/mmcblkX>\n",
737 exit(1));
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500738
739 device = argv[1];
740
741 fd = open(device, O_RDWR);
742 if (fd < 0) {
743 perror("open");
744 exit(1);
745 }
746
747 ret = read_extcsd(fd, ext_csd);
748 if (ret) {
749 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
750 exit(1);
751 }
752
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100753 ext_csd_rev = ext_csd[192];
754
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800755 if ((ext_csd_rev < sizeof(ver_str)/sizeof(char*)) &&
756 (ext_csd_rev != 4))
757 str = ver_str[ext_csd_rev];
758 else
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100759 goto out_free;
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800760
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100761 printf("=============================================\n");
762 printf(" Extended CSD rev 1.%d (MMC %s)\n", ext_csd_rev, str);
763 printf("=============================================\n\n");
764
765 if (ext_csd_rev < 3)
766 goto out_free; /* No ext_csd */
767
768 /* Parse the Extended CSD registers.
769 * Reserved bit should be read as "0" in case of spec older
770 * than A441.
771 */
772 reg = ext_csd[EXT_CSD_S_CMD_SET];
773 printf("Card Supported Command sets [S_CMD_SET: 0x%02x]\n", reg);
774 if (!reg)
Chris Ballb9c7a172012-02-20 12:34:25 -0500775 printf(" - Standard MMC command sets\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100776
777 reg = ext_csd[EXT_CSD_HPI_FEATURE];
778 printf("HPI Features [HPI_FEATURE: 0x%02x]: ", reg);
779 if (reg & EXT_CSD_HPI_SUPP) {
780 if (reg & EXT_CSD_HPI_IMPL)
Chris Ballb9c7a172012-02-20 12:34:25 -0500781 printf("implementation based on CMD12\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100782 else
783 printf("implementation based on CMD13\n");
784 }
785
786 printf("Background operations support [BKOPS_SUPPORT: 0x%02x]\n",
787 ext_csd[502]);
788
789 if (ext_csd_rev >= 6) {
790 printf("Max Packet Read Cmd [MAX_PACKED_READS: 0x%02x]\n",
791 ext_csd[501]);
792 printf("Max Packet Write Cmd [MAX_PACKED_WRITES: 0x%02x]\n",
793 ext_csd[500]);
794 printf("Data TAG support [DATA_TAG_SUPPORT: 0x%02x]\n",
795 ext_csd[499]);
796
797 printf("Data TAG Unit Size [TAG_UNIT_SIZE: 0x%02x]\n",
798 ext_csd[498]);
799 printf("Tag Resources Size [TAG_RES_SIZE: 0x%02x]\n",
800 ext_csd[497]);
801 printf("Context Management Capabilities"
802 " [CONTEXT_CAPABILITIES: 0x%02x]\n", ext_csd[496]);
803 printf("Large Unit Size [LARGE_UNIT_SIZE_M1: 0x%02x]\n",
804 ext_csd[495]);
805 printf("Extended partition attribute support"
806 " [EXT_SUPPORT: 0x%02x]\n", ext_csd[494]);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800807 }
808 if (ext_csd_rev >= 7) {
809 int j;
810 int eol_info;
811 char* eol_info_str[] = {
812 "Not Defined", /* 0 */
813 "Normal", /* 1 */
814 "Warning", /* 2 */
815 "Urgent", /* 3 */
816 };
817
818 printf("Supported modes [SUPPORTED_MODES: 0x%02x]\n",
819 ext_csd[493]);
820 printf("FFU features [FFU_FEATURES: 0x%02x]\n",
821 ext_csd[492]);
822 printf("Operation codes timeout"
823 " [OPERATION_CODE_TIMEOUT: 0x%02x]\n",
824 ext_csd[491]);
825 printf("FFU Argument [FFU_ARG: 0x%08x]\n",
826 get_word_from_ext_csd(&ext_csd[487]));
827 printf("Number of FW sectors correctly programmed"
828 " [NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED: %d]\n",
829 get_word_from_ext_csd(&ext_csd[302]));
830 printf("Vendor proprietary health report:\n");
831 for (j = 301; j >= 270; j--)
832 printf("[VENDOR_PROPRIETARY_HEALTH_REPORT[%d]]:"
833 " 0x%02x\n", j, ext_csd[j]);
834 for (j = 269; j >= 268; j--) {
835 __u8 life_used=ext_csd[j];
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -0800836 char est_type = 'B' + (j - 269);
837 printf("Device life time estimation type %c"
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800838 " [DEVICE_LIFE_TIME_EST_TYP_%c: 0x%02x]\n",
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -0800839 est_type, est_type, life_used);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800840 if (life_used >= 0x1 && life_used <= 0xa)
841 printf(" i.e. %d%% - %d%% device life time"
842 " used\n",
843 (life_used - 1) * 10, life_used * 10);
844 else if (life_used == 0xb)
845 printf(" i.e. Exceeded its maximum estimated"
846 " device life time\n");
847 }
848 eol_info = ext_csd[267];
849 printf("Pre EOL information [PRE_EOL_INFO: 0x%02x]\n",
850 eol_info);
851 if (eol_info < sizeof(eol_info_str)/sizeof(char*))
852 printf(" i.e. %s\n", eol_info_str[eol_info]);
853 else
854 printf(" i.e. Reserved\n");
855
856 printf("Optimal read size [OPTIMAL_READ_SIZE: 0x%02x]\n",
857 ext_csd[266]);
858 printf("Optimal write size [OPTIMAL_WRITE_SIZE: 0x%02x]\n",
859 ext_csd[265]);
860 printf("Optimal trim unit size"
861 " [OPTIMAL_TRIM_UNIT_SIZE: 0x%02x]\n", ext_csd[264]);
862 printf("Device version [DEVICE_VERSION: 0x%02x - 0x%02x]\n",
863 ext_csd[263], ext_csd[262]);
864 printf("Firmware version:\n");
865 for (j = 261; j >= 254; j--)
866 printf("[FIRMWARE_VERSION[%d]]:"
867 " 0x%02x\n", j, ext_csd[j]);
868
869 printf("Power class for 200MHz, DDR at VCC= 3.6V"
870 " [PWR_CL_DDR_200_360: 0x%02x]\n", ext_csd[253]);
871 }
872 if (ext_csd_rev >= 6) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100873 printf("Generic CMD6 Timer [GENERIC_CMD6_TIME: 0x%02x]\n",
874 ext_csd[248]);
875 printf("Power off notification [POWER_OFF_LONG_TIME: 0x%02x]\n",
876 ext_csd[247]);
877 printf("Cache Size [CACHE_SIZE] is %d KiB\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800878 get_word_from_ext_csd(&ext_csd[249]));
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100879 }
880
881 /* A441: Reserved [501:247]
882 A43: reserved [246:229] */
883 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100884 printf("Background operations status"
Chris Ballb9c7a172012-02-20 12:34:25 -0500885 " [BKOPS_STATUS: 0x%02x]\n", ext_csd[246]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100886
887 /* CORRECTLY_PRG_SECTORS_NUM [245:242] TODO */
888
889 printf("1st Initialisation Time after programmed sector"
890 " [INI_TIMEOUT_AP: 0x%02x]\n", ext_csd[241]);
891
892 /* A441: reserved [240] */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100893 printf("Power class for 52MHz, DDR at 3.6V"
894 " [PWR_CL_DDR_52_360: 0x%02x]\n", ext_csd[239]);
895 printf("Power class for 52MHz, DDR at 1.95V"
896 " [PWR_CL_DDR_52_195: 0x%02x]\n", ext_csd[238]);
897
898 /* A441: reserved [237-236] */
899
900 if (ext_csd_rev >= 6) {
901 printf("Power class for 200MHz at 3.6V"
902 " [PWR_CL_200_360: 0x%02x]\n", ext_csd[237]);
903 printf("Power class for 200MHz, at 1.95V"
904 " [PWR_CL_200_195: 0x%02x]\n", ext_csd[236]);
905 }
Chris Ballb9c7a172012-02-20 12:34:25 -0500906 printf("Minimum Performance for 8bit at 52MHz in DDR mode:\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100907 printf(" [MIN_PERF_DDR_W_8_52: 0x%02x]\n", ext_csd[235]);
908 printf(" [MIN_PERF_DDR_R_8_52: 0x%02x]\n", ext_csd[234]);
909 /* A441: reserved [233] */
910 printf("TRIM Multiplier [TRIM_MULT: 0x%02x]\n", ext_csd[232]);
911 printf("Secure Feature support [SEC_FEATURE_SUPPORT: 0x%02x]\n",
912 ext_csd[231]);
913 }
914 if (ext_csd_rev == 5) { /* Obsolete in 4.5 */
915 printf("Secure Erase Multiplier [SEC_ERASE_MULT: 0x%02x]\n",
916 ext_csd[230]);
917 printf("Secure TRIM Multiplier [SEC_TRIM_MULT: 0x%02x]\n",
918 ext_csd[229]);
919 }
920 reg = ext_csd[EXT_CSD_BOOT_INFO];
921 printf("Boot Information [BOOT_INFO: 0x%02x]\n", reg);
922 if (reg & EXT_CSD_BOOT_INFO_ALT)
923 printf(" Device supports alternative boot method\n");
924 if (reg & EXT_CSD_BOOT_INFO_DDR_DDR)
925 printf(" Device supports dual data rate during boot\n");
926 if (reg & EXT_CSD_BOOT_INFO_HS_MODE)
927 printf(" Device supports high speed timing during boot\n");
928
929 /* A441/A43: reserved [227] */
930 printf("Boot partition size [BOOT_SIZE_MULTI: 0x%02x]\n", ext_csd[226]);
931 printf("Access size [ACC_SIZE: 0x%02x]\n", ext_csd[225]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400932
933 reg = get_hc_erase_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100934 printf("High-capacity erase unit size [HC_ERASE_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400935 reg);
936 printf(" i.e. %u KiB\n", 512 * reg);
937
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100938 printf("High-capacity erase timeout [ERASE_TIMEOUT_MULT: 0x%02x]\n",
939 ext_csd[223]);
940 printf("Reliable write sector count [REL_WR_SEC_C: 0x%02x]\n",
941 ext_csd[222]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400942
943 reg = get_hc_wp_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100944 printf("High-capacity W protect group size [HC_WP_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400945 reg);
946 printf(" i.e. %lu KiB\n", 512l * get_hc_erase_grp_size(ext_csd) * reg);
947
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100948 printf("Sleep current (VCC) [S_C_VCC: 0x%02x]\n", ext_csd[220]);
949 printf("Sleep current (VCCQ) [S_C_VCCQ: 0x%02x]\n", ext_csd[219]);
950 /* A441/A43: reserved [218] */
951 printf("Sleep/awake timeout [S_A_TIMEOUT: 0x%02x]\n", ext_csd[217]);
952 /* A441/A43: reserved [216] */
Ben Gardiner4e850232013-05-30 17:12:49 -0400953
954 unsigned int sectors = get_sector_count(ext_csd);
955 printf("Sector Count [SEC_COUNT: 0x%08x]\n", sectors);
956 if (is_blockaddresed(ext_csd))
957 printf(" Device is block-addressed\n");
958 else
959 printf(" Device is NOT block-addressed\n");
960
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100961 /* A441/A43: reserved [211] */
962 printf("Minimum Write Performance for 8bit:\n");
963 printf(" [MIN_PERF_W_8_52: 0x%02x]\n", ext_csd[210]);
964 printf(" [MIN_PERF_R_8_52: 0x%02x]\n", ext_csd[209]);
965 printf(" [MIN_PERF_W_8_26_4_52: 0x%02x]\n", ext_csd[208]);
966 printf(" [MIN_PERF_R_8_26_4_52: 0x%02x]\n", ext_csd[207]);
967 printf("Minimum Write Performance for 4bit:\n");
968 printf(" [MIN_PERF_W_4_26: 0x%02x]\n", ext_csd[206]);
969 printf(" [MIN_PERF_R_4_26: 0x%02x]\n", ext_csd[205]);
970 /* A441/A43: reserved [204] */
971 printf("Power classes registers:\n");
972 printf(" [PWR_CL_26_360: 0x%02x]\n", ext_csd[203]);
973 printf(" [PWR_CL_52_360: 0x%02x]\n", ext_csd[202]);
974 printf(" [PWR_CL_26_195: 0x%02x]\n", ext_csd[201]);
975 printf(" [PWR_CL_52_195: 0x%02x]\n", ext_csd[200]);
976
977 /* A43: reserved [199:198] */
978 if (ext_csd_rev >= 5) {
979 printf("Partition switching timing "
980 "[PARTITION_SWITCH_TIME: 0x%02x]\n", ext_csd[199]);
981 printf("Out-of-interrupt busy timing"
982 " [OUT_OF_INTERRUPT_TIME: 0x%02x]\n", ext_csd[198]);
983 }
984
985 /* A441/A43: reserved [197] [195] [193] [190] [188]
986 * [186] [184] [182] [180] [176] */
987
988 if (ext_csd_rev >= 6)
989 printf("I/O Driver Strength [DRIVER_STRENGTH: 0x%02x]\n",
990 ext_csd[197]);
991
Oleg Matcovschi64f63a32013-05-23 17:11:07 -0700992 /* DEVICE_TYPE in A45, CARD_TYPE in A441 */
993 reg = ext_csd[196];
994 printf("Card Type [CARD_TYPE: 0x%02x]\n", reg);
995 if (reg & 0x20) printf(" HS200 Single Data Rate eMMC @200MHz 1.2VI/O\n");
996 if (reg & 0x10) printf(" HS200 Single Data Rate eMMC @200MHz 1.8VI/O\n");
997 if (reg & 0x08) printf(" HS Dual Data Rate eMMC @52MHz 1.2VI/O\n");
998 if (reg & 0x04) printf(" HS Dual Data Rate eMMC @52MHz 1.8V or 3VI/O\n");
999 if (reg & 0x02) printf(" HS eMMC @52MHz - at rated device voltage(s)\n");
1000 if (reg & 0x01) printf(" HS eMMC @26MHz - at rated device voltage(s)\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001001
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001002 printf("CSD structure version [CSD_STRUCTURE: 0x%02x]\n", ext_csd[194]);
1003 /* ext_csd_rev = ext_csd[192] (already done!!!) */
1004 printf("Command set [CMD_SET: 0x%02x]\n", ext_csd[191]);
1005 printf("Command set revision [CMD_SET_REV: 0x%02x]\n", ext_csd[189]);
1006 printf("Power class [POWER_CLASS: 0x%02x]\n", ext_csd[187]);
1007 printf("High-speed interface timing [HS_TIMING: 0x%02x]\n",
1008 ext_csd[185]);
1009 /* bus_width: ext_csd[183] not readable */
1010 printf("Erased memory content [ERASED_MEM_CONT: 0x%02x]\n",
1011 ext_csd[181]);
1012 reg = ext_csd[EXT_CSD_BOOT_CFG];
1013 printf("Boot configuration bytes [PARTITION_CONFIG: 0x%02x]\n", reg);
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001014 switch ((reg & EXT_CSD_BOOT_CFG_EN)>>3) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001015 case 0x0:
1016 printf(" Not boot enable\n");
1017 break;
1018 case 0x1:
1019 printf(" Boot Partition 1 enabled\n");
1020 break;
1021 case 0x2:
1022 printf(" Boot Partition 2 enabled\n");
1023 break;
1024 case 0x7:
1025 printf(" User Area Enabled for boot\n");
1026 break;
1027 }
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001028 boot_access = reg & EXT_CSD_BOOT_CFG_ACC;
1029 if (boot_access < sizeof(boot_access_str) / sizeof(char*))
1030 printf(" %s\n", boot_access_str[boot_access]);
1031 else
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001032 printf(" Access to General Purpose partition %d\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001033 boot_access - 3);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001034
1035 printf("Boot config protection [BOOT_CONFIG_PROT: 0x%02x]\n",
1036 ext_csd[178]);
1037 printf("Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x%02x]\n",
1038 ext_csd[177]);
1039 printf("High-density erase group definition"
Ben Gardinerd91d3692013-05-30 17:12:51 -04001040 " [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[EXT_CSD_ERASE_GROUP_DEF]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001041
Chris Ballb9c7a172012-02-20 12:34:25 -05001042 print_writeprotect_status(ext_csd);
1043
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001044 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001045 /* A441]: reserved [172] */
1046 printf("User area write protection register"
1047 " [USER_WP]: 0x%02x\n", ext_csd[171]);
1048 /* A441]: reserved [170] */
1049 printf("FW configuration [FW_CONFIG]: 0x%02x\n", ext_csd[169]);
1050 printf("RPMB Size [RPMB_SIZE_MULT]: 0x%02x\n", ext_csd[168]);
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001051
1052 reg = ext_csd[EXT_CSD_WR_REL_SET];
1053 const char * const fast = "existing data is at risk if a power "
1054 "failure occurs during a write operation";
1055 const char * const reliable = "the device protects existing "
1056 "data if a power failure occurs during a write "
1057 "operation";
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001058 printf("Write reliability setting register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001059 " [WR_REL_SET]: 0x%02x\n", reg);
1060
1061 printf(" user area: %s\n", reg & (1<<0) ? reliable : fast);
1062 int i;
1063 for (i = 1; i <= 4; i++) {
1064 printf(" partition %d: %s\n", i,
1065 reg & (1<<i) ? reliable : fast);
1066 }
1067
1068 reg = ext_csd[EXT_CSD_WR_REL_PARAM];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001069 printf("Write reliability parameter register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001070 " [WR_REL_PARAM]: 0x%02x\n", reg);
1071 if (reg & 0x01)
1072 printf(" Device supports writing EXT_CSD_WR_REL_SET\n");
1073 if (reg & 0x04)
1074 printf(" Device supports the enhanced def. of reliable "
1075 "write\n");
1076
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001077 /* sanitize_start ext_csd[165]]: not readable
1078 * bkops_start ext_csd[164]]: only writable */
1079 printf("Enable background operations handshake"
1080 " [BKOPS_EN]: 0x%02x\n", ext_csd[163]);
1081 printf("H/W reset function"
1082 " [RST_N_FUNCTION]: 0x%02x\n", ext_csd[162]);
1083 printf("HPI management [HPI_MGMT]: 0x%02x\n", ext_csd[161]);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001084 reg = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001085 printf("Partitioning Support [PARTITIONING_SUPPORT]: 0x%02x\n",
1086 reg);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001087 if (reg & EXT_CSD_PARTITIONING_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001088 printf(" Device support partitioning feature\n");
1089 else
1090 printf(" Device NOT support partitioning feature\n");
Ben Gardiner82bd9502013-06-27 11:04:10 -04001091 if (reg & EXT_CSD_ENH_ATTRIBUTE_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001092 printf(" Device can have enhanced tech.\n");
1093 else
1094 printf(" Device cannot have enhanced tech.\n");
1095
Oliver Metz11f2cea2013-09-23 08:40:52 +02001096 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
Oliver Metz22f26412013-09-23 08:40:51 +02001097 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
1098 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
1099
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001100 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n",
Oliver Metz11f2cea2013-09-23 08:40:52 +02001101 regl);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001102 unsigned int wp_sz = get_hc_wp_grp_size(ext_csd);
1103 unsigned int erase_sz = get_hc_erase_grp_size(ext_csd);
Oliver Metz11f2cea2013-09-23 08:40:52 +02001104 printf(" i.e. %lu KiB\n", 512l * regl * wp_sz * erase_sz);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001105
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001106 printf("Partitions attribute [PARTITIONS_ATTRIBUTE]: 0x%02x\n",
Ben Gardinerd91d3692013-05-30 17:12:51 -04001107 ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]);
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001108 reg = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001109 printf("Partitioning Setting"
1110 " [PARTITION_SETTING_COMPLETED]: 0x%02x\n",
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001111 reg);
1112 if (reg)
1113 printf(" Device partition setting complete\n");
1114 else
1115 printf(" Device partition setting NOT complete\n");
1116
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001117 printf("General Purpose Partition Size\n"
1118 " [GP_SIZE_MULT_4]: 0x%06x\n", (ext_csd[154] << 16) |
1119 (ext_csd[153] << 8) | ext_csd[152]);
1120 printf(" [GP_SIZE_MULT_3]: 0x%06x\n", (ext_csd[151] << 16) |
1121 (ext_csd[150] << 8) | ext_csd[149]);
1122 printf(" [GP_SIZE_MULT_2]: 0x%06x\n", (ext_csd[148] << 16) |
1123 (ext_csd[147] << 8) | ext_csd[146]);
1124 printf(" [GP_SIZE_MULT_1]: 0x%06x\n", (ext_csd[145] << 16) |
1125 (ext_csd[144] << 8) | ext_csd[143]);
1126
Oliver Metz11f2cea2013-09-23 08:40:52 +02001127 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001128 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
1129 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001130 printf("Enhanced User Data Area Size"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001131 " [ENH_SIZE_MULT]: 0x%06x\n", regl);
1132 printf(" i.e. %lu KiB\n", 512l * regl *
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001133 get_hc_erase_grp_size(ext_csd) *
1134 get_hc_wp_grp_size(ext_csd));
Ben Gardiner68f490b2013-05-30 17:12:48 -04001135
Oliver Metz11f2cea2013-09-23 08:40:52 +02001136 regl = (ext_csd[EXT_CSD_ENH_START_ADDR_3] << 24) |
Ben Gardiner68f490b2013-05-30 17:12:48 -04001137 (ext_csd[EXT_CSD_ENH_START_ADDR_2] << 16) |
1138 (ext_csd[EXT_CSD_ENH_START_ADDR_1] << 8) |
1139 ext_csd[EXT_CSD_ENH_START_ADDR_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001140 printf("Enhanced User Data Start Address"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001141 " [ENH_START_ADDR]: 0x%06x\n", regl);
Ben Gardiner4e850232013-05-30 17:12:49 -04001142 printf(" i.e. %lu bytes offset\n", (is_blockaddresed(ext_csd) ?
Oliver Metz11f2cea2013-09-23 08:40:52 +02001143 1l : 512l) * regl);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001144
1145 /* A441]: reserved [135] */
1146 printf("Bad Block Management mode"
1147 " [SEC_BAD_BLK_MGMNT]: 0x%02x\n", ext_csd[134]);
1148 /* A441: reserved [133:0] */
1149 }
1150 /* B45 */
1151 if (ext_csd_rev >= 6) {
1152 int j;
1153 /* tcase_support ext_csd[132] not readable */
1154 printf("Periodic Wake-up [PERIODIC_WAKEUP]: 0x%02x\n",
1155 ext_csd[131]);
1156 printf("Program CID/CSD in DDR mode support"
1157 " [PROGRAM_CID_CSD_DDR_SUPPORT]: 0x%02x\n",
1158 ext_csd[130]);
1159
1160 for (j = 127; j >= 64; j--)
1161 printf("Vendor Specific Fields"
1162 " [VENDOR_SPECIFIC_FIELD[%d]]: 0x%02x\n",
1163 j, ext_csd[j]);
1164
Gwendal Grignoue966e672014-07-07 14:03:13 -07001165 reg = ext_csd[63];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001166 printf("Native sector size [NATIVE_SECTOR_SIZE]: 0x%02x\n",
Gwendal Grignoue966e672014-07-07 14:03:13 -07001167 reg);
1168 if (reg == 0x00)
1169 printf(" i.e. 512 B\n");
1170 else if (reg == 0x01)
1171 printf(" i.e. 4 KiB\n");
1172 else
1173 printf(" i.e. Reserved\n");
1174
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001175 printf("Sector size emulation [USE_NATIVE_SECTOR]: 0x%02x\n",
1176 ext_csd[62]);
Gwendal Grignoue966e672014-07-07 14:03:13 -07001177 reg = ext_csd[61];
1178 printf("Sector size [DATA_SECTOR_SIZE]: 0x%02x\n", reg);
1179 if (reg == 0x00)
1180 printf(" i.e. 512 B\n");
1181 else if (reg == 0x01)
1182 printf(" i.e. 4 KiB\n");
1183 else
1184 printf(" i.e. Reserved\n");
1185
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001186 printf("1st initialization after disabling sector"
1187 " size emulation [INI_TIMEOUT_EMU]: 0x%02x\n",
1188 ext_csd[60]);
1189 printf("Class 6 commands control [CLASS_6_CTRL]: 0x%02x\n",
1190 ext_csd[59]);
1191 printf("Number of addressed group to be Released"
1192 "[DYNCAP_NEEDED]: 0x%02x\n", ext_csd[58]);
1193 printf("Exception events control"
1194 " [EXCEPTION_EVENTS_CTRL]: 0x%04x\n",
1195 (ext_csd[57] << 8) | ext_csd[56]);
1196 printf("Exception events status"
1197 "[EXCEPTION_EVENTS_STATUS]: 0x%04x\n",
1198 (ext_csd[55] << 8) | ext_csd[54]);
1199 printf("Extended Partitions Attribute"
1200 " [EXT_PARTITIONS_ATTRIBUTE]: 0x%04x\n",
1201 (ext_csd[53] << 8) | ext_csd[52]);
1202
1203 for (j = 51; j >= 37; j--)
1204 printf("Context configuration"
1205 " [CONTEXT_CONF[%d]]: 0x%02x\n", j, ext_csd[j]);
1206
1207 printf("Packed command status"
1208 " [PACKED_COMMAND_STATUS]: 0x%02x\n", ext_csd[36]);
1209 printf("Packed command failure index"
1210 " [PACKED_FAILURE_INDEX]: 0x%02x\n", ext_csd[35]);
1211 printf("Power Off Notification"
1212 " [POWER_OFF_NOTIFICATION]: 0x%02x\n", ext_csd[34]);
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001213 printf("Control to turn the Cache ON/OFF"
1214 " [CACHE_CTRL]: 0x%02x\n", ext_csd[33]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001215 /* flush_cache ext_csd[32] not readable */
1216 /*Reserved [31:0] */
1217 }
Gwendal Grignoue966e672014-07-07 14:03:13 -07001218 if (ext_csd_rev >= 7) {
1219 printf("Mode config [MODE_CONFIG: 0x%02x]\n", ext_csd[30]);
1220 printf("Mode operation codes [MODE_OPERATION_CODES: 0x%02x]\n",
1221 ext_csd[29]);
1222
1223 reg = ext_csd[26];
1224 printf("FFU status [FFU_STATUS: 0x%02x]\n", reg);
1225 switch (reg) {
1226 case 0x00:
1227 printf(" Success\n");
1228 break;
1229 case 0x10:
1230 printf(" General error\n");
1231 break;
1232 case 0x11:
1233 printf(" Firmware install error\n");
1234 break;
1235 case 0x12:
1236 printf(" Error in downloading firmware\n");
1237 break;
1238 default:
1239 printf(" Reserved\n");
1240 }
1241 printf("Pre loading data size [PRE_LOADING_DATA_SIZE] is"
1242 " %d sector size\n",
1243 get_word_from_ext_csd(&ext_csd[22]));
1244 printf("Max pre loading data size [MAX_PRE_LOADING_DATA_SIZE] is"
1245 " %d sector size\n",
1246 get_word_from_ext_csd(&ext_csd[18]));
1247 printf("Product state awareness enablement"
1248 " [PRODUCT_STATE_AWARENESS_ENABLEMENT: 0x%02x]\n",
1249 ext_csd[17]);
1250 printf("Secure Removal Type [SECURE_REMOVAL_TYPE: 0x%02x]\n",
1251 ext_csd[16]);
1252 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001253
1254out_free:
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001255 return ret;
1256}
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001257
Nick Sanders9d57aa72014-03-05 21:38:54 -08001258int do_dump_extcsd(int nargs, char **argv)
1259{
1260 __u8 ext_csd[EXT_CSD_SIZE];
1261 int fd, ret;
1262 char *device;
1263 int i, j;
1264
1265 CHECK(nargs != 2, "Usage: mmc extcsd dump </path/to/mmcblkX>\n",
1266 exit(1));
1267
1268 device = argv[1];
1269
1270 fd = open(device, O_RDWR);
1271 if (fd < 0) {
1272 perror("Failed to open mmc device");
1273 exit(1);
1274 }
1275
1276 ret = read_extcsd(fd, ext_csd);
1277 if (ret) {
1278 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1279 exit(1);
1280 }
1281
1282 /* Dump all bytes so that any undecoded or proprietary registers */
1283 /* can be acessed. */
1284 printf("EXT_CSD binary dump:\n");
1285 for (i = 0; i < EXT_CSD_SIZE; i+= 16) {
1286 printf(" %3d: %3x: ", i, i);
1287 for (j = 0; (j < 16) && (i + j < EXT_CSD_SIZE); j++) {
1288 printf(" %02x", ext_csd[i+j]);
1289 }
1290 printf("\n");
1291 }
1292
1293 return ret;
1294}
1295
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001296int do_sanitize(int nargs, char **argv)
1297{
1298 int fd, ret;
1299 char *device;
1300
1301 CHECK(nargs != 2, "Usage: mmc sanitize </path/to/mmcblkX>\n",
1302 exit(1));
1303
1304 device = argv[1];
1305
1306 fd = open(device, O_RDWR);
1307 if (fd < 0) {
1308 perror("open");
1309 exit(1);
1310 }
1311
1312 ret = write_extcsd_value(fd, EXT_CSD_SANITIZE_START, 1);
1313 if (ret) {
1314 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1315 1, EXT_CSD_SANITIZE_START, device);
1316 exit(1);
1317 }
1318
1319 return ret;
1320
1321}
1322
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001323static const char* const mmc_ffu_hack_names[] = {
1324 [MMC_OVERRIDE_FFU_ARG] = "ffu_arg",
1325};
1326
Gwendal Grignou771984c2014-07-01 12:46:18 -07001327int do_emmc50_ffu (int nargs, char **argv)
1328{
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001329 int fd, ret, i, argc=1, ffu_hack=0;
1330 char *device, *type, *path;
1331 __u64 value;
1332 union {
1333 __u8 data[FFU_DATA_SIZE];
1334 struct mmc_ffu_args ffu_args;
1335 } ffu_data;
1336 struct mmc_ffu_args *ffu_args = &ffu_data.ffu_args;
Gwendal Grignou0f757342014-10-16 16:52:46 -07001337 struct mmc_ioc_cmd mmc_ioc_cmd;
Gwendal Grignou771984c2014-07-01 12:46:18 -07001338
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001339 while (!strcmp("-k", argv[argc])) {
1340 ret = sscanf(argv[++argc], "%m[^:]:0x%llx", &type, &value);
1341 if (ret < 1) {
1342 fprintf(stderr, "Invalid hack: %s\n", argv[argc]);
1343 exit(1);
1344 }
1345 for (i = 0; i < MMC_HACK_LEN; i++) {
1346 if (!strcmp(type, mmc_ffu_hack_names[i])) {
1347 ffu_args->hack[ffu_hack].type = i;
1348 if (ret == 2) {
1349 ffu_args->hack[ffu_hack].value = value;
1350 }
1351 ffu_hack++;
1352 if (ffu_hack * sizeof(struct mmc_ffu_hack) +
1353 sizeof(struct mmc_ffu_args) >
1354 FFU_DATA_SIZE) {
1355 fprintf(stderr, "Too many %d hacks",
1356 ffu_hack);
1357 exit(1);
1358 }
1359 break;
1360 }
1361 }
1362 if (i == MMC_HACK_LEN) {
1363 fprintf(stderr, "Hack type %s not found\n", type);
1364 fprintf(stderr, "Supported types are: ");
1365 for (i = 0; i < MMC_HACK_LEN; i++)
1366 fprintf(stderr, "%s%s", mmc_ffu_hack_names[i],
1367 (i == MMC_HACK_LEN-1 ? "\n": ", "));
Gwendal Grignou771984c2014-07-01 12:46:18 -07001368
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001369 exit(1);
1370 }
1371 free(type);
1372 argc++;
1373 }
1374 ffu_args->hack_nb = ffu_hack;
1375
1376 path = argv[argc++];
1377 if (strlen(path) >= FFU_NAME_LEN) {
Gwendal Grignou771984c2014-07-01 12:46:18 -07001378 fprintf(stderr, "Filename \"%.20s\" too long\n", path);
1379 exit(1);
1380 }
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001381 strcpy(ffu_args->name, path);
1382 device = argv[argc++];
Gwendal Grignou771984c2014-07-01 12:46:18 -07001383 fd = open(device, O_RDWR);
1384 if (fd < 0) {
1385 perror("open");
1386 exit(1);
1387 }
1388
Gwendal Grignou0f757342014-10-16 16:52:46 -07001389 /* prepare and send ioctl */
1390 memset(&mmc_ioc_cmd, 0, sizeof(mmc_ioc_cmd));
1391 mmc_ioc_cmd.opcode = MMC_FFU_INVOKE_OP;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001392 mmc_ioc_cmd.blksz = FFU_DATA_SIZE;
Gwendal Grignou0f757342014-10-16 16:52:46 -07001393 mmc_ioc_cmd.blocks = 1;
1394 mmc_ioc_cmd.arg = 0;
1395 mmc_ioc_cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
1396 mmc_ioc_cmd.write_flag = 1;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001397 mmc_ioc_cmd_set_data(mmc_ioc_cmd, ffu_args);
Gwendal Grignou0f757342014-10-16 16:52:46 -07001398 ret = ioctl(fd, MMC_IOC_CMD, &mmc_ioc_cmd);
Gwendal Grignou771984c2014-07-01 12:46:18 -07001399 if (ret) {
1400 fprintf(stderr, "FFU install failed : %s\n", strerror(errno));
1401 exit(1);
1402 }
1403
1404 close(fd);
1405 return 0;
1406}
1407