[U-Boot] [PATCH 0/2] mmc: Fix capacity calculation and erase_group_size

These patches are addressing two issues that I had after activating enhanced user area feature for a 4GB card so the capacity was less than 2 GB afterwards.
1. The capacity for a high density device is calculated in a wrong way. I was not able to find any hints for this implementation in JEDEC 4.41. Anyhow, if we have ext_csd revision >= 2 we can use SEC_COUNT field to calculate the size.
2. If we have a partitioned device, e.g. enhanced user area, the ERASE_GROUP_DEF bit in ext_csd shall be set (see JEDEC 4.41, chapter 7.2.3 Configure partitions). This bit defaults to "0" on power on.
Oliver Metz (2): When using a high capacity card with a density less than 2 GB a wrong size is shown. According to JEDEC 4.41 there is no differentiation for C_SIZE register between low and high capacity cards EXT_CSD_ERASE_GROUP_DEF is lost every time after a reset or reboot. Set it if device has enhanced partitions.
drivers/mmc/mmc.c | 49 +++++++++++++++++++++++-------------------------- include/mmc.h | 2 ++ 2 files changed, 25 insertions(+), 26 deletions(-)

When using a high capacity card with a density less than 2 GB a wrong size is calculated. According to JEDEC 4.41 there is no differentiation for C_SIZE register between low and high capacity cards. Use ext_csd sector count to calculate capacity instead.
Signed-off-by: Oliver Metz oliver@freetz.org --- drivers/mmc/mmc.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-)
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 84dae4d..ff11ff9 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -884,15 +884,9 @@ static int mmc_startup(struct mmc *mmc) else mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf);
- if (mmc->high_capacity) { - csize = (mmc->csd[1] & 0x3f) << 16 - | (mmc->csd[2] & 0xffff0000) >> 16; - cmult = 8; - } else { - csize = (mmc->csd[1] & 0x3ff) << 2 - | (mmc->csd[2] & 0xc0000000) >> 30; - cmult = (mmc->csd[2] & 0x00038000) >> 15; - } + csize = (mmc->csd[1] & 0x3ff) << 2 + | (mmc->csd[2] & 0xc0000000) >> 30; + cmult = (mmc->csd[2] & 0x00038000) >> 15;
mmc->capacity_user = (csize + 1) << (cmult + 2); mmc->capacity_user *= mmc->read_bl_len; @@ -927,18 +921,12 @@ static int mmc_startup(struct mmc *mmc) /* check ext_csd version and capacity */ err = mmc_send_ext_csd(mmc, ext_csd); if (!err && (ext_csd[EXT_CSD_REV] >= 2)) { - /* - * According to the JEDEC Standard, the value of - * ext_csd's capacity is valid if the value is more - * than 2GB - */ capacity = ext_csd[EXT_CSD_SEC_CNT] << 0 | ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | ext_csd[EXT_CSD_SEC_CNT + 2] << 16 | ext_csd[EXT_CSD_SEC_CNT + 3] << 24; capacity *= MMC_MAX_BLOCK_LEN; - if ((capacity >> 20) > 2 * 1024) - mmc->capacity_user = capacity; + mmc->capacity_user = capacity; }
switch (ext_csd[EXT_CSD_REV]) {

Hi Oliver,
On Oct 1, 2013, at 9:32 PM, Oliver Metz wrote:
When using a high capacity card with a density less than 2 GB a wrong size is calculated. According to JEDEC 4.41 there is no differentiation for C_SIZE register between low and high capacity cards. Use ext_csd sector count to calculate capacity instead.
Signed-off-by: Oliver Metz oliver@freetz.org
drivers/mmc/mmc.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-)
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 84dae4d..ff11ff9 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -884,15 +884,9 @@ static int mmc_startup(struct mmc *mmc) else mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf);
- if (mmc->high_capacity) {
csize = (mmc->csd[1] & 0x3f) << 16
| (mmc->csd[2] & 0xffff0000) >> 16;
cmult = 8;
- } else {
csize = (mmc->csd[1] & 0x3ff) << 2
| (mmc->csd[2] & 0xc0000000) >> 30;
cmult = (mmc->csd[2] & 0x00038000) >> 15;
- }
csize = (mmc->csd[1] & 0x3ff) << 2
| (mmc->csd[2] & 0xc0000000) >> 30;
cmult = (mmc->csd[2] & 0x00038000) >> 15;
mmc->capacity_user = (csize + 1) << (cmult + 2); mmc->capacity_user *= mmc->read_bl_len;
@@ -927,18 +921,12 @@ static int mmc_startup(struct mmc *mmc) /* check ext_csd version and capacity */ err = mmc_send_ext_csd(mmc, ext_csd); if (!err && (ext_csd[EXT_CSD_REV] >= 2)) {
/*
* According to the JEDEC Standard, the value of
* ext_csd's capacity is valid if the value is more
* than 2GB
*/ capacity = ext_csd[EXT_CSD_SEC_CNT] << 0 | ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | ext_csd[EXT_CSD_SEC_CNT + 2] << 16 | ext_csd[EXT_CSD_SEC_CNT + 3] << 24; capacity *= MMC_MAX_BLOCK_LEN;
if ((capacity >> 20) > 2 * 1024)
mmc->capacity_user = capacity;
mmc->capacity_user = capacity;
}
switch (ext_csd[EXT_CSD_REV]) {
-- 1.8.4
Something's not right with this:
U-Boot SPL 2013.10-00105-g2ea5f3f (Oct 31 2013 - 09:24:02) reading args MMC: block number 0x15c exceeds max(0x100) spl: error reading image args, err - -1 reading u-boot.img MMC: block number 0x15c exceeds max(0x100) spl: error reading image u-boot.img, err - -1 ### ERROR ### Please RESET the board ###
This happens on beaglebone black for both external MMC card (Kingston 4GB) and the internal eMMC part.
When I have some free time I'll try to debug this, but for the moment the patch is rejected.
Regards
-- Pantelis

EXT_CSD_ERASE_GROUP_DEF is lost every time after a reset or power off. Set it if device has enhanced partitions.
Signed-off-by: Oliver Metz oliver@freetz.org --- drivers/mmc/mmc.c | 17 +++++++++++++---- include/mmc.h | 2 ++ 2 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index ff11ff9..17c6b11 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -948,15 +948,24 @@ static int mmc_startup(struct mmc *mmc) }
/* - * Check whether GROUP_DEF is set, if yes, read out - * group size from ext_csd directly, or calculate - * the group size from the csd value. + * Host needs to enable ERASE_GRP_DEF bit if device is + * partitioned. This bit will be lost every time after a reset + * or power off. This will affect erase size. */ - if (ext_csd[EXT_CSD_ERASE_GROUP_DEF]) { + if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) && + (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) { + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_ERASE_GROUP_DEF, 1); + + if (err) + return err; + + /* Read out group size from ext_csd */ mmc->erase_grp_size = ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * MMC_MAX_BLOCK_LEN * 1024; } else { + /* Calculate the group size from the csd value. */ int erase_gsz, erase_gmul; erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10; erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5; diff --git a/include/mmc.h b/include/mmc.h index 214b9ed..cb558da 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -148,6 +148,7 @@ * EXT_CSD fields */ #define EXT_CSD_GP_SIZE_MULT 143 /* R/W */ +#define EXT_CSD_PARTITIONS_ATTRIBUTE 156 /* R/W */ #define EXT_CSD_PARTITIONING_SUPPORT 160 /* RO */ #define EXT_CSD_RPMB_MULT 168 /* RO */ #define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */ @@ -210,6 +211,7 @@ #define MMCPART_NOAVAILABLE (0xff) #define PART_ACCESS_MASK (0x7) #define PART_SUPPORT (0x1) +#define PART_ENH_ATTRIB (0x1f)
/* Maximum block size for MMC */ #define MMC_MAX_BLOCK_LEN 512

Hi Oliver,
On Oct 1, 2013, at 9:32 PM, Oliver Metz wrote:
EXT_CSD_ERASE_GROUP_DEF is lost every time after a reset or power off. Set it if device has enhanced partitions.
Signed-off-by: Oliver Metz oliver@freetz.org
drivers/mmc/mmc.c | 17 +++++++++++++---- include/mmc.h | 2 ++ 2 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index ff11ff9..17c6b11 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -948,15 +948,24 @@ static int mmc_startup(struct mmc *mmc) }
/*
* Check whether GROUP_DEF is set, if yes, read out
* group size from ext_csd directly, or calculate
* the group size from the csd value.
* Host needs to enable ERASE_GRP_DEF bit if device is
* partitioned. This bit will be lost every time after a reset
*/* or power off. This will affect erase size.
if (ext_csd[EXT_CSD_ERASE_GROUP_DEF]) {
if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) &&
(ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) {
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_ERASE_GROUP_DEF, 1);
if (err)
return err;
} else {/* Read out group size from ext_csd */ mmc->erase_grp_size = ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * MMC_MAX_BLOCK_LEN * 1024;
/* Calculate the group size from the csd value. */ int erase_gsz, erase_gmul; erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10; erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5;
diff --git a/include/mmc.h b/include/mmc.h index 214b9ed..cb558da 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -148,6 +148,7 @@
- EXT_CSD fields
*/ #define EXT_CSD_GP_SIZE_MULT 143 /* R/W */ +#define EXT_CSD_PARTITIONS_ATTRIBUTE 156 /* R/W */ #define EXT_CSD_PARTITIONING_SUPPORT 160 /* RO */ #define EXT_CSD_RPMB_MULT 168 /* RO */ #define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */ @@ -210,6 +211,7 @@ #define MMCPART_NOAVAILABLE (0xff) #define PART_ACCESS_MASK (0x7) #define PART_SUPPORT (0x1) +#define PART_ENH_ATTRIB (0x1f)
/* Maximum block size for MMC */
#define MMC_MAX_BLOCK_LEN 512
1.8.4
Applied, thanks.
Acked-by: Pantelis Antoniou panto@antoniou-consulting.com
participants (2)
-
Oliver Metz
-
Pantelis Antoniou