From nobody Mon May 11 04:11:52 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id ED899C433EF for ; Fri, 15 Apr 2022 03:49:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349228AbiDODvt (ORCPT ); Thu, 14 Apr 2022 23:51:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:32962 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349204AbiDODv0 (ORCPT ); Thu, 14 Apr 2022 23:51:26 -0400 Received: from mail-pg1-x529.google.com (mail-pg1-x529.google.com [IPv6:2607:f8b0:4864:20::529]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DED4A890B3 for ; Thu, 14 Apr 2022 20:48:58 -0700 (PDT) Received: by mail-pg1-x529.google.com with SMTP id q19so6402284pgm.6 for ; Thu, 14 Apr 2022 20:48:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=DIl3AqikPZER6I4mKf6gSASG8Lh6vs6cR1nnnXqOKQY=; b=MJXz4Ho7xuiIss/oargKVAqXgRQvvxUMc7y2EqJpUpzb7Y7plRzjCGbmTSSFs8dhbF 7URwyO+mQhwckawTecF2qPgg3qv7XOFYUsm8pY5fPyFsmkm27f/XTVe7FtpzOjJDtK3P 1rBqqJQDNDgsyFAl9i8LgyoWFQWblXWNGjufCgmy9n6863WjOf/fL7LDES3k0aF3U8Xg Hv4EZa3CENbXIAhh+QW3OQjAYLwxi1f4CbTYcqlqkGsAGMNquPRYhwJ19OuZ8pKHHgQ6 4wd9uHexkeZf4OzF2Dh944pq72Wgjzr7/E5H1dbSPUgQsRZK2EoZT5red5IqGX1isl6O 3iAQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=DIl3AqikPZER6I4mKf6gSASG8Lh6vs6cR1nnnXqOKQY=; b=taNXj5LOeekNr9vJAfUA7L1ZEkLlqOsCFJaa1T/+iyCbAROASaLW+VDVAxdheIATtn PoPmxoQecxczL71G+UwcecR0P0iV3x5BiCXEacVkVab/XgbSQsV7bdUcLIMeMF4L8d3+ vSt9vADGeXIk2i8iYrDOnkEMtps2sUrm/49tT38Sgd1jWI3Uci9NYBA7rOY4+Rs9xd2v g9HPA4MmoS4eKevIzrdHpD/6HGH+n7gRpiUcWSP9feNnDnRVxu4c67ESqRNA+PXRk/Pw 5sFVJOjlIBZzLghVYtp1WJ4nXcHEWAFhemzMXZpkUe3S8MnkO9pcn/Op9ZJj+kitjjBy gw2w== X-Gm-Message-State: AOAM530wY2dqeKB6vHdJF2+9p8KSPJZxDbRCKPReWMSJ/gHWByaiODn1 rk8PdUbosOR5f4uLfIys1M4= X-Google-Smtp-Source: ABdhPJwS+c+KhGwtHbJN3xlH20Hs/O2VnEsfuanibjXw1qDcTJlSqXCpnCd0R5Xk+F3Ol12lSl+h0A== X-Received: by 2002:a63:64c5:0:b0:3a0:8b4:70ca with SMTP id y188-20020a6364c5000000b003a008b470camr4716406pgb.449.1649994538317; Thu, 14 Apr 2022 20:48:58 -0700 (PDT) Received: from guoguo-omen.lan ([2401:c080:1400:4da2:b701:47d5:9291:4cf9]) by smtp.gmail.com with ESMTPSA id ng17-20020a17090b1a9100b001c9f79927bfsm7426380pjb.25.2022.04.14.20.48.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 14 Apr 2022 20:48:57 -0700 (PDT) From: Chuanhong Guo To: linux-mtd@lists.infradead.org Cc: Chuanhong Guo , Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Patrice Chotard , Boris Brezillon , Christophe Kerello , Mark Brown , Daniel Palmer , linux-kernel@vger.kernel.org (open list) Subject: [PATCH v2 1/3] mtd: nand: extract some onfi functions to nandcore Date: Fri, 15 Apr 2022 11:48:42 +0800 Message-Id: <20220415034844.1024538-2-gch981213@gmail.com> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220415034844.1024538-1-gch981213@gmail.com> References: <20220415034844.1024538-1-gch981213@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" These functions will be shared between raw nand and spi nand. Extract them into mtd/nand/onfi.c. Signed-off-by: Chuanhong Guo --- Change since v1: new patch drivers/mtd/nand/Makefile | 2 +- drivers/mtd/nand/onfi.c | 115 ++++++++++++++++++++++++++++++ drivers/mtd/nand/raw/internals.h | 1 - drivers/mtd/nand/raw/nand_base.c | 18 ----- drivers/mtd/nand/raw/nand_jedec.c | 4 +- drivers/mtd/nand/raw/nand_onfi.c | 70 ++---------------- include/linux/mtd/onfi.h | 8 +++ 7 files changed, 130 insertions(+), 88 deletions(-) create mode 100644 drivers/mtd/nand/onfi.c diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 19e1291ac4d5..4251be5e655d 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 onfi.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/onfi.c b/drivers/mtd/nand/onfi.c new file mode 100644 index 000000000000..bc8020cf70a9 --- /dev/null +++ b/drivers/mtd/nand/onfi.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ONFI helpers shared between raw nand and spi nand support + */ +#include + +/** + * onfi_bit_wise_majority() - recover data from multiple copies using bit-= wise + * majority + * + * @srcbufs: array of pointers to multiple copies + * @nsrcbufs: number of copies + * @dstbuf: pointer to the destination buffer + * @bufsize: length of data to recover + */ +void onfi_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; + } +} +EXPORT_SYMBOL_GPL(onfi_bit_wise_majority); + +/** + * onfi_crc16() - calculate CRC16 in ONFI parameter page + * @crc: value to start with + * @p: data buffer + * @len: length of the data + */ +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; +} +EXPORT_SYMBOL_GPL(onfi_crc16); + +/** + * onfi_parse_memorg() - parse nand memory organization from onfi paramete= rs + * @p: ONFI parameters + * @memorg: nand memory organization struct to write to + */ +void onfi_parse_memorg(const struct nand_onfi_params *p, + struct nand_memory_organization *memorg) +{ + memorg->pagesize =3D le32_to_cpu(p->byte_per_page); + + /* + * pages_per_block and blocks_per_lun may not be a power-of-2 size + * (don't ask me who thought of this...). MTD assumes that these + * dimensions will be power-of-2, so just truncate the remaining area. + */ + memorg->pages_per_eraseblock =3D + 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); + + memorg->oobsize =3D le16_to_cpu(p->spare_bytes_per_page); + + memorg->luns_per_target =3D p->lun_count; + memorg->planes_per_lun =3D 1 << p->interleaved_bits; + + /* See erasesize comment */ + memorg->eraseblocks_per_lun =3D + 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); + memorg->max_bad_eraseblocks_per_lun =3D le32_to_cpu(p->blocks_per_lun); + memorg->bits_per_cell =3D p->bits_per_cell; +} +EXPORT_SYMBOL_GPL(onfi_parse_memorg); + +/** + * onfi_sanitize_string() - Sanitize ONFI strings so we can safely print t= hem + * @s: string to sanitize + * @len: length of the buffer + */ +void onfi_sanitize_string(uint8_t *s, size_t len) +{ + ssize_t i; + + /* Null terminate */ + s[len - 1] =3D 0; + + /* Remove non printable chars */ + for (i =3D 0; i < len - 1; i++) { + if (s[i] < ' ' || s[i] > 127) + s[i] =3D '?'; + } + + /* Remove trailing spaces */ + strim(s); +} +EXPORT_SYMBOL_GPL(onfi_sanitize_string); diff --git a/drivers/mtd/nand/raw/internals.h b/drivers/mtd/nand/raw/intern= als.h index 7016e0f38398..e41c7d6d2c69 100644 --- a/drivers/mtd/nand/raw/internals.h +++ b/drivers/mtd/nand/raw/internals.h @@ -110,7 +110,6 @@ int nand_read_param_page_op(struct nand_chip *chip, u8 = page, void *buf, unsigned int len); void nand_decode_ext_id(struct nand_chip *chip); void panic_nand_wait(struct nand_chip *chip, unsigned long timeo); -void sanitize_string(uint8_t *s, size_t len); =20 static inline bool nand_has_exec_op(struct nand_chip *chip) { diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_b= ase.c index 6b67b7dfe7ce..b11b9042ff25 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -4715,24 +4715,6 @@ static void nand_set_defaults(struct nand_chip *chip) chip->buf_align =3D 1; } =20 -/* Sanitize ONFI strings so we can safely print them */ -void sanitize_string(uint8_t *s, size_t len) -{ - ssize_t i; - - /* Null terminate */ - s[len - 1] =3D 0; - - /* Remove non printable chars */ - for (i =3D 0; i < len - 1; i++) { - if (s[i] < ' ' || s[i] > 127) - s[i] =3D '?'; - } - - /* Remove trailing spaces */ - strim(s); -} - /* * nand_id_has_period - Check if an ID string has a given wraparound period * @id_data: the ID string diff --git a/drivers/mtd/nand/raw/nand_jedec.c b/drivers/mtd/nand/raw/nand_= jedec.c index 85b6d9372d80..e3fc59587da2 100644 --- a/drivers/mtd/nand/raw/nand_jedec.c +++ b/drivers/mtd/nand/raw/nand_jedec.c @@ -87,8 +87,8 @@ int nand_jedec_detect(struct nand_chip *chip) goto free_jedec_param_page; } =20 - sanitize_string(p->manufacturer, sizeof(p->manufacturer)); - sanitize_string(p->model, sizeof(p->model)); + onfi_sanitize_string(p->manufacturer, sizeof(p->manufacturer)); + onfi_sanitize_string(p->model, sizeof(p->model)); chip->parameters.model =3D kstrdup(p->model, GFP_KERNEL); if (!chip->parameters.model) { ret =3D -ENOMEM; diff --git a/drivers/mtd/nand/raw/nand_onfi.c b/drivers/mtd/nand/raw/nand_o= nfi.c index 7586befce7f9..1d5734942b08 100644 --- a/drivers/mtd/nand/raw/nand_onfi.c +++ b/drivers/mtd/nand/raw/nand_onfi.c @@ -18,18 +18,6 @@ =20 #define ONFI_PARAM_PAGES 3 =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; -} - /* Parse the Extended Parameter Page. */ static int nand_flash_detect_ext_param_page(struct nand_chip *chip, struct nand_onfi_params *p) @@ -107,37 +95,6 @@ static int nand_flash_detect_ext_param_page(struct nand= _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. */ @@ -201,7 +158,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, + onfi_bit_wise_majority(srcbufs, ONFI_PARAM_PAGES, pbuf, sizeof(*pbuf)); =20 crc =3D onfi_crc16(ONFI_CRC_BASE, (u8 *)pbuf, 254); @@ -234,38 +191,19 @@ int nand_onfi_detect(struct nand_chip *chip) goto free_onfi_param_page; } =20 - sanitize_string(p->manufacturer, sizeof(p->manufacturer)); - sanitize_string(p->model, sizeof(p->model)); + onfi_sanitize_string(p->manufacturer, sizeof(p->manufacturer)); + onfi_sanitize_string(p->model, sizeof(p->model)); chip->parameters.model =3D kstrdup(p->model, GFP_KERNEL); if (!chip->parameters.model) { ret =3D -ENOMEM; goto free_onfi_param_page; } =20 - memorg->pagesize =3D le32_to_cpu(p->byte_per_page); + onfi_parse_memorg(p, memorg); mtd->writesize =3D memorg->pagesize; - - /* - * pages_per_block and blocks_per_lun may not be a power-of-2 size - * (don't ask me who thought of this...). MTD assumes that these - * dimensions will be power-of-2, so just truncate the remaining area. - */ - memorg->pages_per_eraseblock =3D - 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); mtd->erasesize =3D memorg->pages_per_eraseblock * memorg->pagesize; - - memorg->oobsize =3D le16_to_cpu(p->spare_bytes_per_page); mtd->oobsize =3D memorg->oobsize; =20 - memorg->luns_per_target =3D p->lun_count; - memorg->planes_per_lun =3D 1 << p->interleaved_bits; - - /* See erasesize comment */ - memorg->eraseblocks_per_lun =3D - 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); - memorg->max_bad_eraseblocks_per_lun =3D le32_to_cpu(p->blocks_per_lun); - memorg->bits_per_cell =3D p->bits_per_cell; - if (le16_to_cpu(p->features) & ONFI_FEATURE_16_BIT_BUS) chip->options |=3D NAND_BUSWIDTH_16; =20 diff --git a/include/linux/mtd/onfi.h b/include/linux/mtd/onfi.h index a7376f9beddf..728d9ee9dabe 100644 --- a/include/linux/mtd/onfi.h +++ b/include/linux/mtd/onfi.h @@ -12,6 +12,7 @@ =20 #include #include +#include =20 /* ONFI version bits */ #define ONFI_VERSION_1_0 BIT(1) @@ -186,4 +187,11 @@ struct onfi_params { u8 vendor[88]; }; =20 +void onfi_bit_wise_majority(const void **srcbufs, unsigned int nsrcbufs, + void *dstbuf, unsigned int bufsize); +u16 onfi_crc16(u16 crc, u8 const *p, size_t len); +void onfi_parse_memorg(const struct nand_onfi_params *p, + struct nand_memory_organization *memorg); +void onfi_sanitize_string(uint8_t *s, size_t len); + #endif /* __LINUX_MTD_ONFI_H */ --=20 2.35.1 From nobody Mon May 11 04:11:52 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 91C89C433F5 for ; Fri, 15 Apr 2022 03:49:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349276AbiDODwA (ORCPT ); Thu, 14 Apr 2022 23:52:00 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33198 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349239AbiDODvo (ORCPT ); Thu, 14 Apr 2022 23:51:44 -0400 Received: from mail-pf1-x42d.google.com (mail-pf1-x42d.google.com [IPv6:2607:f8b0:4864:20::42d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 836368BF22 for ; Thu, 14 Apr 2022 20:49:03 -0700 (PDT) Received: by mail-pf1-x42d.google.com with SMTP id n43so2439184pfv.10 for ; Thu, 14 Apr 2022 20:49:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=cOu5HOeeJxgrmt06WrnIN1Ju5cm6rMHpTJlllT1DDNQ=; b=EfClDapPtVo1yqlijxLL7X/Bucnv+wzfD1WZpi+OSqW7b1vd1WdluJYO1IYUQNZo+w ozmZqNDNuZvrXsGLeqcWeoL6igjv8JCq42Yoo475fX/0Qi7F4LzGmsMEoc21T3UTOn00 smYHgJnX/0AZSY0oqwPFydf37jkzkQWFxkaXWnOpvzvOrHxfxKq3l4QTML1j25nDjejM KDN4vV2uuk6dtE9f40LGRijb4RH5tQ5x2iE8Pd2n2vtgO4WzktBe11AdOqRYNJ0d1YZY D5z9oWDnkdzhKgnv7yv/ypS+u//K0WNp46OHHeCj4oDXG3e+MuAWzlKfM7EPq5gd7//q 9e9w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=cOu5HOeeJxgrmt06WrnIN1Ju5cm6rMHpTJlllT1DDNQ=; b=yoASQg5yokpTQWVVX6Ihf3plB/nXTZZBNQHatUSib2o2659BQ8cdVeXJ9Ukhld9Bjd QH8FIHoScTyCFvsv7FPqVJmlz4USgklkSM7u9Tp1zbQPfYuJ4vBc9Yfk1DpIuapWOZwm X/4mMDqBddrXOSpUsCtnRM4yBlxrZEMKj6P15dsQcox0H4EchJesZB6v8rz3jPWb2kvY JZgEjjQFoiD+NB4rBYlQBlDje2EENrYjABKZ3rX5KTS2bWNad8HQDcD1KNPTm4sB6bH8 OiK4FBveleV7qU+L4RWlnI//JXE+4njN3xkv5KUnIxLpzcUGCPPNlGK07GzJEDze0p9u W2TQ== X-Gm-Message-State: AOAM533lNt5zX4FSyrFm+H3y9SVcC6Ed8zu/mX2UyM22lfAETbDFPy5A ov+DklE6wHCkm5ZHvqf6Hh4= X-Google-Smtp-Source: ABdhPJxMzGvfNso7R08vvW0ZL/vN9D5T1cMUnBq1Ng2xq0RpC/6Iq+Ah2wKErNe4ZbEWQRxxDe752Q== X-Received: by 2002:a63:cf41:0:b0:399:3e74:d249 with SMTP id b1-20020a63cf41000000b003993e74d249mr4880310pgj.475.1649994542974; Thu, 14 Apr 2022 20:49:02 -0700 (PDT) Received: from guoguo-omen.lan ([2401:c080:1400:4da2:b701:47d5:9291:4cf9]) by smtp.gmail.com with ESMTPSA id ng17-20020a17090b1a9100b001c9f79927bfsm7426380pjb.25.2022.04.14.20.48.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 14 Apr 2022 20:49:02 -0700 (PDT) From: Chuanhong Guo To: linux-mtd@lists.infradead.org Cc: Chuanhong Guo , Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Patrice Chotard , Boris Brezillon , Christophe Kerello , Mark Brown , Daniel Palmer , linux-kernel@vger.kernel.org (open list) Subject: [PATCH v2 2/3] mtd: spinand: add support for detection with param page Date: Fri, 15 Apr 2022 11:48:43 +0800 Message-Id: <20220415034844.1024538-3-gch981213@gmail.com> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220415034844.1024538-1-gch981213@gmail.com> References: <20220415034844.1024538-1-gch981213@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" SPI-NAND detection using chip ID isn't always reliable. Here are two known cases: 1. ESMT uses JEDEC ID from other vendors. This may collapse with future chips. 2. Winbond W25N01KV uses the exact same JEDEC ID as W25N01GV while having completely different chip parameters. Recent SPI-NANDs have a parameter page which is stored in the same format as raw NAND ONFI data. There are strings for chip manufacturer and chip model. Chip ECC requirement and memory organization are available too. This patch adds support for detecting SPI-NANDs with the parameter page after ID matching failed. In this detection, memory organization and ECC requirements are taken from the parameter page, and other SPI-NAND specific parameters are obtained by matching chip model string with a separated table. It's common for vendors to release a series of SPI-NANDs with the same SPI-NAND parameters in different voltages and/or capacities. The chip table defined in this patch supports multiple model strings in one entry, and multiple chip models can be covered using only one entry. Signed-off-by: Chuanhong Guo --- The parameter page has sufficient checking to reject garbage data, and the reading sequence seems to be the same across vendors. So I think it isn't necessary to keep it vendor-private. Change since v1: drop duplicated code from rawnand and use extracted functions instead. drivers/mtd/nand/spi/Makefile | 2 +- drivers/mtd/nand/spi/core.c | 23 ++-- drivers/mtd/nand/spi/onfi.c | 217 ++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 50 ++++++++ 4 files changed, 280 insertions(+), 12 deletions(-) create mode 100644 drivers/mtd/nand/spi/onfi.c diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index 9662b9c1d5a9..a4e057cbdaf7 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs :=3D core.o gigadevice.o macronix.o micron.o paragon.o toshib= a.o winbond.o +spinand-objs :=3D core.o gigadevice.o macronix.o micron.o onfi.o paragon.o= toshiba.o winbond.o obj-$(CONFIG_MTD_SPI_NAND) +=3D spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index ff8336870bc0..3b51ca7232d0 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -20,7 +20,7 @@ #include #include =20 -static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 = *val) +int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val) { struct spi_mem_op op =3D SPINAND_GET_FEATURE_OP(reg, spinand->scratchbuf); @@ -34,7 +34,7 @@ static int spinand_read_reg_op(struct spinand_device *spi= nand, u8 reg, u8 *val) return 0; } =20 -static int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8= val) +int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val) { struct spi_mem_op op =3D SPINAND_SET_FEATURE_OP(reg, spinand->scratchbuf); @@ -339,7 +339,7 @@ static void spinand_ondie_ecc_save_status(struct nand_d= evice *nand, u8 status) engine_conf->status =3D status; } =20 -static int spinand_write_enable_op(struct spinand_device *spinand) +int spinand_write_enable_op(struct spinand_device *spinand) { struct spi_mem_op op =3D SPINAND_WR_EN_DIS_OP(true); =20 @@ -496,10 +496,8 @@ static int spinand_erase_op(struct spinand_device *spi= nand, return spi_mem_exec_op(spinand->spimem, &op); } =20 -static int spinand_wait(struct spinand_device *spinand, - unsigned long initial_delay_us, - unsigned long poll_delay_us, - u8 *s) +int spinand_wait(struct spinand_device *spinand, unsigned long initial_del= ay_us, + unsigned long poll_delay_us, u8 *s) { struct spi_mem_op op =3D SPINAND_GET_FEATURE_OP(REG_STATUS, spinand->scratchbuf); @@ -1006,7 +1004,7 @@ static void spinand_manufacturer_cleanup(struct spina= nd_device *spinand) return spinand->manufacturer->ops->cleanup(spinand); } =20 -static const struct spi_mem_op * +const struct spi_mem_op * spinand_select_op_variant(struct spinand_device *spinand, const struct spinand_op_variants *variants) { @@ -1117,9 +1115,12 @@ static int spinand_detect(struct spinand_device *spi= nand) =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; + ret =3D spinand_onfi_detect(spinand); + if (ret) { + dev_err(dev, "unknown raw ID %*phN\n", + SPINAND_MAX_ID_LEN, spinand->id.data); + return ret; + } } =20 if (nand->memorg.ntargets > 1 && !spinand->select_target) { diff --git a/drivers/mtd/nand/spi/onfi.c b/drivers/mtd/nand/spi/onfi.c new file mode 100644 index 000000000000..6d3a7e7c4f6e --- /dev/null +++ b/drivers/mtd/nand/spi/onfi.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Detect and match SPI-NAND info using ONFI parameter page + * + * Author: + * Chuanhong Guo + * + * Part of this code comes from nand_onfi.c in raw nand support. + * + */ +#include +#include + +#define SPINAND_IDR_EN BIT(6) +#define SPINAND_PARAM_PAGE 1 +#define ONFI_PARAM_PAGES 3 + +static int spinand_onfi_read(struct spinand_device *spinand, void *buf, + size_t len) +{ + const struct spi_mem_op load_page =3D + SPINAND_PAGE_READ_OP(SPINAND_PARAM_PAGE); + struct spi_mem_op read_cache =3D + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, buf, len); + int ret; + u8 cfg; + + ret =3D spinand_read_reg_op(spinand, REG_CFG, &cfg); + if (ret) + return ret; + + ret =3D spinand_write_enable_op(spinand); + if (ret) + return ret; + + ret =3D spinand_write_reg_op(spinand, REG_CFG, cfg | SPINAND_IDR_EN); + if (ret) + return ret; + + ret =3D spinand_read_reg_op(spinand, REG_CFG, &cfg); + if (ret) + return ret; + + if (!(cfg & SPINAND_IDR_EN)) + return -EINVAL; + + ret =3D spi_mem_exec_op(spinand->spimem, &load_page); + if (ret) + goto cleanup; + + ret =3D spinand_wait(spinand, SPINAND_READ_INITIAL_DELAY_US, + SPINAND_READ_POLL_DELAY_US, NULL); + if (ret) + goto cleanup; + + while (len) { + ret =3D spi_mem_adjust_op_size(spinand->spimem, &read_cache); + if (ret) + goto cleanup; + ret =3D spi_mem_exec_op(spinand->spimem, &read_cache); + if (ret) + goto cleanup; + read_cache.addr.val +=3D read_cache.data.nbytes; + read_cache.data.buf.in +=3D read_cache.data.nbytes; + len -=3D read_cache.data.nbytes; + read_cache.data.nbytes =3D len; + } +cleanup: + spinand_write_reg_op(spinand, REG_CFG, cfg & ~SPINAND_IDR_EN); + return ret; +} + +static bool spinand_onfi_validate(const struct nand_onfi_params *p) +{ + u16 crc; + + if (strncmp("ONFI", p->sig, 4)) + return false; + crc =3D onfi_crc16(ONFI_CRC_BASE, (u8 *)p, sizeof(*p) - 2); + return crc =3D=3D le16_to_cpu(p->crc); +} + +static const struct spinand_manufacturer *spinand_onfi_manufacturers[] =3D= {}; + +static const struct spinand_onfi_info * +spinand_onfi_chip_match(struct nand_onfi_params *p, + const struct spinand_manufacturer *m) +{ + size_t i, j; + + for (i =3D 0; i < m->nchips; i++) + for (j =3D 0; m->onfi_chips[i].models[j]; j++) + if (!strcasecmp(m->onfi_chips[i].models[j], p->model)) + return &m->onfi_chips[i]; + return NULL; +} + +static const struct spinand_onfi_info * +spinand_onfi_manufacturer_match(struct spinand_device *spinand, + struct nand_onfi_params *p) +{ + const struct spinand_onfi_info *ret; + size_t i; + + for (i =3D 0; i < ARRAY_SIZE(spinand_onfi_manufacturers); i++) { + if (strcasecmp(spinand_onfi_manufacturers[i]->name, + p->manufacturer)) + continue; + spinand->manufacturer =3D spinand_onfi_manufacturers[i]; + ret =3D spinand_onfi_chip_match(p, spinand_onfi_manufacturers[i]); + if (ret) + return ret; + } + return NULL; +} + +/** + * spinand_onfi_detect() - match SPI-NAND using ONFI parameter page + * @spinand: spinand private structure + * + * Return: + * 0: Success + * -EINVAL: failed to read a valid parameter page + * -EOPNOTSUPP: chip isn't supported + */ +int spinand_onfi_detect(struct spinand_device *spinand) +{ + struct nand_onfi_params *p =3D NULL, *pbuf; + size_t params_len =3D sizeof(*pbuf) * ONFI_PARAM_PAGES; + struct nand_device *nand =3D spinand_to_nand(spinand); + struct nand_memory_organization *memorg =3D nanddev_get_memorg(nand); + const struct spi_mem_op *op; + const struct spinand_onfi_info *info; + int i, ret; + + pbuf =3D kzalloc(params_len, GFP_KERNEL); + if (!pbuf) + return -ENOMEM; + + ret =3D spinand_onfi_read(spinand, pbuf, params_len); + if (ret) + goto cleanup; + + for (i =3D 0; i < ONFI_PARAM_PAGES; i++) { + if (spinand_onfi_validate(&pbuf[i])) { + p =3D &pbuf[i]; + break; + } + } + + if (!p) { + const void *srcbufs[ONFI_PARAM_PAGES]; + unsigned int j; + + for (j =3D 0; j < ONFI_PARAM_PAGES; j++) + srcbufs[j] =3D pbuf + j; + onfi_bit_wise_majority(srcbufs, ONFI_PARAM_PAGES, pbuf, + sizeof(*pbuf)); + if (spinand_onfi_validate(pbuf)) + p =3D pbuf; + } + + if (!p) { + ret =3D -EINVAL; + goto cleanup; + } + + onfi_sanitize_string(p->manufacturer, sizeof(p->manufacturer)); + onfi_sanitize_string(p->model, sizeof(p->model)); + + info =3D spinand_onfi_manufacturer_match(spinand, p); + if (!info) { + dev_err(&spinand->spimem->spi->dev, "unknown chip: %s %s", + p->manufacturer, p->model); + ret =3D -EOPNOTSUPP; + goto cleanup; + } + + onfi_parse_memorg(p, memorg); + memorg->ntargets =3D 1; + + if (p->ecc_bits !=3D 0xff) { + struct nand_ecc_props requirements =3D { + .strength =3D p->ecc_bits, + .step_size =3D p->data_bytes_per_ppage, + }; + + nanddev_set_ecc_requirements(nand, &requirements); + } else { + ret =3D -EINVAL; + } + + /* setup spi-nand specific ops */ + spinand->eccinfo =3D info->eccinfo; + spinand->flags =3D info->flags; + spinand->id.len =3D 0; + spinand->select_target =3D info->select_target; + + op =3D spinand_select_op_variant(spinand, info->op_variants.read_cache); + if (!op) + return -EOPNOTSUPP; + + spinand->op_templates.read_cache =3D op; + + op =3D spinand_select_op_variant(spinand, info->op_variants.write_cache); + if (!op) + return -EOPNOTSUPP; + + spinand->op_templates.write_cache =3D op; + + op =3D spinand_select_op_variant(spinand, info->op_variants.update_cache); + spinand->op_templates.update_cache =3D op; + +cleanup: + kfree(pbuf); + return ret; +} diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 3aa28240a77f..dc218082d773 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -255,6 +255,7 @@ struct spinand_manufacturer { u8 id; char *name; const struct spinand_info *chips; + const struct spinand_onfi_info *onfi_chips; const size_t nchips; const struct spinand_manufacturer_ops *ops; }; @@ -386,6 +387,46 @@ struct spinand_info { __VA_ARGS__ \ } =20 +/** + * struct spinand_onfi_info - Structure used to describe SPI NAND with ONFI + * parameter page + * @models: Model name array. Null terminated. + * @flags: OR-ing of the SPINAND_XXX flags + * @eccinfo: on-die ECC info + * @op_variants: operations variants + * @op_variants.read_cache: variants of the read-cache operation + * @op_variants.write_cache: variants of the write-cache operation + * @op_variants.update_cache: variants of the update-cache operation + * @select_target: function used to select a target/die. Required only for + * multi-die chips + * + * Each SPI NAND manufacturer driver should have a spinand_onfi_info table + * describing all the chips supported by the driver. + */ +struct spinand_onfi_info { + const char **const models; + u32 flags; + struct spinand_ecc_info eccinfo; + struct { + const struct spinand_op_variants *read_cache; + const struct spinand_op_variants *write_cache; + const struct spinand_op_variants *update_cache; + } op_variants; + int (*select_target)(struct spinand_device *spinand, + unsigned int target); +}; + +#define SPINAND_ONFI_MODELS(...) \ + .models =3D (const char *[]){ __VA_ARGS__, NULL } + +#define SPINAND_ONFI_INFO(__models, __op_variants, __flags, ...) \ + { \ + __models, \ + .op_variants =3D __op_variants, \ + .flags =3D __flags, \ + __VA_ARGS__ \ + } + struct spinand_dirmap { struct spi_mem_dirmap_desc *wdesc; struct spi_mem_dirmap_desc *rdesc; @@ -511,7 +552,16 @@ int spinand_match_and_init(struct spinand_device *spin= and, unsigned int table_size, enum spinand_readid_method rdid_method); =20 +int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val); +int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val); +int spinand_write_enable_op(struct spinand_device *spinand); int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val); +int spinand_onfi_detect(struct spinand_device *spinand); +const struct spi_mem_op * +spinand_select_op_variant(struct spinand_device *spinand, + const struct spinand_op_variants *variants); int spinand_select_target(struct spinand_device *spinand, unsigned int tar= get); +int spinand_wait(struct spinand_device *spinand, unsigned long initial_del= ay_us, + unsigned long poll_delay_us, u8 *s); =20 #endif /* __LINUX_MTD_SPINAND_H */ --=20 2.35.1 From nobody Mon May 11 04:11:52 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9AD8EC433EF for ; Fri, 15 Apr 2022 03:49:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349295AbiDODwJ (ORCPT ); Thu, 14 Apr 2022 23:52:09 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33202 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349240AbiDODvo (ORCPT ); Thu, 14 Apr 2022 23:51:44 -0400 Received: from mail-pl1-x631.google.com (mail-pl1-x631.google.com [IPv6:2607:f8b0:4864:20::631]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 070839A9AB for ; Thu, 14 Apr 2022 20:49:08 -0700 (PDT) Received: by mail-pl1-x631.google.com with SMTP id c23so6288040plo.0 for ; Thu, 14 Apr 2022 20:49:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=hmXe1JnisYjIy8E3ctv5eLkxXX1YzruQ3DOG/Iu3yuw=; b=b/yYhWtksjk3exw7rSoqjIkeOofJYEP5CgsOJkjkYUZBZAFlAsRtUUFTgiUeVy3yKF AM1Ooe0GXd2xMR/8a8Qc8v5DRM3sq+IMC5BIa7jBY4ExR4mNjBS+kEla4kEER1WwKqCr mXLEupITf2Qkng+472V44axDCkP7C0RV089LF8BmqZ0XlGSpkTvBVPoWM9d5G654Ps8R J5YYf1JwO3g6Viwwm+1CeKXprslSbCNdriAPG/pv79bDLpQfoSZhbyMctp23K6F/P1Lp QAUzdCJvDatRxT8r9n4DhpxaLRVtFyR0luR2a5DaPIyCMLEJ2rD0ohp7/Fi/EDWamgae cXog== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=hmXe1JnisYjIy8E3ctv5eLkxXX1YzruQ3DOG/Iu3yuw=; b=iemmrzFZ/h9QwSG2g/h4f294lTPxVIuCSa/mHweZ/li78hWcFayPlx/D58rfnfZK8L eto9r6JlaZ8w7ba01ATk/ZeDkkGm1aOSOD6vPyBPY9591540vfTEiBad17Lj3wdMxb77 t/y67pJ8A5GsBF/htfjIO0Cmh+srtvtB4EBpbN/7TS7hZSoXx+HO1uf2RP7pithAKRmo E+R6sMP5sps9vwN8lopH4zcFHYnV8YwGh9JhJxHxov3tJfdDUUe9d7rGZYVyDFJsDjnV 6sW8Ih5TNrdLFX9w5Aw7BTtHXhRcKnJawQtH3F+yN1QeCRR9txCVWjwpDYFNaVFxSq2q aiHQ== X-Gm-Message-State: AOAM532rPcmopOUPn1XSSWiXpzxvJfCQ9ZQQBJFoii/fE+XIq1b2kLNj kYTjfT8rAEAI559N5dSoGEI= X-Google-Smtp-Source: ABdhPJwG/IiJivi3KNf7cYN8eCaNCZZrwpo7X42fMk+AI66iUpJnCF7j8NSW42TdYA/40tUEUjVhzg== X-Received: by 2002:a17:902:e891:b0:158:8b58:b94 with SMTP id w17-20020a170902e89100b001588b580b94mr15047555plg.161.1649994547585; Thu, 14 Apr 2022 20:49:07 -0700 (PDT) Received: from guoguo-omen.lan ([2401:c080:1400:4da2:b701:47d5:9291:4cf9]) by smtp.gmail.com with ESMTPSA id ng17-20020a17090b1a9100b001c9f79927bfsm7426380pjb.25.2022.04.14.20.49.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 14 Apr 2022 20:49:07 -0700 (PDT) From: Chuanhong Guo To: linux-mtd@lists.infradead.org Cc: Chuanhong Guo , Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Patrice Chotard , Boris Brezillon , Christophe Kerello , Mark Brown , Daniel Palmer , linux-kernel@vger.kernel.org (open list) Subject: [PATCH v2 3/3] mtd: spinand: probe Winbond W25N01GV/W using param page Date: Fri, 15 Apr 2022 11:48:44 +0800 Message-Id: <20220415034844.1024538-4-gch981213@gmail.com> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220415034844.1024538-1-gch981213@gmail.com> References: <20220415034844.1024538-1-gch981213@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" The JEDEC ID of EFAA21 is assigned to both W25N01G and W25N01K. Probing the chip with JEDEC ID isn't reliable anymore. Use parameter page instead. Signed-off-by: Chuanhong Guo --- Change since v1: none drivers/mtd/nand/spi/onfi.c | 4 +++- drivers/mtd/nand/spi/winbond.c | 25 ++++++++++++++++--------- include/linux/mtd/spinand.h | 3 +++ 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/nand/spi/onfi.c b/drivers/mtd/nand/spi/onfi.c index 6d3a7e7c4f6e..45447112a96d 100644 --- a/drivers/mtd/nand/spi/onfi.c +++ b/drivers/mtd/nand/spi/onfi.c @@ -80,7 +80,9 @@ static bool spinand_onfi_validate(const struct nand_onfi_= params *p) return crc =3D=3D le16_to_cpu(p->crc); } =20 -static const struct spinand_manufacturer *spinand_onfi_manufacturers[] =3D= {}; +static const struct spinand_manufacturer *spinand_onfi_manufacturers[] =3D= { + &winbond_onfi_spinand_manufacturer, +}; =20 static const struct spinand_onfi_info * spinand_onfi_chip_match(struct nand_onfi_params *p, diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index 76684428354e..601316c80b3e 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -85,15 +85,15 @@ static const struct spinand_info winbond_spinand_table[= ] =3D { 0, SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), SPINAND_SELECT_TARGET(w25m02gv_select_target)), - SPINAND_INFO("W25N01GV", - SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa), - NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), - NAND_ECCREQ(1, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), - 0, - SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), +}; + +static const struct spinand_onfi_info winbond_spinand_onfi_table[] =3D { + SPINAND_ONFI_INFO(SPINAND_ONFI_MODELS("W25N01GV", "W25N01GW"), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), }; =20 static int winbond_spinand_init(struct spinand_device *spinand) @@ -125,3 +125,10 @@ const struct spinand_manufacturer winbond_spinand_manu= facturer =3D { .nchips =3D ARRAY_SIZE(winbond_spinand_table), .ops =3D &winbond_spinand_manuf_ops, }; + +const struct spinand_manufacturer winbond_onfi_spinand_manufacturer =3D { + .name =3D "Winbond", + .onfi_chips =3D winbond_spinand_onfi_table, + .nchips =3D ARRAY_SIZE(winbond_spinand_onfi_table), + .ops =3D &winbond_spinand_manuf_ops, +}; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index dc218082d773..610320b03773 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -268,6 +268,9 @@ extern const struct spinand_manufacturer paragon_spinan= d_manufacturer; extern const struct spinand_manufacturer toshiba_spinand_manufacturer; extern const struct spinand_manufacturer winbond_spinand_manufacturer; =20 +/* SPI NAND manufacturers with ONFI parameter page support */ +extern const struct spinand_manufacturer winbond_onfi_spinand_manufacturer; + /** * struct spinand_op_variants - SPI NAND operation variants * @ops: the list of variants for a given operation --=20 2.35.1