From nobody Thu Apr 2 22:00:10 2026 Received: from smtpout-02.galae.net (smtpout-02.galae.net [185.246.84.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BDFB14035CD for ; Thu, 26 Mar 2026 16:26:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.84.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774542378; cv=none; b=qQDIU8fv/PWjBWIDQHy3zji03SysCfY3hR/kPOAlTuBc2ukkre83vu7H2rIy4cdgqFWqKVCYnsWX/oFwFtMl76C2DaHzj17l3u2zzM8zKhA4AaUU27QeH2snd15gY9jM7mPaR1Q9rlQJq/2zMF8jlcCoGQsL1vwREI5OejKYeCQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774542378; c=relaxed/simple; bh=TccMusl6o+BjgyLnvQLGHUHiygf8wYGhTl2JaQXdtAI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=IMCLB3fLwJcWNEzDZNFmLjn8FeSg1/wgRPCwbdW6eCBHl5bpTZntVZJnrP8wvDw0459R1ZEN39HfzOTaKlCDXNM7raW2HRrYrQuT/Rs+OjJu7zpfIBzhMM8EumKwuWi6UiEPC263rVLSXaBEx28XHgXNZAkpwsLKDe4Px/M9nzQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=FX+aXyR+; arc=none smtp.client-ip=185.246.84.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="FX+aXyR+" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-02.galae.net (Postfix) with ESMTPS id 6769C1A300A; Thu, 26 Mar 2026 16:26:15 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 370DB601FA; Thu, 26 Mar 2026 16:26:15 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id A616F10450C95; Thu, 26 Mar 2026 17:26:13 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1774542374; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:references; bh=lFv1r9faEQTu1UkMy2vWIxsC6tbdpaaG5r+kI87RNaQ=; b=FX+aXyR+ZQsx+lELxkC4W095Bv8MkKG/9OTCbji+dUaqS65mJiWgCk0ASLmRNqcL3iwEzS T2ZkfcTlE5B/JcSuQ3YOvob6hCeUYSlNCe0g9NkIUk9nq1VonjwevpGMQzQ1x13yOg5t0b 1C0uypUPLqEAxwHNkz2Kjh90/pwE14rGCyAOJ2rfhIh6GyUMUbRxBJHXw1UlonDort14bT 6bBpVV5o8lDaRaKJEfZRTFvXZDVLyE4u+WX7Ar5D2mPzXFbM5e0iYnyRlF8ePvjSTGctTm ns/IF97kyBQ1dHiCKbQ2hBKfoCZoBbvw4BWtPWoR0niCRSq1OmLBkth9JDsUjw== From: Miquel Raynal Date: Thu, 26 Mar 2026 17:25:53 +0100 Subject: [PATCH v2 06/11] mtd: spinand: Use secondary ops for continuous reads Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260326-winbond-v6-18-rc1-cont-read-v2-6-643de97a68a3@bootlin.com> References: <20260326-winbond-v6-18-rc1-cont-read-v2-0-643de97a68a3@bootlin.com> In-Reply-To: <20260326-winbond-v6-18-rc1-cont-read-v2-0-643de97a68a3@bootlin.com> To: Mark Brown , Richard Weinberger , Vignesh Raghavendra , Michael Walle , Miquel Raynal Cc: Pratyush Yadav , Thomas Petazzoni , Steam Lin , Santhosh Kumar K , linux-spi@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mtd@lists.infradead.org X-Mailer: b4 0.14.3 X-Last-TLS-Session-Version: TLSv1.3 In case a chip supports continuous reads, but uses a slightly different cache operation for these, it may provide a secondary operation template which will be used only during continuous cache read operations. From a vendor driver point of view, enabling this feature implies providing a new set of templates for these continuous read operations. The core will automatically pick the fastest variant, depending on the hardware capabilities. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/core.c | 61 +++++++++++++++++++++++++++++++++++++++++= +++- include/linux/mtd/spinand.h | 12 +++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index a66510747b31..45c3afb9cceb 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -489,6 +489,11 @@ static int spinand_read_from_cache_op(struct spinand_d= evice *spinand, =20 rdesc =3D spinand->dirmaps[req->pos.plane].rdesc; =20 + if (spinand->op_templates->cont_read_cache && req->continuous) + rdesc->info.op_tmpl =3D &rdesc->info.secondary_op_tmpl; + else + rdesc->info.op_tmpl =3D &rdesc->info.primary_op_tmpl; + if (nand->ecc.engine->integration =3D=3D NAND_ECC_ENGINE_INTEGRATION_PIPE= LINED && req->mode !=3D MTD_OPS_RAW) rdesc->info.op_tmpl->data.ecc =3D true; @@ -1221,6 +1226,7 @@ static struct spi_mem_dirmap_desc *spinand_create_rde= sc( * its spi controller, use regular reading */ spinand->cont_read_possible =3D false; + memset(&info->secondary_op_tmpl, 0, sizeof(info->secondary_op_tmpl)); =20 info->length =3D nanddev_page_size(nand) + nanddev_per_page_oobsize(nand); @@ -1237,11 +1243,24 @@ static int spinand_create_dirmap(struct spinand_dev= ice *spinand, struct nand_device *nand =3D spinand_to_nand(spinand); struct spi_mem_dirmap_info info =3D { 0 }; struct spi_mem_dirmap_desc *desc; - bool enable_ecc =3D false; + bool enable_ecc =3D false, secondary_op =3D false; =20 if (nand->ecc.engine->integration =3D=3D NAND_ECC_ENGINE_INTEGRATION_PIPE= LINED) enable_ecc =3D true; =20 + if (spinand->cont_read_possible && spinand->op_templates->cont_read_cache) + secondary_op =3D true; + + /* + * Continuous read implies that only the main data is retrieved, backed + * by an on-die ECC engine. It is not possible to use a pipelind ECC + * engine with continuous read. + */ + if (enable_ecc && secondary_op) { + secondary_op =3D false; + spinand->cont_read_possible =3D false; + } + /* The plane number is passed in MSB just above the column address */ info.offset =3D plane << fls(nand->memorg.pagesize); =20 @@ -1259,6 +1278,10 @@ static int spinand_create_dirmap(struct spinand_devi= ce *spinand, /* Read descriptor */ info.primary_op_tmpl =3D *spinand->op_templates->read_cache; info.primary_op_tmpl.data.ecc =3D enable_ecc; + if (secondary_op) { + info.secondary_op_tmpl =3D *spinand->op_templates->cont_read_cache; + info.secondary_op_tmpl.data.ecc =3D enable_ecc; + } desc =3D spinand_create_rdesc(spinand, &info); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -1607,6 +1630,33 @@ int spinand_match_and_init(struct spinand_device *sp= inand, if (ret) return ret; =20 + if (info->op_variants.cont_read_cache) { + op =3D spinand_select_op_variant(spinand, SSDR, + info->op_variants.cont_read_cache); + if (op) { + const struct spi_mem_op *read_op; + + read_op =3D spinand->ssdr_op_templates.read_cache; + + /* + * Sometimes the fastest continuous read variant may not + * be supported. In this case, prefer to use the fastest + * read from cache variant and disable continuous reads. + */ + if (read_op->cmd.buswidth > op->cmd.buswidth || + (read_op->cmd.dtr && !op->cmd.dtr) || + read_op->addr.buswidth > op->addr.buswidth || + (read_op->addr.dtr && !op->addr.dtr) || + read_op->data.buswidth > op->data.buswidth || + (read_op->data.dtr && !op->data.dtr)) + spinand->cont_read_possible =3D false; + else + spinand->ssdr_op_templates.cont_read_cache =3D op; + } else { + spinand->cont_read_possible =3D false; + } + } + /* I/O variants selection with octo-spi DDR commands (optional) */ =20 ret =3D spinand_init_odtr_instruction_set(spinand); @@ -1629,6 +1679,15 @@ int spinand_match_and_init(struct spinand_device *sp= inand, info->op_variants.update_cache); spinand->odtr_op_templates.update_cache =3D op; =20 + if (info->op_variants.cont_read_cache) { + op =3D spinand_select_op_variant(spinand, ODTR, + info->op_variants.cont_read_cache); + if (op) + spinand->odtr_op_templates.cont_read_cache =3D op; + else + spinand->cont_read_possible =3D false; + } + return 0; } =20 diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index bbf17c346887..aab17e2c8ac9 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -575,6 +575,7 @@ enum spinand_bus_interface { * @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 + * @op_variants.cont_read_cache: variants of the continuous read-cache ope= ration * @vendor_ops: vendor specific operations * @select_target: function used to select a target/die. Required only for * multi-die chips @@ -599,6 +600,7 @@ struct spinand_info { const struct spinand_op_variants *read_cache; const struct spinand_op_variants *write_cache; const struct spinand_op_variants *update_cache; + const struct spinand_op_variants *cont_read_cache; } op_variants; const struct spinand_op_variants *vendor_ops; int (*select_target)(struct spinand_device *spinand, @@ -628,6 +630,14 @@ struct spinand_info { .update_cache =3D __update, \ } =20 +#define SPINAND_INFO_OP_VARIANTS_WITH_CONT(__read, __write, __update, __co= nt_read) \ + { \ + .read_cache =3D __read, \ + .write_cache =3D __write, \ + .update_cache =3D __update, \ + .cont_read_cache =3D __cont_read, \ + } + #define SPINAND_INFO_VENDOR_OPS(__ops) \ .vendor_ops =3D __ops =20 @@ -699,6 +709,7 @@ struct spinand_dirmap { * @read_cache: read cache op template * @write_cache: write cache op template * @update_cache: update cache op template + * @cont_read_cache: continuous read cache op template (optional) */ struct spinand_mem_ops { struct spi_mem_op reset; @@ -713,6 +724,7 @@ struct spinand_mem_ops { const struct spi_mem_op *read_cache; const struct spi_mem_op *write_cache; const struct spi_mem_op *update_cache; + const struct spi_mem_op *cont_read_cache; }; =20 /** --=20 2.51.1