* [PATCH v2 1/3] dt-bindings: mtd: sunxi: Add randomized OOB flag
2026-09-04 21:22 [PATCH v2 0/3] mtd: rawnand: sunxi: support the Allwinner randomized OOB format James Hilliard
@ 2026-09-04 21:22 ` James Hilliard
2026-09-04 21:22 ` [PATCH v2 2/3] mtd: rawnand: sunxi: support randomized OOB formats James Hilliard
2026-09-04 21:22 ` [PATCH v2 3/3] mtd: rawnand: sunxi: select the packed H6/H616 OOB layout James Hilliard
2 siblings, 0 replies; 5+ messages in thread
From: James Hilliard @ 2026-09-04 21:22 UTC (permalink / raw)
To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland, Maxime Ripard
Cc: linux-mtd, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, James Hilliard
Allwinner NAND firmware stores the bad block marker through the
controller randomizer. The mainline driver instead compensates the first
two randomized bytes so that the marker remains plain on flash.
On H6 and H616, the firmware also uses a fixed protected user-data
placement while mainline fills the OOB space left after ECC. Add an
opt-in property selecting the firmware's normal-page format for the
configured controller hardware-ECC geometry. This covers the
vendor-specific randomizer, bad block marker representation and protected
user-data placement needed to access BSP-derived images.
The property does not change raw-access semantics. Raw operations still
bypass ECC and randomization and expose physical main and OOB bytes.
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
.../devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/Documentation/devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml b/Documentation/devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml
index 9d061e2216cb..8ecafc40ddcb 100644
--- a/Documentation/devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml
+++ b/Documentation/devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml
@@ -79,6 +79,16 @@ patternProperties:
minimum: 0
maximum: 1
+ allwinner,randomized-oob:
+ type: boolean
+ description:
+ Select the normal-page format used by Allwinner NAND firmware for
+ the configured controller hardware-ECC geometry. Normal page
+ transfers use the controller randomizer, the bad block marker remains
+ in its data stream instead of being compensated to stay plain on
+ flash, and protected user data uses the controller-specific placement
+ expected by the firmware.
+
unevaluatedProperties: false
required:
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v2 2/3] mtd: rawnand: sunxi: support randomized OOB formats
2026-09-04 21:22 [PATCH v2 0/3] mtd: rawnand: sunxi: support the Allwinner randomized OOB format James Hilliard
2026-09-04 21:22 ` [PATCH v2 1/3] dt-bindings: mtd: sunxi: Add randomized OOB flag James Hilliard
@ 2026-09-04 21:22 ` James Hilliard
2026-09-08 15:51 ` Miquel Raynal
2026-09-04 21:22 ` [PATCH v2 3/3] mtd: rawnand: sunxi: select the packed H6/H616 OOB layout James Hilliard
2 siblings, 1 reply; 5+ messages in thread
From: James Hilliard @ 2026-09-04 21:22 UTC (permalink / raw)
To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland, Maxime Ripard
Cc: linux-mtd, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, James Hilliard
The controller randomizer covers the bad block marker along with the
rest of the OOB data. The driver currently compensates the marker bytes
before writes and after reads so that they remain plain on flash.
Allwinner NAND firmware instead stores the marker through the
randomizer. Media using that format appears to contain bad blocks unless
the randomizer is enabled while reading the marker.
Honor the allwinner,randomized-oob property by marking the NAND as
requiring scrambling during normal hardware-ECC transfers and leaving
the marker in the randomizer data stream. Keep the existing plain marker
behavior when the property is absent. Reject the property with software
or disabled ECC because those paths do not operate the controller
randomizer.
MTD_OPS_RAW behavior remains unchanged: raw accesses bypass both ECC and
randomization and expose the physical representation. The raw-NAND
core's bad-block and flash-BBT paths use decoded accesses and therefore
continue through the hardware-ECC/randomizer path.
This changes marker handling on all supported controllers; the H6/H616
protected user-data placement is handled separately.
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/mtd/nand/raw/sunxi_nand.c | 25 +++++++++++++++++++++----
1 file changed, 21 insertions(+), 4 deletions(-)
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index 83666dd6cb2a..c1246a9268ca 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -265,6 +265,7 @@ struct sunxi_nfc_timings {
* @clk_rate: clk_rate required for this NAND chip
* @timing_cfg: TIMING_CFG register value for this NAND chip
* @timing_ctl: TIMING_CTL register value for this NAND chip
+ * @randomized_oob: use the randomized normal-page OOB format
* @nsels: number of CS lines required by the NAND chip
* @sels: array of CS lines descriptions
* @user_data_bytes: array of user data lengths for all ECC steps
@@ -277,6 +278,7 @@ struct sunxi_nand_chip {
u32 timing_cfg;
u32 timing_ctl;
u8 *user_data_bytes;
+ bool randomized_oob;
int nsels;
struct sunxi_nand_chip_sel sels[] __counted_by(nsels);
};
@@ -945,8 +947,9 @@ static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob,
}
}
- /* De-randomize the Bad Block Marker. */
- if (bbm && (nand->options & NAND_NEED_SCRAMBLING))
+ /* Undo hardware de-randomization for a plain on-flash BBM. */
+ if (bbm && (nand->options & NAND_NEED_SCRAMBLING) &&
+ !sunxi_nand->randomized_oob)
sunxi_nfc_randomize_bbm(nand, page, oob);
}
@@ -1006,8 +1009,9 @@ static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand,
unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step);
u8 user_data[SUNXI_NFC_MAX_USER_DATA_SZ] = {};
- /* Randomize the Bad Block Marker. */
- if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) {
+ /* Pre-randomize the BBM so the hardware writes it plain on flash. */
+ if (bbm && (nand->options & NAND_NEED_SCRAMBLING) &&
+ !sunxi_nand->randomized_oob) {
memcpy(user_data, oob, user_data_sz);
sunxi_nfc_randomize_bbm(nand, page, user_data);
oob = user_data;
@@ -2229,6 +2233,8 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
static int sunxi_nand_attach_chip(struct nand_chip *nand)
{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
const struct nand_ecc_props *requirements =
nanddev_get_ecc_requirements(&nand->base);
struct nand_ecc_ctrl *ecc = &nand->ecc;
@@ -2238,6 +2244,14 @@ static int sunxi_nand_attach_chip(struct nand_chip *nand)
if (nand->bbt_options & NAND_BBT_USE_FLASH)
nand->bbt_options |= NAND_BBT_NO_OOB;
+ if (sunxi_nand->randomized_oob &&
+ ecc->engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST)
+ return dev_err_probe(nfc->dev, -EINVAL,
+ "Allwinner OOB format requires controller ECC\n");
+
+ if (sunxi_nand->randomized_oob)
+ nand->options |= NAND_NEED_SCRAMBLING;
+
if (nand->options & NAND_NEED_SCRAMBLING)
nand->options |= NAND_NO_SUBPAGE_WRITE;
@@ -2467,6 +2481,9 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
if (!sunxi_nand)
return -ENOMEM;
+ sunxi_nand->randomized_oob =
+ of_property_read_bool(np, "allwinner,randomized-oob");
+
sunxi_nand->nsels = nsels;
for (i = 0; i < nsels; i++) {
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH v2 2/3] mtd: rawnand: sunxi: support randomized OOB formats
2026-09-04 21:22 ` [PATCH v2 2/3] mtd: rawnand: sunxi: support randomized OOB formats James Hilliard
@ 2026-09-08 15:51 ` Miquel Raynal
0 siblings, 0 replies; 5+ messages in thread
From: Miquel Raynal @ 2026-09-08 15:51 UTC (permalink / raw)
To: James Hilliard
Cc: Richard Weinberger, Vignesh Raghavendra, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Maxime Ripard, linux-mtd, devicetree,
linux-arm-kernel, linux-sunxi, linux-kernel
Hi James,
On 04/09/2026 at 15:22:12 -06, James Hilliard <james.hilliard1@gmail.com> wrote:
> The controller randomizer covers the bad block marker along with the
> rest of the OOB data. The driver currently compensates the marker bytes
> before writes and after reads so that they remain plain on flash.
>
> Allwinner NAND firmware instead stores the marker through the
> randomizer. Media using that format appears to contain bad blocks unless
> the randomizer is enabled while reading the marker.
>
> Honor the allwinner,randomized-oob property by marking the NAND as
> requiring scrambling during normal hardware-ECC transfers and leaving
> the marker in the randomizer data stream. Keep the existing plain marker
> behavior when the property is absent. Reject the property with software
> or disabled ECC because those paths do not operate the controller
> randomizer.
>
> MTD_OPS_RAW behavior remains unchanged: raw accesses bypass both ECC and
> randomization and expose the physical representation. The raw-NAND
> core's bad-block and flash-BBT paths use decoded accesses and therefore
> continue through the hardware-ECC/randomizer path.
>
> This changes marker handling on all supported controllers; the H6/H616
> protected user-data placement is handled separately.
>
> Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
I believe it is still not completely working. If you look at the error
path in the read functions, in case we face a factory bad block, there
will be no data, we will face the "page is maybe empty" case where we
still address columns like before. Since data is randomized with these
accesses, I believe we would return errors which will not be interpreted
correctly by the core. The discovery of the factory bad block markers is
thus broken during BBT scan. But a BBT read is also broken, because even
if errors are ignored, we perform pattern matching on data which is, if
my understanding of your implementation is correct, not de-randomized in
this case.
Thanks,
Miquèl
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 3/3] mtd: rawnand: sunxi: select the packed H6/H616 OOB layout
2026-09-04 21:22 [PATCH v2 0/3] mtd: rawnand: sunxi: support the Allwinner randomized OOB format James Hilliard
2026-09-04 21:22 ` [PATCH v2 1/3] dt-bindings: mtd: sunxi: Add randomized OOB flag James Hilliard
2026-09-04 21:22 ` [PATCH v2 2/3] mtd: rawnand: sunxi: support randomized OOB formats James Hilliard
@ 2026-09-04 21:22 ` James Hilliard
2 siblings, 0 replies; 5+ messages in thread
From: James Hilliard @ 2026-09-04 21:22 UTC (permalink / raw)
To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland, Maxime Ripard
Cc: linux-mtd, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, James Hilliard
The H6/H616 controller can configure a separate protected user-data
length for every ECC step. Mainline fills all space left after ECC with
user data. This changes the physical ECC offsets from those used by
Allwinner NAND firmware, so pages written by one layout cannot be
decoded with the other.
The allwinner,randomized-oob property selects the firmware's normal-page
format for the configured controller hardware-ECC geometry. Use four
protected user-data bytes per 1 KiB ECC step, cap the total at 16 bytes,
and assign the entire total to ECC step zero. Reserve the same total before
maximizing ECC strength so the selected strength leaves enough room.
Keep the existing maximized mainline user-data layout when the property
is absent. Older controllers have fixed four-byte user-data registers
whose placement already matches the firmware and need no additional
change.
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/mtd/nand/raw/sunxi_nand.c | 31 +++++++++++++++++++++++++------
1 file changed, 25 insertions(+), 6 deletions(-)
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index c1246a9268ca..1265d900fc27 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -224,6 +224,9 @@
#define USER_DATA_SZ 4
#define SUNXI_NFC_MAX_USER_DATA_SZ 32
+/* The randomized H6/H616 layout packs at most 16 bytes before ECC step 0. */
+#define SUNXI_NFC_H6_MAX_USER_DATA_SZ 16
+
/**
* struct sunxi_nand_chip_sel - stores information related to NAND Chip Select
*
@@ -2037,8 +2040,14 @@ static void sunxi_nand_detach_chip(struct nand_chip *nand)
sunxi_nand->user_data_bytes = NULL;
}
-static int sunxi_nfc_maximize_user_data(struct nand_chip *nand, uint32_t oobsize,
- int ecc_bytes, int nsectors)
+static unsigned int sunxi_nfc_h6_user_data_sz(int nsectors)
+{
+ return min(nsectors * USER_DATA_SZ,
+ SUNXI_NFC_H6_MAX_USER_DATA_SZ);
+}
+
+static int sunxi_nfc_init_user_data(struct nand_chip *nand, uint32_t oobsize,
+ int ecc_bytes, int nsectors)
{
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
@@ -2054,6 +2063,12 @@ static int sunxi_nfc_maximize_user_data(struct nand_chip *nand, uint32_t oobsize
if (!sunxi_nand->user_data_bytes)
return -ENOMEM;
+ if (sunxi_nand->randomized_oob) {
+ sunxi_nand->user_data_bytes[0] =
+ sunxi_nfc_h6_user_data_sz(nsectors);
+ return 0;
+ }
+
for (step = 0; (step < nsectors) && (remaining_bytes > 0); step++) {
for (i = 0; i < c->nuser_data_tab; i++) {
if (c->user_data_len_tab[i] > remaining_bytes)
@@ -2111,6 +2126,10 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
bytes -= 2;
bytes -= total_user_data_sz;
+ } else if (sunxi_nand->randomized_oob) {
+ total_user_data_sz =
+ sunxi_nfc_h6_user_data_sz(nsectors);
+ bytes -= total_user_data_sz;
} else {
/*
* User-data lengths are encoded in four-byte units. Reserve
@@ -2177,12 +2196,12 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
nsectors = mtd->writesize / ecc->size;
/*
- * The rationale for variable data length is to prioritize maximum ECC
- * strength, and then use the remaining space for user data.
+ * The default variable-length layout prioritizes maximum ECC strength,
+ * then uses the remaining space for user data.
*/
if (nfc->caps->reg_user_data_len) {
- ret = sunxi_nfc_maximize_user_data(nand, mtd->oobsize,
- ecc->bytes, nsectors);
+ ret = sunxi_nfc_init_user_data(nand, mtd->oobsize,
+ ecc->bytes, nsectors);
if (ret)
return ret;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread