summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Gardiner <bengardiner@nanometrics.ca>2013-05-30 17:12:51 -0400
committerChris Ball <cjb@laptop.org>2013-06-27 11:11:05 -0400
commitd91d3698c6464a83b7c301eb84da109f9f94b54c (patch)
tree2590231379ba9e68639fdd692db032c2258e91d2
parentf82e27a1b11e6fb52565b61827563316dcbb2cc4 (diff)
downloadmmc-utils-d91d3698c6464a83b7c301eb84da109f9f94b54c.tar.gz
support setting the OTP enhanced user area parameters
Signed-off-by: Ben Gardiner <bengardiner@nanometrics.ca> Signed-off-by: Chris Ball <cjb@laptop.org>
-rw-r--r--mmc.c5
-rw-r--r--mmc.h3
-rw-r--r--mmc_cmds.c170
-rw-r--r--mmc_cmds.h1
4 files changed, 177 insertions, 2 deletions
diff --git a/mmc.c b/mmc.c
index dcbab18..725b842 100644
--- a/mmc.c
+++ b/mmc.c
@@ -70,6 +70,11 @@ static struct Command commands[] = {
"Set the eMMC data sector size to 4KB by disabling emulation on\n<device>.",
NULL
},
+ { do_enh_area_set, -4,
+ "enh_area set", "<-y|-n> " "<start KiB> " "<length KiB> " "<device>\n"
+ "Enable the enhanced user area for the <device>.\nDry-run only unless -y is passed.\nNOTE! This is a one-time programmable (unreversible) change.",
+ NULL
+ },
{ do_status_get, -1,
"status get", "<device>\n"
"Print the response to STATUS_SEND (CMD13).",
diff --git a/mmc.h b/mmc.h
index aa67242..ad07b44 100644
--- a/mmc.h
+++ b/mmc.h
@@ -44,12 +44,14 @@
#define EXT_CSD_PART_SWITCH_TIME 199
#define EXT_CSD_BOOT_CFG 179
#define EXT_CSD_PART_CONFIG 179
+#define EXT_CSD_ERASE_GROUP_DEF 175
#define EXT_CSD_BOOT_WP 173
#define EXT_CSD_WR_REL_PARAM 166
#define EXT_CSD_SANITIZE_START 165
#define EXT_CSD_BKOPS_EN 163 /* R/W */
#define EXT_CSD_RST_N_FUNCTION 162 /* R/W */
#define EXT_CSD_PARTITIONING_SUPPORT 160 /* RO */
+#define EXT_CSD_PARTITIONS_ATTRIBUTE 156 /* R/W */
#define EXT_CSD_PARTITION_SETTING_COMPLETED 155 /* R/W */
#define EXT_CSD_ENH_SIZE_MULT_2 142
#define EXT_CSD_ENH_SIZE_MULT_1 141
@@ -99,6 +101,7 @@
#define EXT_CSD_PART_CONFIG_ACC_ACK (0x40)
#define EXT_CSD_PARTITIONING_EN (1<<0)
#define EXT_CSD_ENH_ATTRIBUTE_EN (1<<1)
+#define EXT_CSD_ENH_USR (1<<0)
/* From kernel linux/mmc/core.h */
#define MMC_RSP_PRESENT (1 << 0)
diff --git a/mmc_cmds.c b/mmc_cmds.c
index 3483c87..ba4f9cf 100644
--- a/mmc_cmds.c
+++ b/mmc_cmds.c
@@ -449,6 +449,172 @@ unsigned int get_hc_erase_grp_size(__u8 *ext_csd)
return ext_csd[224];
}
+int do_enh_area_set(int nargs, char **argv)
+{
+ __u8 value;
+ __u8 ext_csd[512];
+ int fd, ret;
+ char *device;
+ int dry_run = 1;
+ unsigned int start_kib, length_kib, enh_start_addr, enh_size_mult;
+ unsigned long align;
+
+ CHECK(nargs != 5, "Usage: mmc enh_area set <-y|-n> <start KiB> <length KiB> "
+ "</path/to/mmcblkX>\n", exit(1));
+
+ if (!strcmp("-y", argv[1]))
+ dry_run = 0;
+
+ start_kib = strtol(argv[2], NULL, 10);
+ length_kib = strtol(argv[3], NULL, 10);
+ device = argv[4];
+
+ fd = open(device, O_RDWR);
+ if (fd < 0) {
+ perror("open");
+ exit(1);
+ }
+
+ ret = read_extcsd(fd, ext_csd);
+ if (ret) {
+ fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
+ exit(1);
+ }
+
+ /* assert ENH_ATTRIBUTE_EN */
+ if (!(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & EXT_CSD_ENH_ATTRIBUTE_EN))
+ {
+ printf(" Device cannot have enhanced tech.\n");
+ exit(1);
+ }
+
+ /* assert not PARTITION_SETTING_COMPLETED */
+ if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED])
+ {
+ printf(" Device is already partitioned\n");
+ exit(1);
+ }
+
+ align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd);
+
+ enh_size_mult = (length_kib + align/2l) / align;
+
+ enh_start_addr = start_kib * 1024 / (is_blockaddresed(ext_csd) ? 512 : 1);
+ enh_start_addr /= align;
+ enh_start_addr *= align;
+
+ /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */
+ ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1);
+ if (ret) {
+ fprintf(stderr, "Could not write 0x1 to "
+ "EXT_CSD[%d] in %s\n",
+ EXT_CSD_ERASE_GROUP_DEF, device);
+ exit(1);
+ }
+
+ /* write to ENH_START_ADDR and ENH_SIZE_MULT and PARTITIONS_ATTRIBUTE's ENH_USR bit */
+ value = (enh_start_addr >> 24) & 0xff;
+ ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_3, value);
+ if (ret) {
+ fprintf(stderr, "Could not write 0x%02x to "
+ "EXT_CSD[%d] in %s\n", value,
+ EXT_CSD_ENH_START_ADDR_3, device);
+ exit(1);
+ }
+ value = (enh_start_addr >> 16) & 0xff;
+ ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_2, value);
+ if (ret) {
+ fprintf(stderr, "Could not write 0x%02x to "
+ "EXT_CSD[%d] in %s\n", value,
+ EXT_CSD_ENH_START_ADDR_2, device);
+ exit(1);
+ }
+ value = (enh_start_addr >> 8) & 0xff;
+ ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_1, value);
+ if (ret) {
+ fprintf(stderr, "Could not write 0x%02x to "
+ "EXT_CSD[%d] in %s\n", value,
+ EXT_CSD_ENH_START_ADDR_1, device);
+ exit(1);
+ }
+ value = enh_start_addr & 0xff;
+ ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_0, value);
+ if (ret) {
+ fprintf(stderr, "Could not write 0x%02x to "
+ "EXT_CSD[%d] in %s\n", value,
+ EXT_CSD_ENH_START_ADDR_0, device);
+ exit(1);
+ }
+
+ value = (enh_size_mult >> 16) & 0xff;
+ ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_2, value);
+ if (ret) {
+ fprintf(stderr, "Could not write 0x%02x to "
+ "EXT_CSD[%d] in %s\n", value,
+ EXT_CSD_ENH_SIZE_MULT_2, device);
+ exit(1);
+ }
+ value = (enh_size_mult >> 8) & 0xff;
+ ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_1, value);
+ if (ret) {
+ fprintf(stderr, "Could not write 0x%02x to "
+ "EXT_CSD[%d] in %s\n", value,
+ EXT_CSD_ENH_SIZE_MULT_1, device);
+ exit(1);
+ }
+ value = enh_size_mult & 0xff;
+ ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_0, value);
+ if (ret) {
+ fprintf(stderr, "Could not write 0x%02x to "
+ "EXT_CSD[%d] in %s\n", value,
+ EXT_CSD_ENH_SIZE_MULT_0, device);
+ exit(1);
+ }
+
+ ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, EXT_CSD_ENH_USR);
+ if (ret) {
+ fprintf(stderr, "Could not write EXT_CSD_ENH_USR to "
+ "EXT_CSD[%d] in %s\n",
+ EXT_CSD_PARTITIONS_ATTRIBUTE, device);
+ exit(1);
+ }
+
+ if (dry_run)
+ {
+ fprintf(stderr, "NOT setting PARTITION_SETTING_COMPLETED\n");
+ exit(1);
+ }
+
+ fprintf(stderr, "setting OTP PARTITION_SETTING_COMPLETED!\n");
+ ret = write_extcsd_value(fd, EXT_CSD_PARTITION_SETTING_COMPLETED, 0x1);
+ if (ret) {
+ fprintf(stderr, "Could not write 0x1 to "
+ "EXT_CSD[%d] in %s\n",
+ EXT_CSD_PARTITION_SETTING_COMPLETED, device);
+ exit(1);
+ }
+
+ __u32 response;
+ ret = send_status(fd, &response);
+ if (ret) {
+ fprintf(stderr, "Could not get response to SEND_STATUS from %s\n", device);
+ exit(1);
+ }
+
+ if (response & R1_SWITCH_ERROR)
+ {
+ fprintf(stderr, "Setting ENH_USR area failed on %s\n", device);
+ exit(1);
+ }
+
+ fprintf(stderr, "Setting ENH_USR area on %s SUCCESS\n", device);
+ fprintf(stderr, "Device power cycle needed for settings to take effect.\n"
+ "Confirm that PARTITION_SETTING_COMPLETED bit is set using 'extcsd read'"
+ "after power cycle\n");
+
+ return 0;
+}
+
int do_read_extcsd(int nargs, char **argv)
{
__u8 ext_csd[512], ext_csd_rev, reg;
@@ -723,7 +889,7 @@ int do_read_extcsd(int nargs, char **argv)
printf("Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x%02x]\n",
ext_csd[177]);
printf("High-density erase group definition"
- " [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[175]);
+ " [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[EXT_CSD_ERASE_GROUP_DEF]);
print_writeprotect_status(ext_csd);
@@ -766,7 +932,7 @@ int do_read_extcsd(int nargs, char **argv)
printf(" i.e. %lu KiB\n", 512l * reg * wp_sz * erase_sz);
printf("Partitions attribute [PARTITIONS_ATTRIBUTE]: 0x%02x\n",
- ext_csd[156]);
+ ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]);
reg = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED];
printf("Partitioning Setting"
" [PARTITION_SETTING_COMPLETED]: 0x%02x\n",
diff --git a/mmc_cmds.h b/mmc_cmds.h
index 4fb01c8..fa347f5 100644
--- a/mmc_cmds.h
+++ b/mmc_cmds.h
@@ -26,3 +26,4 @@ int do_hwreset_en(int nargs, char **argv);
int do_hwreset_dis(int nargs, char **argv);
int do_sanitize(int nargs, char **argv);
int do_status_get(int nargs, char **argv);
+int do_enh_area_set(int nargs, char **argv);