From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 82CD2CD3424 for ; Fri, 1 May 2026 12:47:24 +0000 (UTC) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id E772780F0E; Fri, 1 May 2026 14:47:22 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=fail (p=none dis=none) header.from=arm.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=arm.com header.i=@arm.com header.b="YT9CUQjZ"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 246C980086; Fri, 1 May 2026 14:47:21 +0200 (CEST) Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by phobos.denx.de (Postfix) with ESMTP id E9B1C80086 for ; Fri, 1 May 2026 14:47:17 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=andre.przywara@arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id BF207176B; Fri, 1 May 2026 05:47:11 -0700 (PDT) Received: from ryzen.lan (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 280293F7B4; Fri, 1 May 2026 05:47:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1777639637; bh=2CRG0u+PLVYd+r4htS3ZCjpKLI7+OKAKf5POtLcZ17o=; h=Date:From:To:Cc:Subject:In-Reply-To:References:From; b=YT9CUQjZiLK+evcBQK8DI5hzYgg0HN3ilQHMSCHyi/PZJQOo8NqnDKhZPFodlhRA2 /tdOoroI0SgnFKjKGY4vXYT/xAzySED/yWBOPxDoNmCrLh3B1W2O+XMjS8KY8h6TzD OsMMhIGHpP6vIIoCisiTf1HNPRN05GPjxeM+L33U= Date: Fri, 1 May 2026 14:46:13 +0200 From: Andre Przywara To: Richard Genoud Cc: Dario Binacchi , Michael Trimarchi , Tom Rini , Andrew Goodbody , Miquel Raynal , James Hilliard , Boris Brezillon , Thomas Petazzoni , u-boot@lists.denx.de Subject: Re: [PATCH v2 4/5] mtd: rawnand: sunxi: introduce variable user data length Message-ID: <20260501144613.2a0da02a@ryzen.lan> In-Reply-To: <20260327140508.3680105-5-richard.genoud@bootlin.com> References: <20260327140508.3680105-1-richard.genoud@bootlin.com> <20260327140508.3680105-5-richard.genoud@bootlin.com> Organization: Arm Ltd. X-Mailer: Claws Mail 4.4.0 (GTK 3.24.31; x86_64-slackware-linux-gnu) MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean On Fri, 27 Mar 2026 15:05:07 +0100 Richard Genoud wrote: Hi, > In Allwinner SoCs, user data can be added in OOB before each ECC data. > For older SoCs like A10, the user data size was the size of a register > (4 bytes) and was mandatory before each ECC step. > So, the A10 OOB Layout is: > [4Bytes USER_DATA_STEP0] [ECC_STEP0 bytes] > [4bytes USER_DATA_STEP1] [ECC_STEP1 bytes] > ... > NB: the BBM is stored at the beginning of the USER_DATA_STEP0. > > Now, for H6/H616 NAND flash controller, this user data can have a > different size for each step. > So, we are maximizing the user data length to use as many OOB bytes as > possible. > > Fixes: 7d1de9801151 ("mtd: rawnand: sunxi_spl: add support for H6/H616 nand controller") > Fixes: f163da5e6d26 ("mtd: rawnand: sunxi: add support for H6/H616 nand controller") > Signed-off-by: Richard Genoud So I just skimmed over this, as I have no clue what's going on exactly, but the code makes some sense, and more importantly it roughly matches the respective Linux patch that went into v7.1-rc1, so I assume that's good. From a mainline perspective it's mostly dead code anyway, as there is no mainline board using raw NAND outside of the two C.H.I.P. boards from about a decade ago (hint hint: please upstream support for your board!) So I am just taking this as-is, as I don't expect to see proper reviews anytime soon anyway. If people find bugs, we can always fix them. Cheers, Andre > --- > drivers/mtd/nand/raw/sunxi_nand.c | 235 +++++++++++++++++++++----- > drivers/mtd/nand/raw/sunxi_nand_spl.c | 107 ++++++++++-- > 2 files changed, 287 insertions(+), 55 deletions(-) > > diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c > index ecab9ebc9576..c3bec1fe517e 100644 > --- a/drivers/mtd/nand/raw/sunxi_nand.c > +++ b/drivers/mtd/nand/raw/sunxi_nand.c > @@ -114,6 +114,7 @@ struct sunxi_nand_hw_ecc { > * @clk_rate: clk_rate required for this NAND chip > * @timing_cfg TIMING_CFG register value for this NAND chip > * @selected: current active CS > + * @user_data_bytes array of user data lengths for all ECC steps > * @nsels: number of CS lines required by the NAND chip > * @sels: array of CS lines descriptions > */ > @@ -128,6 +129,7 @@ struct sunxi_nand_chip { > u32 addr[2]; > int cmd_cycles; > u8 cmd[2]; > + u8 *user_data_bytes; > int nsels; > struct sunxi_nand_chip_sel sels[0]; > }; > @@ -744,19 +746,73 @@ static void sunxi_nfc_set_user_data_len(struct sunxi_nfc *nfc, > writel(val, nfc->regs + NFC_REG_USER_DATA_LEN(nfc, step)); > } > > +static u8 sunxi_nfc_user_data_sz(const struct sunxi_nand_chip *sunxi_nand, int step) > +{ > + if (!sunxi_nand->user_data_bytes) > + return USER_DATA_SZ; > + > + return sunxi_nand->user_data_bytes[step]; > +} > + > +static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob, > + int step, bool bbm, int page, > + unsigned int user_data_sz) > +{ > + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); > + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); > + u32 user_data; > + > + if (!nfc->caps->reg_user_data_len) { > + /* > + * For A10, the user data for step n is in the nth > + * REG_USER_DATA > + */ > + user_data = readl(nfc->regs + NFC_REG_USER_DATA(nfc, step)); > + sunxi_nfc_user_data_to_buf(user_data, oob); > + > + } else { > + /* > + * For H6 NAND controller, the user data for all steps is > + * contained in 32 user data registers, but not at a specific > + * offset for each step, they are just concatenated. > + */ > + unsigned int user_data_off = 0; > + unsigned int reg_off; > + u8 *ptr = oob; > + unsigned int i; > + > + for (i = 0; i < step; i++) > + user_data_off += sunxi_nfc_user_data_sz(sunxi_nand, i); > + > + user_data_off /= 4; > + for (i = 0; i < user_data_sz / 4; i++, ptr += 4) { > + reg_off = NFC_REG_USER_DATA(nfc, user_data_off + i); > + user_data = readl(nfc->regs + reg_off); > + sunxi_nfc_user_data_to_buf(user_data, ptr); > + } > + } > + > + /* De-randomize the Bad Block Marker. */ > + if (bbm && nand->options & NAND_NEED_SCRAMBLING) > + sunxi_nfc_randomize_bbm(&nand->mtd, page, oob); > +} > + > static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, > u8 *data, int data_off, > u8 *oob, int oob_off, > int *cur_off, > unsigned int *max_bitflips, > - bool bbm, int page) > + int step, int page) > { > struct nand_chip *nand = mtd_to_nand(mtd); > struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); > + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); > + unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step); > struct nand_ecc_ctrl *ecc = &nand->ecc; > int raw_mode = 0; > u32 status; > u32 pattern_found; > + bool bbm = !step; > int ret; > /* From the controller point of view, we are at step 0 */ > const int nfc_step = 0; > @@ -773,8 +829,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, > if (ret) > return ret; > > - sunxi_nfc_reset_user_data_len(nfc); > - sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, nfc_step); > + sunxi_nfc_set_user_data_len(nfc, user_data_sz, nfc_step); > > sunxi_nfc_randomizer_enable(mtd); > writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, > @@ -785,7 +840,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, > if (ret) > return ret; > > - *cur_off = oob_off + ecc->bytes + USER_DATA_SZ; > + *cur_off = oob_off + ecc->bytes + user_data_sz; > > pattern_found = readl(nfc->regs + nfc->caps->reg_pat_found); > pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found); > @@ -796,7 +851,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, > pattern = 0x0; > > memset(data, pattern, ecc->size); > - memset(oob, pattern, ecc->bytes + USER_DATA_SZ); > + memset(oob, pattern, ecc->bytes + user_data_sz); > > return 1; > } > @@ -806,7 +861,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, > memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); > > nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); > - sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + USER_DATA_SZ, true, page); > + sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + user_data_sz, true, page); > > status = readl(nfc->regs + NFC_REG_ECC_ST); > if (status & NFC_ECC_ERR(nfc_step)) { > @@ -818,26 +873,21 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, > nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1); > nand->read_buf(mtd, data, ecc->size); > nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); > - nand->read_buf(mtd, oob, ecc->bytes + USER_DATA_SZ); > + nand->read_buf(mtd, oob, ecc->bytes + user_data_sz); > } > > ret = nand_check_erased_ecc_chunk(data, ecc->size, > - oob, ecc->bytes + USER_DATA_SZ, > + oob, ecc->bytes + user_data_sz, > NULL, 0, ecc->strength); > if (ret >= 0) > raw_mode = 1; > } else { > /* > - * The engine protects USER_DATA_SZ bytes of OOB data per chunk. > + * The engine protects user_data_sz bytes of OOB data per chunk. > * Retrieve the corrected OOB bytes. > */ > - sunxi_nfc_user_data_to_buf(readl(nfc->regs + > - NFC_REG_USER_DATA(nfc, nfc_step)), > - oob); > - > - /* De-randomize the Bad Block Marker. */ > - if (bbm && nand->options & NAND_NEED_SCRAMBLING) > - sunxi_nfc_randomize_bbm(mtd, page, oob); > + sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, nfc_step, > + bbm, page, user_data_sz); > } > > if (ret < 0) { > @@ -850,13 +900,30 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, > return raw_mode; > } > > +/* > + * Returns the offset of the OOB for each step. > + * (it includes the user data before the ECC data.) > + */ > +static int sunxi_get_oob_offset(struct sunxi_nand_chip *sunxi_nand, > + struct nand_ecc_ctrl *ecc, int step) > +{ > + int ecc_off = step * ecc->bytes; > + int i; > + > + for (i = 0; i < step; i++) > + ecc_off += sunxi_nfc_user_data_sz(sunxi_nand, i); > + > + return ecc_off; > +} > + > static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd, > u8 *oob, int *cur_off, > bool randomize, int page) > { > struct nand_chip *nand = mtd_to_nand(mtd); > struct nand_ecc_ctrl *ecc = &nand->ecc; > - int offset = ((ecc->bytes + USER_DATA_SZ) * ecc->steps); > + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); > + int offset = sunxi_get_oob_offset(sunxi_nand, ecc, ecc->steps); > int len = mtd->oobsize - offset; > > if (len <= 0) > @@ -883,12 +950,15 @@ static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf) > static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, > const u8 *data, int data_off, > const u8 *oob, int oob_off, > - int *cur_off, bool bbm, > + int *cur_off, int step, > int page) > { > struct nand_chip *nand = mtd_to_nand(mtd); > struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); > + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); > + unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step); > struct nand_ecc_ctrl *ecc = &nand->ecc; > + bool bbm = !step; > int ret; > /* From the controller point of view, we are at step 0 */ > const int nfc_step = 0; > @@ -900,12 +970,17 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, > > /* Fill OOB data in */ > if ((nand->options & NAND_NEED_SCRAMBLING) && bbm) { > - u8 user_data[USER_DATA_SZ]; > + u8 *user_data; > > - memcpy(user_data, oob, USER_DATA_SZ); > + user_data = kzalloc(user_data_sz, GFP_KERNEL); > + if (!user_data) > + return -ENOMEM; > + > + memcpy(user_data, oob, user_data_sz); > sunxi_nfc_randomize_bbm(mtd, page, user_data); > writel(sunxi_nfc_buf_to_user_data(user_data), > nfc->regs + NFC_REG_USER_DATA(nfc, nfc_step)); > + kfree(user_data); > } else { > writel(sunxi_nfc_buf_to_user_data(oob), > nfc->regs + NFC_REG_USER_DATA(nfc, nfc_step)); > @@ -918,8 +993,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, > if (ret) > return ret; > > - sunxi_nfc_reset_user_data_len(nfc); > - sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, nfc_step); > + sunxi_nfc_set_user_data_len(nfc, user_data_sz, nfc_step); > > sunxi_nfc_randomizer_enable(mtd); > writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | > @@ -931,7 +1005,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, > if (ret) > return ret; > > - *cur_off = oob_off + ecc->bytes + USER_DATA_SZ; > + *cur_off = oob_off + ecc->bytes + user_data_sz; > > return 0; > } > @@ -942,7 +1016,8 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd, > { > struct nand_chip *nand = mtd_to_nand(mtd); > struct nand_ecc_ctrl *ecc = &nand->ecc; > - int offset = ((ecc->bytes + USER_DATA_SZ) * ecc->steps); > + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); > + int offset = sunxi_get_oob_offset(sunxi_nand, ecc, ecc->steps); > int len = mtd->oobsize - offset; > > if (len <= 0) > @@ -961,6 +1036,8 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, > struct nand_chip *chip, uint8_t *buf, > int oob_required, int page) > { > + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); > + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); > struct nand_ecc_ctrl *ecc = &chip->ecc; > unsigned int max_bitflips = 0; > int ret, i, cur_off = 0; > @@ -968,16 +1045,17 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, > > sunxi_nfc_hw_ecc_enable(mtd); > > + sunxi_nfc_reset_user_data_len(nfc); > for (i = 0; i < ecc->steps; i++) { > int data_off = i * ecc->size; > - int oob_off = i * (ecc->bytes + USER_DATA_SZ); > + int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i); > u8 *data = buf + data_off; > u8 *oob = chip->oob_poi + oob_off; > > ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, > oob_off + mtd->writesize, > &cur_off, &max_bitflips, > - !i, page); > + i, page); > if (ret < 0) > return ret; > else if (ret) > @@ -998,23 +1076,26 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd, > uint32_t data_offs, uint32_t readlen, > uint8_t *bufpoi, int page) > { > + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); > + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); > struct nand_ecc_ctrl *ecc = &chip->ecc; > int ret, i, cur_off = 0; > unsigned int max_bitflips = 0; > > sunxi_nfc_hw_ecc_enable(mtd); > > + sunxi_nfc_reset_user_data_len(nfc); > chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); > for (i = data_offs / ecc->size; > i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) { > int data_off = i * ecc->size; > - int oob_off = i * (ecc->bytes + USER_DATA_SZ); > + int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i); > u8 *data = bufpoi + data_off; > u8 *oob = chip->oob_poi + oob_off; > > ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, > oob, oob_off + mtd->writesize, > - &cur_off, &max_bitflips, !i, page); > + &cur_off, &max_bitflips, i, page); > if (ret < 0) > return ret; > } > @@ -1029,20 +1110,23 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, > const uint8_t *buf, int oob_required, > int page) > { > + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); > + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); > struct nand_ecc_ctrl *ecc = &chip->ecc; > int ret, i, cur_off = 0; > > sunxi_nfc_hw_ecc_enable(mtd); > > + sunxi_nfc_reset_user_data_len(nfc); > for (i = 0; i < ecc->steps; i++) { > int data_off = i * ecc->size; > - int oob_off = i * (ecc->bytes + USER_DATA_SZ); > + int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i); > const u8 *data = buf + data_off; > const u8 *oob = chip->oob_poi + oob_off; > > ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob, > oob_off + mtd->writesize, > - &cur_off, !i, page); > + &cur_off, i, page); > if (ret) > return ret; > } > @@ -1062,21 +1146,24 @@ static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd, > const u8 *buf, int oob_required, > int page) > { > + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); > + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); > struct nand_ecc_ctrl *ecc = &chip->ecc; > int ret, i, cur_off = 0; > > sunxi_nfc_hw_ecc_enable(mtd); > > + sunxi_nfc_reset_user_data_len(nfc); > for (i = data_offs / ecc->size; > i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) { > int data_off = i * ecc->size; > - int oob_off = i * (ecc->bytes + USER_DATA_SZ); > + int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i); > const u8 *data = buf + data_off; > const u8 *oob = chip->oob_poi + oob_off; > > ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob, > oob_off + mtd->writesize, > - &cur_off, !i, page); > + &cur_off, i, page); > if (ret) > return ret; > } > @@ -1091,18 +1178,23 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, > uint8_t *buf, int oob_required, > int page) > { > + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); > + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); > struct nand_ecc_ctrl *ecc = &chip->ecc; > unsigned int max_bitflips = 0; > int ret, i, cur_off = 0; > bool raw_mode = false; > + /* With hw_syndrome, user data length is fixed */ > + unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, 0); > > sunxi_nfc_hw_ecc_enable(mtd); > > + sunxi_nfc_reset_user_data_len(nfc); > for (i = 0; i < ecc->steps; i++) { > - int data_off = i * (ecc->size + ecc->bytes + USER_DATA_SZ); > + int data_off = i * (ecc->size + ecc->bytes + user_data_sz); > int oob_off = data_off + ecc->size; > u8 *data = buf + (i * ecc->size); > - u8 *oob = chip->oob_poi + (i * (ecc->bytes + USER_DATA_SZ)); > + u8 *oob = chip->oob_poi + (i * (ecc->bytes + user_data_sz)); > > ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, > oob_off, &cur_off, > @@ -1127,16 +1219,19 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, > const uint8_t *buf, > int oob_required, int page) > { > + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); > struct nand_ecc_ctrl *ecc = &chip->ecc; > int ret, i, cur_off = 0; > + /* With hw_syndrome, user data length is fixed */ > + unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, 0); > > sunxi_nfc_hw_ecc_enable(mtd); > > for (i = 0; i < ecc->steps; i++) { > - int data_off = i * (ecc->size + ecc->bytes + USER_DATA_SZ); > + int data_off = i * (ecc->size + ecc->bytes + user_data_sz); > int oob_off = data_off + ecc->size; > const u8 *data = buf + (i * ecc->size); > - const u8 *oob = chip->oob_poi + (i * (ecc->bytes + USER_DATA_SZ)); > + const u8 *oob = chip->oob_poi + (i * (ecc->bytes + user_data_sz)); > > ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, > oob, oob_off, &cur_off, > @@ -1338,6 +1433,34 @@ static int sunxi_nand_chip_init_timings(struct sunxi_nfc *nfc, > return sunxi_nand_chip_set_timings(nfc, chip, timings); > } > > +static int sunxi_nfc_maximize_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); > + const struct sunxi_nfc_caps *c = nfc->caps; > + int remaining_bytes = oobsize - (ecc_bytes * nsectors); > + int i, step; > + > + sunxi_nand->user_data_bytes = devm_kzalloc(nfc->dev, nsectors, > + GFP_KERNEL); > + if (!sunxi_nand->user_data_bytes) > + return -ENOMEM; > + > + 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) > + break; > + sunxi_nand->user_data_bytes[step] = c->user_data_len_tab[i]; > + } > + remaining_bytes -= sunxi_nand->user_data_bytes[step]; > + if (sunxi_nand->user_data_bytes[step] == 0) > + break; > + } > + > + return 0; > +} > + > static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, > struct nand_ecc_ctrl *ecc) > { > @@ -1346,6 +1469,7 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, > struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); > struct sunxi_nand_hw_ecc *data; > struct nand_ecclayout *layout; > + unsigned int total_user_data_sz = 0; > int nsectors; > int ret; > int i; > @@ -1394,7 +1518,15 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, > layout = &data->layout; > nsectors = mtd->writesize / ecc->size; > > - if (mtd->oobsize < ((ecc->bytes + USER_DATA_SZ) * nsectors)) { > + /* Use the remaining OOB space for user data */ > + if (nfc->caps->reg_user_data_len) > + sunxi_nfc_maximize_user_data(nand, mtd->oobsize, ecc->bytes, > + nsectors); > + > + for (i = 0; i < nsectors; i++) > + total_user_data_sz += sunxi_nfc_user_data_sz(sunxi_nand, i); > + > + if (mtd->oobsize < ecc->bytes * nsectors + total_user_data_sz) { > ret = -EINVAL; > goto err; > } > @@ -1408,6 +1540,8 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, > > err: > kfree(data); > + devm_kfree(nfc->dev, sunxi_nand->user_data_bytes); > + sunxi_nand->user_data_bytes = NULL; > > return ret; > } > @@ -1422,7 +1556,10 @@ static void sunxi_nand_hw_common_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc) > static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, > struct nand_ecc_ctrl *ecc) > { > + struct nand_chip *nand = mtd_to_nand(mtd); > + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); > struct nand_ecclayout *layout; > + unsigned int total_user_data_sz = 0; > int nsectors; > int i, j; > int ret; > @@ -1444,14 +1581,14 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, > layout->oobfree[i - 1].offset + > layout->oobfree[i - 1].length + > ecc->bytes; > - layout->oobfree[i].length = USER_DATA_SZ; > + layout->oobfree[i].length = sunxi_nfc_user_data_sz(sunxi_nand, i); > } else { > /* > * The first 2 bytes are used for BB markers, hence we > - * only have USER_DATA_SZ - 2 bytes available in the > + * only have user_data_len(0) - 2 bytes available in the > * first user data section. > */ > - layout->oobfree[i].length = USER_DATA_SZ - 2; > + layout->oobfree[i].length = sunxi_nfc_user_data_sz(sunxi_nand, i) - 2; > layout->oobfree[i].offset = 2; > } > > @@ -1461,13 +1598,16 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, > layout->oobfree[i].length + j; > } > > - if (mtd->oobsize > (ecc->bytes + USER_DATA_SZ) * nsectors) { > + for (i = 0; i < nsectors; i++) > + total_user_data_sz += sunxi_nfc_user_data_sz(sunxi_nand, i); > + > + if (mtd->oobsize > ecc->bytes * nsectors + total_user_data_sz) { > layout->oobfree[nsectors].offset = > layout->oobfree[nsectors - 1].offset + > layout->oobfree[nsectors - 1].length + > ecc->bytes; > layout->oobfree[nsectors].length = mtd->oobsize - > - ((ecc->bytes + USER_DATA_SZ) * nsectors); > + (ecc->bytes * nsectors + total_user_data_sz); > } > > return 0; > @@ -1476,6 +1616,8 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, > static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd, > struct nand_ecc_ctrl *ecc) > { > + struct nand_chip *nand = mtd_to_nand(mtd); > + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); > struct nand_ecclayout *layout; > int nsectors; > int i; > @@ -1485,7 +1627,13 @@ static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd, > if (ret) > return ret; > > - ecc->prepad = USER_DATA_SZ; > + for (i = 0; i < nsectors; i++) > + if (sunxi_nfc_user_data_sz(sunxi_nand, i) != > + sunxi_nfc_user_data_sz(sunxi_nand, 0)) { > + dev_err(mtd->dev, "Variable user data length not upported with NAND_ECC_HW_SYNDROME\n"); > + return -EOPNOTSUPP; > + } > + ecc->prepad = sunxi_nfc_user_data_sz(sunxi_nand, 0); > ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page; > ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page; > > @@ -1735,6 +1883,7 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) > nand_release(&chip->mtd); > sunxi_nand_ecc_cleanup(&chip->nand.ecc); > list_del(&chip->node); > + devm_kfree(nfc->dev, chip->user_data_bytes); > kfree(chip); > } > } > diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c > index 0d1f060cc425..784ffb00cf70 100644 > --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c > +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c > @@ -28,6 +28,7 @@ struct nfc_config { > bool randomize; > bool valid; > const struct sunxi_nfc_caps *caps; > + u8 *user_data_bytes; > }; > > /* minimal "boot0" style NAND support for Allwinner A20 */ > @@ -271,16 +272,17 @@ static void sunxi_nfc_set_user_data_len(const struct nfc_config *nfc, > > /* > * Values in this table are obtained by doing: > - * DIV_ROUND_UP(info->ecc_strength * 14, 8) + USER_DATA_SZ > - * So it's the number of bytes needed for ECC + user data for one step. > + * DIV_ROUND_UP(info->ecc_strength * 14, 8) > + * So it's the number of bytes needed for ECC one step > + * (not counting the user data length) > */ > #if defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_H6) > static const int ecc_bytes[] = { > - 32, 46, 54, 60, 74, 82, 88, 96, 102, 110, 116, 124, 130, 138, 144 > + 28, 42, 50, 56, 70, 78, 84, 92, 98, 106, 112, 120, 126, 134, 140 > }; > #else > static const int ecc_bytes[] = { > - 32, 46, 54, 60, 74, 88, 102, 110, 116 > + 28, 42, 50, 56, 70, 84, 98, 106, 112 > }; > #endif > > @@ -293,6 +295,14 @@ static void nand_readlcpy(u32 *dest, u32 * __iomem src, size_t len) > *dest++ = readl(src++); > } > > +static u8 nand_user_data_sz(const struct nfc_config *conf, int step) > +{ > + if (!conf->user_data_bytes) > + return USER_DATA_SZ; > + > + return conf->user_data_bytes[step]; > +} > + > static int nand_read_page(const struct nfc_config *conf, u32 offs, > void *dest, int len) > { > @@ -300,6 +310,7 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, > u16 rand_seed = 0; > int oob_chunk_sz = ecc_bytes[conf->ecc_strength]; > int page = offs / conf->page_size; > + int oob_off = conf->page_size; > u32 ecc_st, pattern_found; > int i; > /* From the controller point of view, we are at step 0 */ > @@ -316,9 +327,9 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, > /* Retrieve data from SRAM (PIO) */ > for (i = 0; i < nsectors; i++) { > int data_off = i * conf->ecc_size; > - int oob_off = conf->page_size + (i * oob_chunk_sz); > u8 *data = dest + data_off; > u32 ecc512_bit = 0; > + unsigned int user_data_sz = nand_user_data_sz(conf, i); > > if (conf->caps->has_ecc_block_512 && conf->ecc_size == 512) > ecc512_bit = NFC_ECC_BLOCK_512; > @@ -345,7 +356,7 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, > nand_change_column(oob_off); > > sunxi_nfc_reset_user_data_len(conf); > - sunxi_nfc_set_user_data_len(conf, USER_DATA_SZ, nfc_step); > + sunxi_nfc_set_user_data_len(conf, user_data_sz, nfc_step); > > nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_OP); > /* Get the ECC status */ > @@ -371,13 +382,61 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, > nand_readlcpy((u32 *)data, > (void *)(uintptr_t)SUNXI_NFC_BASE + NFC_RAM0_BASE, > conf->ecc_size); > - > /* Stop the ECC engine */ > writel_nfc(readl_nfc(NFC_REG_ECC_CTL) & ~NFC_ECC_EN, > NFC_REG_ECC_CTL); > > if (data_off + conf->ecc_size >= len) > break; > + > + oob_off += oob_chunk_sz + user_data_sz; > + } > + > + return 0; > +} > + > +static int nand_min_user_data_sz(struct nfc_config *conf, int nsectors) > +{ > + const struct sunxi_nfc_caps *c = conf->caps; > + int min_user_data_sz = 0; > + int i; > + > + if (!c->reg_user_data_len) { > + for (i = 0; i < nsectors; i++) > + min_user_data_sz += nand_user_data_sz(conf, i); > + } else { > + for (i = 0; i < c->nuser_data_tab; i++) > + /* We want at least enough size for the BBM */ > + if (c->user_data_len_tab[i] >= 2) > + break; > + min_user_data_sz = c->user_data_len_tab[i]; > + } > + > + return min_user_data_sz; > +} > + > +static int nand_maximize_user_data(struct nfc_config *conf, uint32_t oobsize, > + int ecc_len, int nsectors) > +{ > + const struct sunxi_nfc_caps *c = conf->caps; > + int remaining_bytes = oobsize - (ecc_len * nsectors); > + int i, step; > + > + kfree(conf->user_data_bytes); > + > + conf->user_data_bytes = kzalloc(nsectors, GFP_KERNEL); > + if (!conf->user_data_bytes) > + return -ENOMEM; > + > + 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) > + break; > + conf->user_data_bytes[step] = c->user_data_len_tab[i]; > + } > + remaining_bytes -= conf->user_data_bytes[step]; > + if (conf->user_data_bytes[step] == 0) > + break; > } > > return 0; > @@ -387,7 +446,8 @@ static int nand_max_ecc_strength(struct nfc_config *conf) > { > int max_oobsize, max_ecc_bytes; > int nsectors = conf->page_size / conf->ecc_size; > - int i; > + unsigned int total_user_data_sz = 0; > + int ecc_idx, i; > > /* > * ECC strength is limited by the size of the OOB area which is > @@ -412,15 +472,38 @@ static int nand_max_ecc_strength(struct nfc_config *conf) > > max_ecc_bytes = max_oobsize / nsectors; > > - for (i = 0; i < ARRAY_SIZE(ecc_bytes); i++) { > - if (ecc_bytes[i] > max_ecc_bytes) > + /* > + * nand_min_user_data_sz() will return the total_user_data_sz in case > + * of a fixed user data length, or the minimal usable user data size > + * in case of variable data length (with at least enough space for the > + * BBM. > + */ > + total_user_data_sz = nand_min_user_data_sz(conf, nsectors); > + > + for (ecc_idx = 0; ecc_idx < ARRAY_SIZE(ecc_bytes); ecc_idx++) { > + if (ecc_bytes[ecc_idx] + total_user_data_sz > max_ecc_bytes) > break; > } > > - if (!i) > + if (!ecc_idx) > return -EINVAL; > > - return i - 1; > + ecc_idx--; > + > + /* > + * The rationale for variable data length is to prioritize maximum ECC > + * strength, and then use the remaining space for user data. > + */ > + if (conf->caps->reg_user_data_len) { > + nand_maximize_user_data(conf, max_oobsize, > + ecc_bytes[ecc_idx], nsectors); > + > + total_user_data_sz = 0; > + for (i = 0; i < nsectors; i++) > + total_user_data_sz += nand_user_data_sz(conf, i); > + } > + > + return ecc_idx; > } > > static int nand_detect_ecc_config(struct nfc_config *conf, u32 offs,