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:
parent
d1048aaf6d
commit
2febcdf84b
|
@ -111,7 +111,132 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
|
||||||
return true;
|
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 bch_geometry *geo = &this->bch_geometry;
|
||||||
struct mtd_info *mtd = &this->mtd;
|
struct mtd_info *mtd = &this->mtd;
|
||||||
|
@ -223,6 +348,11 @@ int common_nfc_set_geometry(struct gpmi_nand_data *this)
|
||||||
return 0;
|
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)
|
struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
|
||||||
{
|
{
|
||||||
int chipnr = this->current_chip;
|
int chipnr = this->current_chip;
|
||||||
|
|
Loading…
Reference in a new issue