2018-09-07 00:38:51 +02:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2000 Steven J . Hill ( sjhill @ realitydiluted . com )
* 2002 - 2006 Thomas Gleixner ( tglx @ linutronix . de )
*
* Credits :
* David Woodhouse for adding multichip support
*
* Aleph One Ltd . and Toby Churchill Ltd . for supporting the
* rework for 2 K page size chips
*
* This file contains all ONFI helpers .
*/
# include <linux/slab.h>
# include "internals.h"
2020-04-28 11:43:01 +02:00
# define JEDEC_PARAM_PAGES 3
2018-09-07 00:38:51 +02:00
/*
* Check if the NAND chip is JEDEC compliant , returns 1 if it is , 0 otherwise .
*/
int nand_jedec_detect ( struct nand_chip * chip )
{
2020-08-27 10:52:02 +02:00
struct nand_device * base = & chip - > base ;
2018-09-07 00:38:51 +02:00
struct mtd_info * mtd = nand_to_mtd ( chip ) ;
2018-10-25 17:10:37 +02:00
struct nand_memory_organization * memorg ;
2018-09-07 00:38:51 +02:00
struct nand_jedec_params * p ;
struct jedec_ecc_info * ecc ;
2020-05-07 12:52:38 +02:00
bool use_datain = false ;
2018-09-07 00:38:51 +02:00
int jedec_version = 0 ;
char id [ 5 ] ;
int i , val , ret ;
2020-04-28 11:43:02 +02:00
u16 crc ;
2018-09-07 00:38:51 +02:00
2018-10-25 17:10:37 +02:00
memorg = nanddev_get_memorg ( & chip - > base ) ;
2018-09-07 00:38:51 +02:00
/* Try JEDEC for unknown chip or LP */
ret = nand_readid_op ( chip , 0x40 , id , sizeof ( id ) ) ;
if ( ret | | strncmp ( id , " JEDEC " , sizeof ( id ) ) )
return 0 ;
/* JEDEC chip: allocate a buffer to hold its parameter page */
p = kzalloc ( sizeof ( * p ) , GFP_KERNEL ) ;
if ( ! p )
return - ENOMEM ;
2020-05-07 12:52:38 +02:00
if ( ! nand_has_exec_op ( chip ) | |
! nand_read_data_op ( chip , p , sizeof ( * p ) , true , true ) )
use_datain = true ;
2018-09-07 00:38:51 +02:00
2020-04-28 11:43:01 +02:00
for ( i = 0 ; i < JEDEC_PARAM_PAGES ; i + + ) {
2020-05-07 12:52:38 +02:00
if ( ! i )
ret = nand_read_param_page_op ( chip , 0x40 , p ,
sizeof ( * p ) ) ;
else if ( use_datain )
ret = nand_read_data_op ( chip , p , sizeof ( * p ) , true ,
false ) ;
else
ret = nand_change_read_column_op ( chip , sizeof ( * p ) * i ,
p , sizeof ( * p ) , true ) ;
2018-09-07 00:38:51 +02:00
if ( ret ) {
ret = 0 ;
goto free_jedec_param_page ;
}
2020-04-28 11:43:02 +02:00
crc = onfi_crc16 ( ONFI_CRC_BASE , ( u8 * ) p , 510 ) ;
if ( crc = = le16_to_cpu ( p - > crc ) )
2018-09-07 00:38:51 +02:00
break ;
}
2020-04-28 11:43:01 +02:00
if ( i = = JEDEC_PARAM_PAGES ) {
2018-09-07 00:38:51 +02:00
pr_err ( " Could not find valid JEDEC parameter page; aborting \n " ) ;
goto free_jedec_param_page ;
}
/* Check version */
val = le16_to_cpu ( p - > revision ) ;
if ( val & ( 1 < < 2 ) )
jedec_version = 10 ;
else if ( val & ( 1 < < 1 ) )
jedec_version = 1 ; /* vendor specific version */
if ( ! jedec_version ) {
pr_info ( " unsupported JEDEC version: %d \n " , val ) ;
goto free_jedec_param_page ;
}
sanitize_string ( p - > manufacturer , sizeof ( p - > manufacturer ) ) ;
sanitize_string ( p - > model , sizeof ( p - > model ) ) ;
chip - > parameters . model = kstrdup ( p - > model , GFP_KERNEL ) ;
if ( ! chip - > parameters . model ) {
ret = - ENOMEM ;
goto free_jedec_param_page ;
}
2018-10-25 17:10:37 +02:00
memorg - > pagesize = le32_to_cpu ( p - > byte_per_page ) ;
mtd - > writesize = memorg - > pagesize ;
2018-09-07 00:38:51 +02:00
/* Please reference to the comment for nand_flash_detect_onfi. */
2018-10-25 17:10:37 +02:00
memorg - > pages_per_eraseblock =
1 < < ( fls ( le32_to_cpu ( p - > pages_per_block ) ) - 1 ) ;
mtd - > erasesize = memorg - > pages_per_eraseblock * memorg - > pagesize ;
memorg - > oobsize = le16_to_cpu ( p - > spare_bytes_per_page ) ;
mtd - > oobsize = memorg - > oobsize ;
2018-09-07 00:38:51 +02:00
2018-10-25 17:10:37 +02:00
memorg - > luns_per_target = p - > lun_count ;
memorg - > planes_per_lun = 1 < < p - > multi_plane_addr ;
2018-09-07 00:38:51 +02:00
/* Please reference to the comment for nand_flash_detect_onfi. */
2018-10-25 17:10:37 +02:00
memorg - > eraseblocks_per_lun =
1 < < ( fls ( le32_to_cpu ( p - > blocks_per_lun ) ) - 1 ) ;
memorg - > bits_per_cell = p - > bits_per_cell ;
2018-09-07 00:38:51 +02:00
if ( le16_to_cpu ( p - > features ) & JEDEC_FEATURE_16_BIT_BUS )
chip - > options | = NAND_BUSWIDTH_16 ;
/* ECC info */
ecc = & p - > ecc_info [ 0 ] ;
if ( ecc - > codeword_size > = 9 ) {
2020-08-27 10:52:02 +02:00
struct nand_ecc_props requirements = {
. strength = ecc - > ecc_bits ,
. step_size = 1 < < ecc - > codeword_size ,
} ;
nanddev_set_ecc_requirements ( base , & requirements ) ;
2018-09-07 00:38:51 +02:00
} else {
pr_warn ( " Invalid codeword size \n " ) ;
}
2018-12-13 11:55:26 +01:00
ret = 1 ;
2018-09-07 00:38:51 +02:00
free_jedec_param_page :
kfree ( p ) ;
return ret ;
}