get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/2217012/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2217012,
    "url": "http://patchwork.ozlabs.org/api/patches/2217012/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/uboot/patch/20260327140508.3680105-5-richard.genoud@bootlin.com/",
    "project": {
        "id": 18,
        "url": "http://patchwork.ozlabs.org/api/projects/18/?format=api",
        "name": "U-Boot",
        "link_name": "uboot",
        "list_id": "u-boot.lists.denx.de",
        "list_email": "u-boot@lists.denx.de",
        "web_url": null,
        "scm_url": null,
        "webscm_url": null,
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20260327140508.3680105-5-richard.genoud@bootlin.com>",
    "list_archive_url": null,
    "date": "2026-03-27T14:05:07",
    "name": "[v2,4/5] mtd: rawnand: sunxi: introduce variable user data length",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "580492e584bf320905167b33ef0d23ee8bd78abc",
    "submitter": {
        "id": 88519,
        "url": "http://patchwork.ozlabs.org/api/people/88519/?format=api",
        "name": "Richard Genoud",
        "email": "richard.genoud@bootlin.com"
    },
    "delegate": {
        "id": 114289,
        "url": "http://patchwork.ozlabs.org/api/users/114289/?format=api",
        "username": "apritzel",
        "first_name": "Andre",
        "last_name": "Przywara",
        "email": "andre.przywara@arm.com"
    },
    "mbox": "http://patchwork.ozlabs.org/project/uboot/patch/20260327140508.3680105-5-richard.genoud@bootlin.com/mbox/",
    "series": [
        {
            "id": 497775,
            "url": "http://patchwork.ozlabs.org/api/series/497775/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/uboot/list/?series=497775",
            "date": "2026-03-27T14:05:03",
            "name": "mtd: rawnand: sunxi: Fix user data length for H6",
            "version": 2,
            "mbox": "http://patchwork.ozlabs.org/series/497775/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2217012/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2217012/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<u-boot-bounces@lists.denx.de>",
        "X-Original-To": "incoming@patchwork.ozlabs.org",
        "Delivered-To": "patchwork-incoming@legolas.ozlabs.org",
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=bootlin.com header.i=@bootlin.com header.a=rsa-sha256\n header.s=dkim header.b=T33agafP;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de\n (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de;\n envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org)",
            "phobos.denx.de;\n dmarc=pass (p=reject dis=none) header.from=bootlin.com",
            "phobos.denx.de;\n spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de",
            "phobos.denx.de;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=bootlin.com header.i=@bootlin.com header.b=\"T33agafP\";\n\tdkim-atps=neutral",
            "phobos.denx.de;\n dmarc=pass (p=reject dis=none) header.from=bootlin.com",
            "phobos.denx.de;\n spf=pass smtp.mailfrom=richard.genoud@bootlin.com"
        ],
        "Received": [
            "from phobos.denx.de (phobos.denx.de\n [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fj2Vy1qhPz1y1P\n\tfor <incoming@patchwork.ozlabs.org>; Sat, 28 Mar 2026 01:06:18 +1100 (AEDT)",
            "from h2850616.stratoserver.net (localhost [IPv6:::1])\n\tby phobos.denx.de (Postfix) with ESMTP id 8D0C483FEE;\n\tFri, 27 Mar 2026 15:05:43 +0100 (CET)",
            "by phobos.denx.de (Postfix, from userid 109)\n id 5F2EF83FE9; Fri, 27 Mar 2026 15:05:41 +0100 (CET)",
            "from smtpout-02.galae.net (smtpout-02.galae.net [185.246.84.56])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))\n (No client certificate requested)\n by phobos.denx.de (Postfix) with ESMTPS id 970878394E\n for <u-boot@lists.denx.de>; Fri, 27 Mar 2026 15:05:38 +0100 (CET)",
            "from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233])\n by smtpout-02.galae.net (Postfix) with ESMTPS id 467131A3024;\n Fri, 27 Mar 2026 14:05:38 +0000 (UTC)",
            "from mail.galae.net (mail.galae.net [212.83.136.155])\n by smtpout-01.galae.net (Postfix) with ESMTPS id 17A9660230;\n Fri, 27 Mar 2026 14:05:38 +0000 (UTC)",
            "from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon)\n with ESMTPSA id 5E95E10451311; Fri, 27 Mar 2026 15:05:36 +0100 (CET)"
        ],
        "X-Spam-Checker-Version": "SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de",
        "X-Spam-Level": "",
        "X-Spam-Status": "No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED,\n DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_BLOCKED,\n RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED,\n SPF_HELO_PASS,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim;\n t=1774620337; h=from:subject:date:message-id:to:cc:mime-version:\n content-transfer-encoding:in-reply-to:references;\n bh=e6arHqfx3UpIt9MS80W5Xz5KcPyt1hSDoSrI2pam0YQ=;\n b=T33agafPqEZjQXBxm0Gloj0StEzaMcohFz/DQLa9FeJJmfi36GZWYofrYPfzoEkgQH1638\n n7EcGFaL6uNeI8yRB1CVYhW4uFqgiW61RrJXXvyGZsN87ZcTzlqNbWNKgyytq5ui5RmvXf\n e6/QooCC5XrJLkUxEeE7oTH9q2GpwQUyJcE08ipU38p60FvJsl91dGj2/oi2ClXIz0lCgD\n kZ0NLypaYwCCNBB9Nqt3i8ahDymE/5/rUOixC6gaZGfI/wjrJtRxhPEuNgiuGO1QZTMqhM\n TkIsjxwBcsnGqsKuRZoELH+dYHZ9gX/ggCTV5lxoWbGmaKMU9jWu65fDaVgfew==",
        "From": "Richard Genoud <richard.genoud@bootlin.com>",
        "To": "Dario Binacchi <dario.binacchi@amarulasolutions.com>,\n Michael Trimarchi <michael@amarulasolutions.com>,\n Tom Rini <trini@konsulko.com>, Andre Przywara <andre.przywara@arm.com>",
        "Cc": "Andrew Goodbody <andrew.goodbody@linaro.org>,\n Miquel Raynal <miquel.raynal@bootlin.com>,\n James Hilliard <james.hilliard1@gmail.com>,\n Boris Brezillon <bbrezillon@kernel.org>,\n Thomas Petazzoni <thomas.petazzoni@bootlin.com>, u-boot@lists.denx.de,\n Richard Genoud <richard.genoud@bootlin.com>",
        "Subject": "[PATCH v2 4/5] mtd: rawnand: sunxi: introduce variable user data\n length",
        "Date": "Fri, 27 Mar 2026 15:05:07 +0100",
        "Message-ID": "<20260327140508.3680105-5-richard.genoud@bootlin.com>",
        "X-Mailer": "git-send-email 2.47.3",
        "In-Reply-To": "<20260327140508.3680105-1-richard.genoud@bootlin.com>",
        "References": "<20260327140508.3680105-1-richard.genoud@bootlin.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-Last-TLS-Session-Version": "TLSv1.3",
        "X-BeenThere": "u-boot@lists.denx.de",
        "X-Mailman-Version": "2.1.39",
        "Precedence": "list",
        "List-Id": "U-Boot discussion <u-boot.lists.denx.de>",
        "List-Unsubscribe": "<https://lists.denx.de/options/u-boot>,\n <mailto:u-boot-request@lists.denx.de?subject=unsubscribe>",
        "List-Archive": "<https://lists.denx.de/pipermail/u-boot/>",
        "List-Post": "<mailto:u-boot@lists.denx.de>",
        "List-Help": "<mailto:u-boot-request@lists.denx.de?subject=help>",
        "List-Subscribe": "<https://lists.denx.de/listinfo/u-boot>,\n <mailto:u-boot-request@lists.denx.de?subject=subscribe>",
        "Errors-To": "u-boot-bounces@lists.denx.de",
        "Sender": "\"U-Boot\" <u-boot-bounces@lists.denx.de>",
        "X-Virus-Scanned": "clamav-milter 0.103.8 at phobos.denx.de",
        "X-Virus-Status": "Clean"
    },
    "content": "In Allwinner SoCs, user data can be added in OOB before each ECC data.\nFor older SoCs like A10, the user data size was the size of a register\n(4 bytes) and was mandatory before each ECC step.\nSo, the A10 OOB Layout is:\n[4Bytes USER_DATA_STEP0] [ECC_STEP0 bytes]\n[4bytes USER_DATA_STEP1] [ECC_STEP1 bytes]\n...\nNB: the BBM is stored at the beginning of the USER_DATA_STEP0.\n\nNow, for H6/H616 NAND flash controller, this user data can have a\ndifferent size for each step.\nSo, we are maximizing the user data length to use as many OOB bytes as\npossible.\n\nFixes: 7d1de9801151 (\"mtd: rawnand: sunxi_spl: add support for H6/H616 nand controller\")\nFixes: f163da5e6d26 (\"mtd: rawnand: sunxi: add support for H6/H616 nand controller\")\nSigned-off-by: Richard Genoud <richard.genoud@bootlin.com>\n---\n drivers/mtd/nand/raw/sunxi_nand.c     | 235 +++++++++++++++++++++-----\n drivers/mtd/nand/raw/sunxi_nand_spl.c | 107 ++++++++++--\n 2 files changed, 287 insertions(+), 55 deletions(-)",
    "diff": "diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c\nindex ecab9ebc9576..c3bec1fe517e 100644\n--- a/drivers/mtd/nand/raw/sunxi_nand.c\n+++ b/drivers/mtd/nand/raw/sunxi_nand.c\n@@ -114,6 +114,7 @@ struct sunxi_nand_hw_ecc {\n  * @clk_rate:\t\tclk_rate required for this NAND chip\n  * @timing_cfg\t\tTIMING_CFG register value for this NAND chip\n  * @selected:\t\tcurrent active CS\n+ * @user_data_bytes\tarray of user data lengths for all ECC steps\n  * @nsels:\t\tnumber of CS lines required by the NAND chip\n  * @sels:\t\tarray of CS lines descriptions\n  */\n@@ -128,6 +129,7 @@ struct sunxi_nand_chip {\n \tu32 addr[2];\n \tint cmd_cycles;\n \tu8 cmd[2];\n+\tu8 *user_data_bytes;\n \tint nsels;\n \tstruct sunxi_nand_chip_sel sels[0];\n };\n@@ -744,19 +746,73 @@ static void sunxi_nfc_set_user_data_len(struct sunxi_nfc *nfc,\n \twritel(val, nfc->regs + NFC_REG_USER_DATA_LEN(nfc, step));\n }\n \n+static u8 sunxi_nfc_user_data_sz(const struct sunxi_nand_chip *sunxi_nand, int step)\n+{\n+\tif (!sunxi_nand->user_data_bytes)\n+\t\treturn USER_DATA_SZ;\n+\n+\treturn sunxi_nand->user_data_bytes[step];\n+}\n+\n+static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob,\n+\t\t\t\t\t\tint step, bool bbm, int page,\n+\t\t\t\t\t\tunsigned int user_data_sz)\n+{\n+\tstruct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);\n+\tstruct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);\n+\tu32 user_data;\n+\n+\tif (!nfc->caps->reg_user_data_len) {\n+\t\t/*\n+\t\t * For A10, the user data for step n is in the nth\n+\t\t * REG_USER_DATA\n+\t\t */\n+\t\tuser_data = readl(nfc->regs + NFC_REG_USER_DATA(nfc, step));\n+\t\tsunxi_nfc_user_data_to_buf(user_data, oob);\n+\n+\t} else {\n+\t\t/*\n+\t\t * For H6 NAND controller, the user data for all steps is\n+\t\t * contained in 32 user data registers, but not at a specific\n+\t\t * offset for each step, they are just concatenated.\n+\t\t */\n+\t\tunsigned int user_data_off = 0;\n+\t\tunsigned int reg_off;\n+\t\tu8 *ptr = oob;\n+\t\tunsigned int i;\n+\n+\t\tfor (i = 0; i < step; i++)\n+\t\t\tuser_data_off += sunxi_nfc_user_data_sz(sunxi_nand, i);\n+\n+\t\tuser_data_off /= 4;\n+\t\tfor (i = 0; i < user_data_sz / 4; i++, ptr += 4) {\n+\t\t\treg_off = NFC_REG_USER_DATA(nfc, user_data_off + i);\n+\t\t\tuser_data = readl(nfc->regs + reg_off);\n+\t\t\tsunxi_nfc_user_data_to_buf(user_data, ptr);\n+\t\t}\n+\t}\n+\n+\t/* De-randomize the Bad Block Marker. */\n+\tif (bbm && nand->options & NAND_NEED_SCRAMBLING)\n+\t\tsunxi_nfc_randomize_bbm(&nand->mtd, page, oob);\n+}\n+\n static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,\n \t\t\t\t       u8 *data, int data_off,\n \t\t\t\t       u8 *oob, int oob_off,\n \t\t\t\t       int *cur_off,\n \t\t\t\t       unsigned int *max_bitflips,\n-\t\t\t\t       bool bbm, int page)\n+\t\t\t\t       int step, int page)\n {\n \tstruct nand_chip *nand = mtd_to_nand(mtd);\n \tstruct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);\n+\tstruct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);\n+\tunsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step);\n \tstruct nand_ecc_ctrl *ecc = &nand->ecc;\n \tint raw_mode = 0;\n \tu32 status;\n \tu32 pattern_found;\n+\tbool bbm = !step;\n \tint ret;\n \t/* From the controller point of view, we are at step 0 */\n \tconst int nfc_step = 0;\n@@ -773,8 +829,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,\n \tif (ret)\n \t\treturn ret;\n \n-\tsunxi_nfc_reset_user_data_len(nfc);\n-\tsunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, nfc_step);\n+\tsunxi_nfc_set_user_data_len(nfc, user_data_sz, nfc_step);\n \n \tsunxi_nfc_randomizer_enable(mtd);\n \twritel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,\n@@ -785,7 +840,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,\n \tif (ret)\n \t\treturn ret;\n \n-\t*cur_off = oob_off + ecc->bytes + USER_DATA_SZ;\n+\t*cur_off = oob_off + ecc->bytes + user_data_sz;\n \n \tpattern_found = readl(nfc->regs + nfc->caps->reg_pat_found);\n \tpattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found);\n@@ -796,7 +851,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,\n \t\t\tpattern = 0x0;\n \n \t\tmemset(data, pattern, ecc->size);\n-\t\tmemset(oob, pattern, ecc->bytes + USER_DATA_SZ);\n+\t\tmemset(oob, pattern, ecc->bytes + user_data_sz);\n \n \t\treturn 1;\n \t}\n@@ -806,7 +861,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,\n \tmemcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);\n \n \tnand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);\n-\tsunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + USER_DATA_SZ, true, page);\n+\tsunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + user_data_sz, true, page);\n \n \tstatus = readl(nfc->regs + NFC_REG_ECC_ST);\n \tif (status & NFC_ECC_ERR(nfc_step)) {\n@@ -818,26 +873,21 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,\n \t\t\tnand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1);\n \t\t\tnand->read_buf(mtd, data, ecc->size);\n \t\t\tnand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);\n-\t\t\tnand->read_buf(mtd, oob, ecc->bytes + USER_DATA_SZ);\n+\t\t\tnand->read_buf(mtd, oob, ecc->bytes + user_data_sz);\n \t\t}\n \n \t\tret = nand_check_erased_ecc_chunk(data,\tecc->size,\n-\t\t\t\t\t\t  oob, ecc->bytes + USER_DATA_SZ,\n+\t\t\t\t\t\t  oob, ecc->bytes + user_data_sz,\n \t\t\t\t\t\t  NULL, 0, ecc->strength);\n \t\tif (ret >= 0)\n \t\t\traw_mode = 1;\n \t} else {\n \t\t/*\n-\t\t * The engine protects USER_DATA_SZ bytes of OOB data per chunk.\n+\t\t * The engine protects user_data_sz bytes of OOB data per chunk.\n \t\t * Retrieve the corrected OOB bytes.\n \t\t */\n-\t\tsunxi_nfc_user_data_to_buf(readl(nfc->regs +\n-\t\t\t\t\t\t NFC_REG_USER_DATA(nfc, nfc_step)),\n-\t\t\t\t\t   oob);\n-\n-\t\t/* De-randomize the Bad Block Marker. */\n-\t\tif (bbm && nand->options & NAND_NEED_SCRAMBLING)\n-\t\t\tsunxi_nfc_randomize_bbm(mtd, page, oob);\n+\t\tsunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, nfc_step,\n+\t\t\t\t\t\t    bbm, page, user_data_sz);\n \t}\n \n \tif (ret < 0) {\n@@ -850,13 +900,30 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,\n \treturn raw_mode;\n }\n \n+/*\n+ * Returns the offset of the OOB for each step.\n+ * (it includes the user data before the ECC data.)\n+ */\n+static int sunxi_get_oob_offset(struct sunxi_nand_chip *sunxi_nand,\n+\t\t\t\tstruct nand_ecc_ctrl *ecc, int step)\n+{\n+\tint ecc_off = step * ecc->bytes;\n+\tint i;\n+\n+\tfor (i = 0; i < step; i++)\n+\t\tecc_off += sunxi_nfc_user_data_sz(sunxi_nand, i);\n+\n+\treturn ecc_off;\n+}\n+\n static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd,\n \t\t\t\t\t    u8 *oob, int *cur_off,\n \t\t\t\t\t    bool randomize, int page)\n {\n \tstruct nand_chip *nand = mtd_to_nand(mtd);\n \tstruct nand_ecc_ctrl *ecc = &nand->ecc;\n-\tint offset = ((ecc->bytes + USER_DATA_SZ) * ecc->steps);\n+\tstruct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);\n+\tint offset = sunxi_get_oob_offset(sunxi_nand, ecc, ecc->steps);\n \tint len = mtd->oobsize - offset;\n \n \tif (len <= 0)\n@@ -883,12 +950,15 @@ static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)\n static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,\n \t\t\t\t\tconst u8 *data, int data_off,\n \t\t\t\t\tconst u8 *oob, int oob_off,\n-\t\t\t\t\tint *cur_off, bool bbm,\n+\t\t\t\t\tint *cur_off, int step,\n \t\t\t\t\tint page)\n {\n \tstruct nand_chip *nand = mtd_to_nand(mtd);\n \tstruct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);\n+\tstruct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);\n+\tunsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step);\n \tstruct nand_ecc_ctrl *ecc = &nand->ecc;\n+\tbool bbm = !step;\n \tint ret;\n \t/* From the controller point of view, we are at step 0 */\n \tconst int nfc_step = 0;\n@@ -900,12 +970,17 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,\n \n \t/* Fill OOB data in */\n \tif ((nand->options & NAND_NEED_SCRAMBLING) && bbm) {\n-\t\tu8 user_data[USER_DATA_SZ];\n+\t\tu8 *user_data;\n \n-\t\tmemcpy(user_data, oob, USER_DATA_SZ);\n+\t\tuser_data = kzalloc(user_data_sz, GFP_KERNEL);\n+\t\tif (!user_data)\n+\t\t\treturn -ENOMEM;\n+\n+\t\tmemcpy(user_data, oob, user_data_sz);\n \t\tsunxi_nfc_randomize_bbm(mtd, page, user_data);\n \t\twritel(sunxi_nfc_buf_to_user_data(user_data),\n \t\t       nfc->regs + NFC_REG_USER_DATA(nfc, nfc_step));\n+\t\tkfree(user_data);\n \t} else {\n \t\twritel(sunxi_nfc_buf_to_user_data(oob),\n \t\t       nfc->regs + NFC_REG_USER_DATA(nfc, nfc_step));\n@@ -918,8 +993,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,\n \tif (ret)\n \t\treturn ret;\n \n-\tsunxi_nfc_reset_user_data_len(nfc);\n-\tsunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, nfc_step);\n+\tsunxi_nfc_set_user_data_len(nfc, user_data_sz, nfc_step);\n \n \tsunxi_nfc_randomizer_enable(mtd);\n \twritel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |\n@@ -931,7 +1005,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,\n \tif (ret)\n \t\treturn ret;\n \n-\t*cur_off = oob_off + ecc->bytes + USER_DATA_SZ;\n+\t*cur_off = oob_off + ecc->bytes + user_data_sz;\n \n \treturn 0;\n }\n@@ -942,7 +1016,8 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd,\n {\n \tstruct nand_chip *nand = mtd_to_nand(mtd);\n \tstruct nand_ecc_ctrl *ecc = &nand->ecc;\n-\tint offset = ((ecc->bytes + USER_DATA_SZ) * ecc->steps);\n+\tstruct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);\n+\tint offset = sunxi_get_oob_offset(sunxi_nand, ecc, ecc->steps);\n \tint len = mtd->oobsize - offset;\n \n \tif (len <= 0)\n@@ -961,6 +1036,8 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,\n \t\t\t\t      struct nand_chip *chip, uint8_t *buf,\n \t\t\t\t      int oob_required, int page)\n {\n+\tstruct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip);\n+\tstruct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);\n \tstruct nand_ecc_ctrl *ecc = &chip->ecc;\n \tunsigned int max_bitflips = 0;\n \tint ret, i, cur_off = 0;\n@@ -968,16 +1045,17 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,\n \n \tsunxi_nfc_hw_ecc_enable(mtd);\n \n+\tsunxi_nfc_reset_user_data_len(nfc);\n \tfor (i = 0; i < ecc->steps; i++) {\n \t\tint data_off = i * ecc->size;\n-\t\tint oob_off = i * (ecc->bytes + USER_DATA_SZ);\n+\t\tint oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i);\n \t\tu8 *data = buf + data_off;\n \t\tu8 *oob = chip->oob_poi + oob_off;\n \n \t\tret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,\n \t\t\t\t\t\t  oob_off + mtd->writesize,\n \t\t\t\t\t\t  &cur_off, &max_bitflips,\n-\t\t\t\t\t\t  !i, page);\n+\t\t\t\t\t\t  i, page);\n \t\tif (ret < 0)\n \t\t\treturn ret;\n \t\telse if (ret)\n@@ -998,23 +1076,26 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd,\n \t\t\t\t\t uint32_t data_offs, uint32_t readlen,\n \t\t\t\t\t uint8_t *bufpoi, int page)\n {\n+\tstruct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip);\n+\tstruct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);\n \tstruct nand_ecc_ctrl *ecc = &chip->ecc;\n \tint ret, i, cur_off = 0;\n \tunsigned int max_bitflips = 0;\n \n \tsunxi_nfc_hw_ecc_enable(mtd);\n \n+\tsunxi_nfc_reset_user_data_len(nfc);\n \tchip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);\n \tfor (i = data_offs / ecc->size;\n \t     i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) {\n \t\tint data_off = i * ecc->size;\n-\t\tint oob_off = i * (ecc->bytes + USER_DATA_SZ);\n+\t\tint oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i);\n \t\tu8 *data = bufpoi + data_off;\n \t\tu8 *oob = chip->oob_poi + oob_off;\n \n \t\tret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off,\n \t\t\toob, oob_off + mtd->writesize,\n-\t\t\t&cur_off, &max_bitflips, !i, page);\n+\t\t\t&cur_off, &max_bitflips, i, page);\n \t\tif (ret < 0)\n \t\t\treturn ret;\n \t}\n@@ -1029,20 +1110,23 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,\n \t\t\t\t       const uint8_t *buf, int oob_required,\n \t\t\t\t       int page)\n {\n+\tstruct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip);\n+\tstruct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);\n \tstruct nand_ecc_ctrl *ecc = &chip->ecc;\n \tint ret, i, cur_off = 0;\n \n \tsunxi_nfc_hw_ecc_enable(mtd);\n \n+\tsunxi_nfc_reset_user_data_len(nfc);\n \tfor (i = 0; i < ecc->steps; i++) {\n \t\tint data_off = i * ecc->size;\n-\t\tint oob_off = i * (ecc->bytes + USER_DATA_SZ);\n+\t\tint oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i);\n \t\tconst u8 *data = buf + data_off;\n \t\tconst u8 *oob = chip->oob_poi + oob_off;\n \n \t\tret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob,\n \t\t\t\t\t\t   oob_off + mtd->writesize,\n-\t\t\t\t\t\t   &cur_off, !i, page);\n+\t\t\t\t\t\t   &cur_off, i, page);\n \t\tif (ret)\n \t\t\treturn ret;\n \t}\n@@ -1062,21 +1146,24 @@ static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd,\n \t\t\t\t\t  const u8 *buf, int oob_required,\n \t\t\t\t\t  int page)\n {\n+\tstruct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip);\n+\tstruct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);\n \tstruct nand_ecc_ctrl *ecc = &chip->ecc;\n \tint ret, i, cur_off = 0;\n \n \tsunxi_nfc_hw_ecc_enable(mtd);\n \n+\tsunxi_nfc_reset_user_data_len(nfc);\n \tfor (i = data_offs / ecc->size;\n \t     i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) {\n \t\tint data_off = i * ecc->size;\n-\t\tint oob_off = i * (ecc->bytes + USER_DATA_SZ);\n+\t\tint oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i);\n \t\tconst u8 *data = buf + data_off;\n \t\tconst u8 *oob = chip->oob_poi + oob_off;\n \n \t\tret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob,\n \t\t\t\t\t\t   oob_off + mtd->writesize,\n-\t\t\t\t\t\t   &cur_off, !i, page);\n+\t\t\t\t\t\t   &cur_off, i, page);\n \t\tif (ret)\n \t\t\treturn ret;\n \t}\n@@ -1091,18 +1178,23 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,\n \t\t\t\t\t       uint8_t *buf, int oob_required,\n \t\t\t\t\t       int page)\n {\n+\tstruct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip);\n+\tstruct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);\n \tstruct nand_ecc_ctrl *ecc = &chip->ecc;\n \tunsigned int max_bitflips = 0;\n \tint ret, i, cur_off = 0;\n \tbool raw_mode = false;\n+\t/* With hw_syndrome, user data length is fixed */\n+\tunsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, 0);\n \n \tsunxi_nfc_hw_ecc_enable(mtd);\n \n+\tsunxi_nfc_reset_user_data_len(nfc);\n \tfor (i = 0; i < ecc->steps; i++) {\n-\t\tint data_off = i * (ecc->size + ecc->bytes + USER_DATA_SZ);\n+\t\tint data_off = i * (ecc->size + ecc->bytes + user_data_sz);\n \t\tint oob_off = data_off + ecc->size;\n \t\tu8 *data = buf + (i * ecc->size);\n-\t\tu8 *oob = chip->oob_poi + (i * (ecc->bytes + USER_DATA_SZ));\n+\t\tu8 *oob = chip->oob_poi + (i * (ecc->bytes + user_data_sz));\n \n \t\tret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,\n \t\t\t\t\t\t  oob_off, &cur_off,\n@@ -1127,16 +1219,19 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,\n \t\t\t\t\t\tconst uint8_t *buf,\n \t\t\t\t\t\tint oob_required, int page)\n {\n+\tstruct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip);\n \tstruct nand_ecc_ctrl *ecc = &chip->ecc;\n \tint ret, i, cur_off = 0;\n+\t/* With hw_syndrome, user data length is fixed */\n+\tunsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, 0);\n \n \tsunxi_nfc_hw_ecc_enable(mtd);\n \n \tfor (i = 0; i < ecc->steps; i++) {\n-\t\tint data_off = i * (ecc->size + ecc->bytes + USER_DATA_SZ);\n+\t\tint data_off = i * (ecc->size + ecc->bytes + user_data_sz);\n \t\tint oob_off = data_off + ecc->size;\n \t\tconst u8 *data = buf + (i * ecc->size);\n-\t\tconst u8 *oob = chip->oob_poi + (i * (ecc->bytes + USER_DATA_SZ));\n+\t\tconst u8 *oob = chip->oob_poi + (i * (ecc->bytes + user_data_sz));\n \n \t\tret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off,\n \t\t\t\t\t\t   oob, oob_off, &cur_off,\n@@ -1338,6 +1433,34 @@ static int sunxi_nand_chip_init_timings(struct sunxi_nfc *nfc,\n \treturn sunxi_nand_chip_set_timings(nfc, chip, timings);\n }\n \n+static int sunxi_nfc_maximize_user_data(struct nand_chip *nand, uint32_t oobsize,\n+\t\t\t\t\tint ecc_bytes, int nsectors)\n+{\n+\tstruct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);\n+\tstruct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);\n+\tconst struct sunxi_nfc_caps *c = nfc->caps;\n+\tint remaining_bytes = oobsize - (ecc_bytes * nsectors);\n+\tint i, step;\n+\n+\tsunxi_nand->user_data_bytes = devm_kzalloc(nfc->dev, nsectors,\n+\t\t\t\t\t\t   GFP_KERNEL);\n+\tif (!sunxi_nand->user_data_bytes)\n+\t\treturn -ENOMEM;\n+\n+\tfor (step = 0; (step < nsectors) && (remaining_bytes > 0); step++) {\n+\t\tfor (i = 0; i < c->nuser_data_tab; i++) {\n+\t\t\tif (c->user_data_len_tab[i] > remaining_bytes)\n+\t\t\t\tbreak;\n+\t\t\tsunxi_nand->user_data_bytes[step] = c->user_data_len_tab[i];\n+\t\t}\n+\t\tremaining_bytes -= sunxi_nand->user_data_bytes[step];\n+\t\tif (sunxi_nand->user_data_bytes[step] == 0)\n+\t\t\tbreak;\n+\t}\n+\n+\treturn 0;\n+}\n+\n static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,\n \t\t\t\t\t      struct nand_ecc_ctrl *ecc)\n {\n@@ -1346,6 +1469,7 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,\n \tstruct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);\n \tstruct sunxi_nand_hw_ecc *data;\n \tstruct nand_ecclayout *layout;\n+\tunsigned int total_user_data_sz = 0;\n \tint nsectors;\n \tint ret;\n \tint i;\n@@ -1394,7 +1518,15 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,\n \tlayout = &data->layout;\n \tnsectors = mtd->writesize / ecc->size;\n \n-\tif (mtd->oobsize < ((ecc->bytes + USER_DATA_SZ) * nsectors)) {\n+\t/* Use the remaining OOB space for user data */\n+\tif (nfc->caps->reg_user_data_len)\n+\t\tsunxi_nfc_maximize_user_data(nand, mtd->oobsize, ecc->bytes,\n+\t\t\t\t\t     nsectors);\n+\n+\tfor (i = 0; i < nsectors; i++)\n+\t\ttotal_user_data_sz += sunxi_nfc_user_data_sz(sunxi_nand, i);\n+\n+\tif (mtd->oobsize < ecc->bytes * nsectors + total_user_data_sz) {\n \t\tret = -EINVAL;\n \t\tgoto err;\n \t}\n@@ -1408,6 +1540,8 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,\n \n err:\n \tkfree(data);\n+\tdevm_kfree(nfc->dev, sunxi_nand->user_data_bytes);\n+\tsunxi_nand->user_data_bytes = NULL;\n \n \treturn ret;\n }\n@@ -1422,7 +1556,10 @@ static void sunxi_nand_hw_common_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc)\n static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,\n \t\t\t\t       struct nand_ecc_ctrl *ecc)\n {\n+\tstruct nand_chip *nand = mtd_to_nand(mtd);\n+\tstruct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);\n \tstruct nand_ecclayout *layout;\n+\tunsigned int total_user_data_sz = 0;\n \tint nsectors;\n \tint i, j;\n \tint ret;\n@@ -1444,14 +1581,14 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,\n \t\t\t\tlayout->oobfree[i - 1].offset +\n \t\t\t\tlayout->oobfree[i - 1].length +\n \t\t\t\tecc->bytes;\n-\t\t\tlayout->oobfree[i].length = USER_DATA_SZ;\n+\t\t\tlayout->oobfree[i].length = sunxi_nfc_user_data_sz(sunxi_nand, i);\n \t\t} else {\n \t\t\t/*\n \t\t\t * The first 2 bytes are used for BB markers, hence we\n-\t\t\t * only have USER_DATA_SZ - 2 bytes available in the\n+\t\t\t * only have user_data_len(0) - 2 bytes available in the\n \t\t\t * first user data section.\n \t\t\t */\n-\t\t\tlayout->oobfree[i].length = USER_DATA_SZ - 2;\n+\t\t\tlayout->oobfree[i].length = sunxi_nfc_user_data_sz(sunxi_nand, i) - 2;\n \t\t\tlayout->oobfree[i].offset = 2;\n \t\t}\n \n@@ -1461,13 +1598,16 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,\n \t\t\t\t\tlayout->oobfree[i].length + j;\n \t}\n \n-\tif (mtd->oobsize > (ecc->bytes + USER_DATA_SZ) * nsectors) {\n+\tfor (i = 0; i < nsectors; i++)\n+\t\ttotal_user_data_sz += sunxi_nfc_user_data_sz(sunxi_nand, i);\n+\n+\tif (mtd->oobsize > ecc->bytes * nsectors + total_user_data_sz) {\n \t\tlayout->oobfree[nsectors].offset =\n \t\t\t\tlayout->oobfree[nsectors - 1].offset +\n \t\t\t\tlayout->oobfree[nsectors - 1].length +\n \t\t\t\tecc->bytes;\n \t\tlayout->oobfree[nsectors].length = mtd->oobsize -\n-\t\t\t\t((ecc->bytes + USER_DATA_SZ) * nsectors);\n+\t\t\t\t(ecc->bytes * nsectors + total_user_data_sz);\n \t}\n \n \treturn 0;\n@@ -1476,6 +1616,8 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,\n static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd,\n \t\t\t\t\t\tstruct nand_ecc_ctrl *ecc)\n {\n+\tstruct nand_chip *nand = mtd_to_nand(mtd);\n+\tstruct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);\n \tstruct nand_ecclayout *layout;\n \tint nsectors;\n \tint i;\n@@ -1485,7 +1627,13 @@ static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd,\n \tif (ret)\n \t\treturn ret;\n \n-\tecc->prepad = USER_DATA_SZ;\n+\tfor (i = 0; i < nsectors; i++)\n+\t\tif (sunxi_nfc_user_data_sz(sunxi_nand, i) !=\n+\t\t    sunxi_nfc_user_data_sz(sunxi_nand, 0)) {\n+\t\t\tdev_err(mtd->dev, \"Variable user data length not upported with NAND_ECC_HW_SYNDROME\\n\");\n+\t\t\treturn -EOPNOTSUPP;\n+\t\t}\n+\tecc->prepad = sunxi_nfc_user_data_sz(sunxi_nand, 0);\n \tecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page;\n \tecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page;\n \n@@ -1735,6 +1883,7 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)\n \t\tnand_release(&chip->mtd);\n \t\tsunxi_nand_ecc_cleanup(&chip->nand.ecc);\n \t\tlist_del(&chip->node);\n+\t\tdevm_kfree(nfc->dev, chip->user_data_bytes);\n \t\tkfree(chip);\n \t}\n }\ndiff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c\nindex 0d1f060cc425..784ffb00cf70 100644\n--- a/drivers/mtd/nand/raw/sunxi_nand_spl.c\n+++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c\n@@ -28,6 +28,7 @@ struct nfc_config {\n \tbool randomize;\n \tbool valid;\n \tconst struct sunxi_nfc_caps *caps;\n+\tu8 *user_data_bytes;\n };\n \n /* minimal \"boot0\" style NAND support for Allwinner A20 */\n@@ -271,16 +272,17 @@ static void sunxi_nfc_set_user_data_len(const struct nfc_config *nfc,\n \n /*\n  * Values in this table are obtained by doing:\n- * DIV_ROUND_UP(info->ecc_strength * 14, 8) + USER_DATA_SZ\n- * So it's the number of bytes needed for ECC + user data for one step.\n+ * DIV_ROUND_UP(info->ecc_strength * 14, 8)\n+ * So it's the number of bytes needed for ECC one step\n+ * (not counting the user data length)\n  */\n #if defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_H6)\n static const int ecc_bytes[] = {\n-\t32, 46, 54, 60, 74, 82, 88, 96, 102, 110, 116, 124, 130, 138, 144\n+\t28, 42, 50, 56, 70, 78, 84, 92, 98, 106, 112, 120, 126, 134, 140\n };\n #else\n static const int ecc_bytes[] = {\n-\t32, 46, 54, 60, 74, 88, 102, 110, 116\n+\t28, 42, 50, 56, 70, 84, 98, 106, 112\n };\n #endif\n \n@@ -293,6 +295,14 @@ static void nand_readlcpy(u32 *dest, u32 * __iomem src, size_t len)\n \t\t*dest++ = readl(src++);\n }\n \n+static u8 nand_user_data_sz(const struct nfc_config *conf, int step)\n+{\n+\tif (!conf->user_data_bytes)\n+\t\treturn USER_DATA_SZ;\n+\n+\treturn conf->user_data_bytes[step];\n+}\n+\n static int nand_read_page(const struct nfc_config *conf, u32 offs,\n \t\t\t  void *dest, int len)\n {\n@@ -300,6 +310,7 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs,\n \tu16 rand_seed = 0;\n \tint oob_chunk_sz = ecc_bytes[conf->ecc_strength];\n \tint page = offs / conf->page_size;\n+\tint oob_off = conf->page_size;\n \tu32 ecc_st, pattern_found;\n \tint i;\n \t/* From the controller point of view, we are at step 0 */\n@@ -316,9 +327,9 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs,\n \t/* Retrieve data from SRAM (PIO) */\n \tfor (i = 0; i < nsectors; i++) {\n \t\tint data_off = i * conf->ecc_size;\n-\t\tint oob_off = conf->page_size + (i * oob_chunk_sz);\n \t\tu8 *data = dest + data_off;\n \t\tu32 ecc512_bit = 0;\n+\t\tunsigned int user_data_sz = nand_user_data_sz(conf, i);\n \n \t\tif (conf->caps->has_ecc_block_512 && conf->ecc_size == 512)\n \t\t\tecc512_bit = NFC_ECC_BLOCK_512;\n@@ -345,7 +356,7 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs,\n \t\tnand_change_column(oob_off);\n \n \t\tsunxi_nfc_reset_user_data_len(conf);\n-\t\tsunxi_nfc_set_user_data_len(conf, USER_DATA_SZ, nfc_step);\n+\t\tsunxi_nfc_set_user_data_len(conf, user_data_sz, nfc_step);\n \n \t\tnand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_OP);\n \t\t/* Get the ECC status */\n@@ -371,13 +382,61 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs,\n \t\tnand_readlcpy((u32 *)data,\n \t\t\t      (void *)(uintptr_t)SUNXI_NFC_BASE + NFC_RAM0_BASE,\n \t\t\t      conf->ecc_size);\n-\n \t\t/* Stop the ECC engine */\n \t\twritel_nfc(readl_nfc(NFC_REG_ECC_CTL) & ~NFC_ECC_EN,\n \t\t\t   NFC_REG_ECC_CTL);\n \n \t\tif (data_off + conf->ecc_size >= len)\n \t\t\tbreak;\n+\n+\t\toob_off += oob_chunk_sz + user_data_sz;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int nand_min_user_data_sz(struct nfc_config *conf, int nsectors)\n+{\n+\tconst struct sunxi_nfc_caps *c = conf->caps;\n+\tint min_user_data_sz = 0;\n+\tint i;\n+\n+\tif (!c->reg_user_data_len) {\n+\t\tfor (i = 0; i < nsectors; i++)\n+\t\t\tmin_user_data_sz += nand_user_data_sz(conf, i);\n+\t} else {\n+\t\tfor (i = 0; i < c->nuser_data_tab; i++)\n+\t\t\t/* We want at least enough size for the BBM */\n+\t\t\tif (c->user_data_len_tab[i] >= 2)\n+\t\t\t\tbreak;\n+\t\tmin_user_data_sz = c->user_data_len_tab[i];\n+\t}\n+\n+\treturn min_user_data_sz;\n+}\n+\n+static int nand_maximize_user_data(struct nfc_config *conf, uint32_t oobsize,\n+\t\t\t\t   int ecc_len, int nsectors)\n+{\n+\tconst struct sunxi_nfc_caps *c = conf->caps;\n+\tint remaining_bytes = oobsize - (ecc_len * nsectors);\n+\tint i, step;\n+\n+\tkfree(conf->user_data_bytes);\n+\n+\tconf->user_data_bytes = kzalloc(nsectors, GFP_KERNEL);\n+\tif (!conf->user_data_bytes)\n+\t\treturn -ENOMEM;\n+\n+\tfor (step = 0; (step < nsectors) && (remaining_bytes > 0); step++) {\n+\t\tfor (i = 0; i < c->nuser_data_tab; i++) {\n+\t\t\tif (c->user_data_len_tab[i] > remaining_bytes)\n+\t\t\t\tbreak;\n+\t\t\tconf->user_data_bytes[step] = c->user_data_len_tab[i];\n+\t\t}\n+\t\tremaining_bytes -= conf->user_data_bytes[step];\n+\t\tif (conf->user_data_bytes[step] == 0)\n+\t\t\tbreak;\n \t}\n \n \treturn 0;\n@@ -387,7 +446,8 @@ static int nand_max_ecc_strength(struct nfc_config *conf)\n {\n \tint max_oobsize, max_ecc_bytes;\n \tint nsectors = conf->page_size / conf->ecc_size;\n-\tint i;\n+\tunsigned int total_user_data_sz = 0;\n+\tint ecc_idx, i;\n \n \t/*\n \t * ECC strength is limited by the size of the OOB area which is\n@@ -412,15 +472,38 @@ static int nand_max_ecc_strength(struct nfc_config *conf)\n \n \tmax_ecc_bytes = max_oobsize / nsectors;\n \n-\tfor (i = 0; i < ARRAY_SIZE(ecc_bytes); i++) {\n-\t\tif (ecc_bytes[i] > max_ecc_bytes)\n+\t/*\n+\t * nand_min_user_data_sz() will return the total_user_data_sz in case\n+\t * of a fixed user data length, or the minimal usable user data size\n+\t * in case of variable data length (with at least enough space for the\n+\t * BBM.\n+\t */\n+\ttotal_user_data_sz = nand_min_user_data_sz(conf, nsectors);\n+\n+\tfor (ecc_idx = 0; ecc_idx < ARRAY_SIZE(ecc_bytes); ecc_idx++) {\n+\t\tif (ecc_bytes[ecc_idx] + total_user_data_sz > max_ecc_bytes)\n \t\t\tbreak;\n \t}\n \n-\tif (!i)\n+\tif (!ecc_idx)\n \t\treturn -EINVAL;\n \n-\treturn i - 1;\n+\tecc_idx--;\n+\n+\t/*\n+\t * The rationale for variable data length is to prioritize maximum ECC\n+\t * strength, and then use the remaining space for user data.\n+\t */\n+\tif (conf->caps->reg_user_data_len) {\n+\t\tnand_maximize_user_data(conf, max_oobsize,\n+\t\t\t\t\tecc_bytes[ecc_idx], nsectors);\n+\n+\t\ttotal_user_data_sz = 0;\n+\t\tfor (i = 0; i < nsectors; i++)\n+\t\t\ttotal_user_data_sz += nand_user_data_sz(conf, i);\n+\t}\n+\n+\treturn ecc_idx;\n }\n \n static int nand_detect_ecc_config(struct nfc_config *conf, u32 offs,\n",
    "prefixes": [
        "v2",
        "4/5"
    ]
}