ARM: mmc: Added power-on write protection support

Change-Id: Iba7e4bfcbee3f5106ef7d0ecc64e8af175732f7c
Signed-off-by: Gokul Sriram Palanisamy <gokulsri@codeaurora.org>
This commit is contained in:
Gokul Sriram Palanisamy 2017-10-26 16:44:16 +05:30 committed by Gerrit - the friendly Code Review server
parent c0270eedf1
commit 201def52b9
3 changed files with 136 additions and 0 deletions

View file

@ -330,6 +330,31 @@ static int do_mmcrpmb(cmd_tbl_t *cmdtp, int flag,
}
#endif
static int do_mmc_protect (cmd_tbl_t *cmdtp, int flag,
int argc, char * const argv[])
{
struct mmc *mmc;
unsigned int ret;
unsigned int blk, cnt;
if (argc != 3)
return CMD_RET_USAGE;
blk = (unsigned int)simple_strtoul(argv[1], NULL, 16);
cnt = (unsigned int)simple_strtoul(argv[2], NULL, 16);
mmc = init_mmc_device(curr_device, false);
if (!mmc)
return CMD_RET_FAILURE;
ret = mmc_write_protect(mmc, blk, cnt, 1);
if (!ret)
printf("Offset: 0x%x Count: %d blocks\nDone!\n", blk, cnt);
return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
}
static int do_mmc_read(cmd_tbl_t *cmdtp, int flag,
int argc, char * const argv[])
{
@ -802,6 +827,7 @@ static cmd_tbl_t cmd_mmc[] = {
U_BOOT_CMD_MKENT(dev, 3, 0, do_mmc_dev, "", ""),
U_BOOT_CMD_MKENT(list, 1, 1, do_mmc_list, "", ""),
U_BOOT_CMD_MKENT(hwpartition, 28, 0, do_mmc_hwpartition, "", ""),
U_BOOT_CMD_MKENT(protect, 4, 0, do_mmc_protect, "", ""),
#ifdef CONFIG_SUPPORT_EMMC_BOOT
U_BOOT_CMD_MKENT(bootbus, 5, 0, do_mmc_bootbus, "", ""),
U_BOOT_CMD_MKENT(bootpart-resize, 4, 0, do_mmc_boot_resize, "", ""),
@ -858,6 +884,7 @@ U_BOOT_CMD(
" [check|set|complete] - mode, complete set partitioning completed\n"
" WARNING: Partitioning is a write-once setting once it is set to complete.\n"
" Power cycling is required to initialize partitions after set to complete.\n"
"mmc protect start_blk# cnt_blk\n"
#ifdef CONFIG_SUPPORT_EMMC_BOOT
"mmc bootbus dev boot_bus_width reset_boot_bus_width boot_mode\n"
" - Set the BOOT_BUS_WIDTH field of the specified device\n"

View file

@ -1181,6 +1181,8 @@ static int mmc_startup(struct mmc *mmc)
* For SD, its erase group is always one sector
*/
mmc->erase_grp_size = 1;
mmc->wp_grp_size = WP_GRP_SIZE(mmc->csd);
mmc->wp_grp_enable = WP_GRP_ENABLE(mmc->csd);
mmc->part_config = MMCPART_NOAVAILABLE;
if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) {
/* check ext_csd version and capacity */
@ -1836,6 +1838,87 @@ int mmc_initialize(bd_t *bis)
return 0;
}
int mmc_send_wp_set_clr(struct mmc *mmc, unsigned int start,
unsigned int size, int set_clr)
{
unsigned int err;
unsigned int wp_group_size, count, i;
struct mmc_cmd cmd;
wp_group_size = (mmc->wp_grp_size + 1) * mmc->erase_grp_size;
count = DIV_ROUND_UP(size, wp_group_size);
if (set_clr)
cmd.cmdidx = MMC_CMD_SET_WRITE_PROT;
else
cmd.cmdidx = MMC_CMD_CLR_WRITE_PROT;
cmd.resp_type = MMC_RSP_R1b;
for (i = 0; i < count; i++) {
cmd.cmdarg = start + (i * wp_group_size);
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err) {
printf("%s: Error at block 0x%lx - %d\n", __func__,
cmd.cmdarg, err);
return err;
}
if(MMC_ADDR_OUT_OF_RANGE(cmd.response[0])) {
printf("%s: mmc block(0x%lx) out of range", __func__,
cmd.cmdarg);
return -EINVAL;
}
err = mmc_send_status(mmc, MMC_RESP_TIMEOUT);
if (err)
return err;
}
return 0;
}
int mmc_write_protect(struct mmc *mmc, unsigned int start_blk,
unsigned int cnt_blk, int set_clr)
{
ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
int err;
unsigned int wp_group_size;
if (!mmc->wp_grp_enable)
return -1; /* group write protection is not supported */
err = mmc_send_ext_csd(mmc, ext_csd);
if (err) {
debug("ext_csd register cannot be retrieved\n");
return err;
}
if ((ext_csd[EXT_CSD_USER_WP] & EXT_CSD_US_PWR_WP_DIS)
|| (ext_csd[EXT_CSD_USER_WP] & EXT_CSD_US_PERM_WP_EN)) {
printf("User power-on write protection is disabled. \n");
return -1;
}
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_USER_WP,
EXT_CSD_US_PWR_WP_EN);
if (err) {
printf("Failed to enable user power-on write protection\n");
return err;
}
wp_group_size = (mmc->wp_grp_size + 1) * mmc->erase_grp_size;
if (!cnt_blk || start_blk % wp_group_size || cnt_blk % wp_group_size) {
printf("Error: Unaligned offset/count. offset/count should be aligned to 0x%x blocks\n", wp_group_size);
return -1;
}
err = mmc_send_wp_set_clr(mmc, start_blk, cnt_blk, set_clr);
return err;
}
#ifdef CONFIG_SUPPORT_EMMC_BOOT
/*
* This function changes the size of boot partition and the size of rpmb

View file

@ -90,6 +90,10 @@
#define MMC_CMD_SET_BLOCK_COUNT 23
#define MMC_CMD_WRITE_SINGLE_BLOCK 24
#define MMC_CMD_WRITE_MULTIPLE_BLOCK 25
#define MMC_CMD_SET_WRITE_PROT 28
#define MMC_CMD_CLR_WRITE_PROT 29
#define MMC_CMD_SEND_WRITE_PROT 30
#define MMC_CMD_SEND_WRITE_PROT_TYPE 31
#define MMC_CMD_ERASE_GROUP_START 35
#define MMC_CMD_ERASE_GROUP_END 36
#define MMC_CMD_ERASE 38
@ -167,6 +171,17 @@
#define SD_SWITCH_CHECK 0
#define SD_SWITCH_SWITCH 1
#define MMC_RESP_TIMEOUT 2000
#define MMC_ADDR_OUT_OF_RANGE(resp) ((resp >> 31) & 0x01)
/*
* CSD fields
*/
#define WP_GRP_ENABLE(csd) ((csd[3] & 0x80000000) >> 31)
#define WP_GRP_SIZE(csd) ((csd[2] & 0x0000001f))
#define ERASE_GRP_MULT(csd) ((csd[2] & 0x000003e0) >> 5)
#define ERASE_GRP_SIZE(csd) ((csd[2] & 0x00007c00) >> 10)
/*
* EXT_CSD fields
*/
@ -181,6 +196,7 @@
#define EXT_CSD_WR_REL_PARAM 166 /* R */
#define EXT_CSD_WR_REL_SET 167 /* R/W */
#define EXT_CSD_RPMB_MULT 168 /* RO */
#define EXT_CSD_USER_WP 171 /* R/W */
#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */
#define EXT_CSD_BOOT_BUS_WIDTH 177
#define EXT_CSD_PART_CONF 179 /* R/W */
@ -241,6 +257,11 @@
#define EXT_CSD_WR_DATA_REL_USR (1 << 0) /* user data area WR_REL */
#define EXT_CSD_WR_DATA_REL_GP(x) (1 << ((x)+1)) /* GP part (x+1) WR_REL */
#define EXT_CSD_US_PERM_WP_DIS (1 << 4)
#define EXT_CSD_US_PWR_WP_DIS (1 << 3)
#define EXT_CSD_US_PERM_WP_EN (1 << 2)
#define EXT_CSD_US_PWR_WP_EN (1 << 0)
#define R1_ILLEGAL_COMMAND (1 << 22)
#define R1_APP_CMD (1 << 5)
@ -379,6 +400,8 @@ struct mmc {
uint write_bl_len;
uint erase_grp_size; /* in 512-byte sectors */
uint hc_wp_grp_size; /* in 512-byte sectors */
uint wp_grp_size;
uint wp_grp_enable;
u64 capacity;
u64 capacity_user;
u64 capacity_boot;
@ -506,6 +529,9 @@ struct pci_device_id;
int pci_mmc_init(const char *name, struct pci_device_id *mmc_supported,
int num_ids);
int mmc_write_protect(struct mmc *mmc, unsigned int start_blk,
unsigned int cnt_blk, int set_clr);
/* Set block count limit because of 16 bit register limit on some hardware*/
#ifndef CONFIG_SYS_MMC_MAX_BLK_COUNT
#define CONFIG_SYS_MMC_MAX_BLK_COUNT 65535