* [PATCH v2 0/3] mtd: rawnand: sunxi: fix H6/H616 controller timings
@ 2026-07-17 17:33 James Hilliard
2026-07-17 17:33 ` [PATCH v2 1/3] mtd: rawnand: sunxi: group controller delay tables James Hilliard
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: James Hilliard @ 2026-07-17 17:33 UTC (permalink / raw)
To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Chen-Yu Tsai, Jernej Skrabec, Samuel Holland, Richard Genoud
Cc: linux-mtd, linux-arm-kernel, linux-sunxi, linux-kernel,
James Hilliard, stable
The sunxi NAND timing solver currently applies the original A10 NDFC
timing characteristics to every supported controller. The H6/H616 NDFC
uses two internal cycles for several setup and hold intervals and has
different delay-field encodings, so the resulting clock and timing
register values do not match the hardware.
Prepare for per-controller timing data in two behaviorally focused
patches, then add and select the H616 characteristics in the final fix.
Legacy controllers retain their existing setup-cycle behavior and delay
encodings.
Tested on an H616 board with Toshiba TC58NVG1S3H NAND.
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
Changes v1 -> v2:
- Split legacy timing setup from the H616 fix (suggested by Miquel)
- Move tADL/tWHR encodings to a separate patch (suggested by Miquel)
- Keep the timing lookup helper unchanged (suggested by Miquel)
- Drop the minimum-period helper (suggested by Miquel)
- Align H616 timing-data and capability names (suggested by Miquel)
- Add Cc: stable to all three patches (suggested by Miquel)
---
James Hilliard (3):
mtd: rawnand: sunxi: group controller delay tables
mtd: rawnand: sunxi: describe tADL and tWHR delays
mtd: rawnand: sunxi: fix H6/H616 controller timings
drivers/mtd/nand/raw/sunxi_nand.c | 119 +++++++++++++++++++++++++++-----------
1 file changed, 86 insertions(+), 33 deletions(-)
---
base-commit: 6f5156d7a31a8c3b0f34af4675c9299c8f877cbe
change-id: 20260717-submit-sunxi-nand-h6-h616-timings-794a2994267d
Best regards,
--
James Hilliard <james.hilliard1@gmail.com>
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v2 1/3] mtd: rawnand: sunxi: group controller delay tables
2026-07-17 17:33 [PATCH v2 0/3] mtd: rawnand: sunxi: fix H6/H616 controller timings James Hilliard
@ 2026-07-17 17:33 ` James Hilliard
2026-07-17 17:33 ` [PATCH v2 2/3] mtd: rawnand: sunxi: describe tADL and tWHR delays James Hilliard
2026-07-17 17:33 ` [PATCH v2 3/3] mtd: rawnand: sunxi: fix H6/H616 controller timings James Hilliard
2 siblings, 0 replies; 4+ messages in thread
From: James Hilliard @ 2026-07-17 17:33 UTC (permalink / raw)
To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Chen-Yu Tsai, Jernej Skrabec, Samuel Holland, Richard Genoud
Cc: linux-mtd, linux-arm-kernel, linux-sunxi, linux-kernel,
James Hilliard, stable
The tWB and tRHW timing field encodings are controller properties, but
they currently live in standalone lookup tables.
Group them in a timing descriptor selected through the controller
capability data. Point every existing controller at the legacy values so
this is a pure preparation change.
Cc: stable@vger.kernel.org
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/mtd/nand/raw/sunxi_nand.c | 36 ++++++++++++++++++++++++++++--------
1 file changed, 28 insertions(+), 8 deletions(-)
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index 02647565c8ba..d12cbb3c813c 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -237,6 +237,14 @@ struct sunxi_nand_hw_ecc {
u32 ecc_ctl;
};
+#define SUNXI_NFC_TIMING_STEPS 4
+
+/* Delay arrays contain internal NDFC clock cycles for field values 0 to 3. */
+struct sunxi_nfc_timings {
+ s32 tWB[SUNXI_NFC_TIMING_STEPS];
+ s32 tRHW[SUNXI_NFC_TIMING_STEPS];
+};
+
/**
* struct sunxi_nand_chip - stores NAND chip device related information
*
@@ -301,6 +309,7 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
* bytes to write
* @nuser_data_tab: Size of @user_data_len_tab
* @sram_size: Size of the NAND controller SRAM
+ * @timings: Controller timing characteristics
*/
struct sunxi_nfc_caps {
bool has_mdma;
@@ -327,6 +336,7 @@ struct sunxi_nfc_caps {
unsigned int nuser_data_tab;
unsigned int max_ecc_steps;
int sram_size;
+ const struct sunxi_nfc_timings *timings;
};
/**
@@ -1667,8 +1677,10 @@ static int sunxi_nfc_hw_ecc_write_oob(struct nand_chip *nand, int page)
return nand_prog_page_end_op(nand);
}
-static const s32 tWB_lut[] = {6, 12, 16, 20};
-static const s32 tRHW_lut[] = {4, 8, 12, 20};
+static const struct sunxi_nfc_timings sun4i_a10_nfc_timings = {
+ .tWB = { 6, 12, 16, 20 },
+ .tRHW = { 4, 8, 12, 20 },
+};
static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
u32 clk_period)
@@ -1693,6 +1705,7 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
{
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+ const struct sunxi_nfc_timings *nfc_timings = nfc->caps->timings;
const struct nand_sdr_timings *timings;
u32 min_clk_period = 0;
s32 tWB, tADL, tWHR, tRHW, tCAD;
@@ -1763,8 +1776,10 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2);
/* T16 - T19 + tCAD */
- if (timings->tWB_max > (min_clk_period * 20))
- min_clk_period = DIV_ROUND_UP(timings->tWB_max, 20);
+ if (timings->tWB_max >
+ (min_clk_period * nfc_timings->tWB[SUNXI_NFC_TIMING_STEPS - 1]))
+ min_clk_period = DIV_ROUND_UP(timings->tWB_max,
+ nfc_timings->tWB[SUNXI_NFC_TIMING_STEPS - 1]);
if (timings->tADL_min > (min_clk_period * 32))
min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32);
@@ -1772,8 +1787,10 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
if (timings->tWHR_min > (min_clk_period * 32))
min_clk_period = DIV_ROUND_UP(timings->tWHR_min, 32);
- if (timings->tRHW_min > (min_clk_period * 20))
- min_clk_period = DIV_ROUND_UP(timings->tRHW_min, 20);
+ if (timings->tRHW_min >
+ (min_clk_period * nfc_timings->tRHW[SUNXI_NFC_TIMING_STEPS - 1]))
+ min_clk_period = DIV_ROUND_UP(timings->tRHW_min,
+ nfc_timings->tRHW[SUNXI_NFC_TIMING_STEPS - 1]);
/*
* In non-EDO, tREA should be less than tRP to guarantee that the
@@ -1789,7 +1806,7 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
if (timings->tREA_max > min_clk_period && !timings->tRLOH_min)
min_clk_period = timings->tREA_max;
- tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max,
+ tWB = sunxi_nand_lookup_timing(nfc_timings->tWB, timings->tWB_max,
min_clk_period);
if (tWB < 0) {
dev_err(nfc->dev, "unsupported tWB\n");
@@ -1808,7 +1825,7 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
return -EINVAL;
}
- tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min,
+ tRHW = sunxi_nand_lookup_timing(nfc_timings->tRHW, timings->tRHW_min,
min_clk_period);
if (tRHW < 0) {
dev_err(nfc->dev, "unsupported tRHW\n");
@@ -2595,6 +2612,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
.nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10),
.max_ecc_steps = 16,
.sram_size = 1024,
+ .timings = &sun4i_a10_nfc_timings,
};
static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
@@ -2617,6 +2635,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
.nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10),
.max_ecc_steps = 16,
.sram_size = 1024,
+ .timings = &sun4i_a10_nfc_timings,
};
static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = {
@@ -2641,6 +2660,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = {
.nuser_data_tab = ARRAY_SIZE(sunxi_user_data_len_h6),
.max_ecc_steps = 32,
.sram_size = 8192,
+ .timings = &sun4i_a10_nfc_timings,
};
static const struct of_device_id sunxi_nfc_ids[] = {
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v2 2/3] mtd: rawnand: sunxi: describe tADL and tWHR delays
2026-07-17 17:33 [PATCH v2 0/3] mtd: rawnand: sunxi: fix H6/H616 controller timings James Hilliard
2026-07-17 17:33 ` [PATCH v2 1/3] mtd: rawnand: sunxi: group controller delay tables James Hilliard
@ 2026-07-17 17:33 ` James Hilliard
2026-07-17 17:33 ` [PATCH v2 3/3] mtd: rawnand: sunxi: fix H6/H616 controller timings James Hilliard
2 siblings, 0 replies; 4+ messages in thread
From: James Hilliard @ 2026-07-17 17:33 UTC (permalink / raw)
To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Chen-Yu Tsai, Jernej Skrabec, Samuel Holland, Richard Genoud
Cc: linux-mtd, linux-arm-kernel, linux-sunxi, linux-kernel,
James Hilliard, stable
The tADL and tWHR timing fields use four encoded delays, but the driver
currently derives their values with a shift. This hides the actual
controller timing characteristics and lets the clock solver select a
32-cycle delay that the fields cannot encode.
Describe the legacy 7, 15, 23 and 31 cycle thresholds explicitly and use
the tables for both clock selection and field lookup. This prepares the
driver for controllers with different encodings.
Cc: stable@vger.kernel.org
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/mtd/nand/raw/sunxi_nand.c | 30 ++++++++++++++++++++----------
1 file changed, 20 insertions(+), 10 deletions(-)
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index d12cbb3c813c..791f495f6e82 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -242,6 +242,8 @@ struct sunxi_nand_hw_ecc {
/* Delay arrays contain internal NDFC clock cycles for field values 0 to 3. */
struct sunxi_nfc_timings {
s32 tWB[SUNXI_NFC_TIMING_STEPS];
+ s32 tADL[SUNXI_NFC_TIMING_STEPS];
+ s32 tWHR[SUNXI_NFC_TIMING_STEPS];
s32 tRHW[SUNXI_NFC_TIMING_STEPS];
};
@@ -1679,6 +1681,8 @@ static int sunxi_nfc_hw_ecc_write_oob(struct nand_chip *nand, int page)
static const struct sunxi_nfc_timings sun4i_a10_nfc_timings = {
.tWB = { 6, 12, 16, 20 },
+ .tADL = { 7, 15, 23, 31 },
+ .tWHR = { 7, 15, 23, 31 },
.tRHW = { 4, 8, 12, 20 },
};
@@ -1781,11 +1785,15 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
min_clk_period = DIV_ROUND_UP(timings->tWB_max,
nfc_timings->tWB[SUNXI_NFC_TIMING_STEPS - 1]);
- if (timings->tADL_min > (min_clk_period * 32))
- min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32);
+ if (timings->tADL_min >
+ (min_clk_period * nfc_timings->tADL[SUNXI_NFC_TIMING_STEPS - 1]))
+ min_clk_period = DIV_ROUND_UP(timings->tADL_min,
+ nfc_timings->tADL[SUNXI_NFC_TIMING_STEPS - 1]);
- if (timings->tWHR_min > (min_clk_period * 32))
- min_clk_period = DIV_ROUND_UP(timings->tWHR_min, 32);
+ if (timings->tWHR_min >
+ (min_clk_period * nfc_timings->tWHR[SUNXI_NFC_TIMING_STEPS - 1]))
+ min_clk_period = DIV_ROUND_UP(timings->tWHR_min,
+ nfc_timings->tWHR[SUNXI_NFC_TIMING_STEPS - 1]);
if (timings->tRHW_min >
(min_clk_period * nfc_timings->tRHW[SUNXI_NFC_TIMING_STEPS - 1]))
@@ -1813,16 +1821,18 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
return tWB;
}
- tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3;
- if (tADL > 3) {
+ tADL = sunxi_nand_lookup_timing(nfc_timings->tADL,
+ timings->tADL_min, min_clk_period);
+ if (tADL < 0) {
dev_err(nfc->dev, "unsupported tADL\n");
- return -EINVAL;
+ return tADL;
}
- tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3;
- if (tWHR > 3) {
+ tWHR = sunxi_nand_lookup_timing(nfc_timings->tWHR,
+ timings->tWHR_min, min_clk_period);
+ if (tWHR < 0) {
dev_err(nfc->dev, "unsupported tWHR\n");
- return -EINVAL;
+ return tWHR;
}
tRHW = sunxi_nand_lookup_timing(nfc_timings->tRHW, timings->tRHW_min,
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v2 3/3] mtd: rawnand: sunxi: fix H6/H616 controller timings
2026-07-17 17:33 [PATCH v2 0/3] mtd: rawnand: sunxi: fix H6/H616 controller timings James Hilliard
2026-07-17 17:33 ` [PATCH v2 1/3] mtd: rawnand: sunxi: group controller delay tables James Hilliard
2026-07-17 17:33 ` [PATCH v2 2/3] mtd: rawnand: sunxi: describe tADL and tWHR delays James Hilliard
@ 2026-07-17 17:33 ` James Hilliard
2 siblings, 0 replies; 4+ messages in thread
From: James Hilliard @ 2026-07-17 17:33 UTC (permalink / raw)
To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Chen-Yu Tsai, Jernej Skrabec, Samuel Holland, Richard Genoud
Cc: linux-mtd, linux-arm-kernel, linux-sunxi, linux-kernel,
James Hilliard, stable
The NAND timing calculation assumes that command and address setup and
hold intervals T1-T4, T7 and T11 each take one controller clock. It also
uses the original A10 delay encodings for tWB, tADL, tWHR and tRHW.
The H6/H616 NDFC defines the setup and hold intervals as two internal
clock cycles and uses different delay encodings. Add the H616 timing
characteristics and select them through the controller capability data so
the clock solver and timing fields match the hardware.
Fixes: 88fd4e4deae8 ("mtd: rawnand: sunxi: Add support for H616 nand controller")
Cc: stable@vger.kernel.org
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/mtd/nand/raw/sunxi_nand.c | 49 ++++++++++++++++++++++++++++-----------
1 file changed, 36 insertions(+), 13 deletions(-)
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index 791f495f6e82..3d91533a9f13 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -241,6 +241,8 @@ struct sunxi_nand_hw_ecc {
/* Delay arrays contain internal NDFC clock cycles for field values 0 to 3. */
struct sunxi_nfc_timings {
+ /* Internal clock cycles used by T1-T4, T7 and T11. */
+ u8 setup_cycles;
s32 tWB[SUNXI_NFC_TIMING_STEPS];
s32 tADL[SUNXI_NFC_TIMING_STEPS];
s32 tWHR[SUNXI_NFC_TIMING_STEPS];
@@ -1680,12 +1682,21 @@ static int sunxi_nfc_hw_ecc_write_oob(struct nand_chip *nand, int page)
}
static const struct sunxi_nfc_timings sun4i_a10_nfc_timings = {
+ .setup_cycles = 1,
.tWB = { 6, 12, 16, 20 },
.tADL = { 7, 15, 23, 31 },
.tWHR = { 7, 15, 23, 31 },
.tRHW = { 4, 8, 12, 20 },
};
+static const struct sunxi_nfc_timings sun50i_h616_nfc_timings = {
+ .setup_cycles = 2,
+ .tWB = { 28, 44, 60, 76 },
+ .tADL = { 0, 12, 28, 44 },
+ .tWHR = { 0, 12, 28, 44 },
+ .tRHW = { 8, 24, 40, 56 },
+};
+
static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
u32 clk_period)
{
@@ -1720,20 +1731,28 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
return -ENOTSUPP;
/* T1 <=> tCLS */
- if (timings->tCLS_min > min_clk_period)
- min_clk_period = timings->tCLS_min;
+ if (timings->tCLS_min >
+ min_clk_period * nfc_timings->setup_cycles)
+ min_clk_period = DIV_ROUND_UP(timings->tCLS_min,
+ nfc_timings->setup_cycles);
/* T2 <=> tCLH */
- if (timings->tCLH_min > min_clk_period)
- min_clk_period = timings->tCLH_min;
+ if (timings->tCLH_min >
+ min_clk_period * nfc_timings->setup_cycles)
+ min_clk_period = DIV_ROUND_UP(timings->tCLH_min,
+ nfc_timings->setup_cycles);
/* T3 <=> tCS */
- if (timings->tCS_min > min_clk_period)
- min_clk_period = timings->tCS_min;
+ if (timings->tCS_min >
+ min_clk_period * nfc_timings->setup_cycles)
+ min_clk_period = DIV_ROUND_UP(timings->tCS_min,
+ nfc_timings->setup_cycles);
/* T4 <=> tCH */
- if (timings->tCH_min > min_clk_period)
- min_clk_period = timings->tCH_min;
+ if (timings->tCH_min >
+ min_clk_period * nfc_timings->setup_cycles)
+ min_clk_period = DIV_ROUND_UP(timings->tCH_min,
+ nfc_timings->setup_cycles);
/* T5 <=> tWP */
if (timings->tWP_min > min_clk_period)
@@ -1744,8 +1763,10 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
min_clk_period = timings->tWH_min;
/* T7 <=> tALS */
- if (timings->tALS_min > min_clk_period)
- min_clk_period = timings->tALS_min;
+ if (timings->tALS_min >
+ min_clk_period * nfc_timings->setup_cycles)
+ min_clk_period = DIV_ROUND_UP(timings->tALS_min,
+ nfc_timings->setup_cycles);
/* T8 <=> tDS */
if (timings->tDS_min > min_clk_period)
@@ -1760,8 +1781,10 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3);
/* T11 <=> tALH */
- if (timings->tALH_min > min_clk_period)
- min_clk_period = timings->tALH_min;
+ if (timings->tALH_min >
+ min_clk_period * nfc_timings->setup_cycles)
+ min_clk_period = DIV_ROUND_UP(timings->tALH_min,
+ nfc_timings->setup_cycles);
/* T12 <=> tRP */
if (timings->tRP_min > min_clk_period)
@@ -2670,7 +2693,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = {
.nuser_data_tab = ARRAY_SIZE(sunxi_user_data_len_h6),
.max_ecc_steps = 32,
.sram_size = 8192,
- .timings = &sun4i_a10_nfc_timings,
+ .timings = &sun50i_h616_nfc_timings,
};
static const struct of_device_id sunxi_nfc_ids[] = {
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-07-17 17:34 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-17 17:33 [PATCH v2 0/3] mtd: rawnand: sunxi: fix H6/H616 controller timings James Hilliard
2026-07-17 17:33 ` [PATCH v2 1/3] mtd: rawnand: sunxi: group controller delay tables James Hilliard
2026-07-17 17:33 ` [PATCH v2 2/3] mtd: rawnand: sunxi: describe tADL and tWHR delays James Hilliard
2026-07-17 17:33 ` [PATCH v2 3/3] mtd: rawnand: sunxi: fix H6/H616 controller timings James Hilliard
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox