Skip to content

Commit

Permalink
mtd: gpmi: set the BCH's geometry with the ecc info
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Huang Shijie authored and David Woodhouse committed Aug 30, 2013
1 parent d1048aa commit 2febcdf
Showing 1 changed file with 131 additions and 1 deletion.
132 changes: 131 additions & 1 deletion drivers/mtd/nand/gpmi-nand/gpmi-nand.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 2febcdf

Please sign in to comment.