Commit b1206122 authored by Miquel Raynal's avatar Miquel Raynal Committed by Boris Brezillon

mtd: rawnand: gpmi: use core timings instead of an empirical derivation

GPMI driver timings derivation looks very empirical and does not use
the known timings that the core wants to use with the NAND chip, by
using local defined constants that have no special meaning from the
outside world.

Simplify the way all of this is computed and use the NAND core's SDR
timings.

Integrity of the reads/writes has been checked with nandbiterrs, speed
improvements with flash_speed on a Freescale i.MX6 DualLite/Solo SABRE
Automotive Board. Measures are below, variations of less than 150kiB/s
between tests are common and then not significant. Speeds using mode 5
are the same, while speeds using mode 0 are quite improved (+40/50%
from non-optimal computation).

Forcing timings mode 0:

=======================

Before this patch:
------------------
eraseblock write speed is 2298 KiB/s
eraseblock read speed is 3636 KiB/s
page write speed is 2136 KiB/s
page read speed is 3316 KiB/s
2 page write speed is 2199 KiB/s
2 page read speed is 3468 KiB/s

After this patch:
-----------------
eraseblock write speed is 3232 KiB/s
eraseblock read speed is 5663 KiB/s
page write speed is 2915 KiB/s
page read speed is 4904 KiB/s
2 page write speed is 3084 KiB/s
2 page read speed is 5267 KiB/s

Forcing timings mode 5:
=======================

Before this patch:
------------------
eraseblock write speed is 4338 KiB/s
eraseblock read speed is 14883 KiB/s
page write speed is 3786 KiB/s
page read speed is 12800 KiB/s
2 page write speed is 4076 KiB/s
2 page read speed is 14065 KiB/s

After this patch:
-----------------
eraseblock write speed is 4309 KiB/s
eraseblock read speed is 14712 KiB/s
page write speed is 3764 KiB/s
page read speed is 12673 KiB/s
2 page write speed is 4076 KiB/s
2 page read speed is 14065 KiB/s
Signed-off-by: default avatarMiquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: default avatarBoris Brezillon <boris.brezillon@bootlin.com>
parent 76e1a008
This diff is collapsed.
...@@ -94,7 +94,7 @@ static const struct mtd_ooblayout_ops gpmi_ooblayout_ops = { ...@@ -94,7 +94,7 @@ static const struct mtd_ooblayout_ops gpmi_ooblayout_ops = {
static const struct gpmi_devdata gpmi_devdata_imx23 = { static const struct gpmi_devdata gpmi_devdata_imx23 = {
.type = IS_MX23, .type = IS_MX23,
.bch_max_ecc_strength = 20, .bch_max_ecc_strength = 20,
.max_chain_delay = 16, .max_chain_delay = 16000,
.clks = gpmi_clks_for_mx2x, .clks = gpmi_clks_for_mx2x,
.clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x), .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x),
}; };
...@@ -102,7 +102,7 @@ static const struct gpmi_devdata gpmi_devdata_imx23 = { ...@@ -102,7 +102,7 @@ static const struct gpmi_devdata gpmi_devdata_imx23 = {
static const struct gpmi_devdata gpmi_devdata_imx28 = { static const struct gpmi_devdata gpmi_devdata_imx28 = {
.type = IS_MX28, .type = IS_MX28,
.bch_max_ecc_strength = 20, .bch_max_ecc_strength = 20,
.max_chain_delay = 16, .max_chain_delay = 16000,
.clks = gpmi_clks_for_mx2x, .clks = gpmi_clks_for_mx2x,
.clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x), .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x),
}; };
...@@ -114,7 +114,7 @@ static const char * const gpmi_clks_for_mx6[] = { ...@@ -114,7 +114,7 @@ static const char * const gpmi_clks_for_mx6[] = {
static const struct gpmi_devdata gpmi_devdata_imx6q = { static const struct gpmi_devdata gpmi_devdata_imx6q = {
.type = IS_MX6Q, .type = IS_MX6Q,
.bch_max_ecc_strength = 40, .bch_max_ecc_strength = 40,
.max_chain_delay = 12, .max_chain_delay = 12000,
.clks = gpmi_clks_for_mx6, .clks = gpmi_clks_for_mx6,
.clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6),
}; };
...@@ -122,7 +122,7 @@ static const struct gpmi_devdata gpmi_devdata_imx6q = { ...@@ -122,7 +122,7 @@ static const struct gpmi_devdata gpmi_devdata_imx6q = {
static const struct gpmi_devdata gpmi_devdata_imx6sx = { static const struct gpmi_devdata gpmi_devdata_imx6sx = {
.type = IS_MX6SX, .type = IS_MX6SX,
.bch_max_ecc_strength = 62, .bch_max_ecc_strength = 62,
.max_chain_delay = 12, .max_chain_delay = 12000,
.clks = gpmi_clks_for_mx6, .clks = gpmi_clks_for_mx6,
.clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6),
}; };
...@@ -134,7 +134,7 @@ static const char * const gpmi_clks_for_mx7d[] = { ...@@ -134,7 +134,7 @@ static const char * const gpmi_clks_for_mx7d[] = {
static const struct gpmi_devdata gpmi_devdata_imx7d = { static const struct gpmi_devdata gpmi_devdata_imx7d = {
.type = IS_MX7D, .type = IS_MX7D,
.bch_max_ecc_strength = 62, .bch_max_ecc_strength = 62,
.max_chain_delay = 12, .max_chain_delay = 12000,
.clks = gpmi_clks_for_mx7d, .clks = gpmi_clks_for_mx7d,
.clks_count = ARRAY_SIZE(gpmi_clks_for_mx7d), .clks_count = ARRAY_SIZE(gpmi_clks_for_mx7d),
}; };
...@@ -695,34 +695,6 @@ static void release_resources(struct gpmi_nand_data *this) ...@@ -695,34 +695,6 @@ static void release_resources(struct gpmi_nand_data *this)
release_dma_channels(this); release_dma_channels(this);
} }
static int init_hardware(struct gpmi_nand_data *this)
{
int ret;
/*
* This structure contains the "safe" GPMI timing that should succeed
* with any NAND Flash device
* (although, with less-than-optimal performance).
*/
struct nand_timing safe_timing = {
.data_setup_in_ns = 80,
.data_hold_in_ns = 60,
.address_setup_in_ns = 25,
.gpmi_sample_delay_in_ns = 6,
.tREA_in_ns = -1,
.tRLOH_in_ns = -1,
.tRHOH_in_ns = -1,
};
/* Initialize the hardwares. */
ret = gpmi_init(this);
if (ret)
return ret;
this->timing = safe_timing;
return 0;
}
static int read_page_prepare(struct gpmi_nand_data *this, static int read_page_prepare(struct gpmi_nand_data *this,
void *destination, unsigned length, void *destination, unsigned length,
void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
...@@ -2107,7 +2079,7 @@ static int gpmi_nand_probe(struct platform_device *pdev) ...@@ -2107,7 +2079,7 @@ static int gpmi_nand_probe(struct platform_device *pdev)
if (ret) if (ret)
goto exit_acquire_resources; goto exit_acquire_resources;
ret = init_hardware(this); ret = gpmi_init(this);
if (ret) if (ret)
goto exit_nfc_init; goto exit_nfc_init;
......
...@@ -86,39 +86,6 @@ enum dma_ops_type { ...@@ -86,39 +86,6 @@ enum dma_ops_type {
DMA_FOR_WRITE_ECC_PAGE DMA_FOR_WRITE_ECC_PAGE
}; };
/**
* struct nand_timing - Fundamental timing attributes for NAND.
* @data_setup_in_ns: The data setup time, in nanoseconds. Usually the
* maximum of tDS and tWP. A negative value
* indicates this characteristic isn't known.
* @data_hold_in_ns: The data hold time, in nanoseconds. Usually the
* maximum of tDH, tWH and tREH. A negative value
* indicates this characteristic isn't known.
* @address_setup_in_ns: The address setup time, in nanoseconds. Usually
* the maximum of tCLS, tCS and tALS. A negative
* value indicates this characteristic isn't known.
* @gpmi_sample_delay_in_ns: A GPMI-specific timing parameter. A negative value
* indicates this characteristic isn't known.
* @tREA_in_ns: tREA, in nanoseconds, from the data sheet. A
* negative value indicates this characteristic isn't
* known.
* @tRLOH_in_ns: tRLOH, in nanoseconds, from the data sheet. A
* negative value indicates this characteristic isn't
* known.
* @tRHOH_in_ns: tRHOH, in nanoseconds, from the data sheet. A
* negative value indicates this characteristic isn't
* known.
*/
struct nand_timing {
int8_t data_setup_in_ns;
int8_t data_hold_in_ns;
int8_t address_setup_in_ns;
int8_t gpmi_sample_delay_in_ns;
int8_t tREA_in_ns;
int8_t tRLOH_in_ns;
int8_t tRHOH_in_ns;
};
enum gpmi_type { enum gpmi_type {
IS_MX23, IS_MX23,
IS_MX28, IS_MX28,
...@@ -137,41 +104,21 @@ struct gpmi_devdata { ...@@ -137,41 +104,21 @@ struct gpmi_devdata {
/** /**
* struct gpmi_nfc_hardware_timing - GPMI hardware timing parameters. * struct gpmi_nfc_hardware_timing - GPMI hardware timing parameters.
* @timing_mode: The timing mode to comply with.
* @must_apply_timings: Whether controller timings have already been * @must_apply_timings: Whether controller timings have already been
* applied or not (useful only while there is * applied or not (useful only while there is
* support for only one chip select) * support for only one chip select)
* @clk_rate: The clock rate that must be used to derive the * @clk_rate: The clock rate that must be used to derive the
* following parameters. * following parameters
* @data_setup_in_cycles: The data setup time, in cycles. * @timing0: HW_GPMI_TIMING0 register
* @data_hold_in_cycles: The data hold time, in cycles. * @timing1: HW_GPMI_TIMING1 register
* @address_setup_in_cycles: The address setup time, in cycles. * @ctrl1n: HW_GPMI_CTRL1n register
* @device_busy_timeout: The timeout waiting for NAND Ready/Busy,
* this value is the number of cycles multiplied
* by 4096.
* @use_half_periods: Indicates the clock is running slowly, so the
* NFC DLL should use half-periods.
* @sample_delay_factor: The sample delay factor.
* @wrn_dly_sel: The delay on the GPMI write strobe.
*/ */
struct gpmi_nfc_hardware_timing { struct gpmi_nfc_hardware_timing {
unsigned int timing_mode;
bool must_apply_timings; bool must_apply_timings;
unsigned long int clk_rate; unsigned long int clk_rate;
u32 timing0;
/* for HW_GPMI_TIMING0 */ u32 timing1;
u8 data_setup_in_cycles; u32 ctrl1n;
u8 data_hold_in_cycles;
u8 address_setup_in_cycles;
/* for HW_GPMI_TIMING1 */
u16 device_busy_timeout;
#define GPMI_DEFAULT_BUSY_TIMEOUT 0x500 /* default busy timeout value.*/
/* for HW_GPMI_CTRL1 */
bool use_half_periods;
u8 sample_delay_factor;
u8 wrn_dly_sel;
}; };
struct gpmi_nand_data { struct gpmi_nand_data {
...@@ -186,8 +133,6 @@ struct gpmi_nand_data { ...@@ -186,8 +133,6 @@ struct gpmi_nand_data {
struct resources resources; struct resources resources;
/* Flash Hardware */ /* Flash Hardware */
struct nand_timing timing;
int timing_mode;
struct gpmi_nfc_hardware_timing hw; struct gpmi_nfc_hardware_timing hw;
/* BCH */ /* BCH */
...@@ -241,40 +186,6 @@ struct gpmi_nand_data { ...@@ -241,40 +186,6 @@ struct gpmi_nand_data {
void *private; void *private;
}; };
/**
* struct timing_threshold - Timing threshold
* @max_data_setup_cycles: The maximum number of data setup cycles that
* can be expressed in the hardware.
* @internal_data_setup_in_ns: The time, in ns, that the NFC hardware requires
* for data read internal setup. In the Reference
* Manual, see the chapter "High-Speed NAND
* Timing" for more details.
* @max_sample_delay_factor: The maximum sample delay factor that can be
* expressed in the hardware.
* @max_dll_clock_period_in_ns: The maximum period of the GPMI clock that the
* sample delay DLL hardware can possibly work
* with (the DLL is unusable with longer periods).
* If the full-cycle period is greater than HALF
* this value, the DLL must be configured to use
* half-periods.
* @max_dll_delay_in_ns: The maximum amount of delay, in ns, that the
* DLL can implement.
* @clock_frequency_in_hz: The clock frequency, in Hz, during the current
* I/O transaction. If no I/O transaction is in
* progress, this is the clock frequency during
* the most recent I/O transaction.
*/
struct timing_threshold {
const unsigned int max_chip_count;
const unsigned int max_data_setup_cycles;
const unsigned int internal_data_setup_in_ns;
const unsigned int max_sample_delay_factor;
const unsigned int max_dll_clock_period_in_ns;
const unsigned int max_dll_delay_in_ns;
unsigned long clock_frequency_in_hz;
};
/* Common Services */ /* Common Services */
int common_nfc_set_geometry(struct gpmi_nand_data *); int common_nfc_set_geometry(struct gpmi_nand_data *);
struct dma_chan *get_dma_chan(struct gpmi_nand_data *); struct dma_chan *get_dma_chan(struct gpmi_nand_data *);
......
...@@ -147,6 +147,11 @@ ...@@ -147,6 +147,11 @@
#define BM_GPMI_CTRL1_GPMI_MODE (1 << 0) #define BM_GPMI_CTRL1_GPMI_MODE (1 << 0)
#define BM_GPMI_CTRL1_CLEAR_MASK (BM_GPMI_CTRL1_WRN_DLY_SEL | \
BM_GPMI_CTRL1_DLL_ENABLE | \
BM_GPMI_CTRL1_RDN_DELAY | \
BM_GPMI_CTRL1_HALF_PERIOD)
#define HW_GPMI_TIMING0 0x00000070 #define HW_GPMI_TIMING0 0x00000070
#define BP_GPMI_TIMING0_ADDRESS_SETUP 16 #define BP_GPMI_TIMING0_ADDRESS_SETUP 16
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment