From nobody Sun Feb 8 07:35:00 2026 Received: from mailgw01.mediatek.com (unknown [60.244.123.138]) (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 1061DB674 for ; Sun, 20 Oct 2024 13:28:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=60.244.123.138 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1729430893; cv=none; b=YkVog2yj7sIPw1C/hN15qppP+igshK4U/qb6E4+RqpW0twh4yBUtj+Uc8rxOg3VCPCQGR9rHtIKdipzpEbZQZ84qrUkSM34qGoioWHoeJ1vaPJ25qoaznN/h2QYlGbcy+kr7IDlWoMDReR+cg5pOBuN6fWMYj7pw6Cq6TVefOeQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1729430893; c=relaxed/simple; bh=9JGdTDmPTWfzXXj/tVrnvu/B96nNas6yO6AjySaOV3Q=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=ZOH50fIF6FjGAU/p3WSKXqQxFl8qWmpjQHw8IIODOyUGDtX8DHf3cUthfBAux1mgZhmalfXhUftynRF139y3kJswZUxsMoAcKkxbMh7T9hCsQBqb1kWaQl66TO3RmxzoPFrOLTVaqcEWvgXWIFVX2ynadX4zNTYiwotGcDsSnAk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com; spf=pass smtp.mailfrom=mediatek.com; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b=RHerWdWA; arc=none smtp.client-ip=60.244.123.138 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mediatek.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b="RHerWdWA" X-UUID: 23d856aa8ee711efb88477ffae1fc7a5-20241020 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Type:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From; bh=4GWSP202kE1ugZwHMnGXy7L1M5++8SvxO/XWigtfn0E=; b=RHerWdWA9uXm4hrWuoj4mZUIJoqV+ZgLLiIUbmXXIKQ5kDUxBKu2t6XlQG09vuCpLVdVhDxNjE6IWNB+NwHMJF+O/ZNk2kBjqxdrxCsBCSOWEtR9wDP8gan4BcPL/D1Ge/JwcbFUAlFnTTTcCO+iMjRXCh9pzye5Mu4ylOOrorw=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.1.42,REQID:6ab98f7b-e5a3-41f4-87a9-718371903293,IP:0,U RL:0,TC:0,Content:-25,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTIO N:release,TS:-25 X-CID-META: VersionHash:b0fcdc3,CLOUDID:8e498941-8751-41b2-98dd-475503d45150,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:102,TC:nil,Content:0,EDM:-3,IP:nil,U RL:0,File:nil,RT:nil,Bulk:nil,QS:nil,BEC:nil,COL:0,OSI:0,OSA:0,AV:0,LES:1, SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0,ARC:0 X-CID-BVR: 0 X-CID-BAS: 0,_,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR X-UUID: 23d856aa8ee711efb88477ffae1fc7a5-20241020 Received: from mtkmbs10n2.mediatek.inc [(172.21.101.183)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 105369874; Sun, 20 Oct 2024 21:28:06 +0800 Received: from mtkmbs13n1.mediatek.inc (172.21.101.193) by mtkmbs10n1.mediatek.inc (172.21.101.34) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.26; Sun, 20 Oct 2024 21:28:04 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkmbs13n1.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.1118.26 via Frontend Transport; Sun, 20 Oct 2024 21:28:04 +0800 From: Sky Huang To: Matthias Brugger , Miquel Raynal , AngeloGioacchino Del Regno , Richard Weinberger , Vignesh Raghavendra , Daniel Golle , Chia-Lin Kao , "Mika Westerberg" , Cheng Ming Lin , , , , CC: Steven Liu , Sky.Huang Subject: [RFC PATCH nand/next 1/4] mtd: nand: Create param.c to do CRC check and bitwise majority for Parameter & CASN page Date: Sun, 20 Oct 2024 21:27:19 +0800 Message-ID: <20241020132722.20565-2-SkyLake.Huang@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20241020132722.20565-1-SkyLake.Huang@mediatek.com> References: <20241020132722.20565-1-SkyLake.Huang@mediatek.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-TM-AS-Product-Ver: SMEX-14.0.0.3152-9.1.1006-23728.005 X-TM-AS-Result: No-10--11.493500-8.000000 X-TMASE-MatchedRID: dY/STlX7LWI0Z9sXcK7F6V3oTRqVXDv5sXPI9D4Uri5aW2Ktn+I8/v5Q PMcorVJC8AyWk2NFMNZM8qdoCvOVvj13WcdbGR6Qtw+xHnsmQjPDHSNFHFxB801KG1YrOQW/Oqn cbTLK4XiFuNbf28fb6PCUhGX/S6sVNngTMoCUVRTkKCFOKwAEzO3+iQEtoSj401IIGqgAbw3Z6K rkcB2NwXa9QqC2Cxib3BTxUdcaKkIfE8yM4pjsD23U7j2vVURrxEHRux+uk8hfNjF5BHUO+5hdg toyVDGsvXehZr+2YIXDWemDa0iNuN+SRkUpHtt1peNVPeL5BOo= X-TM-AS-User-Approved-Sender: No X-TM-AS-User-Blocked-Sender: No X-TMASE-Result: 10--11.493500-8.000000 X-TMASE-Version: SMEX-14.0.0.3152-9.1.1006-23728.005 X-TM-SNTS-SMTP: 893E3C62F29F17B06B98F8A0F5729048C8707D38DE12E4F30D58DAF9AF2EB93C2000:8 X-MTK: N Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: "Sky.Huang" Create drivers/mtd/nand/param.c so ONFI parameter page & CASN page can both use nanddev_crc16() and nanddev_bit_wise_majority() directly like this: * For ONFI Parameter page: onfi_crc16() -> nanddev_crc16() * For CASN page: nanddev_crc16() nanddev_bit_wise_majority() is same as nand_bit_wise_majority(). nanddev_crc16() is same as onfi_crc16(). But there are lots of onfi_crc16() call, so keep onfi_crc16() there and hook it to nanddev_crc16(). Signed-off-by: Sky Huang --- drivers/mtd/nand/Makefile | 2 +- drivers/mtd/nand/param.c | 52 ++++++++++++++++++++++++++++++++ drivers/mtd/nand/raw/nand_onfi.c | 43 ++------------------------ include/linux/mtd/param.h | 20 ++++++++++++ 4 files changed, 76 insertions(+), 41 deletions(-) create mode 100644 drivers/mtd/nand/param.c create mode 100644 include/linux/mtd/param.h diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 19e1291ac4d5..790bde0148d1 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 =20 -nandcore-objs :=3D core.o bbt.o +nandcore-objs :=3D core.o bbt.o param.o obj-$(CONFIG_MTD_NAND_CORE) +=3D nandcore.o obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) +=3D ecc-mtk.o =20 diff --git a/drivers/mtd/nand/param.c b/drivers/mtd/nand/param.c new file mode 100644 index 000000000000..f67b9fe633d9 --- /dev/null +++ b/drivers/mtd/nand/param.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023 - Mediatek + * + * Author: Sky Huang + */ + +#include + +u16 nanddev_crc16(u16 crc, u8 const *p, size_t len) +{ + int i; + + while (len--) { + crc ^=3D *p++ << 8; + for (i =3D 0; i < 8; i++) + crc =3D (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0); + } + + return crc; +} + +/* + * Recover data with bit-wise majority + */ +void nanddev_bit_wise_majority(const void **srcbufs, + unsigned int nsrcbufs, + void *dstbuf, + unsigned int bufsize) +{ + int i, j, k; + + for (i =3D 0; i < bufsize; i++) { + u8 val =3D 0; + + for (j =3D 0; j < 8; j++) { + unsigned int cnt =3D 0; + + for (k =3D 0; k < nsrcbufs; k++) { + const u8 *srcbuf =3D srcbufs[k]; + + if (srcbuf[i] & BIT(j)) + cnt++; + } + + if (cnt > nsrcbufs / 2) + val |=3D BIT(j); + } + + ((u8 *)dstbuf)[i] =3D val; + } +} diff --git a/drivers/mtd/nand/raw/nand_onfi.c b/drivers/mtd/nand/raw/nand_o= nfi.c index 861975e44b55..5d330dd53e8f 100644 --- a/drivers/mtd/nand/raw/nand_onfi.c +++ b/drivers/mtd/nand/raw/nand_onfi.c @@ -12,6 +12,7 @@ * This file contains all ONFI helpers. */ =20 +#include #include =20 #include "internals.h" @@ -20,14 +21,7 @@ =20 u16 onfi_crc16(u16 crc, u8 const *p, size_t len) { - int i; - while (len--) { - crc ^=3D *p++ << 8; - for (i =3D 0; i < 8; i++) - crc =3D (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0); - } - - return crc; + return nanddev_crc16(crc, p, len); } =20 /* Parse the Extended Parameter Page. */ @@ -107,37 +101,6 @@ static int nand_flash_detect_ext_param_page(struct nan= d_chip *chip, return ret; } =20 -/* - * Recover data with bit-wise majority - */ -static void nand_bit_wise_majority(const void **srcbufs, - unsigned int nsrcbufs, - void *dstbuf, - unsigned int bufsize) -{ - int i, j, k; - - for (i =3D 0; i < bufsize; i++) { - u8 val =3D 0; - - for (j =3D 0; j < 8; j++) { - unsigned int cnt =3D 0; - - for (k =3D 0; k < nsrcbufs; k++) { - const u8 *srcbuf =3D srcbufs[k]; - - if (srcbuf[i] & BIT(j)) - cnt++; - } - - if (cnt > nsrcbufs / 2) - val |=3D BIT(j); - } - - ((u8 *)dstbuf)[i] =3D val; - } -} - /* * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwi= se. */ @@ -200,7 +163,7 @@ int nand_onfi_detect(struct nand_chip *chip) srcbufs[j] =3D pbuf + j; =20 pr_warn("Could not find a valid ONFI parameter page, trying bit-wise maj= ority to recover it\n"); - nand_bit_wise_majority(srcbufs, ONFI_PARAM_PAGES, pbuf, + nanddev_bit_wise_majority(srcbufs, ONFI_PARAM_PAGES, pbuf, sizeof(*pbuf)); =20 crc =3D onfi_crc16(ONFI_CRC_BASE, (u8 *)pbuf, 254); diff --git a/include/linux/mtd/param.h b/include/linux/mtd/param.h new file mode 100644 index 000000000000..39636f66f1b4 --- /dev/null +++ b/include/linux/mtd/param.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 - Mediatek + * + * Author: Sky Huang + */ + +#ifndef __LINUX_NAND_PARAM +#define __LINUX_NAND_PARAM + +#include + +u16 nanddev_crc16(u16 crc, u8 const *p, size_t len); +void nanddev_bit_wise_majority(const void **srcbufs, + unsigned int nsrcbufs, + void *dstbuf, + unsigned int bufsize); + +#endif /* __LINUX_NAND_PARAM */ + --=20 2.45.2 From nobody Sun Feb 8 07:35:00 2026 Received: from mailgw01.mediatek.com (unknown [60.244.123.138]) (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 049B0B674 for ; Sun, 20 Oct 2024 13:28:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=60.244.123.138 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1729430921; cv=none; b=l5Lc7FDV4L4fSssonKgfvtY5WLAKqGLNGjfq0t+dJDZcR66IH2y8ThwqNv61/bdCrUYn3wP0uRQhdt51fN2nf+zoHpsYNMKFpxjNG3e8kDHxxwXjZRP1doi44B+hG2hrio6/2SmPVjkiJgdRdXvVGmYrBeZYPOcpcqAqFxRgbpo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1729430921; c=relaxed/simple; bh=F10NV8Mm0rSSx0vO59N28DC9rjRiUF3fSnPwl/i0Xmc=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=K9nBGR3sHCpnVyGr2Jhzee0KP8gVWB6mAWrJVzFpYmuy4JZgPpMXmxF4SZa378okDvVYIoly/GSPS7KIkTDCYsVAnM3aNyrBLqTk4By+GN0EJ7R4gnu6zEX/wOWIIawmJpX3tg6wAHhWRUOtGu1kLHjDzJzU9XjEaOkcrrTRPc8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com; spf=pass smtp.mailfrom=mediatek.com; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b=WldJ++BG; arc=none smtp.client-ip=60.244.123.138 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mediatek.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b="WldJ++BG" X-UUID: 3299278c8ee711efb88477ffae1fc7a5-20241020 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Type:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From; bh=u4Q+MpwvDdBjqr/mZ0lChqQSwuAsSECFSr3oxS9NmH4=; b=WldJ++BGMIeqr7QJZLXsYfFpempzCIrhA07pdDrI7FO7eS8EAP9rMkb/CxMo9BOR6n1EzamikZ6fnVonM5Ghvs7et9p1mEDuFvf9UExHzIYduQeggV6SJ5rxQz9tGxDQW4R8tj7UOq+m1h5H847mxqdrS1aCrZZaxWtXw5HP8uk=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.1.42,REQID:bd26795f-542e-4c15-ba61-2835e2966dfb,IP:0,U RL:0,TC:0,Content:-25,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTIO N:release,TS:-25 X-CID-META: VersionHash:b0fcdc3,CLOUDID:1fbf130d-1043-475c-b800-3262c01ea3e5,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:102,TC:nil,Content:0,EDM:-3,IP:nil,U RL:1,File:nil,RT:nil,Bulk:nil,QS:nil,BEC:nil,COL:0,OSI:0,OSA:0,AV:0,LES:1, SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0,ARC:0 X-CID-BVR: 0 X-CID-BAS: 0,_,0,_ X-CID-FACTOR: TF_CID_SPAM_ULS,TF_CID_SPAM_SNR X-UUID: 3299278c8ee711efb88477ffae1fc7a5-20241020 Received: from mtkmbs14n2.mediatek.inc [(172.21.101.76)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 54362067; Sun, 20 Oct 2024 21:28:30 +0800 Received: from mtkmbs13n1.mediatek.inc (172.21.101.193) by MTKMBS09N1.mediatek.inc (172.21.101.35) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.26; Sun, 20 Oct 2024 21:28:29 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkmbs13n1.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.1118.26 via Frontend Transport; Sun, 20 Oct 2024 21:28:29 +0800 From: Sky Huang To: Matthias Brugger , Miquel Raynal , AngeloGioacchino Del Regno , Richard Weinberger , Vignesh Raghavendra , Daniel Golle , Chia-Lin Kao , Mika Westerberg , Cheng Ming Lin , , , , CC: Steven Liu , Sky Huang Subject: [RFC PATCH nand/next 2/4] include: mtd: Add CASN page definition and related structs. Date: Sun, 20 Oct 2024 21:27:20 +0800 Message-ID: <20241020132722.20565-3-SkyLake.Huang@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20241020132722.20565-1-SkyLake.Huang@mediatek.com> References: <20241020132722.20565-1-SkyLake.Huang@mediatek.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MTK: N Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: "Sky Huang" This patch adds CASN page definition. You can see corresponding table in CASN page application note referred in cover letter. (https://github.com/mtk-openwrt/doc/blob/ main/CASN%20Page%20Introduction.pdf) Signed-off-by: Sky Huang --- include/linux/mtd/casn.h | 191 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 include/linux/mtd/casn.h diff --git a/include/linux/mtd/casn.h b/include/linux/mtd/casn.h new file mode 100644 index 000000000000..65ebd23c17c1 --- /dev/null +++ b/include/linux/mtd/casn.h @@ -0,0 +1,191 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 - Mediatek + * + * Author: Sky Huang + */ + +#ifndef __LINUX_MTD_CASN_H +#define __LINUX_MTD_CASN_H + +#define CASN_CRC_BASE 0x4341 +#define CASN_SIGNATURE 0x4341534EU +#define SPINAND_CASN_V1_CRC_OFS (254) +#define CASN_PAGE_V1_COPIES (3) + +#define SDR_READ_1_1_1 BIT(0) +#define SDR_READ_1_1_1_FAST BIT(1) +#define SDR_READ_1_1_2 BIT(2) +#define SDR_READ_1_2_2 BIT(3) +#define SDR_READ_1_1_4 BIT(4) +#define SDR_READ_1_4_4 BIT(5) +#define SDR_READ_1_1_8 BIT(6) +#define SDR_READ_1_8_8 BIT(7) + +#define SDR_WRITE_1_1_1 BIT(0) +#define SDR_WRITE_1_1_4 BIT(1) + +#define SDR_UPDATE_1_1_1 BIT(0) +#define SDR_UPDATE_1_1_4 BIT(1) + +struct op_slice { + u8 cmd_opcode; +#if defined(__LITTLE_ENDIAN_BITFIELD) + u8 dummy_nbytes : 4; + u8 addr_nbytes : 4; +#elif defined(__BIG_ENDIAN_BITFIELD) + u8 addr_nbytes : 4; + u8 dummy_nbytes : 4; +#endif +}; + +struct SPINAND_FLAGS { +#if defined(__LITTLE_ENDIAN_BITFIELD) + u8 has_qe_bit : 1; + u8 has_cr_feat_bit : 1; + u8 conti_read_cap : 1; + u8 on_die_ecc : 1; + u8 legacy_ecc_status : 1; + u8 adv_ecc_status : 1; + u8 ecc_parity_readable : 1; + u8 ecc_alg : 1; /* ECC algorithm */ +#elif defined(__BIG_ENDIAN_BITFIELD) + u8 ecc_alg : 1; /* ECC algorithm */ + u8 ecc_parity_readable : 1; + u8 adv_ecc_status : 1; + u8 legacy_ecc_status : 1; + u8 on_die_ecc : 1; + u8 conti_read_cap : 1; + u8 has_cr_feat_bit : 1; + u8 has_qe_bit : 1; +#endif +}; + +struct ADV_ECC_STATUS { + u8 cmd; + u8 addr; + u8 addr_nbytes; + u8 addr_buswidth; + u8 dummy_nbytes; + u8 dummy_buswidth; + u8 status_nbytes; + u16 status_mask; + u8 pre_op; /* pre-process operator */ + u8 pre_mask; /* pre-process mask */ +} __packed; + +struct CASN_OOB { + u8 layout_type; + + /* OOB free layout */ + u8 free_start; + u8 free_length; + u8 bbm_length; + + /* ECC parity layout */ + u8 ecc_parity_start; + u8 ecc_parity_space; + u8 ecc_parity_real_length; +}; + +enum oob_overall { + OOB_DISCRETE =3D 0, + OOB_CONTINUOUS, +}; + +struct nand_casn { + /* CASN signature must be 4 chars: 'C','A','S','N' */ + union { + u8 sig[4]; + u32 signature; + }; + + u8 version; + char manufacturer[13]; + char model[16]; + + __be32 bits_per_cell; + __be32 bytes_per_page; + __be32 spare_bytes_per_page; + __be32 pages_per_block; + __be32 blocks_per_lun; + __be32 max_bb_per_lun; + __be32 planes_per_lun; + __be32 luns_per_target; + __be32 total_target; + + __be32 ecc_strength; + __be32 ecc_step_size; + + u8 flags; + u8 reserved1; + + __be16 sdr_read_cap; + struct op_slice sdr_read_1_1_1; + struct op_slice sdr_read_1_1_1_fast; + struct op_slice sdr_read_1_1_2; + struct op_slice sdr_read_1_2_2; + struct op_slice sdr_read_1_1_4; + struct op_slice sdr_read_1_4_4; + struct op_slice sdr_read_1_1_8; + struct op_slice sdr_read_1_8_8; + + struct op_slice sdr_cont_read_1_1_1; + struct op_slice sdr_cont_read_1_1_1_fast; + struct op_slice sdr_cont_read_1_1_2; + struct op_slice sdr_cont_read_1_2_2; + struct op_slice sdr_cont_read_1_1_4; + struct op_slice sdr_cont_read_1_4_4; + struct op_slice sdr_cont_read_1_1_8; + struct op_slice sdr_cont_read_1_8_8; + + __be16 ddr_read_cap; + struct op_slice ddr_read_1_1_1; + struct op_slice ddr_read_1_1_1_fast; + struct op_slice ddr_read_1_1_2; + struct op_slice ddr_read_1_2_2; + struct op_slice ddr_read_1_1_4; + struct op_slice ddr_read_1_4_4; + struct op_slice ddr_read_1_1_8; + struct op_slice ddr_read_1_8_8; + + struct op_slice ddr_cont_read_1_1_1; + struct op_slice ddr_cont_read_1_1_1_fast; + struct op_slice ddr_cont_read_1_1_2; + struct op_slice ddr_cont_read_1_2_2; + struct op_slice ddr_cont_read_1_1_4; + struct op_slice ddr_cont_read_1_4_4; + struct op_slice ddr_cont_read_1_1_8; + struct op_slice ddr_cont_read_1_8_8; + + u8 sdr_write_cap; + struct op_slice sdr_write_1_1_1; + struct op_slice sdr_write_1_1_4; + struct op_slice reserved2[6]; + u8 ddr_write_cap; + struct op_slice reserved3[8]; + + u8 sdr_update_cap; + struct op_slice sdr_update_1_1_1; + struct op_slice sdr_update_1_1_4; + struct op_slice reserved4[6]; + u8 ddr_update_cap; + struct op_slice reserved5[8]; + + struct CASN_OOB casn_oob; + + /* Advanced ECC status CMD0 (higher bits) */ + struct ADV_ECC_STATUS ecc_status_high; + /* Advanced ECC status CMD1 (lower bits) */ + struct ADV_ECC_STATUS ecc_status_low; + + u8 advecc_noerr_status; + u8 advecc_uncor_status; + u8 advecc_post_op; + u8 advecc_post_mask; + + u8 reserved6[5]; + __be16 crc; +} __packed; + +#endif /* __LINUX_MTD_CASN_H */ --=20 2.45.2 From nobody Sun Feb 8 07:35:00 2026 Received: from mailgw01.mediatek.com (unknown [60.244.123.138]) (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 1D2EF178372 for ; Sun, 20 Oct 2024 13:28:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=60.244.123.138 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1729430930; cv=none; b=punMosKJziFF5FmUxZKfwRUHelQqaVchGVy2PhstQwhCQJEODKChBC+x8Mc9ijKipCaWKJt7ZoGjsITlcOyTWWUKZkJSLRMDkNVQw/Kp8u7G7KJWsr3cb0P3L4vIz2WlL190PtwbRyUM1vlKPRD/Dz/YMoZ1mkEcfx0Bltzw9SA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1729430930; c=relaxed/simple; bh=7Hk2wBpYm1bEYEDi8D/w8p85p7kOvvVUUA9tuUvbRWo=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=KqZHeQw6jCGfmtxL25BNLZxOyrZ8w482KW40GAcMlB4C51YPJmTnqcjkFa5QfgybZTx7tZG0WXgf9i6gwGEcl6I+/5rHHYf0Fht6HmTjD6T7Vwr3dVoqnjnuk9/ug1x3KnVefuSiUokhziLMTMVKl+cXs789Ctsq1hteUuK1AAg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com; spf=pass smtp.mailfrom=mediatek.com; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b=jpyzAVCH; arc=none smtp.client-ip=60.244.123.138 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mediatek.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b="jpyzAVCH" X-UUID: 3ae223e48ee711efb88477ffae1fc7a5-20241020 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Type:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From; bh=2G1oHhlieHVhAY567gkNMmzlsnu2+sPvTe7p5o7ZsNk=; b=jpyzAVCHpGKDVyz8vX5qobQk0Iy/BG/4AZxBSHZzpLF6NiWwCoPA1CY3Jgz8U5ZOfSsT0tvaXCY0kvBTQyH4yyAsk0IzDtkEbXhTw6x8s1L3mIpnt7G0FNjZ2FfeG/Fbv8eVkJRfwYBftE6e+ARw0CE1OTGhow+KHp60/tUgeWo=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.1.42,REQID:f4d8e727-18f6-44b8-b3cf-f3a23d0ea666,IP:0,U RL:0,TC:0,Content:-25,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTIO N:release,TS:-25 X-CID-META: VersionHash:b0fcdc3,CLOUDID:94255925-9cd9-4037-af6e-f4241b90f84d,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:102,TC:nil,Content:0,EDM:-3,IP:nil,U RL:0,File:nil,RT:nil,Bulk:nil,QS:nil,BEC:nil,COL:0,OSI:0,OSA:0,AV:0,LES:1, SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0,ARC:0 X-CID-BVR: 0 X-CID-BAS: 0,_,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR X-UUID: 3ae223e48ee711efb88477ffae1fc7a5-20241020 Received: from mtkmbs14n2.mediatek.inc [(172.21.101.76)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 2108882854; Sun, 20 Oct 2024 21:28:44 +0800 Received: from mtkmbs13n1.mediatek.inc (172.21.101.193) by MTKMBS14N2.mediatek.inc (172.21.101.76) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.26; Sun, 20 Oct 2024 21:28:42 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkmbs13n1.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.1118.26 via Frontend Transport; Sun, 20 Oct 2024 21:28:42 +0800 From: Sky Huang To: Matthias Brugger , Miquel Raynal , AngeloGioacchino Del Regno , Richard Weinberger , Vignesh Raghavendra , Daniel Golle , Chia-Lin Kao , "Mika Westerberg" , Cheng Ming Lin , , , , CC: Steven Liu , Sky Huang Subject: [RFC PATCH nand/next 3/4] include: mtd: spinand: Add CASN page related macros and flags. Date: Sun, 20 Oct 2024 21:27:21 +0800 Message-ID: <20241020132722.20565-4-SkyLake.Huang@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20241020132722.20565-1-SkyLake.Huang@mediatek.com> References: <20241020132722.20565-1-SkyLake.Huang@mediatek.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-TM-AS-Product-Ver: SMEX-14.0.0.3152-9.1.1006-23728.005 X-TM-AS-Result: No-10--2.446200-8.000000 X-TMASE-MatchedRID: oCsWEly1aNNM4VjZ76ar2LMjW/sniEQKmX+W7bzPOQF+YesuCgkiXJpx JPA5ZIe+2lO9wdJIjwWwYEHOVohSR8a1KU/oEEOPogGd8wIUGIJo3Yq5PCwLAhL6MU7t349bo7e vliYi17DYcB744kzEdqnConkZyCes/Y8bXtyBbJB0CDjJ3XioBPBkYT/50xzWf7dwGsQvXvzIrf 3gn7w8oqP8wcVXpl7OgzXM/1S8rGibGHSBj+j5Wd35+5/2RxqmCv3j7wdXxBibKItl61J/yZ+in TK0bC9eKrauXd3MZDX371moSn0VOL9msMtG43RP5Oy4n7TTsM8D5mBwNiJFK4Dro/jMlndlBrNs Id/ubdefkcesyQmuoXXWg7RXgpNfEi71zuMNe2ygzDosNXeeAhdGg+ZY7eN6THB2Q+oKru8MTI3 4nyF36MJL1aANdU8Knqg/VrSZEiM= X-TM-AS-User-Approved-Sender: No X-TM-AS-User-Blocked-Sender: No X-TMASE-Result: 10--2.446200-8.000000 X-TMASE-Version: SMEX-14.0.0.3152-9.1.1006-23728.005 X-TM-SNTS-SMTP: 7EF0422110CF8F2371BA6587828AFCBCFFF5F5E629928B88ECDE63BE23BB0CB12000:8 X-MTK: N Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: "Sky Huang" This patch adds SPINAND CASN page manipulation macros for SPI-NAND driver to use. Also, some important flag bits, like SPINAND_SUPR_CR(continuous read), are added to show the SPI-NAND device's capability. Signed-off-by: Sky Huang --- include/linux/mtd/spinand.h | 100 +++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 2 deletions(-) diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 702e5fb13dae..de97994c357b 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -62,6 +62,59 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) =20 +/* Macros for CASN */ +#define SPINAND_CASN_PAGE_READ_FROM_CACHE_OP(fast, naddr, addr, ndummy, bu= f, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(fast ? 0x0b : 0x03, 1), \ + SPI_MEM_OP_ADDR(naddr, addr, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 1)) + +#define SPINAND_CASN_PAGE_READ_FROM_CACHE_X2_OP(naddr, addr, ndummy, buf, = len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1), \ + SPI_MEM_OP_ADDR(naddr, addr, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 2)) + +#define SPINAND_CASN_PAGE_READ_FROM_CACHE_DUALIO_OP(naddr, addr, ndummy, b= uf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1), \ + SPI_MEM_OP_ADDR(naddr, addr, 2), \ + SPI_MEM_OP_DUMMY(ndummy, 2), \ + SPI_MEM_OP_DATA_IN(len, buf, 2)) + +#define SPINAND_CASN_PAGE_READ_FROM_CACHE_X4_OP(naddr, addr, ndummy, buf, = len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1), \ + SPI_MEM_OP_ADDR(naddr, addr, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 4)) + +#define SPINAND_CASN_PAGE_READ_FROM_CACHE_QUADIO_OP(naddr, addr, ndummy, b= uf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xeb, 1), \ + SPI_MEM_OP_ADDR(naddr, addr, 4), \ + SPI_MEM_OP_DUMMY(ndummy, 4), \ + SPI_MEM_OP_DATA_IN(len, buf, 4)) + +#define SPINAND_CASN_PROG_LOAD(reset, naddr, addr, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(reset ? 0x02 : 0x84, 1), \ + SPI_MEM_OP_ADDR(naddr, addr, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(len, buf, 1)) + +#define SPINAND_CASN_PROG_LOAD_X4(reset, naddr, addr, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(reset ? 0x32 : 0x34, 1), \ + SPI_MEM_OP_ADDR(naddr, addr, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(len, buf, 4)) + +#define SPINAND_CASN_ADVECC_OP(casn_adv_ecc_status, buf) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(casn_adv_ecc_status.cmd, 1), \ + SPI_MEM_OP_ADDR(casn_adv_ecc_status.addr_nbytes, \ + casn_adv_ecc_status.addr, \ + casn_adv_ecc_status.addr_buswidth), \ + SPI_MEM_OP_DUMMY(casn_adv_ecc_status.dummy_nbytes, \ + casn_adv_ecc_status.dummy_buswidth), \ + SPI_MEM_OP_DATA_IN(casn_adv_ecc_status.status_nbytes, buf, 1)) +/* Macros for CASN end */ + #define SPINAND_PAGE_READ_FROM_CACHE_OP(fast, addr, ndummy, buf, len) \ SPI_MEM_OP(SPI_MEM_OP_CMD(fast ? 0x0b : 0x03, 1), \ SPI_MEM_OP_ADDR(2, addr, 1), \ @@ -312,8 +365,11 @@ struct spinand_ecc_info { =20 #define SPINAND_HAS_QE_BIT BIT(0) #define SPINAND_HAS_CR_FEAT_BIT BIT(1) -#define SPINAND_HAS_PROG_PLANE_SELECT_BIT BIT(2) -#define SPINAND_HAS_READ_PLANE_SELECT_BIT BIT(3) +#define SPINAND_SUP_CR BIT(2) +#define SPINAND_SUP_ON_DIE_ECC BIT(3) +#define SPINAND_SUP_LEGACY_ECC_STATUS BIT(4) +#define SPINAND_SUP_ADV_ECC_STATUS BIT(5) +#define SPINAND_ECC_PARITY_READABLE BIT(6) =20 /** * struct spinand_ondie_ecc_conf - private SPI-NAND on-die ECC engine stru= cture @@ -406,6 +462,28 @@ struct spinand_dirmap { struct spi_mem_dirmap_desc *rdesc_ecc; }; =20 +/** + * struct CASN_ADVECC - CASN's advanced ECC description + * @cmd: Command to access SPI-NAND on-chip ECC status registers + * @mask: Mask to access SPI-NAND on-chip ECC status registers. + * ADV_ECC_STATUS->status_nbytes | CASN_ADVECC->mask + * 1 | 0 to 0xff + * 2 | 0 to 0xffff + * @shift: How many bits to shift to get on-chip ECC status + * @pre_op: This comes from CASN page's ADV_ECC_STATUS's pre_op. + * After reading on-chip ECC status, we need to do some math + * operations if this is specified. + * @pre_mask: This comes from CASN page's ADV_ECC_STATUS's pre_mask. + * This is used in companion with pre_op above. + */ +struct CASN_ADVECC { + u8 cmd; + u16 mask; + u8 shift; + u8 pre_op; + u8 pre_mask; +}; + /** * struct spinand_device - SPI NAND device instance * @base: NAND device instance @@ -464,6 +542,23 @@ struct spinand_device { u8 *oobbuf; u8 *scratchbuf; const struct spinand_manufacturer *manufacturer; + + bool use_casn; + struct nand_casn *casn; + struct spi_mem_op *advecc_high_ops; /* ops to read higher part of advance= d ECC status*/ + struct spi_mem_op *advecc_low_ops; + struct CASN_OOB *casn_oob; + struct CASN_ADVECC *advecc_high; + struct CASN_ADVECC *advecc_low; + + u8 advecc_low_bitcnt; + u8 advecc_noerr_status; + u8 advecc_uncor_status; + u8 advecc_post_op; + u8 advecc_post_mask; + + size_t (*eccsr_math_op[4])(size_t, size_t); + void *priv; =20 bool cont_read_possible; @@ -539,3 +634,4 @@ int spinand_write_reg_op(struct spinand_device *spinand= , u8 reg, u8 val); int spinand_select_target(struct spinand_device *spinand, unsigned int tar= get); =20 #endif /* __LINUX_MTD_SPINAND_H */ + --=20 2.45.2 From nobody Sun Feb 8 07:35:00 2026 Received: from mailgw01.mediatek.com (unknown [60.244.123.138]) (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 12D5BB674 for ; Sun, 20 Oct 2024 13:29:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=60.244.123.138 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1729430949; cv=none; b=GqtNYWPio+Jk7cEbXn7gdDV8s5R68ANNAkUd74b+Px/cBEHRJECGT8fpe7W3CUU8RKMmV+7Nqclqnm4IcvUCqClw5WvqJeU7RWqXA6aCjWOAonqgAzGWcSUQLzqPiwBhsXpWwyjcVvdPoeB8gXNNv+dTNhdOGsMlQhGqCoHPi0s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1729430949; c=relaxed/simple; bh=uTr+p262V66lyjnEN6erpoWB9MCGAeLUbU9TVdQj8PQ=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=jwSDvbljo6Fc8hl1KFxDQ9ef32SS6OsBFj3j3p6O3AdfIa9MvCrEZ646XOiDSTCMv6Cz4NSW7wvVqOWf6Po53T5MNhfVAgPr0EJPWBcE0hWQ/bqialiKCvlHW6Rd3hNBgNclImVWIDhqPXiJ6GtRv4FPkKXz+V3zOsT43rQiDIU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com; spf=pass smtp.mailfrom=mediatek.com; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b=SQC9+Cvn; arc=none smtp.client-ip=60.244.123.138 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mediatek.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b="SQC9+Cvn" X-UUID: 458bf3388ee711efb88477ffae1fc7a5-20241020 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Type:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From; bh=ICByNUPg0Iy6Y7kVIRAl7dHZrrdeU90KoDn/qQyaETA=; b=SQC9+Cvnz7/+an9Y0ByBPnBcsR9WkIdcEtInQSZU/F5GanvH6wLVIsqHSAbGO1YkF5fBE5ONhIhT9VJ6SWG0v8GTN1Ru04OU0CYu+3OQ2auewF8GgvbT86bk+RdC8jfXpDal8b/0Qq0cZWS+EBnfJQX/YaNv3Q6SLQ2qOsn9LoY=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.1.42,REQID:e392eb61-615e-4602-b34e-8331f5ac8443,IP:0,U RL:0,TC:0,Content:-25,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTIO N:release,TS:-25 X-CID-META: VersionHash:b0fcdc3,CLOUDID:fd255925-9cd9-4037-af6e-f4241b90f84d,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:102,TC:nil,Content:0,EDM:-3,IP:nil,U RL:11|1,File:nil,RT:nil,Bulk:nil,QS:nil,BEC:nil,COL:0,OSI:0,OSA:0,AV:0,LES :1,SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0,ARC:0 X-CID-BVR: 0 X-CID-BAS: 0,_,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR,TF_CID_SPAM_ULN X-UUID: 458bf3388ee711efb88477ffae1fc7a5-20241020 Received: from mtkmbs13n2.mediatek.inc [(172.21.101.108)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 208300156; Sun, 20 Oct 2024 21:29:02 +0800 Received: from mtkmbs13n1.mediatek.inc (172.21.101.193) by mtkmbs10n1.mediatek.inc (172.21.101.34) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.26; Sun, 20 Oct 2024 21:29:01 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkmbs13n1.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.1118.26 via Frontend Transport; Sun, 20 Oct 2024 21:29:00 +0800 From: Sky Huang To: Matthias Brugger , Miquel Raynal , AngeloGioacchino Del Regno , Richard Weinberger , Vignesh Raghavendra , Daniel Golle , Chia-Lin Kao , "Mika Westerberg" , Cheng Ming Lin , , , , CC: Steven Liu , Sky Huang Subject: [RFC PATCH nand/next 4/4] mtd: nand: spi: Add CASN page support Date: Sun, 20 Oct 2024 21:27:22 +0800 Message-ID: <20241020132722.20565-5-SkyLake.Huang@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20241020132722.20565-1-SkyLake.Huang@mediatek.com> References: <20241020132722.20565-1-SkyLake.Huang@mediatek.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-TM-AS-Product-Ver: SMEX-14.0.0.3152-9.1.1006-23728.005 X-TM-AS-Result: No-10--9.444200-8.000000 X-TMASE-MatchedRID: /bKxxJqDGVcwE0n9oAwkPXtItsHAduPxBGvINcfHqhdNZ/CnTeI8lmEj qlizhhlNpJ0OYMrT0yma/3zDweDPK1SU1d+VJ4IjGVyS87Wb4lyscK/K2DlvjsA0eOJZYj7tx+W T4fjiMGeFuNbf28fb6Lrw3PfJA6xhZz86fqAj7POEJ5wBUYI5/ZnaxzJFBx6vXCmcAC8DBrPQ1I IT+AWI0blK18H/cd3hp8qiS2HjSrPM2WV/gPPibLE3FpMbg63SKVrLOZD1BXTxxaAXDrCns0Z3u pZH64zDo+u9XRmCBoTuKpag/ZE3vbPmM/zWNUUfEdFsavUQKAeYZJKiXhR6LUYx760ONDcWhzQH k5Qv+7VS2oPdDIIOObiW/VRR7ZTRZ1otclUep4/d+fuf9kcaplo1rFkFFs1ayGbI60ZH8Wb6rx1 jnOZw+qrnKAj6elaaIp2nL7ZFABfMHUInqqZ02s36paW7ZnFo5Y0kb0hqatzRziMbBeTI+R6AtY tAKdbYZflHUyH8MzPv6gcLh2moHiBPHVH0QwOY9Ib/6w+1lWSjkoGZwmjMNpsoi2XrUn/JUTdY/ mdfTXtJKW4mDlJsMcK21zBg2KlfyMdyHKes7lse67lDQhMclzJ9Yj2F3cvEMgX+XpjX0UPPTy7H 7An6Fg== X-TM-AS-User-Approved-Sender: No X-TM-AS-User-Blocked-Sender: No X-TMASE-Result: 10--9.444200-8.000000 X-TMASE-Version: SMEX-14.0.0.3152-9.1.1006-23728.005 X-TM-SNTS-SMTP: 91FD138A61C70715A0CA3900CBE42C26709F1E28979A36A6B50440177768D9C72000:8 X-MTK: N Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: "Sky Huang" This patch implements how CASN page detection works for SPI-NAND and how CASN page interprets ECC information of all SPI-NAND flash vendors. Probe flow will become like this: * If CASN page exists, check its validity. --> If a certain CASN page is valid, we will know the SPI-NAND device's information, including memory organization, flash on-chip ECC engine's design, etc. * If CASN page doesn't exist, switch to reading ID spinand_dump_casn() can be moved to sysfs, debugfs or something else later, I guess. Signed-off-by: Sky Huang --- drivers/mtd/nand/spi/core.c | 741 +++++++++++++++++++++++++++++++++++- 1 file changed, 729 insertions(+), 12 deletions(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 4d76f9f71a0e..7d7408e00b5e 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -9,10 +9,13 @@ =20 #define pr_fmt(fmt) "spi-nand: " fmt =20 +#include #include #include #include #include +#include +#include #include #include #include @@ -20,6 +23,8 @@ #include #include =20 +extern void sanitize_string(uint8_t *s, size_t len); + static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 = *val) { struct spi_mem_op op =3D SPINAND_GET_FEATURE_OP(reg, @@ -200,6 +205,63 @@ static int spinand_ecc_enable(struct spinand_device *s= pinand, enable ? CFG_ECC_ENABLE : 0); } =20 +static size_t eccsr_none_op(size_t val, size_t mask) { return val; } +static size_t eccsr_and_op(size_t val, size_t mask) { return val & mask; } +static size_t eccsr_add_op(size_t val, size_t mask) { return val + mask; } +static size_t eccsr_minus_op(size_t val, size_t mask) { return val - mask;= } +static size_t eccsr_mul_op(size_t val, size_t mask) { return val * mask; } + +static void spinand_read_adv_ecc(struct spinand_device *spinand, + struct spi_mem_op *ops, u16 *eccsr, + u16 mask, u8 shift, + u8 pre_op, u8 pre_mask) +{ + u8 *p =3D spinand->scratchbuf; + + spi_mem_exec_op(spinand->spimem, ops); + + if (likely(mask <=3D 0xff)) + *eccsr +=3D (*p & mask) >> shift; + else + *eccsr +=3D (((*p << 8) | (*p+1)) & mask) >> shift; + + *eccsr =3D spinand->eccsr_math_op[pre_op](*eccsr, pre_mask); +} + +static int spinand_casn_get_ecc_status(struct spinand_device *spinand, + u8 status) +{ + struct mtd_info *mtd =3D spinand_to_mtd(spinand); + struct CASN_ADVECC *ah =3D spinand->advecc_high; + struct CASN_ADVECC *al =3D spinand->advecc_low; + u16 eccsr_high =3D 0; + u16 eccsr_low =3D 0; + u32 eccsr =3D 0; + + if (al->cmd) { + spinand_read_adv_ecc(spinand, + spinand->advecc_low_ops, &eccsr_low, + al->mask, al->shift, + al->pre_op, al->pre_mask); + eccsr +=3D eccsr_low; + } + if (ah->cmd) { + spinand_read_adv_ecc(spinand, + spinand->advecc_high_ops, &eccsr_high, + ah->mask, ah->shift, + ah->pre_op, ah->pre_mask); + eccsr +=3D eccsr_high << spinand->advecc_low_bitcnt; + } + + if (eccsr =3D=3D spinand->advecc_noerr_status) + return 0; + else if (eccsr =3D=3D spinand->advecc_uncor_status) + return -EBADMSG; + eccsr =3D spinand->eccsr_math_op[spinand->advecc_post_op](eccsr, spinand-= >advecc_post_mask); + + return eccsr > mtd->ecc_strength ? mtd->ecc_strength : eccsr; +} + static int spinand_cont_read_enable(struct spinand_device *spinand, bool enable) { @@ -1149,6 +1211,242 @@ static int spinand_manufacturer_match(struct spinan= d_device *spinand, return -EOPNOTSUPP; } =20 +static int spinand_check_casn_validity(struct spinand_device *spinand, + struct nand_casn *casn) +{ + struct device *dev =3D &spinand->spimem->spi->dev; + + if (be32_to_cpu(casn->bits_per_cell) !=3D 1) { + dev_err(dev, "[CASN] bits-per-cell must be 1\n"); + return -EINVAL; + } + + switch (be32_to_cpu(casn->bytes_per_page)) { + case 2048: + case 4096: + break; + default: + dev_err(dev, "[CASN] page size must be 2048/4096\n"); + return -EINVAL; + } + + switch (be32_to_cpu(casn->spare_bytes_per_page)) { + case 64: + case 96: + case 128: + case 256: + break; + default: + dev_err(dev, "[CASN] spare size must be 64/128/256\n"); + return -EINVAL; + } + + switch (be32_to_cpu(casn->pages_per_block)) { + case 64: + case 128: + break; + default: + dev_err(dev, "[CASN] pages_per_block must be 64/128\n"); + return -EINVAL; + } + + switch (be32_to_cpu(casn->blocks_per_lun)) { + case 1024: + if (be32_to_cpu(casn->max_bb_per_lun) !=3D 20) { + dev_err(dev, "[CASN] max_bb_per_lun must be 20 when blocks_per_lun is 1= 024\n"); + return -EINVAL; + } + break; + case 2048: + if (be32_to_cpu(casn->max_bb_per_lun) !=3D 40) { + dev_err(dev, "[CASN] max_bb_per_lun must be 40 when blocks_per_lun is 2= 048\n"); + return -EINVAL; + } + break; + case 4096: + if (be32_to_cpu(casn->max_bb_per_lun) !=3D 80) { + dev_err(dev, "[CASN] max_bb_per_lun must be 80 when blocks_per_lun is 4= 096\n"); + return -EINVAL; + } + break; + default: + dev_err(dev, "[CASN] blocks_per_lun must be 1024/2048/4096\n"); + return -EINVAL; + } + + switch (be32_to_cpu(casn->planes_per_lun)) { + case 1: + case 2: + break; + default: + dev_err(dev, "[CASN] planes_per_lun must be 1/2\n"); + return -EINVAL; + } + + switch (be32_to_cpu(casn->luns_per_target)) { + case 1: + case 2: + break; + default: + dev_err(dev, "[CASN] luns_per_target must be 1/2\n"); + return -EINVAL; + } + + switch (be32_to_cpu(casn->total_target)) { + case 1: + case 2: + break; + default: + dev_err(dev, "[CASN] ntargets must be 1/2\n"); + return -EINVAL; + } + + if (casn->casn_oob.layout_type !=3D OOB_CONTINUOUS && + casn->casn_oob.layout_type !=3D OOB_DISCRETE) { + dev_err(dev, "[CASN] OOB layout type isn't correct.\n"); + return -EINVAL; + } + + if (casn->ecc_status_high.status_nbytes > 2 || + casn->ecc_status_low.status_nbytes > 2) { + dev_err(dev, "[CASN] ADVECC status nbytes must be no more than 2\n"); + return -EINVAL; + } + + return 0; +} + +static int spinand_check_casn(struct spinand_device *spinand, + struct nand_casn *casn, unsigned int *sel) +{ + struct device *dev =3D &spinand->spimem->spi->dev; + uint16_t crc =3D be16_to_cpu(casn->crc); + uint16_t crc_compute; + int ret =3D 0; + int i; + + /* There are 3 copies of CASN Pages V1. Choose one avabilable copy + * first. If none of the copies is available, try to recover. + */ + for (i =3D 0; i < CASN_PAGE_V1_COPIES; i++) { + if (be32_to_cpu(casn[i].signature) !=3D CASN_SIGNATURE) { + ret =3D -EINVAL; + continue; + } + crc_compute =3D nanddev_crc16(CASN_CRC_BASE, (u8 *)(casn + i), + SPINAND_CASN_V1_CRC_OFS); + dev_dbg(dev, "CASN COPY %d CRC read: 0x%x, compute: 0x%x\n", + i, crc, crc_compute); + if (crc !=3D crc_compute) { + ret =3D -EBADMSG; + continue; + } + ret =3D spinand_check_casn_validity(spinand, casn + i); + if (ret < 0) + continue; + *sel =3D i; + break; + } + + if (i =3D=3D CASN_PAGE_V1_COPIES && ret =3D=3D -EBADMSG) { + const void *srcbufs[CASN_PAGE_V1_COPIES]; + int j; + + for (j =3D 0; j < CASN_PAGE_V1_COPIES; j++) + srcbufs[j] =3D casn + j; + dev_info(dev, "Couldn't find a valid CASN page, try bitwise majority to = recover it\n"); + nanddev_bit_wise_majority(srcbufs, CASN_PAGE_V1_COPIES, casn, + sizeof(*casn)); + crc_compute =3D nanddev_crc16(CASN_CRC_BASE, (uint8_t *)casn, + SPINAND_CASN_V1_CRC_OFS); + if (crc_compute !=3D crc) { + dev_err(dev, "CASN page recovery failed, aborting\n"); + return -EBADMSG; + } + ret =3D spinand_check_casn_validity(spinand, casn + i); + if (ret < 0) + return ret; + dev_info(dev, "CASN page recovery succeeded\n"); + *sel =3D 0; + } + + return ret; +} + +static int spinand_casn_detect(struct spinand_device *spinand, + struct nand_casn *casn, unsigned int *sel) +{ + struct device *dev =3D &spinand->spimem->spi->dev; + uint8_t casn_offset[3] =3D {0x0, 0x1, 0x4}; + struct nand_page_io_req req; + struct spi_mem_op op; + struct nand_pos pos; + int check_ret =3D 0; + uint8_t status; + int final_ret; + int ret =3D 0; + u8 cfg_reg; + int i; + + ret =3D spinand_read_reg_op(spinand, REG_CFG, &cfg_reg); + if (ret) + return ret; + + ret =3D spinand_write_reg_op(spinand, REG_CFG, cfg_reg | BIT(6)); + if (ret) + return ret; + + memset(&pos, 0, sizeof(pos)); + + req =3D (struct nand_page_io_req){ + .pos =3D pos, + .dataoffs =3D 0, + .datalen =3D 256 * CASN_PAGE_V1_COPIES, + .databuf.in =3D (u8 *)casn, + .mode =3D MTD_OPS_AUTO_OOB, + }; + + for (i =3D 0; i < sizeof(casn_offset)/sizeof(uint8_t); i++) { + req.pos.page =3D casn_offset[i]; + ret =3D spinand_load_page_op(spinand, &req); + if (ret) + goto finish; + + ret =3D spinand_wait(spinand, + SPINAND_READ_INITIAL_DELAY_US, + SPINAND_READ_POLL_DELAY_US, + &status); + if (ret < 0) + goto finish; + + op =3D (struct spi_mem_op) SPINAND_PAGE_READ_FROM_CACHE_OP( + false, 768, 1, (u8 *)casn, 256 * CASN_PAGE_V1_COPIES); + ret =3D spi_mem_exec_op(spinand->spimem, &op); + if (ret < 0) + goto finish; + + check_ret =3D spinand_check_casn(spinand, casn, sel); + if (!check_ret) + break; + } + +finish: + /* We need to restore configuration register. */ + final_ret =3D spinand_write_reg_op(spinand, REG_CFG, cfg_reg); + if (final_ret) + return final_ret; + + if (check_ret) { + dev_err(dev, "CASN page check failed\n"); + return check_ret; + } + + if (ret) + dev_err(dev, "CASN page read failed\n"); + + return ret; +} + static int spinand_id_detect(struct spinand_device *spinand) { u8 *id =3D spinand->id.data; @@ -1180,7 +1478,7 @@ static int spinand_id_detect(struct spinand_device *s= pinand) =20 static int spinand_manufacturer_init(struct spinand_device *spinand) { - if (spinand->manufacturer->ops->init) + if (!spinand->use_casn && spinand->manufacturer->ops->init) return spinand->manufacturer->ops->init(spinand); =20 return 0; @@ -1189,7 +1487,7 @@ static int spinand_manufacturer_init(struct spinand_d= evice *spinand) static void spinand_manufacturer_cleanup(struct spinand_device *spinand) { /* Release manufacturer private data */ - if (spinand->manufacturer->ops->cleanup) + if (!spinand->use_casn && spinand->manufacturer->ops->cleanup) return spinand->manufacturer->ops->cleanup(spinand); } =20 @@ -1293,37 +1591,456 @@ int spinand_match_and_init(struct spinand_device *= spinand, return -ENOTSUPP; } =20 +static int spinand_casn_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + struct spinand_device *spinand =3D mtd_to_spinand(mtd); + int sectionp; + struct CASN_OOB *co =3D spinand->casn_oob; + + sectionp =3D spinand->base.memorg.pagesize/mtd->ecc_step_size; + if (section >=3D sectionp) + return -ERANGE; + + if (co->layout_type =3D=3D OOB_DISCRETE) { + region->offset =3D co->ecc_parity_start + + (co->free_length + co->ecc_parity_space) + * section; + } else if (co->layout_type =3D=3D OOB_CONTINUOUS) { + region->offset =3D co->ecc_parity_start + co->ecc_parity_space * section; + } + region->length =3D co->ecc_parity_real_length; + + return 0; +} + +static int spinand_casn_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + struct spinand_device *spinand =3D mtd_to_spinand(mtd); + int sectionp; + struct CASN_OOB *co =3D spinand->casn_oob; + + sectionp =3D spinand->base.memorg.pagesize/mtd->ecc_step_size; + if (section >=3D sectionp) + return -ERANGE; + + if (!section) { + region->offset =3D co->free_start + co->bbm_length; + region->length =3D co->free_length - co->bbm_length; + } else { + if (co->layout_type =3D=3D OOB_DISCRETE) { + region->offset =3D co->free_start + + (co->free_length + + co->ecc_parity_space) * section; + } else if (co->layout_type =3D=3D OOB_CONTINUOUS) { + region->offset =3D co->free_start + + co->free_length * section; + } + region->length =3D co->free_length; + } + + return 0; +} + +static const struct mtd_ooblayout_ops spinand_casn_ooblayout =3D { + .ecc =3D spinand_casn_ooblayout_ecc, + .free =3D spinand_casn_ooblayout_free, +}; + +static int spinand_set_read_op_variants(struct spinand_device *spinand, + struct nand_casn *casn) +{ + struct spinand_op_variants casn_read_cache_variants; + u16 sdr_read_cap =3D be16_to_cpu(casn->sdr_read_cap); + struct spi_mem_op *read_ops; + const struct spi_mem_op *op; + int i =3D 0; + + read_ops =3D devm_kzalloc(&spinand->spimem->spi->dev, + sizeof(struct spi_mem_op) * + hweight16(sdr_read_cap), + GFP_KERNEL); + if (!read_ops) + return -ENOMEM; + + if (FIELD_GET(SDR_READ_1_4_4, sdr_read_cap)) { + read_ops[i] =3D (struct spi_mem_op) + SPINAND_CASN_PAGE_READ_FROM_CACHE_QUADIO_OP( + casn->sdr_read_1_4_4.addr_nbytes, 0, + casn->sdr_read_1_4_4.dummy_nbytes, NULL, 0 + ); + i++; + } + if (FIELD_GET(SDR_READ_1_1_4, sdr_read_cap)) { + read_ops[i] =3D (struct spi_mem_op) + SPINAND_CASN_PAGE_READ_FROM_CACHE_X4_OP( + casn->sdr_read_1_1_4.addr_nbytes, 0, + casn->sdr_read_1_1_4.dummy_nbytes, NULL, 0 + ); + i++; + } + if (FIELD_GET(SDR_READ_1_2_2, sdr_read_cap)) { + read_ops[i] =3D (struct spi_mem_op) + SPINAND_CASN_PAGE_READ_FROM_CACHE_DUALIO_OP( + casn->sdr_read_1_2_2.addr_nbytes, 0, + casn->sdr_read_1_2_2.dummy_nbytes, NULL, 0 + ); + i++; + } + if (FIELD_GET(SDR_READ_1_1_2, sdr_read_cap)) { + read_ops[i] =3D (struct spi_mem_op) + SPINAND_CASN_PAGE_READ_FROM_CACHE_X2_OP( + casn->sdr_read_1_1_2.addr_nbytes, 0, + casn->sdr_read_1_1_2.dummy_nbytes, NULL, 0 + ); + i++; + } + if (FIELD_GET(SDR_READ_1_1_1_FAST, sdr_read_cap)) { + read_ops[i] =3D (struct spi_mem_op) + SPINAND_CASN_PAGE_READ_FROM_CACHE_OP( + true, casn->sdr_read_1_1_1_fast.addr_nbytes, 0, + casn->sdr_read_1_1_1_fast.dummy_nbytes, NULL, 0 + ); + i++; + } + if (FIELD_GET(SDR_READ_1_1_1, sdr_read_cap)) { + read_ops[i] =3D (struct spi_mem_op) + SPINAND_CASN_PAGE_READ_FROM_CACHE_OP( + false, casn->sdr_read_1_1_1.addr_nbytes, 0, + casn->sdr_read_1_1_1.dummy_nbytes, NULL, 0 + ); + i++; + } + + casn_read_cache_variants =3D (struct spinand_op_variants){ + .ops =3D read_ops, + .nops =3D hweight16(sdr_read_cap), + }; + + op =3D spinand_select_op_variant(spinand, &casn_read_cache_variants); + if (!op) { + devm_kfree(&spinand->spimem->spi->dev, read_ops); + return -EOPNOTSUPP; + } + spinand->op_templates.read_cache =3D op; + + return 0; +} + +static int spinand_set_write_op_variants(struct spinand_device *spinand, + struct nand_casn *casn) +{ + struct spinand_op_variants casn_write_cache_variants; + struct spi_mem_op *write_ops; + const struct spi_mem_op *op; + int i =3D 0; + + write_ops =3D devm_kzalloc(&spinand->spimem->spi->dev, + sizeof(struct spi_mem_op) * + hweight8(casn->sdr_write_cap), + GFP_KERNEL); + if (!write_ops) + return -ENOMEM; + + if (FIELD_GET(SDR_WRITE_1_1_4, casn->sdr_write_cap)) { + write_ops[i] =3D (struct spi_mem_op) + SPINAND_CASN_PROG_LOAD_X4( + true, casn->sdr_write_1_1_4.addr_nbytes, 0, + NULL, 0); + i++; + } + if (FIELD_GET(SDR_WRITE_1_1_1, casn->sdr_write_cap)) { + write_ops[i] =3D (struct spi_mem_op) + SPINAND_CASN_PROG_LOAD( + true, casn->sdr_write_1_1_1.addr_nbytes, 0, + NULL, 0); + i++; + } + + casn_write_cache_variants =3D (struct spinand_op_variants){ + .ops =3D write_ops, + .nops =3D hweight8(casn->sdr_write_cap), + }; + + op =3D spinand_select_op_variant(spinand, &casn_write_cache_variants); + if (!op) { + devm_kfree(&spinand->spimem->spi->dev, write_ops); + return -EOPNOTSUPP; + } + spinand->op_templates.write_cache =3D op; + + return 0; +} + +static int spinand_set_update_op_variants(struct spinand_device *spinand, + struct nand_casn *casn) +{ + struct spinand_op_variants casn_update_cache_variants; + struct spi_mem_op *update_ops; + const struct spi_mem_op *op; + int i =3D 0; + + update_ops =3D devm_kzalloc(&spinand->spimem->spi->dev, + sizeof(struct spi_mem_op) * + hweight8(casn->sdr_update_cap), + GFP_KERNEL); + if (!update_ops) + return -ENOMEM; + + if (FIELD_GET(SDR_UPDATE_1_1_4, casn->sdr_update_cap)) { + update_ops[i] =3D (struct spi_mem_op) + SPINAND_CASN_PROG_LOAD_X4( + false, casn->sdr_update_1_1_4.addr_nbytes, 0, + NULL, 0); + i++; + } + if (FIELD_GET(SDR_UPDATE_1_1_1, casn->sdr_update_cap)) { + update_ops[i] =3D (struct spi_mem_op) + SPINAND_CASN_PROG_LOAD( + false, casn->sdr_update_1_1_1.addr_nbytes, 0, + NULL, 0); + i++; + } + + casn_update_cache_variants =3D (struct spinand_op_variants){ + .ops =3D update_ops, + .nops =3D hweight8(casn->sdr_update_cap), + }; + + op =3D spinand_select_op_variant(spinand, &casn_update_cache_variants); + if (!op) { + devm_kfree(&spinand->spimem->spi->dev, update_ops); + return -EOPNOTSUPP; + } + spinand->op_templates.update_cache =3D op; + + return 0; +} + +static int spinand_init_via_casn(struct spinand_device *spinand, + struct nand_casn *casn) +{ + struct nand_device *nand =3D spinand_to_nand(spinand); + u32 val; + int ret; + int i; + + /* Set members of nand->memorg via CASN. */ + for (i =3D 0; i < 9; i++) { + val =3D be32_to_cpu(*(&casn->bits_per_cell + i)); + memcpy((u32 *)&nand->memorg.bits_per_cell + i, &val, sizeof(u32)); + } + nand->ecc.requirements.strength =3D be32_to_cpu(casn->ecc_strength); + nand->ecc.requirements.step_size =3D be32_to_cpu(casn->ecc_step_size); + spinand->flags =3D casn->flags; + + if (spinand->flags & SPINAND_SUP_ADV_ECC_STATUS) { + spinand->eccinfo =3D (struct spinand_ecc_info) { + &spinand_casn_get_ecc_status, &spinand_casn_ooblayout}; + } else { + spinand->eccinfo =3D (struct spinand_ecc_info) { + NULL, &spinand_casn_ooblayout }; + } + + spinand->advecc_high_ops =3D devm_kzalloc(&spinand->spimem->spi->dev, + sizeof(struct spi_mem_op), + GFP_KERNEL); + if (!spinand->advecc_high_ops) + return -ENOMEM; + spinand->advecc_low_ops =3D devm_kzalloc(&spinand->spimem->spi->dev, + sizeof(struct spi_mem_op), + GFP_KERNEL); + if (!spinand->advecc_low_ops) + return -ENOMEM; + spinand->casn_oob =3D devm_kzalloc(&spinand->spimem->spi->dev, + sizeof(struct CASN_OOB), + GFP_KERNEL); + if (!spinand->casn_oob) + return -ENOMEM; + spinand->advecc_high =3D devm_kzalloc(&spinand->spimem->spi->dev, + sizeof(struct CASN_ADVECC), + GFP_KERNEL); + if (!spinand->advecc_high) + return -ENOMEM; + spinand->advecc_low =3D devm_kzalloc(&spinand->spimem->spi->dev, + sizeof(struct CASN_ADVECC), + GFP_KERNEL); + if (!spinand->advecc_low) + return -ENOMEM; + + *spinand->advecc_high_ops =3D (struct spi_mem_op) + SPINAND_CASN_ADVECC_OP(casn->ecc_status_high, spinand->scratchbuf); + *spinand->advecc_low_ops =3D (struct spi_mem_op) + SPINAND_CASN_ADVECC_OP(casn->ecc_status_low, spinand->scratchbuf); + + memcpy(spinand->casn_oob, &casn->casn_oob, sizeof(struct CASN_OOB)); + + spinand->advecc_high->cmd =3D casn->ecc_status_high.cmd; + spinand->advecc_high->mask =3D be16_to_cpu(casn->ecc_status_high.status_m= ask); + spinand->advecc_high->shift =3D spinand->advecc_high->mask ? + ffs(spinand->advecc_high->mask)-1 : 0; + spinand->advecc_high->pre_op =3D casn->ecc_status_high.pre_op; + spinand->advecc_high->pre_mask =3D casn->ecc_status_high.pre_mask; + + spinand->advecc_low->cmd =3D casn->ecc_status_low.cmd; + spinand->advecc_low->mask =3D be16_to_cpu(casn->ecc_status_low.status_mas= k); + spinand->advecc_low->shift =3D spinand->advecc_low->mask ? + ffs(spinand->advecc_low->mask)-1 : 0; + spinand->advecc_low->pre_op =3D casn->ecc_status_low.pre_op; + spinand->advecc_low->pre_mask =3D casn->ecc_status_low.pre_mask; + + spinand->advecc_low_bitcnt =3D hweight16(spinand->advecc_low->mask); + + spinand->advecc_noerr_status =3D casn->advecc_noerr_status; + spinand->advecc_uncor_status =3D casn->advecc_uncor_status; + spinand->advecc_post_op =3D casn->advecc_post_op; + spinand->advecc_post_mask =3D casn->advecc_post_mask; + spinand->eccsr_math_op[0] =3D eccsr_none_op; + spinand->eccsr_math_op[1] =3D eccsr_and_op; + spinand->eccsr_math_op[2] =3D eccsr_add_op; + spinand->eccsr_math_op[3] =3D eccsr_minus_op; + spinand->eccsr_math_op[4] =3D eccsr_mul_op; + + ret =3D spinand_set_read_op_variants(spinand, casn); + if (ret < 0) + return ret; + ret =3D spinand_set_write_op_variants(spinand, casn); + if (ret < 0) + return ret; + ret =3D spinand_set_update_op_variants(spinand, casn); + if (ret < 0) + return ret; + + return 0; +} + +static void spinand_dump_casn(struct spinand_device *spinand, struct nand_= casn *casn) +{ + int i; + + dev_dbg(&spinand->spimem->spi->dev, + "---Start dumping full CASN page---\n"); + for (i =3D 0; i < 64; i++) + pr_debug("0x%08x", *((u32 *)casn + i)); + + pr_debug("** Dump critical fields **\n"); + pr_debug("signature: 0x%04x\n", be32_to_cpu(casn->signature)); + pr_debug("version: v%u.%u\n", casn->version >> 4, casn->version & 0xf); + pr_debug("[Memory Organization]\n"); + pr_debug(" bits_per_cell: %d\n", be32_to_cpu(casn->bits_per_cell)); + pr_debug(" bytes_per_page: %d\n", be32_to_cpu(casn->bytes_per_page)); + pr_debug(" spare_bytes_per_page: %d\n", + be32_to_cpu(casn->spare_bytes_per_page)); + pr_debug(" pages_per_block: %d\n", + be32_to_cpu(casn->pages_per_block)); + pr_debug(" blocks_per_lun: %d\n", be32_to_cpu(casn->blocks_per_lun)); + pr_debug(" max_bb_per_lun: %d\n", be32_to_cpu(casn->max_bb_per_lun)); + pr_debug(" planes_per_lun: %d\n", be32_to_cpu(casn->planes_per_lun)); + pr_debug(" luns_per_target: %d\n", + be32_to_cpu(casn->luns_per_target)); + pr_debug(" total_target: %d\n", be32_to_cpu(casn->total_target)); + pr_debug("[flags]\n"); + pr_debug(" 0. Have QE bit? %s\n", + casn->flags & SPINAND_HAS_QE_BIT ? "Yes" : "No"); + pr_debug(" 1. Have continuous read feature bit? %s\n", + casn->flags & SPINAND_HAS_CR_FEAT_BIT ? "Yes" : "No"); + pr_debug(" 2. Support continuous read? %s\n", + casn->flags & SPINAND_SUP_CR ? "Yes" : "No"); + pr_debug(" 3. Support on-die ECC? %s\n", + casn->flags & SPINAND_SUP_ON_DIE_ECC ? "Yes" : "No"); + pr_debug(" 4. Support legacy ECC status? %s\n", + casn->flags & SPINAND_SUP_LEGACY_ECC_STATUS ? "Yes" : "No"); + pr_debug(" 5. Support advanced ECC status? %s\n", + casn->flags & SPINAND_SUP_ADV_ECC_STATUS ? "Yes" : "No"); + pr_debug(" 6. ECC parity readable? %s\n", + casn->flags & SPINAND_ECC_PARITY_READABLE ? "Yes" : "No"); + pr_debug("[R/W ability]\n"); + pr_debug(" read ability: %x\n", be16_to_cpu(casn->sdr_read_cap)); + pr_debug(" write ability: %x\n", casn->sdr_write_cap); + pr_debug(" update ability: %x\n", casn->sdr_update_cap); + pr_debug("advanced ECC no error state: %x\n", + casn->advecc_noerr_status); + pr_debug("advecced ECC uncorrectable state: %x\n", + casn->advecc_uncor_status); + pr_debug("CRC: 0x%04x\n", be16_to_cpu(casn->crc)); + + dev_dbg(&spinand->spimem->spi->dev, + "---Dumping full CASN page ends here.---\n"); +} + static int spinand_detect(struct spinand_device *spinand) { - struct device *dev =3D &spinand->spimem->spi->dev; struct nand_device *nand =3D spinand_to_nand(spinand); + struct device *dev =3D &spinand->spimem->spi->dev; + struct nand_casn *casn; + char manufacturer[14]; + unsigned int sel =3D 0; + char model[17]; int ret; =20 ret =3D spinand_reset_op(spinand); if (ret) return ret; =20 - ret =3D spinand_id_detect(spinand); - if (ret) { - dev_err(dev, "unknown raw ID %*phN\n", SPINAND_MAX_ID_LEN, - spinand->id.data); - return ret; + spinand->use_casn =3D false; + casn =3D kzalloc((sizeof(struct nand_casn) * CASN_PAGE_V1_COPIES), GFP_KE= RNEL); + if (!casn) + return -ENOMEM; + + ret =3D spinand_casn_detect(spinand, casn, &sel); + if (!ret) { + spinand->use_casn =3D true; + strscpy(manufacturer, casn[sel].manufacturer, sizeof(manufacturer)-1); + sanitize_string(manufacturer, sizeof(manufacturer)); + strscpy(model, casn[sel].model, sizeof(model)-1); + sanitize_string(model, sizeof(model)); + + spinand_dump_casn(spinand, casn + sel); + + ret =3D spinand_init_via_casn(spinand, casn + sel); + if (ret) + dev_err(dev, "Initializing spinand via CASN fails: %d\n", ret); + } + + if (ret < 0) { + dev_warn(dev, "Fallback to read ID\n"); + + ret =3D spinand_reset_op(spinand); + if (ret) + goto free_casn; + ret =3D spinand_id_detect(spinand); + if (ret) { + dev_err(dev, "unknown raw ID %*phN\n", SPINAND_MAX_ID_LEN, + spinand->id.data); + goto free_casn; + } } =20 if (nand->memorg.ntargets > 1 && !spinand->select_target) { dev_err(dev, "SPI NANDs with more than one die must implement ->select_target()\n"); - return -EINVAL; + ret =3D -EINVAL; + goto free_casn; + } + + if (spinand->use_casn) { + dev_info(&spinand->spimem->spi->dev, + "%s %s SPI NAND was found.\n", manufacturer, model); + } else { + dev_info(&spinand->spimem->spi->dev, + "%s SPI NAND was found.\n", spinand->manufacturer->name); } =20 - dev_info(&spinand->spimem->spi->dev, - "%s SPI NAND was found.\n", spinand->manufacturer->name); dev_info(&spinand->spimem->spi->dev, "%llu MiB, block size: %zu KiB, page size: %zu, OOB size: %u\n", nanddev_size(nand) >> 20, nanddev_eraseblock_size(nand) >> 10, nanddev_page_size(nand), nanddev_per_page_oobsize(nand)); =20 - return 0; +free_casn: + kfree(casn); + + return ret; } =20 static int spinand_init_flash(struct spinand_device *spinand) --=20 2.45.2