@@ -266,6 +266,39 @@ static bool gpmi_check_ecc(struct gpmi_nand_data *this)
266266 return true;
267267}
268268
269+ /* check if bbm locates in data chunk rather than ecc chunk */
270+ static bool bbm_in_data_chunk (struct gpmi_nand_data * this ,
271+ unsigned int * chunk_num )
272+ {
273+ struct bch_geometry * geo = & this -> bch_geometry ;
274+ struct nand_chip * chip = & this -> nand ;
275+ struct mtd_info * mtd = nand_to_mtd (chip );
276+ unsigned int i , j ;
277+
278+ if (geo -> ecc0_chunk_size != geo -> eccn_chunk_size ) {
279+ dev_err (this -> dev ,
280+ "The size of ecc0_chunk must equal to eccn_chunk\n" );
281+ return false;
282+ }
283+
284+ i = (mtd -> writesize * 8 - geo -> metadata_size * 8 ) /
285+ (geo -> gf_len * geo -> ecc_strength +
286+ geo -> eccn_chunk_size * 8 );
287+
288+ j = (mtd -> writesize * 8 - geo -> metadata_size * 8 ) -
289+ (geo -> gf_len * geo -> ecc_strength +
290+ geo -> eccn_chunk_size * 8 ) * i ;
291+
292+ if (j < geo -> eccn_chunk_size * 8 ) {
293+ * chunk_num = i + 1 ;
294+ dev_dbg (this -> dev , "Set ecc to %d and bbm in chunk %d\n" ,
295+ geo -> ecc_strength , * chunk_num );
296+ return true;
297+ }
298+
299+ return false;
300+ }
301+
269302/*
270303 * If we can get the ECC information from the nand chip, we do not
271304 * need to calculate them ourselves.
@@ -415,6 +448,134 @@ static inline int get_ecc_strength(struct gpmi_nand_data *this)
415448 return round_down (ecc_strength , 2 );
416449}
417450
451+ static int set_geometry_for_large_oob (struct gpmi_nand_data * this )
452+ {
453+ struct bch_geometry * geo = & this -> bch_geometry ;
454+ struct nand_chip * chip = & this -> nand ;
455+ struct mtd_info * mtd = nand_to_mtd (chip );
456+ const struct nand_ecc_props * requirements =
457+ nanddev_get_ecc_requirements (& chip -> base );
458+ unsigned int block_mark_bit_offset ;
459+ unsigned int max_ecc ;
460+ unsigned int bbm_chunk ;
461+ unsigned int i ;
462+
463+ /* sanity check for the minimum ecc nand required */
464+ if (!(requirements -> strength > 0 &&
465+ requirements -> step_size > 0 ))
466+ return - EINVAL ;
467+ geo -> ecc_strength = requirements -> strength ;
468+
469+ /* check if platform can support this nand */
470+ if (!gpmi_check_ecc (this )) {
471+ dev_err (this -> dev ,
472+ "unsupported NAND chip, minimum ecc required %d\n" ,
473+ geo -> ecc_strength );
474+ return - EINVAL ;
475+ }
476+
477+ /* calculate the maximum ecc platform can support*/
478+ geo -> metadata_size = 10 ;
479+ geo -> gf_len = 14 ;
480+ geo -> ecc0_chunk_size = 1024 ;
481+ geo -> eccn_chunk_size = 1024 ;
482+ geo -> ecc_chunk_count = mtd -> writesize / geo -> eccn_chunk_size ;
483+ max_ecc = min (get_ecc_strength (this ),
484+ this -> devdata -> bch_max_ecc_strength );
485+
486+ /*
487+ * search a supported ecc strength that makes bbm
488+ * located in data chunk
489+ */
490+ geo -> ecc_strength = max_ecc ;
491+ while (!(geo -> ecc_strength < requirements -> strength )) {
492+ if (bbm_in_data_chunk (this , & bbm_chunk ))
493+ goto geo_setting ;
494+ geo -> ecc_strength -= 2 ;
495+ }
496+
497+ /* if none of them works, keep using the minimum ecc */
498+ /* nand required but changing ecc page layout */
499+ geo -> ecc_strength = requirements -> strength ;
500+ /* add extra ecc for meta data */
501+ geo -> ecc0_chunk_size = 0 ;
502+ geo -> ecc_chunk_count = (mtd -> writesize / geo -> eccn_chunk_size ) + 1 ;
503+ geo -> ecc_for_meta = 1 ;
504+ /* check if oob can afford this extra ecc chunk */
505+ if (mtd -> oobsize * 8 < geo -> metadata_size * 8 +
506+ geo -> gf_len * geo -> ecc_strength * geo -> ecc_chunk_count ) {
507+ dev_err (this -> dev , "unsupported NAND chip with new layout\n" );
508+ return - EINVAL ;
509+ }
510+
511+ /* calculate in which chunk bbm located */
512+ bbm_chunk = (mtd -> writesize * 8 - geo -> metadata_size * 8 -
513+ geo -> gf_len * geo -> ecc_strength ) /
514+ (geo -> gf_len * geo -> ecc_strength +
515+ geo -> eccn_chunk_size * 8 ) + 1 ;
516+
517+ geo_setting :
518+
519+ geo -> page_size = mtd -> writesize + geo -> metadata_size +
520+ (geo -> gf_len * geo -> ecc_strength * geo -> ecc_chunk_count ) / 8 ;
521+ geo -> payload_size = mtd -> writesize ;
522+
523+ /*
524+ * The auxiliary buffer contains the metadata and the ECC status. The
525+ * metadata is padded to the nearest 32-bit boundary. The ECC status
526+ * contains one byte for every ECC chunk, and is also padded to the
527+ * nearest 32-bit boundary.
528+ */
529+ geo -> auxiliary_status_offset = ALIGN (geo -> metadata_size , 4 );
530+ geo -> auxiliary_size = ALIGN (geo -> metadata_size , 4 )
531+ + ALIGN (geo -> ecc_chunk_count , 4 );
532+
533+ if (!this -> swap_block_mark )
534+ return 0 ;
535+
536+ /* calculate the number of ecc chunk behind the bbm */
537+ i = (mtd -> writesize / geo -> eccn_chunk_size ) - bbm_chunk + 1 ;
538+
539+ block_mark_bit_offset = mtd -> writesize * 8 -
540+ (geo -> ecc_strength * geo -> gf_len * (geo -> ecc_chunk_count - i )
541+ + geo -> metadata_size * 8 );
542+
543+ geo -> block_mark_byte_offset = block_mark_bit_offset / 8 ;
544+ geo -> block_mark_bit_offset = block_mark_bit_offset % 8 ;
545+
546+ dev_dbg (this -> dev , "BCH Geometry :\n"
547+ "GF length : %u\n"
548+ "ECC Strength : %u\n"
549+ "Page Size in Bytes : %u\n"
550+ "Metadata Size in Bytes : %u\n"
551+ "ECC0 Chunk Size in Bytes: %u\n"
552+ "ECCn Chunk Size in Bytes: %u\n"
553+ "ECC Chunk Count : %u\n"
554+ "Payload Size in Bytes : %u\n"
555+ "Auxiliary Size in Bytes: %u\n"
556+ "Auxiliary Status Offset: %u\n"
557+ "Block Mark Byte Offset : %u\n"
558+ "Block Mark Bit Offset : %u\n"
559+ "Block Mark in chunk : %u\n"
560+ "Ecc for Meta data : %u\n" ,
561+ geo -> gf_len ,
562+ geo -> ecc_strength ,
563+ geo -> page_size ,
564+ geo -> metadata_size ,
565+ geo -> ecc0_chunk_size ,
566+ geo -> eccn_chunk_size ,
567+ geo -> ecc_chunk_count ,
568+ geo -> payload_size ,
569+ geo -> auxiliary_size ,
570+ geo -> auxiliary_status_offset ,
571+ geo -> block_mark_byte_offset ,
572+ geo -> block_mark_bit_offset ,
573+ bbm_chunk ,
574+ geo -> ecc_for_meta );
575+
576+ return 0 ;
577+ }
578+
418579static int legacy_set_geometry (struct gpmi_nand_data * this )
419580{
420581 struct bch_geometry * geo = & this -> bch_geometry ;
@@ -550,6 +711,14 @@ static int common_nfc_set_geometry(struct gpmi_nand_data *this)
550711 return 0 ;
551712 }
552713
714+ /* for large oob nand */
715+ if (mtd -> oobsize > 1024 ) {
716+ dev_dbg (this -> dev , "use large oob bch geometry\n" );
717+ err = set_geometry_for_large_oob (this );
718+ if (!err )
719+ return 0 ;
720+ }
721+
553722 /* otherwise use the minimum ecc nand chip required */
554723 dev_dbg (this -> dev , "use minimum ecc bch geometry\n" );
555724 err = set_geometry_by_ecc_info (this , requirements -> strength ,
@@ -1433,24 +1602,44 @@ static int gpmi_ecc_read_subpage(struct nand_chip *chip, uint32_t offs,
14331602 }
14341603 }
14351604
1605+ /*
1606+ * if there is an ECC dedicate for meta:
1607+ * - need to add an extra ECC size when calculating col and page_size,
1608+ * if the meta size is NOT zero.
1609+ * - ecc0_chunk size need to set to the same size as other chunks,
1610+ * if the meta size is zero.
1611+ */
1612+
14361613 meta = geo -> metadata_size ;
14371614 if (first ) {
1438- col = meta + (size + ecc_parity_size ) * first ;
1615+ if (geo -> ecc_for_meta )
1616+ col = meta + ecc_parity_size
1617+ + (size + ecc_parity_size ) * first ;
1618+ else
1619+ col = meta + (size + ecc_parity_size ) * first ;
1620+
14391621 meta = 0 ;
14401622 buf = buf + first * size ;
14411623 }
14421624
14431625 ecc_parity_size = geo -> gf_len * geo -> ecc_strength / 8 ;
1444-
14451626 n = last - first + 1 ;
1446- page_size = meta + (size + ecc_parity_size ) * n ;
1627+
1628+ if (geo -> ecc_for_meta && meta )
1629+ page_size = meta + ecc_parity_size
1630+ + (size + ecc_parity_size ) * n ;
1631+ else
1632+ page_size = meta + (size + ecc_parity_size ) * n ;
1633+
14471634 ecc_strength = geo -> ecc_strength >> 1 ;
14481635
1449- this -> bch_flashlayout0 = BF_BCH_FLASH0LAYOUT0_NBLOCKS (n - 1 ) |
1636+ this -> bch_flashlayout0 = BF_BCH_FLASH0LAYOUT0_NBLOCKS (
1637+ (geo -> ecc_for_meta ? n : n - 1 )) |
14501638 BF_BCH_FLASH0LAYOUT0_META_SIZE (meta ) |
14511639 BF_BCH_FLASH0LAYOUT0_ECC0 (ecc_strength , this ) |
14521640 BF_BCH_FLASH0LAYOUT0_GF (geo -> gf_len , this ) |
1453- BF_BCH_FLASH0LAYOUT0_DATA0_SIZE (geo -> eccn_chunk_size , this );
1641+ BF_BCH_FLASH0LAYOUT0_DATA0_SIZE ((geo -> ecc_for_meta ?
1642+ 0 : geo -> ecc0_chunk_size ), this );
14541643
14551644 this -> bch_flashlayout1 = BF_BCH_FLASH0LAYOUT1_PAGE_SIZE (page_size ) |
14561645 BF_BCH_FLASH0LAYOUT1_ECCN (ecc_strength , this ) |
0 commit comments