mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 08:19:28 +00:00
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
1 changed files with 131 additions and 1 deletions
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue