From: Rickard Andersson <rickaran@axis.com>
To: <linux-mtd@lists.infradead.org>, <miquel.raynal@bootlin.com>,
<richard@nod.at>, <vigneshr@ti.com>, <s.hauer@pengutronix.de>
Cc: rickaran@axis.com
Subject: [PATCH] mtd: rawnand: Non ONFI specialized timing support
Date: Wed, 22 Apr 2020 14:18:00 +0200 [thread overview]
Message-ID: <20200422121800.7365-1-rickaran@axis.com> (raw)
From: Rickard x Andersson <rickaran@axis.com>
The Kioxia/Toshiba TH58NVG2S3HBAI4 NAND memory is not a
ONFI compliant memory. The timings of that memory are quite
close to ONFI mode 4 but is breaking that spec.
This patch adds a special table with timings that can be
used for non ONFI memories.
Erase block read speed is increased from 6739 KiB/s to
13260 KiB/s. Erase block write speed is increased from
3004 KiB/s to 3872 KiB/s.
Tested on IMX6ULL which has a NAND controller supporting
EDO mode.
Signed-off-by: Rickard x Andersson <rickaran@axis.com>
---
drivers/mtd/nand/raw/internals.h | 3 +-
drivers/mtd/nand/raw/nand_base.c | 73 +++++++++++++++++++++++++++++++++----
drivers/mtd/nand/raw/nand_ids.c | 4 ++
drivers/mtd/nand/raw/nand_timings.c | 66 +++++++++++++++++++++++++++++++--
include/linux/mtd/rawnand.h | 24 ++++++++++++
5 files changed, 157 insertions(+), 13 deletions(-)
diff --git a/drivers/mtd/nand/raw/internals.h b/drivers/mtd/nand/raw/internals.h
index fbf6ca015cd7..4fcb9f87caaf 100644
--- a/drivers/mtd/nand/raw/internals.h
+++ b/drivers/mtd/nand/raw/internals.h
@@ -81,7 +81,8 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
int allowbbt);
int onfi_fill_data_interface(struct nand_chip *chip,
enum nand_data_interface_type type,
- int timing_mode);
+ int timing_mode,
+ enum non_onfi_spec_timing spec_timing);
int nand_get_features(struct nand_chip *chip, int addr, u8 *subfeature_param);
int nand_set_features(struct nand_chip *chip, int addr, u8 *subfeature_param);
int nand_read_page_raw_notsupp(struct nand_chip *chip, u8 *buf,
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index ddd396e93e32..8980e42ec6bd 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -793,7 +793,8 @@ static int nand_reset_data_interface(struct nand_chip *chip, int chipnr)
* timings to timing mode 0.
*/
- onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0);
+ onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0,
+ NON_ONFI_TIMING_NOT_USED);
ret = chip->controller->ops->setup_data_interface(chip, chipnr,
&chip->data_interface);
if (ret)
@@ -875,6 +876,51 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr)
}
/**
+ * nand_handle_non_onfi_spec_timings - Handle non ONFI timings
+ * @chip: The NAND chip
+ *
+ * Returns 0 for success or negative error code otherwise.
+ */
+static int nand_handle_non_onfi_spec_timings(struct nand_chip *chip)
+{
+ int ret;
+
+ if (chip->spec_timing == NON_ONFI_TIMING_NOT_USED)
+ return -EINVAL;
+
+ if (chip->onfi_timing_mode_default)
+ return -EINVAL;
+
+ ret = onfi_fill_data_interface(chip,
+ NAND_SDR_IFACE,
+ chip->onfi_timing_mode_default,
+ chip->spec_timing);
+ if (ret)
+ return ret;
+ /*
+ * Pass NAND_DATA_IFACE_CHECK_ONLY to only check if the
+ * controller supports the requested timings.
+ */
+ ret = chip->controller->ops->setup_data_interface(chip,
+ NAND_DATA_IFACE_CHECK_ONLY,
+ &chip->data_interface);
+
+ if (ret) {
+ /*
+ * Settings were not supported by the controller,
+ * restore safe settings.
+ */
+ chip->spec_timing = NON_ONFI_TIMING_NOT_USED;
+ ret = onfi_fill_data_interface(chip,
+ NAND_SDR_IFACE,
+ chip->onfi_timing_mode_default,
+ chip->spec_timing);
+ }
+
+ return ret;
+}
+
+/**
* nand_init_data_interface - find the best data interface and timings
* @chip: The NAND chip
*
@@ -882,9 +928,11 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr)
* and the driver.
* First tries to retrieve supported timing modes from ONFI information,
* and if the NAND chip does not support ONFI, relies on the
- * ->onfi_timing_mode_default specified in the nand_ids table. After this
- * function nand_chip->data_interface is initialized with the best timing mode
- * available.
+ * ->onfi_timing_mode_default specified in the nand_ids table. If
+ * onfi_timing_mode_default is not provided then ->spec_timing
+ * will be used if set.
+ * After this function nand_chip->data_interface is initialized with the
+ * best timing mode available.
*
* Returns 0 for success or negative error code otherwise.
*/
@@ -903,14 +951,21 @@ static int nand_init_data_interface(struct nand_chip *chip)
if (chip->parameters.onfi) {
modes = chip->parameters.onfi->async_timing_mode;
} else {
- if (!chip->onfi_timing_mode_default)
- return 0;
+ if (!chip->onfi_timing_mode_default) {
+ if (chip->spec_timing) {
+ ret = nand_handle_non_onfi_spec_timings(chip);
+ if (ret)
+ return ret;
+ }
+ return 0;
+ }
modes = GENMASK(chip->onfi_timing_mode_default, 0);
}
for (mode = fls(modes) - 1; mode >= 0; mode--) {
- ret = onfi_fill_data_interface(chip, NAND_SDR_IFACE, mode);
+ ret = onfi_fill_data_interface(chip, NAND_SDR_IFACE, mode,
+ NON_ONFI_TIMING_NOT_USED);
if (ret)
continue;
@@ -4564,6 +4619,7 @@ static bool find_full_id_nand(struct nand_chip *chip,
chip->ecc_step_ds = NAND_ECC_STEP(type);
chip->onfi_timing_mode_default =
type->onfi_timing_mode_default;
+ chip->spec_timing = type->spec_timing;
chip->parameters.model = kstrdup(type->name, GFP_KERNEL);
if (!chip->parameters.model)
@@ -4981,7 +5037,8 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
mutex_init(&chip->lock);
/* Enforce the right timings for reset/detection */
- onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0);
+ onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0,
+ NON_ONFI_TIMING_NOT_USED);
ret = nand_dt_init(chip);
if (ret)
diff --git a/drivers/mtd/nand/raw/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c
index ea5a342cd91e..b1eb508a74e0 100644
--- a/drivers/mtd/nand/raw/nand_ids.c
+++ b/drivers/mtd/nand/raw/nand_ids.c
@@ -56,6 +56,10 @@ struct nand_flash_dev nand_flash_ids[] = {
{ .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} },
SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640,
NAND_ECC_INFO(40, SZ_1K), 4 },
+ {"TH58NVG2S3HBAI4 4G 3.3V 8-bit",
+ { .id = {0x98, 0xdc, 0x91, 0x15, 0x76} },
+ SZ_2K, SZ_512, SZ_128K, 0, 5, 128, NAND_ECC_INFO(8, SZ_512),
+ 0, NON_ONFI_TIMING_1},
LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS),
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
diff --git a/drivers/mtd/nand/raw/nand_timings.c b/drivers/mtd/nand/raw/nand_timings.c
index bea3062d71d6..0de07da36485 100644
--- a/drivers/mtd/nand/raw/nand_timings.c
+++ b/drivers/mtd/nand/raw/nand_timings.c
@@ -271,14 +271,67 @@ static const struct nand_data_interface onfi_sdr_timings[] = {
},
};
+static const struct nand_data_interface non_onfi_spec_sdr_timings[] = {
+ /*
+ * Chosen by enum non_onfi_spec_timing = NON_ONFI_TIMING_1
+ *
+ * Data below is obtained from KIOXIA/Toshiba TH58NVG2S3HBAI4
+ */
+ {
+ .type = NAND_SDR_IFACE,
+ .timings.sdr = {
+ .tCCS_min = 500000,
+ .tR_max = 200000000,
+ .tADL_min = 400000,
+ .tALH_min = 5000,
+ .tALS_min = 12000,
+ .tAR_min = 10000,
+ .tCEA_max = 25000,
+ .tCEH_min = 20000,
+ .tCH_min = 5000,
+ .tCHZ_max = 20000,
+ .tCLH_min = 5000,
+ .tCLR_min = 10000,
+ .tCLS_min = 12000,
+ .tCOH_min = 0,
+ .tCS_min = 20000,
+ .tDH_min = 5000,
+ .tDS_min = 12000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 0,
+ .tITC_max = 1000000,
+ .tRC_min = 25000,
+ .tREA_max = 20000,
+ .tREH_min = 10000,
+ .tRHOH_min = 25000,
+ .tRHW_min = 30000,
+ .tRHZ_max = 60000,
+ .tRLOH_min = 5000,
+ .tRP_min = 12000,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tWC_min = 25000,
+ .tWH_min = 10000,
+ .tWHR_min = 60000,
+ .tWP_min = 12000,
+ .tWW_min = 100000,
+ },
+ },
+};
+
/**
* onfi_fill_data_interface - [NAND Interface] Initialize a data interface from
* given ONFI mode
- * @mode: The ONFI timing mode
+ * @chip: The NAND chip
+ * @onfi_timing_mode: The ONFI timing mode
+ * @type: Timing type
+ * @spec_timing: Special timings to be used for non ONFI NAND memories
*/
int onfi_fill_data_interface(struct nand_chip *chip,
enum nand_data_interface_type type,
- int timing_mode)
+ int onfi_timing_mode,
+ enum non_onfi_spec_timing spec_timing)
{
struct nand_data_interface *iface = &chip->data_interface;
struct onfi_params *onfi = chip->parameters.onfi;
@@ -286,10 +339,15 @@ int onfi_fill_data_interface(struct nand_chip *chip,
if (type != NAND_SDR_IFACE)
return -EINVAL;
- if (timing_mode < 0 || timing_mode >= ARRAY_SIZE(onfi_sdr_timings))
+ if (onfi_timing_mode < 0 ||
+ onfi_timing_mode >= ARRAY_SIZE(onfi_sdr_timings))
return -EINVAL;
- *iface = onfi_sdr_timings[timing_mode];
+ if ((spec_timing > 0) &&
+ (spec_timing <= ARRAY_SIZE(non_onfi_spec_sdr_timings)))
+ *iface = non_onfi_spec_sdr_timings[spec_timing - 1];
+ else
+ *iface = onfi_sdr_timings[onfi_timing_mode];
/*
* Initialize timings that cannot be deduced from timing mode:
diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h
index b7445a44a814..329eff0951ed 100644
--- a/include/linux/mtd/rawnand.h
+++ b/include/linux/mtd/rawnand.h
@@ -961,6 +961,17 @@ struct nand_legacy {
};
/**
+ * enum non_onfi_spec_timing - Special timing that can be used for
+ * non ONFI NAND.
+ * @NON_ONFI_TIMING_NOT_USED: No special timings
+ * @NON_ONFI_TIMING_1: Special timing 1
+ */
+enum non_onfi_spec_timing {
+ NON_ONFI_TIMING_NOT_USED,
+ NON_ONFI_TIMING_1,
+};
+
+/**
* struct nand_chip - NAND Private Flash Chip Data
* @mtd: MTD device registered to the MTD framework
* @legacy: All legacy fields/hooks. If you develop a new driver,
@@ -1001,6 +1012,10 @@ struct nand_legacy {
* set to the actually used ONFI mode if the chip is
* ONFI compliant or deduced from the datasheet if
* the NAND chip is not ONFI compliant.
+ * @spec_timing: [INTERN] This field is set to choose the special
+ * non onfi timings. This is used if the chip is not
+ * ONFI compliant and the timings does not correspond
+ * to any of the ONFI modes.
* @numchips: [INTERN] number of physical chips
* @chipsize: [INTERN] the size of one chip for multichip arrays
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
@@ -1065,6 +1080,7 @@ struct nand_chip {
uint16_t ecc_strength_ds;
uint16_t ecc_step_ds;
int onfi_timing_mode_default;
+ enum non_onfi_spec_timing spec_timing;
int badblockpos;
int badblockbits;
@@ -1202,6 +1218,13 @@ static inline void *nand_get_manufacturer_data(struct nand_chip *chip)
* @onfi_timing_mode_default: the default ONFI timing mode entered after a NAND
* reset. Should be deduced from timings described
* in the datasheet.
+ * @spec_timing: Alternative timing table to be used after a NAND reset.
+ * This can be used for non ONFI NANDs that does not have
+ * timings that correspond well to the ONFI timing modes.
+ * This value references non_onfi_spec_sdr_timings[] in
+ * nand_timings.c.
+ * Should be deduced from timings described
+ * in the datasheet.
*
*/
struct nand_flash_dev {
@@ -1224,6 +1247,7 @@ struct nand_flash_dev {
uint16_t step_ds;
} ecc;
int onfi_timing_mode_default;
+ enum non_onfi_spec_timing spec_timing;
};
int nand_create_bbt(struct nand_chip *chip);
--
2.11.0
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
next reply other threads:[~2020-04-22 12:18 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-04-22 12:18 Rickard Andersson [this message]
2020-05-12 19:09 ` [PATCH] mtd: rawnand: Non ONFI specialized timing support Miquel Raynal
2020-05-13 8:48 ` Miquel Raynal
2020-05-14 9:04 ` SV: " Rickard X Andersson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20200422121800.7365-1-rickaran@axis.com \
--to=rickaran@axis.com \
--cc=linux-mtd@lists.infradead.org \
--cc=miquel.raynal@bootlin.com \
--cc=richard@nod.at \
--cc=s.hauer@pengutronix.de \
--cc=vigneshr@ti.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.