mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-17 16:06:42 +07:00
6a943386ee
Some timings like tBERS (block erase time), tCCs (change column setup time), tPROG (page program time) and tR (page read time) are derived from the ONFI parameter page. They are set in the SDR interface only if the chip is ONFI compliant. It makes these timings unreliable and prevent the driver to use one of these four values with a non-ONFI chip. Fix this situation by taking the highest possible value (or a default one) value for each missing timing (stored as unsigned 16-bit entries in the parameter page). This makes tBERS and tPROG being ~65ms while typical values are at most a few milliseconds. As these are timeouts, it is not impacting at all the performances in nominal use. tR maximum time and tCCS minimum time (delay to wait after a change column) are set, according to the ONFI specification, to default 'slow' values; respectively 200us and 500ns. Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Reviewed-by: Boris Brezillon <boris.brezillon@bootlin.com> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
343 lines
7.6 KiB
C
343 lines
7.6 KiB
C
/*
|
|
* Copyright (C) 2014 Free Electrons
|
|
*
|
|
* Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
*/
|
|
#include <linux/kernel.h>
|
|
#include <linux/err.h>
|
|
#include <linux/export.h>
|
|
#include <linux/mtd/rawnand.h>
|
|
|
|
#define ONFI_DYN_TIMING_MAX U16_MAX
|
|
|
|
static const struct nand_data_interface onfi_sdr_timings[] = {
|
|
/* Mode 0 */
|
|
{
|
|
.type = NAND_SDR_IFACE,
|
|
.timings.sdr = {
|
|
.tCCS_min = 500000,
|
|
.tR_max = 200000000,
|
|
.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.sdr = {
|
|
.tCCS_min = 500000,
|
|
.tR_max = 200000000,
|
|
.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.sdr = {
|
|
.tCCS_min = 500000,
|
|
.tR_max = 200000000,
|
|
.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.sdr = {
|
|
.tCCS_min = 500000,
|
|
.tR_max = 200000000,
|
|
.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.sdr = {
|
|
.tCCS_min = 500000,
|
|
.tR_max = 200000000,
|
|
.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.sdr = {
|
|
.tCCS_min = 500000,
|
|
.tR_max = 200000000,
|
|
.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,
|
|
},
|
|
},
|
|
};
|
|
|
|
/**
|
|
* onfi_async_timing_mode_to_sdr_timings - [NAND Interface] Retrieve NAND
|
|
* timings according to the given ONFI timing mode
|
|
* @mode: ONFI timing mode
|
|
*/
|
|
const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode)
|
|
{
|
|
if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings))
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
return &onfi_sdr_timings[mode].timings.sdr;
|
|
}
|
|
EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings);
|
|
|
|
/**
|
|
* onfi_fill_data_interface - [NAND Interface] Initialize a data interface from
|
|
* given ONFI mode
|
|
* @mode: The ONFI timing mode
|
|
*/
|
|
int onfi_fill_data_interface(struct nand_chip *chip,
|
|
enum nand_data_interface_type type,
|
|
int timing_mode)
|
|
{
|
|
struct nand_data_interface *iface = &chip->data_interface;
|
|
|
|
if (type != NAND_SDR_IFACE)
|
|
return -EINVAL;
|
|
|
|
if (timing_mode < 0 || timing_mode >= ARRAY_SIZE(onfi_sdr_timings))
|
|
return -EINVAL;
|
|
|
|
*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 (chip->parameters.onfi.version) {
|
|
struct nand_parameters *params = &chip->parameters;
|
|
struct nand_sdr_timings *timings = &iface->timings.sdr;
|
|
|
|
/* microseconds -> picoseconds */
|
|
timings->tPROG_max = 1000000ULL * params->onfi.tPROG;
|
|
timings->tBERS_max = 1000000ULL * params->onfi.tBERS;
|
|
timings->tR_max = 1000000ULL * params->onfi.tR;
|
|
|
|
/* nanoseconds -> picoseconds */
|
|
timings->tCCS_min = 1000UL * params->onfi.tCCS;
|
|
} else {
|
|
struct nand_sdr_timings *timings = &iface->timings.sdr;
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
/* microseconds -> picoseconds */
|
|
timings->tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX;
|
|
timings->tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX;
|
|
timings->tR_max = 1000000ULL * 200000000ULL;
|
|
|
|
/* nanoseconds -> picoseconds */
|
|
timings->tCCS_min = 1000UL * 500000;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(onfi_fill_data_interface);
|