mtd: gpmi: set the BCH's geometry with the ecc info

If the nand chip provides us the ECC info, we can use it firstly.
The set_geometry_by_ecc_info() will use the ECC info, and
calculate the parameters we need.

Rename the old code to legacy_set_geometry() which will takes effect
when there is no ECC info from the nand chip or we fails in the ECC info case.

Signed-off-by: Huang Shijie <b32955@freescale.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
This commit is contained in:
Huang Shijie 2013-05-17 11:17:34 +08:00 committed by David Woodhouse
parent d1048aaf6d
commit 2febcdf84b

View File

@ -111,7 +111,132 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
return true;
}
int common_nfc_set_geometry(struct gpmi_nand_data *this)
/*
* If we can get the ECC information from the nand chip, we do not
* need to calculate them ourselves.
*
* We may have available oob space in this case.
*/
static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this)
{
struct bch_geometry *geo = &this->bch_geometry;
struct mtd_info *mtd = &this->mtd;
struct nand_chip *chip = mtd->priv;
struct nand_oobfree *of = gpmi_hw_ecclayout.oobfree;
unsigned int block_mark_bit_offset;
if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0))
return false;
switch (chip->ecc_step_ds) {
case SZ_512:
geo->gf_len = 13;
break;
case SZ_1K:
geo->gf_len = 14;
break;
default:
dev_err(this->dev,
"unsupported nand chip. ecc bits : %d, ecc size : %d\n",
chip->ecc_strength_ds, chip->ecc_step_ds);
return false;
}
geo->ecc_chunk_size = chip->ecc_step_ds;
geo->ecc_strength = round_up(chip->ecc_strength_ds, 2);
if (!gpmi_check_ecc(this))
return false;
/* Keep the C >= O */
if (geo->ecc_chunk_size < mtd->oobsize) {
dev_err(this->dev,
"unsupported nand chip. ecc size: %d, oob size : %d\n",
chip->ecc_step_ds, mtd->oobsize);
return false;
}
/* The default value, see comment in the legacy_set_geometry(). */
geo->metadata_size = 10;
geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
/*
* Now, the NAND chip with 2K page(data chunk is 512byte) shows below:
*
* | P |
* |<----------------------------------------------------->|
* | |
* | (Block Mark) |
* | P' | | | |
* |<-------------------------------------------->| D | | O' |
* | |<---->| |<--->|
* V V V V V
* +---+----------+-+----------+-+----------+-+----------+-+-----+
* | M | data |E| data |E| data |E| data |E| |
* +---+----------+-+----------+-+----------+-+----------+-+-----+
* ^ ^
* | O |
* |<------------>|
* | |
*
* P : the page size for BCH module.
* E : The ECC strength.
* G : the length of Galois Field.
* N : The chunk count of per page.
* M : the metasize of per page.
* C : the ecc chunk size, aka the "data" above.
* P': the nand chip's page size.
* O : the nand chip's oob size.
* O': the free oob.
*
* The formula for P is :
*
* E * G * N
* P = ------------ + P' + M
* 8
*
* The position of block mark moves forward in the ECC-based view
* of page, and the delta is:
*
* E * G * (N - 1)
* D = (---------------- + M)
* 8
*
* Please see the comment in legacy_set_geometry().
* With the condition C >= O , we still can get same result.
* So the bit position of the physical block mark within the ECC-based
* view of the page is :
* (P' - D) * 8
*/
geo->page_size = mtd->writesize + geo->metadata_size +
(geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8;
/* The available oob size we have. */
if (geo->page_size < mtd->writesize + mtd->oobsize) {
of->offset = geo->page_size - mtd->writesize;
of->length = mtd->oobsize - of->offset;
mtd->oobavail = gpmi_hw_ecclayout.oobavail = of->length;
}
geo->payload_size = mtd->writesize;
geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4);
geo->auxiliary_size = ALIGN(geo->metadata_size, 4)
+ ALIGN(geo->ecc_chunk_count, 4);
if (!this->swap_block_mark)
return true;
/* For bit swap. */
block_mark_bit_offset = mtd->writesize * 8 -
(geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1)
+ geo->metadata_size * 8);
geo->block_mark_byte_offset = block_mark_bit_offset / 8;
geo->block_mark_bit_offset = block_mark_bit_offset % 8;
return true;
}
static int legacy_set_geometry(struct gpmi_nand_data *this)
{
struct bch_geometry *geo = &this->bch_geometry;
struct mtd_info *mtd = &this->mtd;
@ -223,6 +348,11 @@ int common_nfc_set_geometry(struct gpmi_nand_data *this)
return 0;
}
int common_nfc_set_geometry(struct gpmi_nand_data *this)
{
return set_geometry_by_ecc_info(this) ? 0 : legacy_set_geometry(this);
}
struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
{
int chipnr = this->current_chip;