From nobody Fri Jul 24 05:21:28 2026 Received: from smtpout-02.galae.net (smtpout-02.galae.net [185.246.84.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8A6A447012A for ; Thu, 23 Jul 2026 13:41:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.84.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784814073; cv=none; b=DuUy0dmjdbdZ/cmYwTvxNed7Jx7T/BBGHurroO1jxf1f3Jq4XdVC68UPk/PI0km1brlUq6X4h50tuwFTTow9i5Q2sGRQb+RX78+JYyrpb4lGQk5qXuWrhE2EYlh9QEP0AcmrphSljnIwGB00ZGUot3q3vDn3Ts4/0GRC1GP4P1E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784814073; c=relaxed/simple; bh=m4citQvomu6b8h5mZZwTIwmI8v3MrwNo1RnCrb+vIcM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=F+twa6TDf4M38NWUvvwrsdvF8xW5HyFzbrWSmgnNo92Ib6FmEY47y24xRuyDKw/7ypJGsfCFyXY7MiyRoOsTjjQmE4e6n8e69s8ZzstMC8BBznPXLS4c112lt1itWjSlnt2jw+Fdfy45tO+5PoME3GUOniM9gk/a4+llRsIYg6A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=Sa9Tbf5D; arc=none smtp.client-ip=185.246.84.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="Sa9Tbf5D" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-02.galae.net (Postfix) with ESMTPS id 05D621A11AC; Thu, 23 Jul 2026 13:41:07 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id C87D3602B8; Thu, 23 Jul 2026 13:41:06 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id A37DE11C10CF7; Thu, 23 Jul 2026 15:41:05 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1784814066; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:references; bh=aJe6iIBz31HfN24k+/q6gdaOXkqI9N3Yg0L4GhhIx5k=; b=Sa9Tbf5Dlr9rlVG9ompl70COm9vEHYUMtJ/cbJ5wgq1OMmTrsM48hCP+VMuvizBjHtgjpG IojY5FwQEiHL4aoqHi6XDt9F4dYWD7fjh7Ovf/pHXjngfmK/davJsqRUm9B+w4SdUGVjsb 2bJ8KS5D1byMo1uYczb5NnuMv+DUfIS1U/mOfAkyjn+XHNTVrFAP8iAywAbeTW3Xp9mDpK v32ADMY2uQwAEe+YSCAfuHgjqwGTrd64yX944hUbWrrHbyxHh6ORPqUbyNeMh2eOiAO3zZ hsX8bgw07kR074beEkrLtPk22dxEbvjyth4U7TiToF3NaT8G6kzgQ/mp62FhAw== From: Bastien Curutchet Date: Thu, 23 Jul 2026 15:41:01 +0200 Subject: [PATCH 1/4] nand: hamming: Replace sm_order boolean with enum Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260723-mix-ecc-v1-1-7361c3baeb07@bootlin.com> References: <20260723-mix-ecc-v1-0-7361c3baeb07@bootlin.com> In-Reply-To: <20260723-mix-ecc-v1-0-7361c3baeb07@bootlin.com> To: Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Michal Simek Cc: Thomas Petazzoni , linux-mtd@lists.infradead.org, linux-kernel@vger.kernel.org, Bastien Curutchet X-Mailer: b4 0.14.3 X-Last-TLS-Session-Version: TLSv1.3 The ECC bit ordering with Hamming code isn't standardized so we can find several kind of ordering. So far two ordering are handled: the 'regular' one and the 'smart media' one. The 'smart media' ordering is selected through the sm_order boolean. Using a boolean to choose the bit ordering prevents from supporting more than two ECC bit orderings. Create a new enum to represent the supported ECC bit ordering for Hamming code. Replace the sm_order boolean with this enum. Signed-off-by: Bastien Curutchet --- drivers/mtd/nand/ecc-sw-hamming.c | 29 ++++++++++++++++++----------- drivers/mtd/nand/raw/nand_base.c | 4 +++- include/linux/mtd/nand-ecc-sw-hamming.h | 19 +++++++++++++------ 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/drivers/mtd/nand/ecc-sw-hamming.c b/drivers/mtd/nand/ecc-sw-ha= mming.c index 460acc1029c3..d4127e30726e 100644 --- a/drivers/mtd/nand/ecc-sw-hamming.c +++ b/drivers/mtd/nand/ecc-sw-hamming.c @@ -113,7 +113,7 @@ static const char addressbits[256] =3D { }; =20 int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_s= ize, - unsigned char *code, bool sm_order) + unsigned char *code, enum ecc_hamming_order ecc_order) { const u32 *bp =3D (uint32_t *)buf; const u32 eccsize_mult =3D (step_size =3D=3D 256) ? 1 : 2; @@ -309,7 +309,8 @@ int ecc_sw_hamming_calculate(const unsigned char *buf, = unsigned int step_size, * possible, but benchmarks showed that on the system this is developed * the code below is the fastest */ - if (sm_order) { + switch (ecc_order) { + case ECC_HAMMING_SM_ORDER: code[0] =3D (invparity[rp7] << 7) | (invparity[rp6] << 6) | (invparity[rp5] << 5) | (invparity[rp4] << 4) | (invparity[rp3] << 3) | (invparity[rp2] << 2) | @@ -318,7 +319,8 @@ int ecc_sw_hamming_calculate(const unsigned char *buf, = unsigned int step_size, (invparity[rp13] << 5) | (invparity[rp12] << 4) | (invparity[rp11] << 3) | (invparity[rp10] << 2) | (invparity[rp9] << 1) | (invparity[rp8]); - } else { + break; + case ECC_HAMMING_REGULAR_ORDER: code[1] =3D (invparity[rp7] << 7) | (invparity[rp6] << 6) | (invparity[rp5] << 5) | (invparity[rp4] << 4) | (invparity[rp3] << 3) | (invparity[rp2] << 2) | @@ -327,6 +329,7 @@ int ecc_sw_hamming_calculate(const unsigned char *buf, = unsigned int step_size, (invparity[rp13] << 5) | (invparity[rp12] << 4) | (invparity[rp11] << 3) | (invparity[rp10] << 2) | (invparity[rp9] << 1) | (invparity[rp8]); + break; } =20 if (eccsize_mult =3D=3D 1) @@ -364,15 +367,16 @@ int nand_ecc_sw_hamming_calculate(struct nand_device = *nand, { struct nand_ecc_sw_hamming_conf *engine_conf =3D nand->ecc.ctx.priv; unsigned int step_size =3D nand->ecc.ctx.conf.step_size; - bool sm_order =3D engine_conf ? engine_conf->sm_order : false; + enum ecc_hamming_order order =3D engine_conf ? engine_conf->ecc_order : + ECC_HAMMING_REGULAR_ORDER; =20 - return ecc_sw_hamming_calculate(buf, step_size, code, sm_order); + return ecc_sw_hamming_calculate(buf, step_size, code, order); } EXPORT_SYMBOL(nand_ecc_sw_hamming_calculate); =20 int ecc_sw_hamming_correct(unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc, unsigned int step_size, - bool sm_order) + enum ecc_hamming_order ecc_order) { const u32 eccsize_mult =3D step_size >> 8; unsigned char b0, b1, b2, bit_addr; @@ -383,14 +387,16 @@ int ecc_sw_hamming_correct(unsigned char *buf, unsign= ed char *read_ecc, * we might need the xor result more than once, * so keep them in a local var */ - if (sm_order) { + switch (ecc_order) { + case ECC_HAMMING_SM_ORDER: b0 =3D read_ecc[0] ^ calc_ecc[0]; b1 =3D read_ecc[1] ^ calc_ecc[1]; - } else { + break; + case ECC_HAMMING_REGULAR_ORDER: b0 =3D read_ecc[1] ^ calc_ecc[1]; b1 =3D read_ecc[0] ^ calc_ecc[0]; + break; } - b2 =3D read_ecc[2] ^ calc_ecc[2]; =20 /* check if there are any bitfaults */ @@ -457,10 +463,11 @@ int nand_ecc_sw_hamming_correct(struct nand_device *n= and, unsigned char *buf, { struct nand_ecc_sw_hamming_conf *engine_conf =3D nand->ecc.ctx.priv; unsigned int step_size =3D nand->ecc.ctx.conf.step_size; - bool sm_order =3D engine_conf ? engine_conf->sm_order : false; + enum ecc_hamming_order ecc_order =3D engine_conf ? engine_conf->ecc_order= : + ECC_HAMMING_REGULAR_ORDER; =20 return ecc_sw_hamming_correct(buf, read_ecc, calc_ecc, step_size, - sm_order); + ecc_order); } EXPORT_SYMBOL(nand_ecc_sw_hamming_correct); =20 diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_b= ase.c index a5b278ab9384..f6b6a4fa2b1f 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5657,7 +5657,9 @@ int rawnand_sw_hamming_init(struct nand_chip *chip) engine_conf =3D base->ecc.ctx.priv; =20 if (chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER) - engine_conf->sm_order =3D true; + engine_conf->ecc_order =3D ECC_HAMMING_SM_ORDER; + else + engine_conf->ecc_order =3D ECC_HAMMING_REGULAR_ORDER; =20 chip->ecc.size =3D base->ecc.ctx.conf.step_size; chip->ecc.strength =3D base->ecc.ctx.conf.strength; diff --git a/include/linux/mtd/nand-ecc-sw-hamming.h b/include/linux/mtd/na= nd-ecc-sw-hamming.h index 2aa2f8ef68d2..ac6f28f35f77 100644 --- a/include/linux/mtd/nand-ecc-sw-hamming.h +++ b/include/linux/mtd/nand-ecc-sw-hamming.h @@ -12,6 +12,11 @@ =20 #include =20 +enum ecc_hamming_order { + ECC_HAMMING_REGULAR_ORDER =3D 0, + ECC_HAMMING_SM_ORDER, +}; + /** * struct nand_ecc_sw_hamming_conf - private software Hamming ECC engine s= tructure * @req_ctx: Save request context and tweak the original request to fit the @@ -19,14 +24,14 @@ * @code_size: Number of bytes needed to store a code (one code per step) * @calc_buf: Buffer to use when calculating ECC bytes * @code_buf: Buffer to use when reading (raw) ECC bytes from the chip - * @sm_order: Smart Media special ordering + * @ecc_order: ECC ordering */ struct nand_ecc_sw_hamming_conf { struct nand_ecc_req_tweak_ctx req_ctx; unsigned int code_size; u8 *calc_buf; u8 *code_buf; - unsigned int sm_order; + enum ecc_hamming_order ecc_order; }; =20 #if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING) @@ -34,13 +39,13 @@ struct nand_ecc_sw_hamming_conf { int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand); void nand_ecc_sw_hamming_cleanup_ctx(struct nand_device *nand); int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_s= ize, - unsigned char *code, bool sm_order); + unsigned char *code, enum ecc_hamming_order ecc_order); int nand_ecc_sw_hamming_calculate(struct nand_device *nand, const unsigned char *buf, unsigned char *code); int ecc_sw_hamming_correct(unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc, unsigned int step_size, - bool sm_order); + enum ecc_hamming_order ecc_order); int nand_ecc_sw_hamming_correct(struct nand_device *nand, unsigned char *b= uf, unsigned char *read_ecc, unsigned char *calc_ecc); @@ -56,7 +61,8 @@ static inline void nand_ecc_sw_hamming_cleanup_ctx(struct= nand_device *nand) {} =20 static inline int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_size, - unsigned char *code, bool sm_order) + unsigned char *code, + enum ecc_hamming_order ecc_order) { return -ENOTSUPP; } @@ -71,7 +77,8 @@ static inline int nand_ecc_sw_hamming_calculate(struct na= nd_device *nand, static inline int ecc_sw_hamming_correct(unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc, - unsigned int step_size, bool sm_order) + unsigned int step_size, + enum ecc_hamming_order ecc_order) { return -ENOTSUPP; } --=20 2.55.0 From nobody Fri Jul 24 05:21:28 2026 Received: from smtpout-03.galae.net (smtpout-03.galae.net [185.246.85.4]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9799C4D8D8D for ; Thu, 23 Jul 2026 13:41:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.85.4 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784814079; cv=none; b=qAKf01hNSP8dQ4qkGnqA+pihjt0XIIBcnHThpxZ9NHcWdnvXk+LkdDykSOyz6kmR28NaGyGbi7RBbwB+pxmqpb7E5FY3zSvS1k13p7zc6BWjT6GZQQATl8GBhUX9qN2q3ammDe+Vs5PocdmhpnHKvRn8v2xOJfqU9Ku1bnIpdnw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784814079; c=relaxed/simple; bh=NnlQEiz1ZsYv5UoM9JqZpUUt1GiPOELDO99EhlRFPvE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Pb8ZIpHoNVU9iK0BAu8YrJfCiMnxhLdtUKr//w/zW/ptzMf0WLN8jt2mQ6Sn5GEodkpkQPmlK3hDey3xEGllYIl/dGPoTBp2ZoU8LRRb3ti/PPA+Isv+LRWQw5Ap6sMs48dMvOXK9M7ruNslpL3bOv+ZgVjWzuXuc6tu5y1odQw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=em3KKgEr; arc=none smtp.client-ip=185.246.85.4 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="em3KKgEr" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-03.galae.net (Postfix) with ESMTPS id 5ADDF4E40F0E; Thu, 23 Jul 2026 13:41:08 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 2A432602B8; Thu, 23 Jul 2026 13:41:08 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 8076F11C10E94; Thu, 23 Jul 2026 15:41:06 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1784814067; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:references; bh=6C/oeJlxlhroVeY4J/H0jgbBKM8DwwJyzWPAp0mGJr4=; b=em3KKgEr2xK4JYRVZqojT8G7n5BSOEeaF8ntS/SjdejUDyrDRUvQ8jNr881WACpbWMO/is fAcGju93yA+S995sdAn/uP6d73Qa9knO8GFNi0GpFtOfdLJbDYfFS1c/QTQBYIMqVgQA5Q ZMs1RZoxWgdwTV8iQYoBRLZrxaqCWSMUzeMsRiBwG5gfjz0qEup05Tfkgyyy3Yiigamz42 E4lbBTlvnFiRG470BNUs6HFlfFr2fhK2eXpQDjDGXJmXjMeoQcmvxYXaBXIbvqWBfhXLFq NFmO96HR+dOhYi2jsU6xyE4qt/cXTQltl78/eWEtsHCxLeyyxPYoiMQ3LjHxug== From: Bastien Curutchet Date: Thu, 23 Jul 2026 15:41:02 +0200 Subject: [PATCH 2/4] nand: hamming: Add support for the PL35x ECC bit ordering Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260723-mix-ecc-v1-2-7361c3baeb07@bootlin.com> References: <20260723-mix-ecc-v1-0-7361c3baeb07@bootlin.com> In-Reply-To: <20260723-mix-ecc-v1-0-7361c3baeb07@bootlin.com> To: Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Michal Simek Cc: Thomas Petazzoni , linux-mtd@lists.infradead.org, linux-kernel@vger.kernel.org, Bastien Curutchet X-Mailer: b4 0.14.3 X-Last-TLS-Session-Version: TLSv1.3 The PL353's HW-ECC engine is broken and fails to report some errors on reading (cf errata 721059 in [1]). So, to still benefit a bit from the HW acceleration, a mixed approach has to be used: where writes are performed by the HW-ECC engine and reads by software. To do so, the software part has to use the same bit ordering than the hardware engine. Add support for the PL35x bit ordering in the generic hamming helpers. Create a new option flag to enable this bit ordering. For consistency this new flag is put next to the SM_ORDER flag which shifts the CACHEPRG one bit away. [1] : https://developer.arm.com/documentation/rlnc000227/a Signed-off-by: Bastien Curutchet --- drivers/mtd/nand/ecc-sw-hamming.c | 150 ++++++++++++++++++++++------= ---- drivers/mtd/nand/raw/nand_base.c | 2 + include/linux/mtd/nand-ecc-sw-hamming.h | 1 + include/linux/mtd/rawnand.h | 5 +- 4 files changed, 111 insertions(+), 47 deletions(-) diff --git a/drivers/mtd/nand/ecc-sw-hamming.c b/drivers/mtd/nand/ecc-sw-ha= mming.c index d4127e30726e..40f453299c7a 100644 --- a/drivers/mtd/nand/ecc-sw-hamming.c +++ b/drivers/mtd/nand/ecc-sw-hamming.c @@ -320,6 +320,24 @@ int ecc_sw_hamming_calculate(const unsigned char *buf,= unsigned int step_size, (invparity[rp11] << 3) | (invparity[rp10] << 2) | (invparity[rp9] << 1) | (invparity[rp8]); break; + case ECC_HAMMING_PL35X_ORDER: + code[1] =3D (invparity[rp0] << 7) | + (invparity[par & 0x0f] << 6) | + (invparity[par & 0x33] << 5) | + (invparity[par & 0x55] << 4) | + (invparity[rp17] << 3) | + (invparity[rp15] << 2) | + (invparity[rp13] << 1) | + (invparity[rp11] << 0); + code[0] =3D (invparity[rp9] << 7) | + (invparity[rp7] << 6) | + (invparity[rp5] << 5) | + (invparity[rp3] << 4) | + (invparity[rp1] << 3) | + (invparity[par & 0xf0] << 2) | + (invparity[par & 0xcc] << 1) | + (invparity[par & 0xaa] << 0); + break; case ECC_HAMMING_REGULAR_ORDER: code[1] =3D (invparity[rp7] << 7) | (invparity[rp6] << 6) | (invparity[rp5] << 5) | (invparity[rp4] << 4) | @@ -332,25 +350,38 @@ int ecc_sw_hamming_calculate(const unsigned char *buf= , unsigned int step_size, break; } =20 - if (eccsize_mult =3D=3D 1) - code[2] =3D - (invparity[par & 0xf0] << 7) | - (invparity[par & 0x0f] << 6) | - (invparity[par & 0xcc] << 5) | - (invparity[par & 0x33] << 4) | - (invparity[par & 0xaa] << 3) | - (invparity[par & 0x55] << 2) | - 3; - else - code[2] =3D - (invparity[par & 0xf0] << 7) | - (invparity[par & 0x0f] << 6) | - (invparity[par & 0xcc] << 5) | - (invparity[par & 0x33] << 4) | - (invparity[par & 0xaa] << 3) | - (invparity[par & 0x55] << 2) | - (invparity[rp17] << 1) | - (invparity[rp16] << 0); + switch (ecc_order) { + case ECC_HAMMING_PL35X_ORDER: + code[2] =3D (invparity[rp16] << 7) | + (invparity[rp14] << 6) | + (invparity[rp12] << 5) | + (invparity[rp10] << 4) | + (invparity[rp8] << 3) | + (invparity[rp6] << 2) | + (invparity[rp4] << 1) | + (invparity[rp2] << 0); + break; + case ECC_HAMMING_REGULAR_ORDER: + case ECC_HAMMING_SM_ORDER: + if (eccsize_mult =3D=3D 1) + code[2] =3D (invparity[par & 0xf0] << 7) | + (invparity[par & 0x0f] << 6) | + (invparity[par & 0xcc] << 5) | + (invparity[par & 0x33] << 4) | + (invparity[par & 0xaa] << 3) | + (invparity[par & 0x55] << 2) | + 3; + else + code[2] =3D (invparity[par & 0xf0] << 7) | + (invparity[par & 0x0f] << 6) | + (invparity[par & 0xcc] << 5) | + (invparity[par & 0x33] << 4) | + (invparity[par & 0xaa] << 3) | + (invparity[par & 0x55] << 2) | + (invparity[rp17] << 1) | + (invparity[rp16] << 0); + break; + } =20 return 0; } @@ -380,6 +411,7 @@ int ecc_sw_hamming_correct(unsigned char *buf, unsigned= char *read_ecc, { const u32 eccsize_mult =3D step_size >> 8; unsigned char b0, b1, b2, bit_addr; + bool single_bit_err =3D false; unsigned int byte_addr; =20 /* @@ -388,6 +420,7 @@ int ecc_sw_hamming_correct(unsigned char *buf, unsigned= char *read_ecc, * so keep them in a local var */ switch (ecc_order) { + case ECC_HAMMING_PL35X_ORDER: case ECC_HAMMING_SM_ORDER: b0 =3D read_ecc[0] ^ calc_ecc[0]; b1 =3D read_ecc[1] ^ calc_ecc[1]; @@ -407,33 +440,60 @@ int ecc_sw_hamming_correct(unsigned char *buf, unsign= ed char *read_ecc, if ((b0 | b1 | b2) =3D=3D 0) return 0; /* no error */ =20 - if ((((b0 ^ (b0 >> 1)) & 0x55) =3D=3D 0x55) && - (((b1 ^ (b1 >> 1)) & 0x55) =3D=3D 0x55) && - ((eccsize_mult =3D=3D 1 && ((b2 ^ (b2 >> 1)) & 0x54) =3D=3D 0x54) || - (eccsize_mult =3D=3D 2 && ((b2 ^ (b2 >> 1)) & 0x55) =3D=3D 0x55))) { /* single bit error */ - /* - * rp17/rp15/13/11/9/7/5/3/1 indicate which byte is the faulty - * byte, cp 5/3/1 indicate the faulty bit. - * A lookup table (called addressbits) is used to filter - * the bits from the byte they are in. - * A marginal optimisation is possible by having three - * different lookup tables. - * One as we have now (for b0), one for b2 - * (that would avoid the >> 1), and one for b1 (with all values - * << 4). However it was felt that introducing two more tables - * hardly justify the gain. - * - * The b2 shift is there to get rid of the lowest two bits. - * We could also do addressbits[b2] >> 1 but for the - * performance it does not make any difference - */ - if (eccsize_mult =3D=3D 1) - byte_addr =3D (addressbits[b1] << 4) + addressbits[b0]; - else - byte_addr =3D (addressbits[b2 & 0x3] << 8) + - (addressbits[b1] << 4) + addressbits[b0]; - bit_addr =3D addressbits[b2 >> 2]; + /* + * rp17/rp15/13/11/9/7/5/3/1 indicate which byte is the faulty + * byte, cp 5/3/1 indicate the faulty bit. + */ + switch (ecc_order) { + case ECC_HAMMING_SM_ORDER: + case ECC_HAMMING_REGULAR_ORDER: + if ((((b0 ^ (b0 >> 1)) & 0x55) =3D=3D 0x55) && + (((b1 ^ (b1 >> 1)) & 0x55) =3D=3D 0x55) && + ((eccsize_mult =3D=3D 1 && ((b2 ^ (b2 >> 1)) & 0x54) =3D=3D 0x54) || + (eccsize_mult =3D=3D 2 && ((b2 ^ (b2 >> 1)) & 0x55) =3D=3D 0x55))) { + single_bit_err =3D true; + /* + * A lookup table (called addressbits) is used to filter + * the bits from the byte they are in. + * A marginal optimisation is possible by having three + * different lookup tables. + * One as we have now (for b0), one for b2 + * (that would avoid the >> 1), and one for b1 (with all values + * << 4). However it was felt that introducing two more tables + * hardly justify the gain. + * + * The b2 shift is there to get rid of the lowest two bits. + * We could also do addressbits[b2] >> 1 but for the + * performance it does not make any difference + */ + if (eccsize_mult =3D=3D 1) + byte_addr =3D (addressbits[b1] << 4) + addressbits[b0]; + else + byte_addr =3D (addressbits[b2 & 0x3] << 8) + + (addressbits[b1] << 4) + addressbits[b0]; + bit_addr =3D addressbits[b2 >> 2]; + } + break; + case ECC_HAMMING_PL35X_ORDER: + if (((((b0 >> 4) ^ b2) & 0xF) =3D=3D 0xF) && + ((((b1 >> 4) ^ b0) & 0xF) =3D=3D 0xF) && + ((((b2 >> 4) ^ b1) & 0xF) =3D=3D 0xF)) { + single_bit_err =3D true; + /* + * We can't use the addressbits table here because the + * bit ordering in the ECC bytes has nothing to do with + * the other ordering + */ + byte_addr =3D (b0 >> 3) & 0xF; + byte_addr |=3D (b0 >> 7) & 0x10; + byte_addr |=3D (b1 << 5) & 0x1e0; + bit_addr =3D b0 & 0x7; + } + break; + } + + if (single_bit_err) { /* flip the bit */ buf[byte_addr] ^=3D (1 << bit_addr); return 1; diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_b= ase.c index f6b6a4fa2b1f..c04c3e74f661 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5658,6 +5658,8 @@ int rawnand_sw_hamming_init(struct nand_chip *chip) =20 if (chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER) engine_conf->ecc_order =3D ECC_HAMMING_SM_ORDER; + else if (chip->ecc.options & NAND_ECC_SOFT_HAMMING_PL35X_ORDER) + engine_conf->ecc_order =3D ECC_HAMMING_PL35X_ORDER; else engine_conf->ecc_order =3D ECC_HAMMING_REGULAR_ORDER; =20 diff --git a/include/linux/mtd/nand-ecc-sw-hamming.h b/include/linux/mtd/na= nd-ecc-sw-hamming.h index ac6f28f35f77..4a0e08dc7f04 100644 --- a/include/linux/mtd/nand-ecc-sw-hamming.h +++ b/include/linux/mtd/nand-ecc-sw-hamming.h @@ -15,6 +15,7 @@ enum ecc_hamming_order { ECC_HAMMING_REGULAR_ORDER =3D 0, ECC_HAMMING_SM_ORDER, + ECC_HAMMING_PL35X_ORDER, }; =20 /** diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 5c70e7bd3ed5..3658315752bf 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -113,10 +113,11 @@ struct gpio_desc; * When using software implementation of Hamming, we can specify which byte * ordering should be used. */ -#define NAND_ECC_SOFT_HAMMING_SM_ORDER BIT(2) +#define NAND_ECC_SOFT_HAMMING_SM_ORDER BIT(2) +#define NAND_ECC_SOFT_HAMMING_PL35X_ORDER BIT(3) =20 /* Chip has cache program function */ -#define NAND_CACHEPRG BIT(3) +#define NAND_CACHEPRG BIT(4) /* Options valid for Samsung large page devices */ #define NAND_SAMSUNG_LP_OPTIONS NAND_CACHEPRG =20 --=20 2.55.0 From nobody Fri Jul 24 05:21:28 2026 Received: from smtpout-03.galae.net (smtpout-03.galae.net [185.246.85.4]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6EE374CA286 for ; Thu, 23 Jul 2026 13:41:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.85.4 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784814083; cv=none; b=sTqotYjuPfJ1J4GDMX66jSFhwLlLFaPs3h0eU18zVUlP2vzvzEqaIPjTrRhLO4fqpzD6PcCbu9hpIfR3n5rKdSwWs4HCnMQUP3Eaf+Dhn1SJFu1mgOgppNUEM6y4qZpEykD/otE1egmmedi4S+SJozODRH8Mw03Gx5Xjd3QDTxg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784814083; c=relaxed/simple; bh=6Hz6Ib7becZ9NbcVgVjfycKGK/IUtNojfGwQSPs54cs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=absM5gIjtW9NkyAZUOonL655czJiGJpPASX0/BJ1higaP/+ue4zqcasQcenW2Rg4BguXzm7N1dEexAESl85FSfqL7NZhX/FvNcOfK8daYTK4C5JyjtxOqZx+pOodMt7EoOgfvbSPCgi01JeAMYRe5qgELF+kEzrj+eq7GmmkKSw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=Oan5aCAz; arc=none smtp.client-ip=185.246.85.4 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="Oan5aCAz" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-03.galae.net (Postfix) with ESMTPS id B9C624E40EBE; Thu, 23 Jul 2026 13:41:10 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 8ED96602B8; Thu, 23 Jul 2026 13:41:10 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id C68F511C10E96; Thu, 23 Jul 2026 15:41:07 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1784814069; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:references; bh=XWe+Pt54+26aQCE30XaKKDodsWuXQQjhkt3Geccg33s=; b=Oan5aCAzlTDX987vYi/UkPlQpdAHJQimS4Kk1sKKU82Dg6o474a9qGN/ab/2qDP9VQ/rs4 bXumAwdjL4V8sSGHRqsS6s2GOW6lDWnQ8j+uKPr0E0481iS8GczCvvnQd2fNGi/hKCDsp8 Dvo9q8w+0exCvFi9J3+7pdZQR1CW9Vp1mJTVIIf3fcZiom7UixAB94D2jB1jzK7TK8T+SM hQSqJe4dBEC7A5ZbjFkfT08MymdV1NlS92WwwMVE+3BQVgg58xjBUvuG+WZT7NCaN3Zehq Mt5stZE9d+f8oWNJ0ji3+UVvAwFm2ndxSB+J5axLcz0AumXGnWSVJCREAMQ/8Q== From: Bastien Curutchet Date: Thu, 23 Jul 2026 15:41:03 +0200 Subject: [PATCH 3/4] rawnand: base: Export nand_read_page_swecc Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260723-mix-ecc-v1-3-7361c3baeb07@bootlin.com> References: <20260723-mix-ecc-v1-0-7361c3baeb07@bootlin.com> In-Reply-To: <20260723-mix-ecc-v1-0-7361c3baeb07@bootlin.com> To: Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Michal Simek Cc: Thomas Petazzoni , linux-mtd@lists.infradead.org, linux-kernel@vger.kernel.org, Bastien Curutchet X-Mailer: b4 0.14.3 X-Last-TLS-Session-Version: TLSv1.3 nand_read_page_swecc() isn't exported while it could be used from the drivers for the mixed case where ECC writes are done by the hardware and ECC reads are done by software. Export nand_read_page_swecc() to make it usable by drivers. Signed-off-by: Bastien Curutchet --- drivers/mtd/nand/raw/nand_base.c | 5 +++-- drivers/mtd/nand/raw/pl35x-nand-controller.c | 1 + include/linux/mtd/rawnand.h | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_b= ase.c index c04c3e74f661..950c079aa27b 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -2941,8 +2941,8 @@ static int nand_read_page_raw_syndrome(struct nand_ch= ip *chip, uint8_t *buf, * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read */ -static int nand_read_page_swecc(struct nand_chip *chip, uint8_t *buf, - int oob_required, int page) +int nand_read_page_swecc(struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) { struct mtd_info *mtd =3D nand_to_mtd(chip); int i, eccsize =3D chip->ecc.size, ret; @@ -2979,6 +2979,7 @@ static int nand_read_page_swecc(struct nand_chip *chi= p, uint8_t *buf, } return max_bitflips; } +EXPORT_SYMBOL_GPL(nand_read_page_swecc); =20 /** * nand_read_subpage - [REPLACEABLE] ECC based sub-page read function diff --git a/drivers/mtd/nand/raw/pl35x-nand-controller.c b/drivers/mtd/nan= d/raw/pl35x-nand-controller.c index bd89aaadd1b2..d61eba938311 100644 --- a/drivers/mtd/nand/raw/pl35x-nand-controller.c +++ b/drivers/mtd/nand/raw/pl35x-nand-controller.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 3658315752bf..5539e96b2cac 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1578,6 +1578,8 @@ int nand_write_data_op(struct nand_chip *chip, const = void *buf, unsigned int len, bool force_8bit); int nand_read_page_hwecc_oob_first(struct nand_chip *chip, uint8_t *buf, int oob_required, int page); +int nand_read_page_swecc(struct nand_chip *chip, uint8_t *buf, + int oob_required, int page); =20 /* Scan and identify a NAND device */ int nand_scan_with_ids(struct nand_chip *chip, unsigned int max_chips, --=20 2.55.0 From nobody Fri Jul 24 05:21:28 2026 Received: from smtpout-02.galae.net (smtpout-02.galae.net [185.246.84.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5FE5F4DC531; Thu, 23 Jul 2026 13:41:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.84.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784814081; cv=none; b=kdrKUhysw95ddFteBbYQw2sDwrY62GMehzioB6Qa9f/rni7bmBescmtbWMmqNoxP6J9QlDBFh/btQ0ciIPN+UZwaixxRsdZv1t+sCG4NbNV7vvBTWVPI0n9ivhb3trGtpENg/voy0yg+QlD6oIjhOO5NsDsdlfZy7foDzoWMuYE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784814081; c=relaxed/simple; bh=+ZpNRgtNyM8AlOTByDEba4TlFxSsdjZtHp5TUaFZTw8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=EbM5qVaJL4xbLD29lP/L6ooyCTuv2H2YC1FXo6teqPwHSFIjGH/3WLVZ9Upzg9OfcdqcXaf30Vh1d/3kUk+ySmhHcgH9Ofx2CzvbuZdcCVZ/bTO5nL/iF0AwJvFhXJ2egf4OztdbPMsrebB2f5bJv0Zw/ne1VFHYbWJzHFSU9JA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=m9E44m3K; arc=none smtp.client-ip=185.246.84.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="m9E44m3K" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-02.galae.net (Postfix) with ESMTPS id 1CA6E1A11AE; Thu, 23 Jul 2026 13:41:12 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id E5C9A602B8; Thu, 23 Jul 2026 13:41:11 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 0EDB811C10E94; Thu, 23 Jul 2026 15:41:09 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1784814071; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:references; bh=1eHXfQkOUwlUyzpIRf3Q6Ks/P8/PZJUj2954tWi5/2I=; b=m9E44m3K1WKZJotwZr0wBKIqS/t+w7V9CNZtQaXteLc3HjC+DyrH5LAkOsVcCMjiAwFXEr NN814MD0qXDkXQ6rnT2RUWPf3sEIzOuOz/DjjNEqnkHfNc/+ZdOpoNMnfrc7VvOWWvctEq QDJS9H2ThOII8fDCVvnfUIHTrANHawm7sD9AUzsDpn4J+7r8O/cyvmY+/lDyxQu4Xpky5l 3ElRhCQfRo9kcD7Dt6eLPb0oorOUMNTz3mNF5oqAkQuSVbToguOnvLKVgxTgcUMWRK0qeT 2c8lxgz64yvxixVEHD6ZaG3FXmZ2pVQOxvQsH19whTS8biPtX5vglBirz2aU/Q== From: Bastien Curutchet Date: Thu, 23 Jul 2026 15:41:04 +0200 Subject: [PATCH 4/4] rawnand: pl35x: Implement mixed ECC computing Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260723-mix-ecc-v1-4-7361c3baeb07@bootlin.com> References: <20260723-mix-ecc-v1-0-7361c3baeb07@bootlin.com> In-Reply-To: <20260723-mix-ecc-v1-0-7361c3baeb07@bootlin.com> To: Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Michal Simek Cc: Thomas Petazzoni , linux-mtd@lists.infradead.org, linux-kernel@vger.kernel.org, Bastien Curutchet , stable@vger.kernel.org X-Mailer: b4 0.14.3 X-Last-TLS-Session-Version: TLSv1.3 The PL353's HW-ECC engine is broken and fails to report some errors on reading (cf errata 721059 in [1]) so hardware reads can't be trusted. Implement a mixed approach to do ECC writes by hardware and ECC reads by software. It allows to benefit from the HW acceleration on writes and to fix the read issue. Use the hamming helpers provided by the core with the PL35x flag on to do the soft computation. Remove the no longer used read_hwecc(). [1] https://developer.arm.com/documentation/rlnc000227/a Cc: stable@vger.kernel.org Fixes: 08d8c62164a3 ("mtd: rawnand: pl353: Add support for the ARM PL353 SM= C NAND controller") Signed-off-by: Bastien Curutchet --- drivers/mtd/nand/raw/pl35x-nand-controller.c | 172 ++---------------------= ---- 1 file changed, 10 insertions(+), 162 deletions(-) diff --git a/drivers/mtd/nand/raw/pl35x-nand-controller.c b/drivers/mtd/nan= d/raw/pl35x-nand-controller.c index d61eba938311..78ddc5e8f311 100644 --- a/drivers/mtd/nand/raw/pl35x-nand-controller.c +++ b/drivers/mtd/nand/raw/pl35x-nand-controller.c @@ -392,50 +392,6 @@ static void pl35x_nand_write_data_op(struct nand_chip = *chip, const u8 *out, pl35x_smc_force_byte_access(chip, false); } =20 -static int pl35x_nand_correct_data(struct pl35x_nandc *nfc, unsigned char = *buf, - unsigned char *read_ecc, - unsigned char *calc_ecc) -{ - unsigned short ecc_odd, ecc_even, read_ecc_lower, read_ecc_upper; - unsigned short calc_ecc_lower, calc_ecc_upper; - unsigned short byte_addr, bit_addr; - - read_ecc_lower =3D (read_ecc[0] | (read_ecc[1] << 8)) & - PL35X_NAND_ECC_BITS_MASK; - read_ecc_upper =3D ((read_ecc[1] >> 4) | (read_ecc[2] << 4)) & - PL35X_NAND_ECC_BITS_MASK; - - calc_ecc_lower =3D (calc_ecc[0] | (calc_ecc[1] << 8)) & - PL35X_NAND_ECC_BITS_MASK; - calc_ecc_upper =3D ((calc_ecc[1] >> 4) | (calc_ecc[2] << 4)) & - PL35X_NAND_ECC_BITS_MASK; - - ecc_odd =3D read_ecc_lower ^ calc_ecc_lower; - ecc_even =3D read_ecc_upper ^ calc_ecc_upper; - - /* No error */ - if (likely(!ecc_odd && !ecc_even)) - return 0; - - /* One error in the main data; to be corrected */ - if (ecc_odd =3D=3D (~ecc_even & PL35X_NAND_ECC_BITS_MASK)) { - /* Bits [11:3] of error code give the byte offset */ - byte_addr =3D (ecc_odd >> 3) & PL35X_NAND_ECC_BYTE_OFF_MASK; - /* Bits [2:0] of error code give the bit offset */ - bit_addr =3D ecc_odd & PL35X_NAND_ECC_BIT_OFF_MASK; - /* Toggle the faulty bit */ - buf[byte_addr] ^=3D (BIT(bit_addr)); - - return 1; - } - - /* One error in the ECC data; no action needed */ - if (hweight32(ecc_odd | ecc_even) =3D=3D 1) - return 1; - - return -EBADMSG; -} - static void pl35x_nand_ecc_reg_to_array(struct nand_chip *chip, u32 ecc_re= g, u8 *ecc_array) { @@ -464,42 +420,6 @@ static int pl35x_nand_read_eccbytes(struct pl35x_nandc= *nfc, return 0; } =20 -static int pl35x_nand_recover_data_hwecc(struct pl35x_nandc *nfc, - struct nand_chip *chip, u8 *data, - u8 *read_ecc) -{ - struct mtd_info *mtd =3D nand_to_mtd(chip); - unsigned int max_bitflips =3D 0, chunk; - u8 calc_ecc[3]; - u32 ecc_value; - int stats; - - for (chunk =3D 0; chunk < chip->ecc.steps; - chunk++, data +=3D chip->ecc.size, read_ecc +=3D chip->ecc.bytes) { - /* Read ECC value for each chunk */ - ecc_value =3D readl(nfc->conf_regs + PL35X_SMC_ECC_VALUE(chunk)); - - if (!PL35X_SMC_ECC_VALUE_IS_VALID(ecc_value)) - return -EINVAL; - - if (PL35X_SMC_ECC_VALUE_HAS_FAILED(ecc_value)) { - mtd->ecc_stats.failed++; - continue; - } - - pl35x_nand_ecc_reg_to_array(chip, ecc_value, calc_ecc); - stats =3D pl35x_nand_correct_data(nfc, data, read_ecc, calc_ecc); - if (stats < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected +=3D stats; - max_bitflips =3D max_t(unsigned int, max_bitflips, stats); - } - } - - return max_bitflips; -} - static int pl35x_nand_write_page_hwecc(struct nand_chip *chip, const u8 *buf, int oob_required, int page) @@ -579,87 +499,6 @@ static int pl35x_nand_write_page_hwecc(struct nand_chi= p *chip, return ret; } =20 -/* - * This functions reads data and checks the data integrity by comparing ha= rdware - * generated ECC values and read ECC values from spare area. - * - * There is a limitation with SMC controller: ECC_LAST must be set on the - * last data access to tell the ECC engine not to expect any further data. - * In practice, this implies to shrink the last data transfert by eg. 4 by= tes, - * and doing a last 4-byte transfer with the additional bit set. The last = block - * should be aligned with the end of an ECC block. Because of this limitat= ion, - * it is not possible to use the core routines. - */ -static int pl35x_nand_read_page_hwecc(struct nand_chip *chip, - u8 *buf, int oob_required, int page) -{ - const struct nand_sdr_timings *sdr =3D - nand_get_sdr_timings(nand_get_interface_config(chip)); - struct pl35x_nandc *nfc =3D to_pl35x_nandc(chip->controller); - struct pl35x_nand *plnand =3D to_pl35x_nand(chip); - struct mtd_info *mtd =3D nand_to_mtd(chip); - unsigned int first_row =3D (mtd->writesize <=3D 512) ? 1 : 2; - unsigned int nrows =3D plnand->addr_cycles; - unsigned int addr1 =3D 0, addr2 =3D 0, row; - u32 cmd_addr; - int i, ret; - - ret =3D pl35x_smc_set_ecc_mode(nfc, chip, PL35X_SMC_ECC_CFG_MODE_APB); - if (ret) - return ret; - - cmd_addr =3D PL35X_SMC_CMD_PHASE | - PL35X_SMC_CMD_PHASE_NADDRS(plnand->addr_cycles) | - PL35X_SMC_CMD_PHASE_CMD0(NAND_CMD_READ0) | - PL35X_SMC_CMD_PHASE_CMD1(NAND_CMD_READSTART) | - PL35X_SMC_CMD_PHASE_CMD1_VALID; - - for (i =3D 0, row =3D first_row; row < nrows; i++, row++) { - u8 addr =3D page >> ((i * 8) & 0xFF); - - if (row < 4) - addr1 |=3D PL35X_SMC_CMD_PHASE_ADDR(row, addr); - else - addr2 |=3D PL35X_SMC_CMD_PHASE_ADDR(row - 4, addr); - } - - /* Send the command and address cycles */ - writel(addr1, nfc->io_regs + cmd_addr); - if (plnand->addr_cycles > 4) - writel(addr2, nfc->io_regs + cmd_addr); - - /* Wait the data to be available in the NAND cache */ - ndelay(PSEC_TO_NSEC(sdr->tRR_min)); - ret =3D pl35x_smc_wait_for_irq(nfc); - if (ret) - goto disable_ecc_engine; - - /* Retrieve the raw data with the engine enabled */ - pl35x_nand_read_data_op(chip, buf, mtd->writesize, false, - 0, PL35X_SMC_DATA_PHASE_ECC_LAST); - ret =3D pl35x_smc_wait_for_ecc_done(nfc); - if (ret) - goto disable_ecc_engine; - - /* Retrieve the stored ECC bytes */ - pl35x_nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, false, - 0, PL35X_SMC_DATA_PHASE_CLEAR_CS); - ret =3D mtd_ooblayout_get_eccbytes(mtd, nfc->ecc_buf, chip->oob_poi, 0, - chip->ecc.total); - if (ret) - goto disable_ecc_engine; - - pl35x_smc_set_ecc_mode(nfc, chip, PL35X_SMC_ECC_CFG_MODE_BYPASS); - - /* Correct the data and report failures */ - return pl35x_nand_recover_data_hwecc(nfc, chip, buf, nfc->ecc_buf); - -disable_ecc_engine: - pl35x_smc_set_ecc_mode(nfc, chip, PL35X_SMC_ECC_CFG_MODE_BYPASS); - - return ret; -} - static int pl35x_nand_exec_op(struct nand_chip *chip, const struct nand_subop *subop) { @@ -916,7 +755,7 @@ static int pl35x_nand_init_hw_ecc_controller(struct pl3= 5x_nandc *nfc, chip->ecc.bytes =3D 3; chip->ecc.size =3D SZ_512; chip->ecc.steps =3D mtd->writesize / chip->ecc.size; - chip->ecc.read_page =3D pl35x_nand_read_page_hwecc; + chip->ecc.read_page =3D nand_read_page_swecc; chip->ecc.write_page =3D pl35x_nand_write_page_hwecc; pl35x_smc_set_ecc_pg_size(nfc, chip, mtd->writesize); =20 @@ -939,6 +778,15 @@ static int pl35x_nand_init_hw_ecc_controller(struct pl= 35x_nandc *nfc, return -EOPNOTSUPP; } =20 + chip->ecc.options |=3D NAND_ECC_SOFT_HAMMING_PL35X_ORDER; + ret =3D rawnand_sw_hamming_init(chip); + if (ret) { + WARN(1, "Hamming ECC initialization failed!\n"); + return ret; + } + chip->ecc.calculate =3D rawnand_sw_hamming_calculate; + chip->ecc.correct =3D rawnand_sw_hamming_correct; + return ret; } =20 --=20 2.55.0