linux/drivers/mtd/nand/raw/nand_timings.c
Miquel Raynal 45606518f9 mtd: rawnand: Add onfi_fill_nvddr_interface_config() helper
Same logic as for the SDR path, let's create a
onfi_fill_nvddr_interface_config() helper to fill an interface
configuration structure with NV-DDR timings, given a specific ONFI mode.

There is one additional thing to do compared to SDR mode: tCAD timing
can be fast or slow and this depends on an ONFI parameter page bit. By
default the slow value is declared in the timings structure definition,
but this helper can shrink it down if necessary.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Link: https://lore.kernel.org/linux-mtd/20210505213750.257417-12-miquel.raynal@bootlin.com
2021-05-26 10:43:50 +02:00

696 lines
17 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
#include "internals.h"
#define ONFI_DYN_TIMING_MAX U16_MAX
/*
* 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 200us and 500ns.
*
* These four values are tweaked to be more accurate in the case of ONFI chips.
*/
static const struct nand_interface_config onfi_sdr_timings[] = {
/* Mode 0 */
{
.type = NAND_SDR_IFACE,
.timings.mode = 0,
.timings.sdr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.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,
},
},
/* Mode 1 */
{
.type = NAND_SDR_IFACE,
.timings.mode = 1,
.timings.sdr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.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,
},
},
/* Mode 2 */
{
.type = NAND_SDR_IFACE,
.timings.mode = 2,
.timings.sdr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.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,
},
},
/* Mode 3 */
{
.type = NAND_SDR_IFACE,
.timings.mode = 3,
.timings.sdr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.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,
},
},
/* Mode 4 */
{
.type = NAND_SDR_IFACE,
.timings.mode = 4,
.timings.sdr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.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,
},
},
/* Mode 5 */
{
.type = NAND_SDR_IFACE,
.timings.mode = 5,
.timings.sdr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.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,
},
},
};
static const struct nand_interface_config onfi_nvddr_timings[] = {
/* Mode 0 */
{
.type = NAND_NVDDR_IFACE,
.timings.mode = 0,
.timings.nvddr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tAC_min = 3000,
.tAC_max = 25000,
.tADL_min = 400000,
.tCAD_min = 45000,
.tCAH_min = 10000,
.tCALH_min = 10000,
.tCALS_min = 10000,
.tCAS_min = 10000,
.tCEH_min = 20000,
.tCH_min = 10000,
.tCK_min = 50000,
.tCS_min = 35000,
.tDH_min = 5000,
.tDQSCK_min = 3000,
.tDQSCK_max = 25000,
.tDQSD_min = 0,
.tDQSD_max = 18000,
.tDQSHZ_max = 20000,
.tDQSQ_max = 5000,
.tDS_min = 5000,
.tDSC_min = 50000,
.tFEAT_max = 1000000,
.tITC_max = 1000000,
.tQHS_max = 6000,
.tRHW_min = 100000,
.tRR_min = 20000,
.tRST_max = 500000000,
.tWB_max = 100000,
.tWHR_min = 80000,
.tWRCK_min = 20000,
.tWW_min = 100000,
},
},
/* Mode 1 */
{
.type = NAND_NVDDR_IFACE,
.timings.mode = 1,
.timings.nvddr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tAC_min = 3000,
.tAC_max = 25000,
.tADL_min = 400000,
.tCAD_min = 45000,
.tCAH_min = 5000,
.tCALH_min = 5000,
.tCALS_min = 5000,
.tCAS_min = 5000,
.tCEH_min = 20000,
.tCH_min = 5000,
.tCK_min = 30000,
.tCS_min = 25000,
.tDH_min = 2500,
.tDQSCK_min = 3000,
.tDQSCK_max = 25000,
.tDQSD_min = 0,
.tDQSD_max = 18000,
.tDQSHZ_max = 20000,
.tDQSQ_max = 2500,
.tDS_min = 3000,
.tDSC_min = 30000,
.tFEAT_max = 1000000,
.tITC_max = 1000000,
.tQHS_max = 3000,
.tRHW_min = 100000,
.tRR_min = 20000,
.tRST_max = 500000000,
.tWB_max = 100000,
.tWHR_min = 80000,
.tWRCK_min = 20000,
.tWW_min = 100000,
},
},
/* Mode 2 */
{
.type = NAND_NVDDR_IFACE,
.timings.mode = 2,
.timings.nvddr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tAC_min = 3000,
.tAC_max = 25000,
.tADL_min = 400000,
.tCAD_min = 45000,
.tCAH_min = 4000,
.tCALH_min = 4000,
.tCALS_min = 4000,
.tCAS_min = 4000,
.tCEH_min = 20000,
.tCH_min = 4000,
.tCK_min = 20000,
.tCS_min = 15000,
.tDH_min = 1700,
.tDQSCK_min = 3000,
.tDQSCK_max = 25000,
.tDQSD_min = 0,
.tDQSD_max = 18000,
.tDQSHZ_max = 20000,
.tDQSQ_max = 1700,
.tDS_min = 2000,
.tDSC_min = 20000,
.tFEAT_max = 1000000,
.tITC_max = 1000000,
.tQHS_max = 2000,
.tRHW_min = 100000,
.tRR_min = 20000,
.tRST_max = 500000000,
.tWB_max = 100000,
.tWHR_min = 80000,
.tWRCK_min = 20000,
.tWW_min = 100000,
},
},
/* Mode 3 */
{
.type = NAND_NVDDR_IFACE,
.timings.mode = 3,
.timings.nvddr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tAC_min = 3000,
.tAC_max = 25000,
.tADL_min = 400000,
.tCAD_min = 45000,
.tCAH_min = 3000,
.tCALH_min = 3000,
.tCALS_min = 3000,
.tCAS_min = 3000,
.tCEH_min = 20000,
.tCH_min = 3000,
.tCK_min = 15000,
.tCS_min = 15000,
.tDH_min = 1300,
.tDQSCK_min = 3000,
.tDQSCK_max = 25000,
.tDQSD_min = 0,
.tDQSD_max = 18000,
.tDQSHZ_max = 20000,
.tDQSQ_max = 1300,
.tDS_min = 1500,
.tDSC_min = 15000,
.tFEAT_max = 1000000,
.tITC_max = 1000000,
.tQHS_max = 1500,
.tRHW_min = 100000,
.tRR_min = 20000,
.tRST_max = 500000000,
.tWB_max = 100000,
.tWHR_min = 80000,
.tWRCK_min = 20000,
.tWW_min = 100000,
},
},
/* Mode 4 */
{
.type = NAND_NVDDR_IFACE,
.timings.mode = 4,
.timings.nvddr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tAC_min = 3000,
.tAC_max = 25000,
.tADL_min = 400000,
.tCAD_min = 45000,
.tCAH_min = 2500,
.tCALH_min = 2500,
.tCALS_min = 2500,
.tCAS_min = 2500,
.tCEH_min = 20000,
.tCH_min = 2500,
.tCK_min = 12000,
.tCS_min = 15000,
.tDH_min = 1100,
.tDQSCK_min = 3000,
.tDQSCK_max = 25000,
.tDQSD_min = 0,
.tDQSD_max = 18000,
.tDQSHZ_max = 20000,
.tDQSQ_max = 1000,
.tDS_min = 1100,
.tDSC_min = 12000,
.tFEAT_max = 1000000,
.tITC_max = 1000000,
.tQHS_max = 1200,
.tRHW_min = 100000,
.tRR_min = 20000,
.tRST_max = 500000000,
.tWB_max = 100000,
.tWHR_min = 80000,
.tWRCK_min = 20000,
.tWW_min = 100000,
},
},
/* Mode 5 */
{
.type = NAND_NVDDR_IFACE,
.timings.mode = 5,
.timings.nvddr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tAC_min = 3000,
.tAC_max = 25000,
.tADL_min = 400000,
.tCAD_min = 45000,
.tCAH_min = 2000,
.tCALH_min = 2000,
.tCALS_min = 2000,
.tCAS_min = 2000,
.tCEH_min = 20000,
.tCH_min = 2000,
.tCK_min = 10000,
.tCS_min = 15000,
.tDH_min = 900,
.tDQSCK_min = 3000,
.tDQSCK_max = 25000,
.tDQSD_min = 0,
.tDQSD_max = 18000,
.tDQSHZ_max = 20000,
.tDQSQ_max = 850,
.tDS_min = 900,
.tDSC_min = 10000,
.tFEAT_max = 1000000,
.tITC_max = 1000000,
.tQHS_max = 1000,
.tRHW_min = 100000,
.tRR_min = 20000,
.tRST_max = 500000000,
.tWB_max = 100000,
.tWHR_min = 80000,
.tWRCK_min = 20000,
.tWW_min = 100000,
},
},
};
/* All NAND chips share the same reset data interface: SDR mode 0 */
const struct nand_interface_config *nand_get_reset_interface_config(void)
{
return &onfi_sdr_timings[0];
}
/**
* 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;
}
/**
* onfi_fill_sdr_interface_config - Initialize a SDR interface config from a
* given ONFI mode
* @chip: The NAND chip
* @iface: The interface configuration to fill
* @timing_mode: The ONFI timing mode
*/
static void onfi_fill_sdr_interface_config(struct nand_chip *chip,
struct nand_interface_config *iface,
unsigned int timing_mode)
{
struct onfi_params *onfi = chip->parameters.onfi;
if (WARN_ON(timing_mode >= ARRAY_SIZE(onfi_sdr_timings)))
return;
*iface = onfi_sdr_timings[timing_mode];
/*
* Initialize timings that cannot be deduced from timing mode:
* tPROG, tBERS, tR and tCCS.
* These information are part of the ONFI parameter page.
*/
if (onfi) {
struct nand_sdr_timings *timings = &iface->timings.sdr;
/* microseconds -> picoseconds */
timings->tPROG_max = 1000000ULL * onfi->tPROG;
timings->tBERS_max = 1000000ULL * onfi->tBERS;
timings->tR_max = 1000000ULL * onfi->tR;
/* nanoseconds -> picoseconds */
timings->tCCS_min = 1000UL * onfi->tCCS;
}
}
/**
* onfi_fill_nvddr_interface_config - Initialize a NVDDR interface config from a
* given ONFI mode
* @chip: The NAND chip
* @iface: The interface configuration to fill
* @timing_mode: The ONFI timing mode
*/
static void onfi_fill_nvddr_interface_config(struct nand_chip *chip,
struct nand_interface_config *iface,
unsigned int timing_mode)
{
struct onfi_params *onfi = chip->parameters.onfi;
if (WARN_ON(timing_mode >= ARRAY_SIZE(onfi_nvddr_timings)))
return;
*iface = onfi_nvddr_timings[timing_mode];
/*
* Initialize timings that cannot be deduced from timing mode:
* tPROG, tBERS, tR, tCCS and tCAD.
* These information are part of the ONFI parameter page.
*/
if (onfi) {
struct nand_nvddr_timings *timings = &iface->timings.nvddr;
/* microseconds -> picoseconds */
timings->tPROG_max = 1000000ULL * onfi->tPROG;
timings->tBERS_max = 1000000ULL * onfi->tBERS;
timings->tR_max = 1000000ULL * onfi->tR;
/* nanoseconds -> picoseconds */
timings->tCCS_min = 1000UL * onfi->tCCS;
if (onfi->fast_tCAD)
timings->tCAD_min = 25000;
}
}
/**
* onfi_fill_interface_config - Initialize an interface config from a given
* ONFI mode
* @chip: The NAND chip
* @iface: The interface configuration to fill
* @type: The interface type
* @timing_mode: The ONFI timing mode
*/
void onfi_fill_interface_config(struct nand_chip *chip,
struct nand_interface_config *iface,
enum nand_interface_type type,
unsigned int timing_mode)
{
if (type == NAND_SDR_IFACE)
return onfi_fill_sdr_interface_config(chip, iface, timing_mode);
else
return onfi_fill_nvddr_interface_config(chip, iface, timing_mode);
}