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…
	
	Add table
		Add a link
		
	
		Reference in a new issue