* [PATCH] mtd: rawnand: sunxi: fix H6/H616 controller timings
@ 2026-07-15 1:31 ` James Hilliard
0 siblings, 0 replies; 5+ messages in thread
From: James Hilliard @ 2026-07-15 1:31 UTC (permalink / raw)
To: linux-mtd, linux-sunxi
Cc: James Hilliard, Miquel Raynal, Richard Weinberger,
Vignesh Raghavendra, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
Richard Genoud, Geert Uytterhoeven, linux-arm-kernel,
linux-kernel
The NAND timing calculation was written for the original A10 NDFC. It
assumes command and address setup and hold intervals T1-T4, T7 and T11
are one controller clock and uses the A10 timing-register encodings.
The H6/H616 NDFC instead defines those intervals as two internal clock
cycles and uses different encodings for tWB, tADL, tWHR and tRHW, as
documented in the H616 User Manual.
Describe the timing characteristics in the controller capability data
so the clock solver can select a rate permitted by the NAND SDR timings
and program valid delay fields. Keep the legacy A10 behavior unchanged.
Fixes: 88fd4e4deae8 ("mtd: rawnand: sunxi: Add support for H616 nand controller")
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/mtd/nand/raw/sunxi_nand.c | 140 +++++++++++++++++-------------
1 file changed, 82 insertions(+), 58 deletions(-)
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index 02647565c8ba..9b7d17b6d81a 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -237,6 +237,18 @@ 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 {
+ /* Internal clock cycles used by T1-T4, T7 and T11. */
+ u8 setup_cycles;
+ u8 tWB[SUNXI_NFC_TIMING_STEPS];
+ u8 tADL[SUNXI_NFC_TIMING_STEPS];
+ u8 tWHR[SUNXI_NFC_TIMING_STEPS];
+ u8 tRHW[SUNXI_NFC_TIMING_STEPS];
+};
+
/**
* struct sunxi_nand_chip - stores NAND chip device related information
*
@@ -301,6 +313,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 +340,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,16 +1681,35 @@ 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 = {
+ .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_h6_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 void sunxi_nand_update_min_period(u32 *min_period, u32 duration,
+ unsigned int cycles)
+{
+ *min_period = max(*min_period, DIV_ROUND_UP(duration, cycles));
+}
-static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
- u32 clk_period)
+static int sunxi_nand_lookup_timing(const u8 *lut, u32 duration,
+ u32 clk_period)
{
u32 clk_cycles = DIV_ROUND_UP(duration, clk_period);
int i;
- for (i = 0; i < lut_size; i++) {
+ for (i = 0; i < SUNXI_NFC_TIMING_STEPS; i++) {
if (clk_cycles <= lut[i])
return i;
}
@@ -1685,14 +1718,12 @@ static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
return -EINVAL;
}
-#define sunxi_nand_lookup_timing(l, p, c) \
- _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
-
static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
const struct nand_interface_config *conf)
{
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;
@@ -1703,77 +1734,65 @@ 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;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tCLS_min,
+ nfc_timings->setup_cycles);
/* T2 <=> tCLH */
- if (timings->tCLH_min > min_clk_period)
- min_clk_period = timings->tCLH_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tCLH_min,
+ nfc_timings->setup_cycles);
/* T3 <=> tCS */
- if (timings->tCS_min > min_clk_period)
- min_clk_period = timings->tCS_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tCS_min,
+ nfc_timings->setup_cycles);
/* T4 <=> tCH */
- if (timings->tCH_min > min_clk_period)
- min_clk_period = timings->tCH_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tCH_min,
+ nfc_timings->setup_cycles);
/* T5 <=> tWP */
- if (timings->tWP_min > min_clk_period)
- min_clk_period = timings->tWP_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tWP_min, 1);
/* T6 <=> tWH */
- if (timings->tWH_min > min_clk_period)
- min_clk_period = timings->tWH_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tWH_min, 1);
/* T7 <=> tALS */
- if (timings->tALS_min > min_clk_period)
- min_clk_period = timings->tALS_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tALS_min,
+ nfc_timings->setup_cycles);
/* T8 <=> tDS */
- if (timings->tDS_min > min_clk_period)
- min_clk_period = timings->tDS_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tDS_min, 1);
/* T9 <=> tDH */
- if (timings->tDH_min > min_clk_period)
- min_clk_period = timings->tDH_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tDH_min, 1);
/* T10 <=> tRR */
- if (timings->tRR_min > (min_clk_period * 3))
- min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3);
+ sunxi_nand_update_min_period(&min_clk_period, timings->tRR_min, 3);
/* T11 <=> tALH */
- if (timings->tALH_min > min_clk_period)
- min_clk_period = timings->tALH_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tALH_min,
+ nfc_timings->setup_cycles);
/* T12 <=> tRP */
- if (timings->tRP_min > min_clk_period)
- min_clk_period = timings->tRP_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tRP_min, 1);
/* T13 <=> tREH */
- if (timings->tREH_min > min_clk_period)
- min_clk_period = timings->tREH_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tREH_min, 1);
/* T14 <=> tRC */
- if (timings->tRC_min > (min_clk_period * 2))
- min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2);
+ sunxi_nand_update_min_period(&min_clk_period, timings->tRC_min, 2);
/* T15 <=> tWC */
- if (timings->tWC_min > (min_clk_period * 2))
- min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2);
+ sunxi_nand_update_min_period(&min_clk_period, 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->tADL_min > (min_clk_period * 32))
- min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32);
-
- 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);
+ sunxi_nand_update_min_period(&min_clk_period, timings->tWB_max,
+ nfc_timings->tWB[SUNXI_NFC_TIMING_STEPS - 1]);
+ sunxi_nand_update_min_period(&min_clk_period, timings->tADL_min,
+ nfc_timings->tADL[SUNXI_NFC_TIMING_STEPS - 1]);
+ sunxi_nand_update_min_period(&min_clk_period, timings->tWHR_min,
+ nfc_timings->tWHR[SUNXI_NFC_TIMING_STEPS - 1]);
+ sunxi_nand_update_min_period(&min_clk_period, 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,26 +1808,28 @@ 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");
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(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");
@@ -1824,7 +1845,7 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
*/
tCAD = 0x7;
- /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */
+ /* TODO: A83 and H6 have more bits for CDQSS, CS, CLHZ, CCS, WC */
sunxi_nand->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD);
/* Convert min_clk_period from picoseconds to nanoseconds */
@@ -2595,6 +2616,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 +2639,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 +2664,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 = &sun50i_h6_nfc_timings,
};
static const struct of_device_id sunxi_nfc_ids[] = {
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH] mtd: rawnand: sunxi: fix H6/H616 controller timings
@ 2026-07-15 1:31 ` James Hilliard
0 siblings, 0 replies; 5+ messages in thread
From: James Hilliard @ 2026-07-15 1:31 UTC (permalink / raw)
To: linux-mtd, linux-sunxi
Cc: James Hilliard, Miquel Raynal, Richard Weinberger,
Vignesh Raghavendra, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
Richard Genoud, Geert Uytterhoeven, linux-arm-kernel,
linux-kernel
The NAND timing calculation was written for the original A10 NDFC. It
assumes command and address setup and hold intervals T1-T4, T7 and T11
are one controller clock and uses the A10 timing-register encodings.
The H6/H616 NDFC instead defines those intervals as two internal clock
cycles and uses different encodings for tWB, tADL, tWHR and tRHW, as
documented in the H616 User Manual.
Describe the timing characteristics in the controller capability data
so the clock solver can select a rate permitted by the NAND SDR timings
and program valid delay fields. Keep the legacy A10 behavior unchanged.
Fixes: 88fd4e4deae8 ("mtd: rawnand: sunxi: Add support for H616 nand controller")
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/mtd/nand/raw/sunxi_nand.c | 140 +++++++++++++++++-------------
1 file changed, 82 insertions(+), 58 deletions(-)
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index 02647565c8ba..9b7d17b6d81a 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -237,6 +237,18 @@ 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 {
+ /* Internal clock cycles used by T1-T4, T7 and T11. */
+ u8 setup_cycles;
+ u8 tWB[SUNXI_NFC_TIMING_STEPS];
+ u8 tADL[SUNXI_NFC_TIMING_STEPS];
+ u8 tWHR[SUNXI_NFC_TIMING_STEPS];
+ u8 tRHW[SUNXI_NFC_TIMING_STEPS];
+};
+
/**
* struct sunxi_nand_chip - stores NAND chip device related information
*
@@ -301,6 +313,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 +340,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,16 +1681,35 @@ 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 = {
+ .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_h6_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 void sunxi_nand_update_min_period(u32 *min_period, u32 duration,
+ unsigned int cycles)
+{
+ *min_period = max(*min_period, DIV_ROUND_UP(duration, cycles));
+}
-static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
- u32 clk_period)
+static int sunxi_nand_lookup_timing(const u8 *lut, u32 duration,
+ u32 clk_period)
{
u32 clk_cycles = DIV_ROUND_UP(duration, clk_period);
int i;
- for (i = 0; i < lut_size; i++) {
+ for (i = 0; i < SUNXI_NFC_TIMING_STEPS; i++) {
if (clk_cycles <= lut[i])
return i;
}
@@ -1685,14 +1718,12 @@ static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
return -EINVAL;
}
-#define sunxi_nand_lookup_timing(l, p, c) \
- _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
-
static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
const struct nand_interface_config *conf)
{
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;
@@ -1703,77 +1734,65 @@ 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;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tCLS_min,
+ nfc_timings->setup_cycles);
/* T2 <=> tCLH */
- if (timings->tCLH_min > min_clk_period)
- min_clk_period = timings->tCLH_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tCLH_min,
+ nfc_timings->setup_cycles);
/* T3 <=> tCS */
- if (timings->tCS_min > min_clk_period)
- min_clk_period = timings->tCS_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tCS_min,
+ nfc_timings->setup_cycles);
/* T4 <=> tCH */
- if (timings->tCH_min > min_clk_period)
- min_clk_period = timings->tCH_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tCH_min,
+ nfc_timings->setup_cycles);
/* T5 <=> tWP */
- if (timings->tWP_min > min_clk_period)
- min_clk_period = timings->tWP_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tWP_min, 1);
/* T6 <=> tWH */
- if (timings->tWH_min > min_clk_period)
- min_clk_period = timings->tWH_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tWH_min, 1);
/* T7 <=> tALS */
- if (timings->tALS_min > min_clk_period)
- min_clk_period = timings->tALS_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tALS_min,
+ nfc_timings->setup_cycles);
/* T8 <=> tDS */
- if (timings->tDS_min > min_clk_period)
- min_clk_period = timings->tDS_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tDS_min, 1);
/* T9 <=> tDH */
- if (timings->tDH_min > min_clk_period)
- min_clk_period = timings->tDH_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tDH_min, 1);
/* T10 <=> tRR */
- if (timings->tRR_min > (min_clk_period * 3))
- min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3);
+ sunxi_nand_update_min_period(&min_clk_period, timings->tRR_min, 3);
/* T11 <=> tALH */
- if (timings->tALH_min > min_clk_period)
- min_clk_period = timings->tALH_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tALH_min,
+ nfc_timings->setup_cycles);
/* T12 <=> tRP */
- if (timings->tRP_min > min_clk_period)
- min_clk_period = timings->tRP_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tRP_min, 1);
/* T13 <=> tREH */
- if (timings->tREH_min > min_clk_period)
- min_clk_period = timings->tREH_min;
+ sunxi_nand_update_min_period(&min_clk_period, timings->tREH_min, 1);
/* T14 <=> tRC */
- if (timings->tRC_min > (min_clk_period * 2))
- min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2);
+ sunxi_nand_update_min_period(&min_clk_period, timings->tRC_min, 2);
/* T15 <=> tWC */
- if (timings->tWC_min > (min_clk_period * 2))
- min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2);
+ sunxi_nand_update_min_period(&min_clk_period, 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->tADL_min > (min_clk_period * 32))
- min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32);
-
- 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);
+ sunxi_nand_update_min_period(&min_clk_period, timings->tWB_max,
+ nfc_timings->tWB[SUNXI_NFC_TIMING_STEPS - 1]);
+ sunxi_nand_update_min_period(&min_clk_period, timings->tADL_min,
+ nfc_timings->tADL[SUNXI_NFC_TIMING_STEPS - 1]);
+ sunxi_nand_update_min_period(&min_clk_period, timings->tWHR_min,
+ nfc_timings->tWHR[SUNXI_NFC_TIMING_STEPS - 1]);
+ sunxi_nand_update_min_period(&min_clk_period, 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,26 +1808,28 @@ 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");
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(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");
@@ -1824,7 +1845,7 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
*/
tCAD = 0x7;
- /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */
+ /* TODO: A83 and H6 have more bits for CDQSS, CS, CLHZ, CCS, WC */
sunxi_nand->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD);
/* Convert min_clk_period from picoseconds to nanoseconds */
@@ -2595,6 +2616,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 +2639,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 +2664,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 = &sun50i_h6_nfc_timings,
};
static const struct of_device_id sunxi_nfc_ids[] = {
--
2.53.0
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH] mtd: rawnand: sunxi: fix H6/H616 controller timings
2026-07-15 1:31 ` James Hilliard
@ 2026-07-17 11:55 ` Miquel Raynal
-1 siblings, 0 replies; 5+ messages in thread
From: Miquel Raynal @ 2026-07-17 11:55 UTC (permalink / raw)
To: James Hilliard
Cc: linux-mtd, linux-sunxi, Richard Weinberger, Vignesh Raghavendra,
Chen-Yu Tsai, Jernej Skrabec, Samuel Holland, Richard Genoud,
Geert Uytterhoeven, linux-arm-kernel, linux-kernel
Hello James,
On 14/07/2026 at 19:31:38 -06, James Hilliard <james.hilliard1@gmail.com> wrote:
> The NAND timing calculation was written for the original A10 NDFC. It
> assumes command and address setup and hold intervals T1-T4, T7 and T11
> are one controller clock and uses the A10 timing-register encodings.
>
> The H6/H616 NDFC instead defines those intervals as two internal clock
> cycles and uses different encodings for tWB, tADL, tWHR and tRHW, as
> documented in the H616 User Manual.
>
> Describe the timing characteristics in the controller capability data
> so the clock solver can select a rate permitted by the NAND SDR timings
> and program valid delay fields. Keep the legacy A10 behavior unchanged.
>
> Fixes: 88fd4e4deae8 ("mtd: rawnand: sunxi: Add support for H616 nand controller")
Cc: stable missing here
> Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
...
> @@ -1667,16 +1681,35 @@ 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 = {
> + .setup_cycles = 1,
> + .tWB = { 6, 12, 16, 20 },
> + .tADL = { 7, 15, 23, 31 },
> + .tWHR = { 7, 15, 23, 31 },
> + .tRHW = { 4, 8, 12, 20 },
> +};
This patch looks overall correct but must be split. For instance, the
fact that you drop the LUT in favour of you own timing array shall be
done in a preparation patch, without adding new timings, nor adding the
new controller timings.
Then in a second time you could add tADL and tWHR support, etc.
> +
> +static const struct sunxi_nfc_timings sun50i_h6_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 void sunxi_nand_update_min_period(u32 *min_period, u32 duration,
> + unsigned int cycles)
> +{
> + *min_period = max(*min_period, DIV_ROUND_UP(duration, cycles));
> +}
>
> -static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
> - u32 clk_period)
> +static int sunxi_nand_lookup_timing(const u8 *lut, u32 duration,
> + u32 clk_period)
This change is fine but likely also unrelated and should (maybe) be moved in its
own commit.
> {
> u32 clk_cycles = DIV_ROUND_UP(duration, clk_period);
> int i;
>
> - for (i = 0; i < lut_size; i++) {
> + for (i = 0; i < SUNXI_NFC_TIMING_STEPS; i++) {
> if (clk_cycles <= lut[i])
> return i;
> }
...
> @@ -1703,77 +1734,65 @@ 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;
> + sunxi_nand_update_min_period(&min_clk_period, timings->tCLS_min,
> + nfc_timings->setup_cycles);
I am not sure I get the added value of this helper?
>
> /* T2 <=> tCLH */
> - if (timings->tCLH_min > min_clk_period)
> - min_clk_period = timings->tCLH_min;
> + sunxi_nand_update_min_period(&min_clk_period, timings->tCLH_min,
> + nfc_timings->setup_cycles);
>
...
>
> static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = {
> @@ -2641,6 +2664,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 = &sun50i_h6_nfc_timings,
And this should be the last change in your series.
BTW would it be relevant to align the various names ? (h6 timings in the
h616 structure).
Thanks,
Miquèl
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH] mtd: rawnand: sunxi: fix H6/H616 controller timings
@ 2026-07-17 11:55 ` Miquel Raynal
0 siblings, 0 replies; 5+ messages in thread
From: Miquel Raynal @ 2026-07-17 11:55 UTC (permalink / raw)
To: James Hilliard
Cc: linux-mtd, linux-sunxi, Richard Weinberger, Vignesh Raghavendra,
Chen-Yu Tsai, Jernej Skrabec, Samuel Holland, Richard Genoud,
Geert Uytterhoeven, linux-arm-kernel, linux-kernel
Hello James,
On 14/07/2026 at 19:31:38 -06, James Hilliard <james.hilliard1@gmail.com> wrote:
> The NAND timing calculation was written for the original A10 NDFC. It
> assumes command and address setup and hold intervals T1-T4, T7 and T11
> are one controller clock and uses the A10 timing-register encodings.
>
> The H6/H616 NDFC instead defines those intervals as two internal clock
> cycles and uses different encodings for tWB, tADL, tWHR and tRHW, as
> documented in the H616 User Manual.
>
> Describe the timing characteristics in the controller capability data
> so the clock solver can select a rate permitted by the NAND SDR timings
> and program valid delay fields. Keep the legacy A10 behavior unchanged.
>
> Fixes: 88fd4e4deae8 ("mtd: rawnand: sunxi: Add support for H616 nand controller")
Cc: stable missing here
> Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
...
> @@ -1667,16 +1681,35 @@ 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 = {
> + .setup_cycles = 1,
> + .tWB = { 6, 12, 16, 20 },
> + .tADL = { 7, 15, 23, 31 },
> + .tWHR = { 7, 15, 23, 31 },
> + .tRHW = { 4, 8, 12, 20 },
> +};
This patch looks overall correct but must be split. For instance, the
fact that you drop the LUT in favour of you own timing array shall be
done in a preparation patch, without adding new timings, nor adding the
new controller timings.
Then in a second time you could add tADL and tWHR support, etc.
> +
> +static const struct sunxi_nfc_timings sun50i_h6_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 void sunxi_nand_update_min_period(u32 *min_period, u32 duration,
> + unsigned int cycles)
> +{
> + *min_period = max(*min_period, DIV_ROUND_UP(duration, cycles));
> +}
>
> -static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
> - u32 clk_period)
> +static int sunxi_nand_lookup_timing(const u8 *lut, u32 duration,
> + u32 clk_period)
This change is fine but likely also unrelated and should (maybe) be moved in its
own commit.
> {
> u32 clk_cycles = DIV_ROUND_UP(duration, clk_period);
> int i;
>
> - for (i = 0; i < lut_size; i++) {
> + for (i = 0; i < SUNXI_NFC_TIMING_STEPS; i++) {
> if (clk_cycles <= lut[i])
> return i;
> }
...
> @@ -1703,77 +1734,65 @@ 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;
> + sunxi_nand_update_min_period(&min_clk_period, timings->tCLS_min,
> + nfc_timings->setup_cycles);
I am not sure I get the added value of this helper?
>
> /* T2 <=> tCLH */
> - if (timings->tCLH_min > min_clk_period)
> - min_clk_period = timings->tCLH_min;
> + sunxi_nand_update_min_period(&min_clk_period, timings->tCLH_min,
> + nfc_timings->setup_cycles);
>
...
>
> static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = {
> @@ -2641,6 +2664,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 = &sun50i_h6_nfc_timings,
And this should be the last change in your series.
BTW would it be relevant to align the various names ? (h6 timings in the
h616 structure).
Thanks,
Miquèl
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH] mtd: rawnand: sunxi: fix H6/H616 controller timings
@ 2026-07-14 23:19 James Hilliard
0 siblings, 0 replies; 5+ messages in thread
From: James Hilliard @ 2026-07-14 23:19 UTC (permalink / raw)
To: u-boot
Cc: James Hilliard, Dario Binacchi, Michael Trimarchi, Tom Rini,
Richard Genoud, Mikhail Kshevetskiy
The NAND timing calculation was written for the original A10 NDFC. It
assumes command and address setup and hold intervals T1-T4, T7 and T11
are one controller clock and uses the A10 timing-register encodings.
The H6/H616 NDFC instead defines those intervals as two internal clock
cycles and uses different encodings for tWB, tADL, tWHR and tRHW, as
documented in the H616 User Manual.
Describe the timing characteristics in the controller capability data
so the clock solver can select a rate permitted by the NAND SDR timings
and program valid delay fields. Keep the legacy A10 behavior unchanged.
Fixes: f163da5e6d26 ("mtd: rawnand: sunxi: add support for H6/H616 nand controller")
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/mtd/nand/raw/sunxi_nand.c | 97 ++++++++++++++++++++++---------
drivers/mtd/nand/raw/sunxi_nand.h | 4 ++
2 files changed, 72 insertions(+), 29 deletions(-)
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index 49748fddf80..869c305d51d 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -1249,16 +1249,41 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
return 0;
}
-static const s32 tWB_lut[] = {6, 12, 16, 20};
-static const s32 tRHW_lut[] = {4, 8, 12, 20};
+#define SUNXI_NFC_TIMING_STEPS 4
+
+/* 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;
+ u8 tWB[SUNXI_NFC_TIMING_STEPS];
+ u8 tADL[SUNXI_NFC_TIMING_STEPS];
+ u8 tWHR[SUNXI_NFC_TIMING_STEPS];
+ u8 tRHW[SUNXI_NFC_TIMING_STEPS];
+};
+
+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_h6_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)
+static int sunxi_nand_lookup_timing(const u8 *lut, u32 duration,
+ u32 clk_period)
{
u32 clk_cycles = DIV_ROUND_UP(duration, clk_period);
int i;
- for (i = 0; i < lut_size; i++) {
+ for (i = 0; i < SUNXI_NFC_TIMING_STEPS; i++) {
if (clk_cycles <= lut[i])
return i;
}
@@ -1267,31 +1292,37 @@ static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
return -EINVAL;
}
-#define sunxi_nand_lookup_timing(l, p, c) \
- _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
-
static int sunxi_nand_chip_set_timings(struct sunxi_nfc *nfc,
struct sunxi_nand_chip *chip,
const struct nand_sdr_timings *timings)
{
+ const struct sunxi_nfc_timings *nfc_timings = nfc->caps->timings;
u32 min_clk_period = 0;
s32 tWB, tADL, tWHR, tRHW, tCAD;
/* 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)
@@ -1302,8 +1333,10 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nfc *nfc,
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)
@@ -1318,8 +1351,10 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nfc *nfc,
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)
@@ -1338,26 +1373,28 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nfc *nfc,
min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2);
/* T16 - T19 + tCAD */
- 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");
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(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");
@@ -1370,7 +1407,7 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nfc *nfc,
*/
tCAD = 0x7;
- /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */
+ /* TODO: A83 and H6 have more bits for CDQSS, CS, CLHZ, CCS, WC */
chip->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD);
/*
@@ -1956,6 +1993,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
.ecc_mode_mask = GENMASK(15, 12),
.random_en_mask = BIT(9),
.max_ecc_steps = 16,
+ .timings = &sun4i_a10_nfc_timings,
};
static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = {
@@ -1973,6 +2011,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = {
.user_data_len_tab = sunxi_user_data_len_h6,
.nuser_data_tab = ARRAY_SIZE(sunxi_user_data_len_h6),
.max_ecc_steps = 32,
+ .timings = &sun50i_h6_nfc_timings,
};
static const struct udevice_id sunxi_nand_ids[] = {
diff --git a/drivers/mtd/nand/raw/sunxi_nand.h b/drivers/mtd/nand/raw/sunxi_nand.h
index 1b2c514852d..12ce26d2e2a 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.h
+++ b/drivers/mtd/nand/raw/sunxi_nand.h
@@ -181,6 +181,8 @@
/* On A10, the user data length register is 4 bytes */
#define USER_DATA_SZ 4
+struct sunxi_nfc_timings;
+
/*
* NAND Controller capabilities structure: stores NAND controller capabilities
* for distinction between compatible strings.
@@ -205,6 +207,7 @@
* @nuser_data_tab: Size of @user_data_len_tab
* @max_ecc_steps: Maximum supported steps for ECC, this is also the
* number of user data registers
+ * @timings: Controller-specific timing characteristics
*/
struct sunxi_nfc_caps {
bool has_ecc_block_512;
@@ -223,6 +226,7 @@ struct sunxi_nfc_caps {
const u8 *user_data_len_tab;
unsigned int nuser_data_tab;
unsigned int max_ecc_steps;
+ const struct sunxi_nfc_timings *timings;
};
#endif
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-07-17 11:55 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-15 1:31 [PATCH] mtd: rawnand: sunxi: fix H6/H616 controller timings James Hilliard
2026-07-15 1:31 ` James Hilliard
2026-07-17 11:55 ` Miquel Raynal
2026-07-17 11:55 ` Miquel Raynal
-- strict thread matches above, loose matches on Subject: below --
2026-07-14 23:19 James Hilliard
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.