2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2014-07-11 11:49:42 +04:00
/*
* Copyright ( C ) 2014 Free Electrons
*
* Author : Boris BREZILLON < boris . brezillon @ free - electrons . com >
*/
# include <linux/kernel.h>
# include <linux/err.h>
# include <linux/export.h>
2018-09-07 01:38:48 +03:00
# include "internals.h"
2014-07-11 11:49:42 +04:00
2018-07-14 13:23:54 +03:00
# define ONFI_DYN_TIMING_MAX U16_MAX
2020-05-29 14:13:11 +03:00
/*
* For non - ONFI chips we use the highest possible value for tPROG and tBERS .
* tR and tCCS will take the default values precised in the ONFI specification
* for timing mode 0 , respectively 200u s and 500 ns .
*
* These four values are tweaked to be more accurate in the case of ONFI chips .
*/
2016-09-15 11:32:47 +03:00
static const struct nand_data_interface onfi_sdr_timings [ ] = {
2014-07-11 11:49:42 +04:00
/* Mode 0 */
{
2016-09-15 11:32:47 +03:00
. type = NAND_SDR_IFACE ,
2020-04-28 12:42:54 +03:00
. timings . mode = 0 ,
2016-09-15 11:32:47 +03:00
. timings . sdr = {
2016-10-01 11:24:02 +03:00
. tCCS_min = 500000 ,
. tR_max = 200000000 ,
2020-05-29 14:13:11 +03:00
. tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX ,
. tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX ,
2016-09-15 11:32:47 +03:00
. tADL_min = 400000 ,
. tALH_min = 20000 ,
. tALS_min = 50000 ,
. tAR_min = 25000 ,
. tCEA_max = 100000 ,
. tCEH_min = 20000 ,
. tCH_min = 20000 ,
. tCHZ_max = 100000 ,
. tCLH_min = 20000 ,
. tCLR_min = 20000 ,
. tCLS_min = 50000 ,
. tCOH_min = 0 ,
. tCS_min = 70000 ,
. tDH_min = 20000 ,
. tDS_min = 40000 ,
. tFEAT_max = 1000000 ,
. tIR_min = 10000 ,
. tITC_max = 1000000 ,
. tRC_min = 100000 ,
. tREA_max = 40000 ,
. tREH_min = 30000 ,
. tRHOH_min = 0 ,
. tRHW_min = 200000 ,
. tRHZ_max = 200000 ,
. tRLOH_min = 0 ,
. tRP_min = 50000 ,
. tRR_min = 40000 ,
. tRST_max = 250000000000ULL ,
. tWB_max = 200000 ,
. tWC_min = 100000 ,
. tWH_min = 30000 ,
. tWHR_min = 120000 ,
. tWP_min = 50000 ,
. tWW_min = 100000 ,
} ,
2014-07-11 11:49:42 +04:00
} ,
/* Mode 1 */
{
2016-09-15 11:32:47 +03:00
. type = NAND_SDR_IFACE ,
2020-04-28 12:42:54 +03:00
. timings . mode = 1 ,
2016-09-15 11:32:47 +03:00
. timings . sdr = {
2016-10-01 11:24:02 +03:00
. tCCS_min = 500000 ,
. tR_max = 200000000 ,
2020-05-29 14:13:11 +03:00
. tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX ,
. tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX ,
2016-09-15 11:32:47 +03:00
. tADL_min = 400000 ,
. tALH_min = 10000 ,
. tALS_min = 25000 ,
. tAR_min = 10000 ,
. tCEA_max = 45000 ,
. tCEH_min = 20000 ,
. tCH_min = 10000 ,
. tCHZ_max = 50000 ,
. tCLH_min = 10000 ,
. tCLR_min = 10000 ,
. tCLS_min = 25000 ,
. tCOH_min = 15000 ,
. tCS_min = 35000 ,
. tDH_min = 10000 ,
. tDS_min = 20000 ,
. tFEAT_max = 1000000 ,
. tIR_min = 0 ,
. tITC_max = 1000000 ,
. tRC_min = 50000 ,
. tREA_max = 30000 ,
. tREH_min = 15000 ,
. tRHOH_min = 15000 ,
. tRHW_min = 100000 ,
. tRHZ_max = 100000 ,
. tRLOH_min = 0 ,
. tRP_min = 25000 ,
. tRR_min = 20000 ,
. tRST_max = 500000000 ,
. tWB_max = 100000 ,
. tWC_min = 45000 ,
. tWH_min = 15000 ,
. tWHR_min = 80000 ,
. tWP_min = 25000 ,
. tWW_min = 100000 ,
} ,
2014-07-11 11:49:42 +04:00
} ,
/* Mode 2 */
{
2016-09-15 11:32:47 +03:00
. type = NAND_SDR_IFACE ,
2020-04-28 12:42:54 +03:00
. timings . mode = 2 ,
2016-09-15 11:32:47 +03:00
. timings . sdr = {
2016-10-01 11:24:02 +03:00
. tCCS_min = 500000 ,
. tR_max = 200000000 ,
2020-05-29 14:13:11 +03:00
. tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX ,
. tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX ,
2016-09-15 11:32:47 +03:00
. tADL_min = 400000 ,
. tALH_min = 10000 ,
. tALS_min = 15000 ,
. tAR_min = 10000 ,
. tCEA_max = 30000 ,
. tCEH_min = 20000 ,
. tCH_min = 10000 ,
. tCHZ_max = 50000 ,
. tCLH_min = 10000 ,
. tCLR_min = 10000 ,
. tCLS_min = 15000 ,
. tCOH_min = 15000 ,
. tCS_min = 25000 ,
. tDH_min = 5000 ,
. tDS_min = 15000 ,
. tFEAT_max = 1000000 ,
. tIR_min = 0 ,
. tITC_max = 1000000 ,
. tRC_min = 35000 ,
. tREA_max = 25000 ,
. tREH_min = 15000 ,
. tRHOH_min = 15000 ,
. tRHW_min = 100000 ,
. tRHZ_max = 100000 ,
. tRLOH_min = 0 ,
. tRR_min = 20000 ,
. tRST_max = 500000000 ,
. tWB_max = 100000 ,
. tRP_min = 17000 ,
. tWC_min = 35000 ,
. tWH_min = 15000 ,
. tWHR_min = 80000 ,
. tWP_min = 17000 ,
. tWW_min = 100000 ,
} ,
2014-07-11 11:49:42 +04:00
} ,
/* Mode 3 */
{
2016-09-15 11:32:47 +03:00
. type = NAND_SDR_IFACE ,
2020-04-28 12:42:54 +03:00
. timings . mode = 3 ,
2016-09-15 11:32:47 +03:00
. timings . sdr = {
2016-10-01 11:24:02 +03:00
. tCCS_min = 500000 ,
. tR_max = 200000000 ,
2020-05-29 14:13:11 +03:00
. tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX ,
. tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX ,
2016-09-15 11:32:47 +03:00
. tADL_min = 400000 ,
. tALH_min = 5000 ,
. tALS_min = 10000 ,
. tAR_min = 10000 ,
. tCEA_max = 25000 ,
. tCEH_min = 20000 ,
. tCH_min = 5000 ,
. tCHZ_max = 50000 ,
. tCLH_min = 5000 ,
. tCLR_min = 10000 ,
. tCLS_min = 10000 ,
. tCOH_min = 15000 ,
. tCS_min = 25000 ,
. tDH_min = 5000 ,
. tDS_min = 10000 ,
. tFEAT_max = 1000000 ,
. tIR_min = 0 ,
. tITC_max = 1000000 ,
. tRC_min = 30000 ,
. tREA_max = 20000 ,
. tREH_min = 10000 ,
. tRHOH_min = 15000 ,
. tRHW_min = 100000 ,
. tRHZ_max = 100000 ,
. tRLOH_min = 0 ,
. tRP_min = 15000 ,
. tRR_min = 20000 ,
. tRST_max = 500000000 ,
. tWB_max = 100000 ,
. tWC_min = 30000 ,
. tWH_min = 10000 ,
. tWHR_min = 80000 ,
. tWP_min = 15000 ,
. tWW_min = 100000 ,
} ,
2014-07-11 11:49:42 +04:00
} ,
/* Mode 4 */
{
2016-09-15 11:32:47 +03:00
. type = NAND_SDR_IFACE ,
2020-04-28 12:42:54 +03:00
. timings . mode = 4 ,
2016-09-15 11:32:47 +03:00
. timings . sdr = {
2016-10-01 11:24:02 +03:00
. tCCS_min = 500000 ,
. tR_max = 200000000 ,
2020-05-29 14:13:11 +03:00
. tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX ,
. tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX ,
2016-09-15 11:32:47 +03:00
. tADL_min = 400000 ,
. tALH_min = 5000 ,
. tALS_min = 10000 ,
. tAR_min = 10000 ,
. tCEA_max = 25000 ,
. tCEH_min = 20000 ,
. tCH_min = 5000 ,
. tCHZ_max = 30000 ,
. tCLH_min = 5000 ,
. tCLR_min = 10000 ,
. tCLS_min = 10000 ,
. tCOH_min = 15000 ,
. tCS_min = 20000 ,
. tDH_min = 5000 ,
. tDS_min = 10000 ,
. tFEAT_max = 1000000 ,
. tIR_min = 0 ,
. tITC_max = 1000000 ,
. tRC_min = 25000 ,
. tREA_max = 20000 ,
. tREH_min = 10000 ,
. tRHOH_min = 15000 ,
. tRHW_min = 100000 ,
. tRHZ_max = 100000 ,
. tRLOH_min = 5000 ,
. tRP_min = 12000 ,
. tRR_min = 20000 ,
. tRST_max = 500000000 ,
. tWB_max = 100000 ,
. tWC_min = 25000 ,
. tWH_min = 10000 ,
. tWHR_min = 80000 ,
. tWP_min = 12000 ,
. tWW_min = 100000 ,
} ,
2014-07-11 11:49:42 +04:00
} ,
/* Mode 5 */
{
2016-09-15 11:32:47 +03:00
. type = NAND_SDR_IFACE ,
2020-04-28 12:42:54 +03:00
. timings . mode = 5 ,
2016-09-15 11:32:47 +03:00
. timings . sdr = {
2016-10-01 11:24:02 +03:00
. tCCS_min = 500000 ,
. tR_max = 200000000 ,
2020-05-29 14:13:11 +03:00
. tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX ,
. tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX ,
2016-09-15 11:32:47 +03:00
. tADL_min = 400000 ,
. tALH_min = 5000 ,
. tALS_min = 10000 ,
. tAR_min = 10000 ,
. tCEA_max = 25000 ,
. tCEH_min = 20000 ,
. tCH_min = 5000 ,
. tCHZ_max = 30000 ,
. tCLH_min = 5000 ,
. tCLR_min = 10000 ,
. tCLS_min = 10000 ,
. tCOH_min = 15000 ,
. tCS_min = 15000 ,
. tDH_min = 5000 ,
. tDS_min = 7000 ,
. tFEAT_max = 1000000 ,
. tIR_min = 0 ,
. tITC_max = 1000000 ,
. tRC_min = 20000 ,
. tREA_max = 16000 ,
. tREH_min = 7000 ,
. tRHOH_min = 15000 ,
. tRHW_min = 100000 ,
. tRHZ_max = 100000 ,
. tRLOH_min = 5000 ,
. tRP_min = 10000 ,
. tRR_min = 20000 ,
. tRST_max = 500000000 ,
. tWB_max = 100000 ,
. tWC_min = 20000 ,
. tWH_min = 7000 ,
. tWHR_min = 80000 ,
. tWP_min = 10000 ,
. tWW_min = 100000 ,
} ,
2014-07-11 11:49:42 +04:00
} ,
} ;
2020-05-29 14:13:09 +03:00
/**
* onfi_find_closest_sdr_mode - Derive the closest ONFI SDR timing mode given a
* set of timings
* @ spec_timings : the timings to challenge
*/
unsigned int
onfi_find_closest_sdr_mode ( const struct nand_sdr_timings * spec_timings )
{
const struct nand_sdr_timings * onfi_timings ;
int mode ;
for ( mode = ARRAY_SIZE ( onfi_sdr_timings ) - 1 ; mode > 0 ; mode - - ) {
onfi_timings = & onfi_sdr_timings [ mode ] . timings . sdr ;
if ( spec_timings - > tCCS_min < = onfi_timings - > tCCS_min & &
spec_timings - > tADL_min < = onfi_timings - > tADL_min & &
spec_timings - > tALH_min < = onfi_timings - > tALH_min & &
spec_timings - > tALS_min < = onfi_timings - > tALS_min & &
spec_timings - > tAR_min < = onfi_timings - > tAR_min & &
spec_timings - > tCEH_min < = onfi_timings - > tCEH_min & &
spec_timings - > tCH_min < = onfi_timings - > tCH_min & &
spec_timings - > tCLH_min < = onfi_timings - > tCLH_min & &
spec_timings - > tCLR_min < = onfi_timings - > tCLR_min & &
spec_timings - > tCLS_min < = onfi_timings - > tCLS_min & &
spec_timings - > tCOH_min < = onfi_timings - > tCOH_min & &
spec_timings - > tCS_min < = onfi_timings - > tCS_min & &
spec_timings - > tDH_min < = onfi_timings - > tDH_min & &
spec_timings - > tDS_min < = onfi_timings - > tDS_min & &
spec_timings - > tIR_min < = onfi_timings - > tIR_min & &
spec_timings - > tRC_min < = onfi_timings - > tRC_min & &
spec_timings - > tREH_min < = onfi_timings - > tREH_min & &
spec_timings - > tRHOH_min < = onfi_timings - > tRHOH_min & &
spec_timings - > tRHW_min < = onfi_timings - > tRHW_min & &
spec_timings - > tRLOH_min < = onfi_timings - > tRLOH_min & &
spec_timings - > tRP_min < = onfi_timings - > tRP_min & &
spec_timings - > tRR_min < = onfi_timings - > tRR_min & &
spec_timings - > tWC_min < = onfi_timings - > tWC_min & &
spec_timings - > tWH_min < = onfi_timings - > tWH_min & &
spec_timings - > tWHR_min < = onfi_timings - > tWHR_min & &
spec_timings - > tWP_min < = onfi_timings - > tWP_min & &
spec_timings - > tWW_min < = onfi_timings - > tWW_min )
return mode ;
}
return 0 ;
}
2016-09-15 11:32:48 +03:00
/**
2020-05-29 14:13:06 +03:00
* onfi_fill_data_interface - Initialize a data interface from a given ONFI mode
* @ chip : The NAND chip
2020-05-29 14:13:07 +03:00
* @ iface : The data interface to fill
2020-05-29 14:13:06 +03:00
* @ type : The data interface type
* @ timing_mode : The ONFI timing mode
2016-09-15 11:32:48 +03:00
*/
2017-11-30 20:01:31 +03:00
int onfi_fill_data_interface ( struct nand_chip * chip ,
2020-05-29 14:13:07 +03:00
struct nand_data_interface * iface ,
2016-09-15 11:32:48 +03:00
enum nand_data_interface_type type ,
2020-05-29 14:13:08 +03:00
unsigned int timing_mode )
2016-09-15 11:32:48 +03:00
{
2018-07-25 16:31:52 +03:00
struct onfi_params * onfi = chip - > parameters . onfi ;
2017-11-30 20:01:31 +03:00
2016-09-15 11:32:48 +03:00
if ( type ! = NAND_SDR_IFACE )
return - EINVAL ;
2020-05-29 14:13:08 +03:00
if ( timing_mode > = ARRAY_SIZE ( onfi_sdr_timings ) )
2016-09-15 11:32:48 +03:00
return - EINVAL ;
* iface = onfi_sdr_timings [ timing_mode ] ;
/*
2016-10-01 11:24:02 +03:00
* Initialize timings that cannot be deduced from timing mode :
2018-07-14 13:23:54 +03:00
* tPROG , tBERS , tR and tCCS .
2016-09-15 11:32:48 +03:00
* These information are part of the ONFI parameter page .
*/
2018-07-25 16:31:52 +03:00
if ( onfi ) {
2016-10-01 11:24:02 +03:00
struct nand_sdr_timings * timings = & iface - > timings . sdr ;
/* microseconds -> picoseconds */
2018-07-25 16:31:52 +03:00
timings - > tPROG_max = 1000000ULL * onfi - > tPROG ;
timings - > tBERS_max = 1000000ULL * onfi - > tBERS ;
timings - > tR_max = 1000000ULL * onfi - > tR ;
2016-10-01 11:24:02 +03:00
/* nanoseconds -> picoseconds */
2018-07-25 16:31:52 +03:00
timings - > tCCS_min = 1000UL * onfi - > tCCS ;
2016-10-01 11:24:02 +03:00
}
2016-09-15 11:32:48 +03:00
return 0 ;
}