17fa804418
Right now, the chip->data_interface field is populated in nand_scan_tail(), so after the whole NAND detection has taken place. This is fine because these timings are not yet used by the core so early in the probe process, but the situation is about to change with the introduction of ->exec_op(). Also, by convention, nand_scan_ident() is not supposed to allocate resources, only nand_scan_tail() can, so this prevent us from allocating and initializing the data_interface object in nand_scan_ident(). In order to solve this problem, directly embed a data_interface object in nand_chip so that we don't have to allocate it, and initialize it to ONFI SDR mode 0 at the very beginning of nand_scan_ident(). Signed-off-by: Miquel Raynal <miquel.raynal@free-electrons.com> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
325 lines
7.1 KiB
C
325 lines
7.1 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>
|
|
|
|
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:
|
|
* tR, tPROG, tCCS, ...
|
|
* These information are part of the ONFI parameter page.
|
|
*/
|
|
if (chip->onfi_version) {
|
|
struct nand_onfi_params *params = &chip->onfi_params;
|
|
struct nand_sdr_timings *timings = &iface->timings.sdr;
|
|
|
|
/* microseconds -> picoseconds */
|
|
timings->tPROG_max = 1000000ULL * le16_to_cpu(params->t_prog);
|
|
timings->tBERS_max = 1000000ULL * le16_to_cpu(params->t_bers);
|
|
timings->tR_max = 1000000ULL * le16_to_cpu(params->t_r);
|
|
|
|
/* nanoseconds -> picoseconds */
|
|
timings->tCCS_min = 1000UL * le16_to_cpu(params->t_ccs);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(onfi_fill_data_interface);
|