blob: 3ea1a61470c0fce2ebbe36459acf548f059c9a71 [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 */
726 };
727 int boot_access;
728 const char* boot_access_str[] = {
729 "No access to boot partition", /* 0 */
730 "R/W Boot Partition 1", /* 1 */
731 "R/W Boot Partition 2", /* 2 */
732 "R/W Replay Protected Memory Block (RPMB)", /* 3 */
733 };
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500734
Chris Ball8ba44662012-04-19 13:22:54 -0400735 CHECK(nargs != 2, "Usage: mmc extcsd read </path/to/mmcblkX>\n",
736 exit(1));
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -0500737
738 device = argv[1];
739
740 fd = open(device, O_RDWR);
741 if (fd < 0) {
742 perror("open");
743 exit(1);
744 }
745
746 ret = read_extcsd(fd, ext_csd);
747 if (ret) {
748 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
749 exit(1);
750 }
751
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100752 ext_csd_rev = ext_csd[192];
753
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800754 if ((ext_csd_rev < sizeof(ver_str)/sizeof(char*)) &&
755 (ext_csd_rev != 4))
756 str = ver_str[ext_csd_rev];
757 else
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100758 goto out_free;
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800759
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100760 printf("=============================================\n");
761 printf(" Extended CSD rev 1.%d (MMC %s)\n", ext_csd_rev, str);
762 printf("=============================================\n\n");
763
764 if (ext_csd_rev < 3)
765 goto out_free; /* No ext_csd */
766
767 /* Parse the Extended CSD registers.
768 * Reserved bit should be read as "0" in case of spec older
769 * than A441.
770 */
771 reg = ext_csd[EXT_CSD_S_CMD_SET];
772 printf("Card Supported Command sets [S_CMD_SET: 0x%02x]\n", reg);
773 if (!reg)
Chris Ballb9c7a172012-02-20 12:34:25 -0500774 printf(" - Standard MMC command sets\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100775
776 reg = ext_csd[EXT_CSD_HPI_FEATURE];
777 printf("HPI Features [HPI_FEATURE: 0x%02x]: ", reg);
778 if (reg & EXT_CSD_HPI_SUPP) {
779 if (reg & EXT_CSD_HPI_IMPL)
Chris Ballb9c7a172012-02-20 12:34:25 -0500780 printf("implementation based on CMD12\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100781 else
782 printf("implementation based on CMD13\n");
783 }
784
785 printf("Background operations support [BKOPS_SUPPORT: 0x%02x]\n",
786 ext_csd[502]);
787
788 if (ext_csd_rev >= 6) {
789 printf("Max Packet Read Cmd [MAX_PACKED_READS: 0x%02x]\n",
790 ext_csd[501]);
791 printf("Max Packet Write Cmd [MAX_PACKED_WRITES: 0x%02x]\n",
792 ext_csd[500]);
793 printf("Data TAG support [DATA_TAG_SUPPORT: 0x%02x]\n",
794 ext_csd[499]);
795
796 printf("Data TAG Unit Size [TAG_UNIT_SIZE: 0x%02x]\n",
797 ext_csd[498]);
798 printf("Tag Resources Size [TAG_RES_SIZE: 0x%02x]\n",
799 ext_csd[497]);
800 printf("Context Management Capabilities"
801 " [CONTEXT_CAPABILITIES: 0x%02x]\n", ext_csd[496]);
802 printf("Large Unit Size [LARGE_UNIT_SIZE_M1: 0x%02x]\n",
803 ext_csd[495]);
804 printf("Extended partition attribute support"
805 " [EXT_SUPPORT: 0x%02x]\n", ext_csd[494]);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800806 }
807 if (ext_csd_rev >= 7) {
808 int j;
809 int eol_info;
810 char* eol_info_str[] = {
811 "Not Defined", /* 0 */
812 "Normal", /* 1 */
813 "Warning", /* 2 */
814 "Urgent", /* 3 */
815 };
816
817 printf("Supported modes [SUPPORTED_MODES: 0x%02x]\n",
818 ext_csd[493]);
819 printf("FFU features [FFU_FEATURES: 0x%02x]\n",
820 ext_csd[492]);
821 printf("Operation codes timeout"
822 " [OPERATION_CODE_TIMEOUT: 0x%02x]\n",
823 ext_csd[491]);
824 printf("FFU Argument [FFU_ARG: 0x%08x]\n",
825 get_word_from_ext_csd(&ext_csd[487]));
826 printf("Number of FW sectors correctly programmed"
827 " [NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED: %d]\n",
828 get_word_from_ext_csd(&ext_csd[302]));
829 printf("Vendor proprietary health report:\n");
830 for (j = 301; j >= 270; j--)
831 printf("[VENDOR_PROPRIETARY_HEALTH_REPORT[%d]]:"
832 " 0x%02x\n", j, ext_csd[j]);
833 for (j = 269; j >= 268; j--) {
834 __u8 life_used=ext_csd[j];
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -0800835 char est_type = 'B' + (j - 269);
836 printf("Device life time estimation type %c"
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800837 " [DEVICE_LIFE_TIME_EST_TYP_%c: 0x%02x]\n",
Puthikorn Voravootivat6bb37ea2014-03-03 17:55:51 -0800838 est_type, est_type, life_used);
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800839 if (life_used >= 0x1 && life_used <= 0xa)
840 printf(" i.e. %d%% - %d%% device life time"
841 " used\n",
842 (life_used - 1) * 10, life_used * 10);
843 else if (life_used == 0xb)
844 printf(" i.e. Exceeded its maximum estimated"
845 " device life time\n");
846 }
847 eol_info = ext_csd[267];
848 printf("Pre EOL information [PRE_EOL_INFO: 0x%02x]\n",
849 eol_info);
850 if (eol_info < sizeof(eol_info_str)/sizeof(char*))
851 printf(" i.e. %s\n", eol_info_str[eol_info]);
852 else
853 printf(" i.e. Reserved\n");
854
855 printf("Optimal read size [OPTIMAL_READ_SIZE: 0x%02x]\n",
856 ext_csd[266]);
857 printf("Optimal write size [OPTIMAL_WRITE_SIZE: 0x%02x]\n",
858 ext_csd[265]);
859 printf("Optimal trim unit size"
860 " [OPTIMAL_TRIM_UNIT_SIZE: 0x%02x]\n", ext_csd[264]);
861 printf("Device version [DEVICE_VERSION: 0x%02x - 0x%02x]\n",
862 ext_csd[263], ext_csd[262]);
863 printf("Firmware version:\n");
864 for (j = 261; j >= 254; j--)
865 printf("[FIRMWARE_VERSION[%d]]:"
866 " 0x%02x\n", j, ext_csd[j]);
867
868 printf("Power class for 200MHz, DDR at VCC= 3.6V"
869 " [PWR_CL_DDR_200_360: 0x%02x]\n", ext_csd[253]);
870 }
871 if (ext_csd_rev >= 6) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100872 printf("Generic CMD6 Timer [GENERIC_CMD6_TIME: 0x%02x]\n",
873 ext_csd[248]);
874 printf("Power off notification [POWER_OFF_LONG_TIME: 0x%02x]\n",
875 ext_csd[247]);
876 printf("Cache Size [CACHE_SIZE] is %d KiB\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -0800877 get_word_from_ext_csd(&ext_csd[249]));
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100878 }
879
880 /* A441: Reserved [501:247]
881 A43: reserved [246:229] */
882 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100883 printf("Background operations status"
Chris Ballb9c7a172012-02-20 12:34:25 -0500884 " [BKOPS_STATUS: 0x%02x]\n", ext_csd[246]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100885
886 /* CORRECTLY_PRG_SECTORS_NUM [245:242] TODO */
887
888 printf("1st Initialisation Time after programmed sector"
889 " [INI_TIMEOUT_AP: 0x%02x]\n", ext_csd[241]);
890
891 /* A441: reserved [240] */
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100892 printf("Power class for 52MHz, DDR at 3.6V"
893 " [PWR_CL_DDR_52_360: 0x%02x]\n", ext_csd[239]);
894 printf("Power class for 52MHz, DDR at 1.95V"
895 " [PWR_CL_DDR_52_195: 0x%02x]\n", ext_csd[238]);
896
897 /* A441: reserved [237-236] */
898
899 if (ext_csd_rev >= 6) {
900 printf("Power class for 200MHz at 3.6V"
901 " [PWR_CL_200_360: 0x%02x]\n", ext_csd[237]);
902 printf("Power class for 200MHz, at 1.95V"
903 " [PWR_CL_200_195: 0x%02x]\n", ext_csd[236]);
904 }
Chris Ballb9c7a172012-02-20 12:34:25 -0500905 printf("Minimum Performance for 8bit at 52MHz in DDR mode:\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100906 printf(" [MIN_PERF_DDR_W_8_52: 0x%02x]\n", ext_csd[235]);
907 printf(" [MIN_PERF_DDR_R_8_52: 0x%02x]\n", ext_csd[234]);
908 /* A441: reserved [233] */
909 printf("TRIM Multiplier [TRIM_MULT: 0x%02x]\n", ext_csd[232]);
910 printf("Secure Feature support [SEC_FEATURE_SUPPORT: 0x%02x]\n",
911 ext_csd[231]);
912 }
913 if (ext_csd_rev == 5) { /* Obsolete in 4.5 */
914 printf("Secure Erase Multiplier [SEC_ERASE_MULT: 0x%02x]\n",
915 ext_csd[230]);
916 printf("Secure TRIM Multiplier [SEC_TRIM_MULT: 0x%02x]\n",
917 ext_csd[229]);
918 }
919 reg = ext_csd[EXT_CSD_BOOT_INFO];
920 printf("Boot Information [BOOT_INFO: 0x%02x]\n", reg);
921 if (reg & EXT_CSD_BOOT_INFO_ALT)
922 printf(" Device supports alternative boot method\n");
923 if (reg & EXT_CSD_BOOT_INFO_DDR_DDR)
924 printf(" Device supports dual data rate during boot\n");
925 if (reg & EXT_CSD_BOOT_INFO_HS_MODE)
926 printf(" Device supports high speed timing during boot\n");
927
928 /* A441/A43: reserved [227] */
929 printf("Boot partition size [BOOT_SIZE_MULTI: 0x%02x]\n", ext_csd[226]);
930 printf("Access size [ACC_SIZE: 0x%02x]\n", ext_csd[225]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400931
932 reg = get_hc_erase_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100933 printf("High-capacity erase unit size [HC_ERASE_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400934 reg);
935 printf(" i.e. %u KiB\n", 512 * reg);
936
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100937 printf("High-capacity erase timeout [ERASE_TIMEOUT_MULT: 0x%02x]\n",
938 ext_csd[223]);
939 printf("Reliable write sector count [REL_WR_SEC_C: 0x%02x]\n",
940 ext_csd[222]);
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400941
942 reg = get_hc_wp_grp_size(ext_csd);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100943 printf("High-capacity W protect group size [HC_WP_GRP_SIZE: 0x%02x]\n",
Ben Gardinerf82e27a2013-05-30 17:12:50 -0400944 reg);
945 printf(" i.e. %lu KiB\n", 512l * get_hc_erase_grp_size(ext_csd) * reg);
946
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100947 printf("Sleep current (VCC) [S_C_VCC: 0x%02x]\n", ext_csd[220]);
948 printf("Sleep current (VCCQ) [S_C_VCCQ: 0x%02x]\n", ext_csd[219]);
949 /* A441/A43: reserved [218] */
950 printf("Sleep/awake timeout [S_A_TIMEOUT: 0x%02x]\n", ext_csd[217]);
951 /* A441/A43: reserved [216] */
Ben Gardiner4e850232013-05-30 17:12:49 -0400952
953 unsigned int sectors = get_sector_count(ext_csd);
954 printf("Sector Count [SEC_COUNT: 0x%08x]\n", sectors);
955 if (is_blockaddresed(ext_csd))
956 printf(" Device is block-addressed\n");
957 else
958 printf(" Device is NOT block-addressed\n");
959
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +0100960 /* A441/A43: reserved [211] */
961 printf("Minimum Write Performance for 8bit:\n");
962 printf(" [MIN_PERF_W_8_52: 0x%02x]\n", ext_csd[210]);
963 printf(" [MIN_PERF_R_8_52: 0x%02x]\n", ext_csd[209]);
964 printf(" [MIN_PERF_W_8_26_4_52: 0x%02x]\n", ext_csd[208]);
965 printf(" [MIN_PERF_R_8_26_4_52: 0x%02x]\n", ext_csd[207]);
966 printf("Minimum Write Performance for 4bit:\n");
967 printf(" [MIN_PERF_W_4_26: 0x%02x]\n", ext_csd[206]);
968 printf(" [MIN_PERF_R_4_26: 0x%02x]\n", ext_csd[205]);
969 /* A441/A43: reserved [204] */
970 printf("Power classes registers:\n");
971 printf(" [PWR_CL_26_360: 0x%02x]\n", ext_csd[203]);
972 printf(" [PWR_CL_52_360: 0x%02x]\n", ext_csd[202]);
973 printf(" [PWR_CL_26_195: 0x%02x]\n", ext_csd[201]);
974 printf(" [PWR_CL_52_195: 0x%02x]\n", ext_csd[200]);
975
976 /* A43: reserved [199:198] */
977 if (ext_csd_rev >= 5) {
978 printf("Partition switching timing "
979 "[PARTITION_SWITCH_TIME: 0x%02x]\n", ext_csd[199]);
980 printf("Out-of-interrupt busy timing"
981 " [OUT_OF_INTERRUPT_TIME: 0x%02x]\n", ext_csd[198]);
982 }
983
984 /* A441/A43: reserved [197] [195] [193] [190] [188]
985 * [186] [184] [182] [180] [176] */
986
987 if (ext_csd_rev >= 6)
988 printf("I/O Driver Strength [DRIVER_STRENGTH: 0x%02x]\n",
989 ext_csd[197]);
990
Oleg Matcovschi64f63a32013-05-23 17:11:07 -0700991 /* DEVICE_TYPE in A45, CARD_TYPE in A441 */
992 reg = ext_csd[196];
993 printf("Card Type [CARD_TYPE: 0x%02x]\n", reg);
994 if (reg & 0x20) printf(" HS200 Single Data Rate eMMC @200MHz 1.2VI/O\n");
995 if (reg & 0x10) printf(" HS200 Single Data Rate eMMC @200MHz 1.8VI/O\n");
996 if (reg & 0x08) printf(" HS Dual Data Rate eMMC @52MHz 1.2VI/O\n");
997 if (reg & 0x04) printf(" HS Dual Data Rate eMMC @52MHz 1.8V or 3VI/O\n");
998 if (reg & 0x02) printf(" HS eMMC @52MHz - at rated device voltage(s)\n");
999 if (reg & 0x01) printf(" HS eMMC @26MHz - at rated device voltage(s)\n");
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001000
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001001 printf("CSD structure version [CSD_STRUCTURE: 0x%02x]\n", ext_csd[194]);
1002 /* ext_csd_rev = ext_csd[192] (already done!!!) */
1003 printf("Command set [CMD_SET: 0x%02x]\n", ext_csd[191]);
1004 printf("Command set revision [CMD_SET_REV: 0x%02x]\n", ext_csd[189]);
1005 printf("Power class [POWER_CLASS: 0x%02x]\n", ext_csd[187]);
1006 printf("High-speed interface timing [HS_TIMING: 0x%02x]\n",
1007 ext_csd[185]);
1008 /* bus_width: ext_csd[183] not readable */
1009 printf("Erased memory content [ERASED_MEM_CONT: 0x%02x]\n",
1010 ext_csd[181]);
1011 reg = ext_csd[EXT_CSD_BOOT_CFG];
1012 printf("Boot configuration bytes [PARTITION_CONFIG: 0x%02x]\n", reg);
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001013 switch ((reg & EXT_CSD_BOOT_CFG_EN)>>3) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001014 case 0x0:
1015 printf(" Not boot enable\n");
1016 break;
1017 case 0x1:
1018 printf(" Boot Partition 1 enabled\n");
1019 break;
1020 case 0x2:
1021 printf(" Boot Partition 2 enabled\n");
1022 break;
1023 case 0x7:
1024 printf(" User Area Enabled for boot\n");
1025 break;
1026 }
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001027 boot_access = reg & EXT_CSD_BOOT_CFG_ACC;
1028 if (boot_access < sizeof(boot_access_str) / sizeof(char*))
1029 printf(" %s\n", boot_access_str[boot_access]);
1030 else
Mario Schuknecht8c0c40d2013-05-15 08:28:04 +02001031 printf(" Access to General Purpose partition %d\n",
Gwendal Grignou9b8d99c2014-01-28 13:48:05 -08001032 boot_access - 3);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001033
1034 printf("Boot config protection [BOOT_CONFIG_PROT: 0x%02x]\n",
1035 ext_csd[178]);
1036 printf("Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x%02x]\n",
1037 ext_csd[177]);
1038 printf("High-density erase group definition"
Ben Gardinerd91d3692013-05-30 17:12:51 -04001039 " [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[EXT_CSD_ERASE_GROUP_DEF]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001040
Chris Ballb9c7a172012-02-20 12:34:25 -05001041 print_writeprotect_status(ext_csd);
1042
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001043 if (ext_csd_rev >= 5) {
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001044 /* A441]: reserved [172] */
1045 printf("User area write protection register"
1046 " [USER_WP]: 0x%02x\n", ext_csd[171]);
1047 /* A441]: reserved [170] */
1048 printf("FW configuration [FW_CONFIG]: 0x%02x\n", ext_csd[169]);
1049 printf("RPMB Size [RPMB_SIZE_MULT]: 0x%02x\n", ext_csd[168]);
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001050
1051 reg = ext_csd[EXT_CSD_WR_REL_SET];
1052 const char * const fast = "existing data is at risk if a power "
1053 "failure occurs during a write operation";
1054 const char * const reliable = "the device protects existing "
1055 "data if a power failure occurs during a write "
1056 "operation";
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001057 printf("Write reliability setting register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001058 " [WR_REL_SET]: 0x%02x\n", reg);
1059
1060 printf(" user area: %s\n", reg & (1<<0) ? reliable : fast);
1061 int i;
1062 for (i = 1; i <= 4; i++) {
1063 printf(" partition %d: %s\n", i,
1064 reg & (1<<i) ? reliable : fast);
1065 }
1066
1067 reg = ext_csd[EXT_CSD_WR_REL_PARAM];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001068 printf("Write reliability parameter register"
Ben Gardiner4da1c0d2013-09-19 11:14:28 -04001069 " [WR_REL_PARAM]: 0x%02x\n", reg);
1070 if (reg & 0x01)
1071 printf(" Device supports writing EXT_CSD_WR_REL_SET\n");
1072 if (reg & 0x04)
1073 printf(" Device supports the enhanced def. of reliable "
1074 "write\n");
1075
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001076 /* sanitize_start ext_csd[165]]: not readable
1077 * bkops_start ext_csd[164]]: only writable */
1078 printf("Enable background operations handshake"
1079 " [BKOPS_EN]: 0x%02x\n", ext_csd[163]);
1080 printf("H/W reset function"
1081 " [RST_N_FUNCTION]: 0x%02x\n", ext_csd[162]);
1082 printf("HPI management [HPI_MGMT]: 0x%02x\n", ext_csd[161]);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001083 reg = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001084 printf("Partitioning Support [PARTITIONING_SUPPORT]: 0x%02x\n",
1085 reg);
Ben Gardiner82bd9502013-06-27 11:04:10 -04001086 if (reg & EXT_CSD_PARTITIONING_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001087 printf(" Device support partitioning feature\n");
1088 else
1089 printf(" Device NOT support partitioning feature\n");
Ben Gardiner82bd9502013-06-27 11:04:10 -04001090 if (reg & EXT_CSD_ENH_ATTRIBUTE_EN)
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001091 printf(" Device can have enhanced tech.\n");
1092 else
1093 printf(" Device cannot have enhanced tech.\n");
1094
Oliver Metz11f2cea2013-09-23 08:40:52 +02001095 regl = (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_2] << 16) |
Oliver Metz22f26412013-09-23 08:40:51 +02001096 (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_1] << 8) |
1097 ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT_0];
1098
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001099 printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n",
Oliver Metz11f2cea2013-09-23 08:40:52 +02001100 regl);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001101 unsigned int wp_sz = get_hc_wp_grp_size(ext_csd);
1102 unsigned int erase_sz = get_hc_erase_grp_size(ext_csd);
Oliver Metz11f2cea2013-09-23 08:40:52 +02001103 printf(" i.e. %lu KiB\n", 512l * regl * wp_sz * erase_sz);
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001104
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001105 printf("Partitions attribute [PARTITIONS_ATTRIBUTE]: 0x%02x\n",
Ben Gardinerd91d3692013-05-30 17:12:51 -04001106 ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]);
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001107 reg = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001108 printf("Partitioning Setting"
1109 " [PARTITION_SETTING_COMPLETED]: 0x%02x\n",
Ben Gardinera6cd98d2013-05-30 17:12:46 -04001110 reg);
1111 if (reg)
1112 printf(" Device partition setting complete\n");
1113 else
1114 printf(" Device partition setting NOT complete\n");
1115
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001116 printf("General Purpose Partition Size\n"
1117 " [GP_SIZE_MULT_4]: 0x%06x\n", (ext_csd[154] << 16) |
1118 (ext_csd[153] << 8) | ext_csd[152]);
1119 printf(" [GP_SIZE_MULT_3]: 0x%06x\n", (ext_csd[151] << 16) |
1120 (ext_csd[150] << 8) | ext_csd[149]);
1121 printf(" [GP_SIZE_MULT_2]: 0x%06x\n", (ext_csd[148] << 16) |
1122 (ext_csd[147] << 8) | ext_csd[146]);
1123 printf(" [GP_SIZE_MULT_1]: 0x%06x\n", (ext_csd[145] << 16) |
1124 (ext_csd[144] << 8) | ext_csd[143]);
1125
Oliver Metz11f2cea2013-09-23 08:40:52 +02001126 regl = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) |
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001127 (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) |
1128 ext_csd[EXT_CSD_ENH_SIZE_MULT_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001129 printf("Enhanced User Data Area Size"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001130 " [ENH_SIZE_MULT]: 0x%06x\n", regl);
1131 printf(" i.e. %lu KiB\n", 512l * regl *
Ben Gardinerf82e27a2013-05-30 17:12:50 -04001132 get_hc_erase_grp_size(ext_csd) *
1133 get_hc_wp_grp_size(ext_csd));
Ben Gardiner68f490b2013-05-30 17:12:48 -04001134
Oliver Metz11f2cea2013-09-23 08:40:52 +02001135 regl = (ext_csd[EXT_CSD_ENH_START_ADDR_3] << 24) |
Ben Gardiner68f490b2013-05-30 17:12:48 -04001136 (ext_csd[EXT_CSD_ENH_START_ADDR_2] << 16) |
1137 (ext_csd[EXT_CSD_ENH_START_ADDR_1] << 8) |
1138 ext_csd[EXT_CSD_ENH_START_ADDR_0];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001139 printf("Enhanced User Data Start Address"
Oliver Metz11f2cea2013-09-23 08:40:52 +02001140 " [ENH_START_ADDR]: 0x%06x\n", regl);
Ben Gardiner4e850232013-05-30 17:12:49 -04001141 printf(" i.e. %lu bytes offset\n", (is_blockaddresed(ext_csd) ?
Oliver Metz11f2cea2013-09-23 08:40:52 +02001142 1l : 512l) * regl);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001143
1144 /* A441]: reserved [135] */
1145 printf("Bad Block Management mode"
1146 " [SEC_BAD_BLK_MGMNT]: 0x%02x\n", ext_csd[134]);
1147 /* A441: reserved [133:0] */
1148 }
1149 /* B45 */
1150 if (ext_csd_rev >= 6) {
1151 int j;
1152 /* tcase_support ext_csd[132] not readable */
1153 printf("Periodic Wake-up [PERIODIC_WAKEUP]: 0x%02x\n",
1154 ext_csd[131]);
1155 printf("Program CID/CSD in DDR mode support"
1156 " [PROGRAM_CID_CSD_DDR_SUPPORT]: 0x%02x\n",
1157 ext_csd[130]);
1158
1159 for (j = 127; j >= 64; j--)
1160 printf("Vendor Specific Fields"
1161 " [VENDOR_SPECIFIC_FIELD[%d]]: 0x%02x\n",
1162 j, ext_csd[j]);
1163
Gwendal Grignoue966e672014-07-07 14:03:13 -07001164 reg = ext_csd[63];
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001165 printf("Native sector size [NATIVE_SECTOR_SIZE]: 0x%02x\n",
Gwendal Grignoue966e672014-07-07 14:03:13 -07001166 reg);
1167 if (reg == 0x00)
1168 printf(" i.e. 512 B\n");
1169 else if (reg == 0x01)
1170 printf(" i.e. 4 KiB\n");
1171 else
1172 printf(" i.e. Reserved\n");
1173
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001174 printf("Sector size emulation [USE_NATIVE_SECTOR]: 0x%02x\n",
1175 ext_csd[62]);
Gwendal Grignoue966e672014-07-07 14:03:13 -07001176 reg = ext_csd[61];
1177 printf("Sector size [DATA_SECTOR_SIZE]: 0x%02x\n", reg);
1178 if (reg == 0x00)
1179 printf(" i.e. 512 B\n");
1180 else if (reg == 0x01)
1181 printf(" i.e. 4 KiB\n");
1182 else
1183 printf(" i.e. Reserved\n");
1184
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001185 printf("1st initialization after disabling sector"
1186 " size emulation [INI_TIMEOUT_EMU]: 0x%02x\n",
1187 ext_csd[60]);
1188 printf("Class 6 commands control [CLASS_6_CTRL]: 0x%02x\n",
1189 ext_csd[59]);
1190 printf("Number of addressed group to be Released"
1191 "[DYNCAP_NEEDED]: 0x%02x\n", ext_csd[58]);
1192 printf("Exception events control"
1193 " [EXCEPTION_EVENTS_CTRL]: 0x%04x\n",
1194 (ext_csd[57] << 8) | ext_csd[56]);
1195 printf("Exception events status"
1196 "[EXCEPTION_EVENTS_STATUS]: 0x%04x\n",
1197 (ext_csd[55] << 8) | ext_csd[54]);
1198 printf("Extended Partitions Attribute"
1199 " [EXT_PARTITIONS_ATTRIBUTE]: 0x%04x\n",
1200 (ext_csd[53] << 8) | ext_csd[52]);
1201
1202 for (j = 51; j >= 37; j--)
1203 printf("Context configuration"
1204 " [CONTEXT_CONF[%d]]: 0x%02x\n", j, ext_csd[j]);
1205
1206 printf("Packed command status"
1207 " [PACKED_COMMAND_STATUS]: 0x%02x\n", ext_csd[36]);
1208 printf("Packed command failure index"
1209 " [PACKED_FAILURE_INDEX]: 0x%02x\n", ext_csd[35]);
1210 printf("Power Off Notification"
1211 " [POWER_OFF_NOTIFICATION]: 0x%02x\n", ext_csd[34]);
Oleg Matcovschi64f63a32013-05-23 17:11:07 -07001212 printf("Control to turn the Cache ON/OFF"
1213 " [CACHE_CTRL]: 0x%02x\n", ext_csd[33]);
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001214 /* flush_cache ext_csd[32] not readable */
1215 /*Reserved [31:0] */
1216 }
Gwendal Grignoue966e672014-07-07 14:03:13 -07001217 if (ext_csd_rev >= 7) {
1218 printf("Mode config [MODE_CONFIG: 0x%02x]\n", ext_csd[30]);
1219 printf("Mode operation codes [MODE_OPERATION_CODES: 0x%02x]\n",
1220 ext_csd[29]);
1221
1222 reg = ext_csd[26];
1223 printf("FFU status [FFU_STATUS: 0x%02x]\n", reg);
1224 switch (reg) {
1225 case 0x00:
1226 printf(" Success\n");
1227 break;
1228 case 0x10:
1229 printf(" General error\n");
1230 break;
1231 case 0x11:
1232 printf(" Firmware install error\n");
1233 break;
1234 case 0x12:
1235 printf(" Error in downloading firmware\n");
1236 break;
1237 default:
1238 printf(" Reserved\n");
1239 }
1240 printf("Pre loading data size [PRE_LOADING_DATA_SIZE] is"
1241 " %d sector size\n",
1242 get_word_from_ext_csd(&ext_csd[22]));
1243 printf("Max pre loading data size [MAX_PRE_LOADING_DATA_SIZE] is"
1244 " %d sector size\n",
1245 get_word_from_ext_csd(&ext_csd[18]));
1246 printf("Product state awareness enablement"
1247 " [PRODUCT_STATE_AWARENESS_ENABLEMENT: 0x%02x]\n",
1248 ext_csd[17]);
1249 printf("Secure Removal Type [SECURE_REMOVAL_TYPE: 0x%02x]\n",
1250 ext_csd[16]);
1251 }
Giuseppe CAVALLAROa5bf4a22012-02-20 09:45:29 +01001252
1253out_free:
Johan RUDHOLMa8bfde72012-02-12 11:46:44 -05001254 return ret;
1255}
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001256
Nick Sanders9d57aa72014-03-05 21:38:54 -08001257int do_dump_extcsd(int nargs, char **argv)
1258{
1259 __u8 ext_csd[EXT_CSD_SIZE];
1260 int fd, ret;
1261 char *device;
1262 int i, j;
1263
1264 CHECK(nargs != 2, "Usage: mmc extcsd dump </path/to/mmcblkX>\n",
1265 exit(1));
1266
1267 device = argv[1];
1268
1269 fd = open(device, O_RDWR);
1270 if (fd < 0) {
1271 perror("Failed to open mmc device");
1272 exit(1);
1273 }
1274
1275 ret = read_extcsd(fd, ext_csd);
1276 if (ret) {
1277 fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
1278 exit(1);
1279 }
1280
1281 /* Dump all bytes so that any undecoded or proprietary registers */
1282 /* can be acessed. */
1283 printf("EXT_CSD binary dump:\n");
1284 for (i = 0; i < EXT_CSD_SIZE; i+= 16) {
1285 printf(" %3d: %3x: ", i, i);
1286 for (j = 0; (j < 16) && (i + j < EXT_CSD_SIZE); j++) {
1287 printf(" %02x", ext_csd[i+j]);
1288 }
1289 printf("\n");
1290 }
1291
1292 return ret;
1293}
1294
Yaniv Gardi21bb4732013-05-26 13:25:33 -04001295int do_sanitize(int nargs, char **argv)
1296{
1297 int fd, ret;
1298 char *device;
1299
1300 CHECK(nargs != 2, "Usage: mmc sanitize </path/to/mmcblkX>\n",
1301 exit(1));
1302
1303 device = argv[1];
1304
1305 fd = open(device, O_RDWR);
1306 if (fd < 0) {
1307 perror("open");
1308 exit(1);
1309 }
1310
1311 ret = write_extcsd_value(fd, EXT_CSD_SANITIZE_START, 1);
1312 if (ret) {
1313 fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n",
1314 1, EXT_CSD_SANITIZE_START, device);
1315 exit(1);
1316 }
1317
1318 return ret;
1319
1320}
1321
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001322static const char* const mmc_ffu_hack_names[] = {
1323 [MMC_OVERRIDE_FFU_ARG] = "ffu_arg",
1324};
1325
Gwendal Grignou771984c2014-07-01 12:46:18 -07001326int do_emmc50_ffu (int nargs, char **argv)
1327{
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001328 int fd, ret, i, argc=1, ffu_hack=0;
1329 char *device, *type, *path;
1330 __u64 value;
1331 union {
1332 __u8 data[FFU_DATA_SIZE];
1333 struct mmc_ffu_args ffu_args;
1334 } ffu_data;
1335 struct mmc_ffu_args *ffu_args = &ffu_data.ffu_args;
Gwendal Grignou0f757342014-10-16 16:52:46 -07001336 struct mmc_ioc_cmd mmc_ioc_cmd;
Gwendal Grignou771984c2014-07-01 12:46:18 -07001337
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001338 while (!strcmp("-k", argv[argc])) {
1339 ret = sscanf(argv[++argc], "%m[^:]:0x%llx", &type, &value);
1340 if (ret < 1) {
1341 fprintf(stderr, "Invalid hack: %s\n", argv[argc]);
1342 exit(1);
1343 }
1344 for (i = 0; i < MMC_HACK_LEN; i++) {
1345 if (!strcmp(type, mmc_ffu_hack_names[i])) {
1346 ffu_args->hack[ffu_hack].type = i;
1347 if (ret == 2) {
1348 ffu_args->hack[ffu_hack].value = value;
1349 }
1350 ffu_hack++;
1351 if (ffu_hack * sizeof(struct mmc_ffu_hack) +
1352 sizeof(struct mmc_ffu_args) >
1353 FFU_DATA_SIZE) {
1354 fprintf(stderr, "Too many %d hacks",
1355 ffu_hack);
1356 exit(1);
1357 }
1358 break;
1359 }
1360 }
1361 if (i == MMC_HACK_LEN) {
1362 fprintf(stderr, "Hack type %s not found\n", type);
1363 fprintf(stderr, "Supported types are: ");
1364 for (i = 0; i < MMC_HACK_LEN; i++)
1365 fprintf(stderr, "%s%s", mmc_ffu_hack_names[i],
1366 (i == MMC_HACK_LEN-1 ? "\n": ", "));
Gwendal Grignou771984c2014-07-01 12:46:18 -07001367
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001368 exit(1);
1369 }
1370 free(type);
1371 argc++;
1372 }
1373 ffu_args->hack_nb = ffu_hack;
1374
1375 path = argv[argc++];
1376 if (strlen(path) >= FFU_NAME_LEN) {
Gwendal Grignou771984c2014-07-01 12:46:18 -07001377 fprintf(stderr, "Filename \"%.20s\" too long\n", path);
1378 exit(1);
1379 }
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001380 strcpy(ffu_args->name, path);
1381 device = argv[argc++];
Gwendal Grignou771984c2014-07-01 12:46:18 -07001382 fd = open(device, O_RDWR);
1383 if (fd < 0) {
1384 perror("open");
1385 exit(1);
1386 }
1387
Gwendal Grignou0f757342014-10-16 16:52:46 -07001388 /* prepare and send ioctl */
1389 memset(&mmc_ioc_cmd, 0, sizeof(mmc_ioc_cmd));
1390 mmc_ioc_cmd.opcode = MMC_FFU_INVOKE_OP;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001391 mmc_ioc_cmd.blksz = FFU_DATA_SIZE;
Gwendal Grignou0f757342014-10-16 16:52:46 -07001392 mmc_ioc_cmd.blocks = 1;
1393 mmc_ioc_cmd.arg = 0;
1394 mmc_ioc_cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
1395 mmc_ioc_cmd.write_flag = 1;
Gwendal Grignou0da2c512015-01-08 15:36:03 -08001396 mmc_ioc_cmd_set_data(mmc_ioc_cmd, ffu_args);
Gwendal Grignou0f757342014-10-16 16:52:46 -07001397 ret = ioctl(fd, MMC_IOC_CMD, &mmc_ioc_cmd);
Gwendal Grignou771984c2014-07-01 12:46:18 -07001398 if (ret) {
1399 fprintf(stderr, "FFU install failed : %s\n", strerror(errno));
1400 exit(1);
1401 }
1402
1403 close(fd);
1404 return 0;
1405}
1406