From nobody Thu Sep 24 20:04:10 2026 Received: from spam01.horizon.auto (spam01.horizon.auto [42.62.85.75]) (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 F26E53B42FB; Mon, 21 Sep 2026 10:28:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=42.62.85.75 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789986513; cv=none; b=Gj902c9waHX5MOvj27EVmzgyADuiWKsuJq9yjEXRgRh1Gz/k42PRBBO20umxUgyze0b11Xe95L3QjKnN5EPDHemDw167dk0U5tc0DdZCBYjwxa2n4+NiIq1mbNptY8w1xuQAm6KELdUucamrqGXXLRmI6wMm2dmjq+whAbNsErY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789986513; c=relaxed/simple; bh=pmpN+0+1w1dzWGzHsryR0vhVnLvScL6Pb0G5/lASwR8=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=dtocazl0HabM5rPLA0iIi7VZWjDR3pAvOATv6CH/QKQL/czhC8Y6AmxZc4NhHINGFcqanHTUnuotLdzeYkFlgmD+cz/DJT4N/9m7cuNh/PzgbsGcenK/Gn1smN4NAXjVxAPHSBXQs8UCJ/2g4rtXxlNTrhfkPNu/K9iLciaMN74= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=horizon.auto; spf=pass smtp.mailfrom=horizon.auto; dkim=fail (2048-bit key) header.d=horizon.auto header.i=@horizon.auto header.b=ex9GAswm reason="signature verification failed"; arc=none smtp.client-ip=42.62.85.75 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=horizon.auto Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=horizon.auto Authentication-Results: smtp.subspace.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=horizon.auto header.i=@horizon.auto header.b="ex9GAswm" Received: from spam01.horizon.auto (localhost [127.0.0.2] (may be forged)) by spam01.horizon.auto with ESMTP id 68L9csok069411; Mon, 21 Sep 2026 17:38:54 +0800 (+08) (envelope-from fei.xie@horizon.auto) Received: from spam01.horizon.auto (exchange003.hobot.cc [10.69.1.10]) by spam01.horizon.auto with ESMTPS id 68L9c19D068819 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NO); Mon, 21 Sep 2026 17:38:01 +0800 (+08) (envelope-from fei.xie@horizon.auto) Received: from server227.horizon.auto (10.9.0.252) by exchange003.hobot.cc (10.69.1.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_RSA_WITH_AES_128_CBC_SHA256) id 15.1.2308.27; Mon, 21 Sep 2026 17:38:00 +0800 From: Fei Xie To: Mark Brown CC: Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Parshuram Thombare , , , , , "fei.xie" Subject: [RFC PATCH 1/4] dt-bindings: spi: cdns,xspi: add SPI NAND compatible Date: Mon, 21 Sep 2026 17:36:58 +0800 Message-ID: <20260921093701.1341766-2-fei.xie@horizon.auto> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260921093701.1341766-1-fei.xie@horizon.auto> References: <20260921093701.1341766-1-fei.xie@horizon.auto> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: exchange004.hobot.cc (10.9.15.112) To exchange003.hobot.cc (10.69.1.10) X-DKIM-Results: exchange003.hobot.cc; dkim=none; X-DNSRBL: X-SPAM-SOURCE-CHECK: pass X-MAIL: spam01.horizon.auto 68L9csok069411 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=horizon.auto; s=auto; t=1789983488; bh=SDAOlAC7vrik7PAuqLK16OWJ52qpC4QqixrLeIZyURc=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=ex9GAswmCgLRF5NDcgpinB+OXxMVKNfrg34uOzVtsFC0UDg3QYk7wHesTylkTmjEw YSTxxR1v/QTFwBhm/wsQNeFgxvL1lSdk1M4iFOfoBo0xYOJvQbovufHTO6640e6XZk 9CHBNeXA9F949GpK6dUA4EPsQAtiBZC3EqOpG8E7lLD2thCW+U71YeN1b8Ff/C0YH+ TyatVmcR3Wu+5QCRA0Xx9YJtKjQP7r32OX5VraB6ITNqziKwzrOP4H+ZtlRfdSQm04 yeqKj4sfJGjRxDHJbaSXx+kkx+e9AybrQk3Q3E1MLEsZ5Tw6fvuNankwbVU6lPmvV+ RlBcH76o6hWZw== Content-Type: text/plain; charset="utf-8" From: "fei.xie" Document the Cadence XSPI controller compatible used with SPI NAND. Signed-off-by: fei.xie --- Documentation/devicetree/bindings/spi/cdns,xspi.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/spi/cdns,xspi.yaml b/Documen= tation/devicetree/bindings/spi/cdns,xspi.yaml index 38a5795589de2..3decf45d1b484 100644 --- a/Documentation/devicetree/bindings/spi/cdns,xspi.yaml +++ b/Documentation/devicetree/bindings/spi/cdns,xspi.yaml @@ -13,11 +13,12 @@ maintainers: description: | The XSPI controller allows SPI protocol communication in single, dual, quad or octal wire transmission modes for - read/write access to slaves such as SPI-NOR flash. + read/write access to slaves such as SPI-NOR and SPI-NAND flash. =20 properties: compatible: enum: + - cdns,xspi-nand - cdns,xspi-nor - marvell,cn10-xspi-nor =20 --=20 2.34.1 From nobody Thu Sep 24 20:04:10 2026 Received: from spam01.horizon.auto (spam01.horizon.auto [42.62.85.75]) (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 78D9C44E67D; Mon, 21 Sep 2026 10:28:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=42.62.85.75 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789986519; cv=none; b=Or52pFa3QXBzqCY7lwaAljHMx1tV+KECIScu1pFzvJXKXHUlWWvO5FSTIs2v58KlAjzCoDGShgq4I9vjEK/VNX6K16l4fUCimD3/1CN0NE0hlZkhaHU33e7oJAT7YilWbkt0NIdyAf53PglA/wJqaoFjfjlyVlcKspTMebgK1y4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789986519; c=relaxed/simple; bh=eRpuG3W7qZFzhSkX/5BP7UEKPvkv71oaz0w6dmJR4I0=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=FnsCkceUNxzZxVjTY3yr5kvsRR9Gu4MW7pSJqbMGUO12+CXQqco9KFr2+LZ4oeuFGGb8rIUvwdjKHzZY44HaW9pMSXNc9kRzRQ3Py0ess6i0H4BlLoF67xyls4Xim8nemk+V/dJ7rUt3Jzs9bmVfF5hp0pAZ0+CI4mkUAxuht2c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=horizon.auto; spf=pass smtp.mailfrom=horizon.auto; dkim=fail (2048-bit key) header.d=horizon.auto header.i=@horizon.auto header.b=JBwRuUxU reason="signature verification failed"; arc=none smtp.client-ip=42.62.85.75 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=horizon.auto Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=horizon.auto Authentication-Results: smtp.subspace.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=horizon.auto header.i=@horizon.auto header.b="JBwRuUxU" Received: from spam01.horizon.auto (localhost [127.0.0.2] (may be forged)) by spam01.horizon.auto with ESMTP id 68L9ct8l069415; Mon, 21 Sep 2026 17:38:55 +0800 (+08) (envelope-from fei.xie@horizon.auto) Received: from spam01.horizon.auto (exchange003.hobot.cc [10.69.1.10]) by spam01.horizon.auto with ESMTPS id 68L9c2QT068824 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NO); Mon, 21 Sep 2026 17:38:02 +0800 (+08) (envelope-from fei.xie@horizon.auto) Received: from server227.horizon.auto (10.9.0.252) by exchange003.hobot.cc (10.69.1.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_RSA_WITH_AES_128_CBC_SHA256) id 15.1.2308.27; Mon, 21 Sep 2026 17:38:01 +0800 From: Fei Xie To: Mark Brown CC: Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Parshuram Thombare , , , , , "fei.xie" Subject: [RFC PATCH 2/4] spi: cadence-xspi: add ACMD support for SPI NAND Date: Mon, 21 Sep 2026 17:36:59 +0800 Message-ID: <20260921093701.1341766-3-fei.xie@horizon.auto> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260921093701.1341766-1-fei.xie@horizon.auto> References: <20260921093701.1341766-1-fei.xie@horizon.auto> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: exchange004.hobot.cc (10.9.15.112) To exchange003.hobot.cc (10.69.1.10) X-DKIM-Results: exchange003.hobot.cc; dkim=none; X-DNSRBL: X-SPAM-SOURCE-CHECK: pass X-MAIL: spam01.horizon.auto 68L9ct8l069415 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=horizon.auto; s=auto; t=1789983493; bh=Fe0svDoY4P+wByAso92+l+8cbApwumiXGixK8icWVUY=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=JBwRuUxUKJ30tWCNVe1uvJw8PVqFJPA9sjNasbW0Uva8p1Jf1sIIpjuPLbBfAnSHs vNhAsUj6jd+Q2/LZfk9a0NAq4g7tI2ioau0S5D+J6YOuoUnYSzBfmT9nps+lmV5GZQ 61Gc3wBeibQIuYcUzVMn7ZR+fWSEihZTvoeHi8C+ckLk2zSSpooWR/IY5rJywxtU2q RApgkfqDCbYwzLdZYKNO5RfEAu2PByqcyHY0MWzA4QS3sEuHmAkScgEvN/tDW0zgge iE5Qt3z+eXkQNCQ2wGHbhbQAjhwgIXR1EN3gPOg3HedKaaD88KaHo+UMGzBrbbdKIH K2rJAlfiRo5wQ== Content-Type: text/plain; charset="utf-8" From: "fei.xie" Add PIO master-DMA support for the controller auto-command mode. Configure the SPI NAND read, program, erase, status and reset sequences, retaining STIG for operations not consumed by ACMD. Signed-off-by: fei.xie --- drivers/spi/spi-cadence-xspi.c | 782 +++++++++++++++++++++++++++++++++ 1 file changed, 782 insertions(+) diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c index 39c868a5b171c..0fedf6cd6a590 100644 --- a/drivers/spi/spi-cadence-xspi.c +++ b/drivers/spi/spi-cadence-xspi.c @@ -4,6 +4,7 @@ =20 #include #include +#include #include #include #include @@ -11,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -54,7 +57,33 @@ #define CDNS_XSPI_CMD_REG_4 0x0010 #define CDNS_XSPI_CMD_REG_5 0x0014 =20 +/* Auto command fields in command register 0 */ +#define CDNS_XSPI_ACMD_MODE GENMASK(31, 30) +#define CDNS_XSPI_ACMD_TRD_NUM GENMASK(26, 24) +#define CDNS_XSPI_ACMD_BANK_NUM GENMASK(22, 20) +#define CDNS_XSPI_ACMD_DMA_SEL BIT(19) +#define CDNS_XSPI_ACMD_INT_EN BIT(18) +#define CDNS_XSPI_ACMD_CMD_TYPE GENMASK(15, 0) + +#define CDNS_XSPI_ACMD_READ_OP 0x2200 +#define CDNS_XSPI_ACMD_PROG_OP 0x2100 +#define CDNS_XSPI_ACMD_ERASE_OP 0x1000 +#define CDNS_XSPI_ACMD_RESET_OP 0x1100 +#define CDNS_XSPI_ACMD_DATA_THREAD 7 +#define CDNS_XSPI_ACMD_ERASE_THREAD 5 +#define CDNS_XSPI_ACMD_TIMEOUT_MS 1000 +#define CDNS_XSPI_ACMD_MODE_PIO 1 + +#define CDNS_XSPI_NAND_OP_GET_FEATURE 0x0f +#define CDNS_XSPI_NAND_OP_WRITE_ENABLE 0x06 +#define CDNS_XSPI_NAND_OP_PROGRAM_EXECUTE 0x10 +#define CDNS_XSPI_NAND_OP_PAGE_READ 0x13 +#define CDNS_XSPI_NAND_OP_BLOCK_ERASE 0xd8 +#define CDNS_XSPI_NAND_OP_RESET 0xff +#define CDNS_XSPI_NAND_STATUS_REG 0xc0 + /* Command status registers */ +#define CDNS_XSPI_CMD_STATUS_PTR_REG 0x0040 #define CDNS_XSPI_CMD_STATUS_REG 0x0044 =20 /* Controller status register */ @@ -143,10 +172,126 @@ #define CDNS_XSPI_CMD_STATUS_CRC_ERROR BIT(2) #define CDNS_XSPI_CMD_STATUS_BUS_ERROR BIT(1) #define CDNS_XSPI_CMD_STATUS_INV_SEQ_ERROR BIT(0) +/* Reset sequence config register */ +#define CDNS_XSPI_RST_SEQ_CFG_0 0x0400 +#define CDNS_XSPI_RST_SEQ_P1_CMD1_VAL GENMASK(15, 8) + +/* Erase sequence config registers */ +#define CDNS_XSPI_ERSS_SEQ_CFG_0 0x0410 +#define CDNS_XSPI_ERSS_SEQ_P1_CMD_VAL GENMASK(7, 0) +#define CDNS_XSPI_ERSS_SEQ_P1_CMD_IOS GENMASK(9, 8) +#define CDNS_XSPI_ERSS_SEQ_P1_CMD_EDGE BIT(11) +#define CDNS_XSPI_ERSS_SEQ_P1_ADDR_CNT GENMASK(14, 12) +#define CDNS_XSPI_ERSS_SEQ_P1_CMD_EXT_EN BIT(15) +#define CDNS_XSPI_ERSS_SEQ_P1_CMD_EXT_VAL GENMASK(23, 16) +#define CDNS_XSPI_ERSS_SEQ_P1_ADDR_IOS GENMASK(25, 24) +#define CDNS_XSPI_ERSS_SEQ_P1_ADDR_EDGE BIT(28) + +#define CDNS_XSPI_ERSS_SEQ_CFG_1 0x0414 +#define CDNS_XSPI_ERSS_SEQ_P1_SECT_SIZE GENMASK(4, 0) + +#define CDNS_XSPI_ERSS_SEQ_CFG_2 0x0418 + +/* Program sequence config registers */ +#define CDNS_XSPI_PROG_SEQ_CFG_0 0x0420 +#define CDNS_XSPI_PROG_SEQ_P1_CMD_VAL GENMASK(7, 0) +#define CDNS_XSPI_PROG_SEQ_P1_CMD_IOS GENMASK(9, 8) +#define CDNS_XSPI_PROG_SEQ_P1_CMD_EDGE BIT(11) +#define CDNS_XSPI_PROG_SEQ_P1_ADDR_CNT GENMASK(14, 12) +#define CDNS_XSPI_PROG_SEQ_P1_ADDR_IOS GENMASK(17, 16) +#define CDNS_XSPI_PROG_SEQ_P1_ADDR_EDGE BIT(19) +#define CDNS_XSPI_PROG_SEQ_P1_DATA_IOS GENMASK(21, 20) +#define CDNS_XSPI_PROG_SEQ_P1_DATA_EDGE BIT(23) +#define CDNS_XSPI_PROG_SEQ_P1_DUMMY_CNT GENMASK(29, 24) + +#define CDNS_XSPI_PROG_SEQ_CFG_1 0x0424 +#define CDNS_XSPI_PROG_SEQ_P1_CMD_EXT_EN BIT(0) +#define CDNS_XSPI_PROG_SEQ_P1_CMD_EXT_VAL GENMASK(15, 8) + +#define CDNS_XSPI_PROG_SEQ_CFG_2 0x0428 + +/* Read sequence config registers */ +#define CDNS_XSPI_READ_SEQ_CFG_0 0x0430 +#define CDNS_XSPI_READ_SEQ_P1_CMD_VAL GENMASK(7, 0) +#define CDNS_XSPI_READ_SEQ_P1_CMD_IOS GENMASK(9, 8) +#define CDNS_XSPI_READ_SEQ_P1_CMD_EDGE BIT(11) +#define CDNS_XSPI_READ_SEQ_P1_ADDR_CNT GENMASK(14, 12) +#define CDNS_XSPI_READ_SEQ_P1_ADDR_IOS GENMASK(17, 16) +#define CDNS_XSPI_READ_SEQ_P1_ADDR_EDGE BIT(19) +#define CDNS_XSPI_READ_SEQ_P1_DATA_IOS GENMASK(21, 20) +#define CDNS_XSPI_READ_SEQ_P1_DATA_EDGE BIT(23) +#define CDNS_XSPI_READ_SEQ_P1_DUMMY_CNT GENMASK(29, 24) + +#define CDNS_XSPI_READ_SEQ_CFG_1 0x0434 +#define CDNS_XSPI_READ_SEQ_P1_CMD_EXT_EN BIT(0) +#define CDNS_XSPI_READ_SEQ_P1_CACHE_RANDOM_READ_EN BIT(4) +#define CDNS_XSPI_READ_SEQ_CFG_2 0x0438 + +/* Write enable sequence config register */ +#define CDNS_XSPI_WE_SEQ_CFG_0 0x0440 +#define CDNS_XSPI_WE_SEQ_P1_CMD_VAL GENMASK(7, 0) +#define CDNS_XSPI_WE_SEQ_P1_CMD_IOS GENMASK(9, 8) +#define CDNS_XSPI_WE_SEQ_P1_CMD_EDGE BIT(11) +#define CDNS_XSPI_WE_SEQ_P1_EN BIT(24) + +/* Status sequence config registers */ +#define CDNS_XSPI_STAT_SEQ_CFG_0 0x0450 +#define CDNS_XSPI_STAT_SEQ_P1_ADDR_CNT GENMASK(9, 8) +#define CDNS_XSPI_STAT_SEQ_CFG_1 0x0454 +#define CDNS_XSPI_P1_DEV_RDY_ADDR_EN BIT(6) +#define CDNS_XSPI_P1_PROG_FAIL_ADDR_EN BIT(22) +#define CDNS_XSPI_P1_ERS_FAIL_ADDR_EN BIT(30) + +#define CDNS_XSPI_STAT_SEQ_CFG_2 0x0458 +#define CDNS_XSPI_STAT_SEQ_P1_DEV_RDY_CMD_VAL GENMASK(7, 0) +#define CDNS_XSPI_STAT_SEQ_P1_ERS_FAIL_CMD_VAL GENMASK(15, 8) +#define CDNS_XSPI_STAT_SEQ_P1_PROG_FAIL_CMD_VAL GENMASK(31, 24) + +#define CDNS_XSPI_STAT_SEQ_CFG_3 0x045c +#define CDNS_XSPI_STAT_SEQ_CFG_4 0x0460 +#define CDNS_XSPI_STAT_SEQ_CFG_5 0x0464 +#define CDNS_XSPI_STAT_SEQ_DEV_RDY_IDX GENMASK(3, 0) +#define CDNS_XSPI_STAT_SEQ_DEV_RDY_EN BIT(6) +#define CDNS_XSPI_STAT_SEQ_ERS_FAIL_IDX GENMASK(11, 8) +#define CDNS_XSPI_STAT_SEQ_ERS_FAIL_VAL BIT(12) +#define CDNS_XSPI_STAT_SEQ_ERS_FAIL_EN BIT(14) +#define CDNS_XSPI_STAT_SEQ_PROG_FAIL_IDX GENMASK(27, 24) +#define CDNS_XSPI_STAT_SEQ_PROG_FAIL_VAL BIT(28) +#define CDNS_XSPI_STAT_SEQ_PROG_FAIL_EN BIT(30) + +#define CDNS_XSPI_STAT_SEQ_CFG_7 0x046c +#define CDNS_XSPI_STAT_SEQ_CFG_8 0x0470 +#define CDNS_XSPI_STAT_SEQ_CFG_9 0x0474 + +#define CDNS_XSPI_STAT_SEQ_CFG_10 0x0478 +#define CDNS_XSPI_STAT_SEQ_ECC_FAIL_EN BIT(31) +#define CDNS_XSPI_STAT_SEQ_CRDY_VAL BIT(27) +#define CDNS_XSPI_STAT_SEQ_CRDY_IDX GENMASK(26, 24) +#define CDNS_XSPI_STAT_SEQ_ECC_CORR_VAL GENMASK(23, 16) +#define CDNS_XSPI_STAT_SEQ_ECC_FAIL_VAL GENMASK(15, 8) +#define CDNS_XSPI_STAT_SEQ_ECC_FAIL_MASK GENMASK(7, 0) =20 #define CDNS_XSPI_STIG_DONE_FLAG BIT(0) #define CDNS_XSPI_TRD_STATUS 0x0104 =20 +#define CDNS_XSPI_FLASH_TYPE_NOR 0 +#define CDNS_XSPI_FLASH_TYPE_NAND 1 + +#define CDNS_XSPI_GLOBAL_SEQ_CFG 0x0390 +#define CDNS_XSPI_SEQ_TYPE GENMASK(24, 23) +#define CDNS_XSPI_SEQ_PAGE_SIZE_PGM GENMASK(7, 4) +#define CDNS_XSPI_SEQ_PAGE_SIZE_RD GENMASK(3, 0) +#define CDNS_XSPI_SEQ_SPI_NAND 3 + +#define CDNS_XSPI_GLOBAL_SEQ_CFG_1 0x0394 +#define CDNS_XSPI_SEQ_PLANE_CNT GENMASK(29, 28) +#define CDNS_XSPI_SEQ_PAGE_PER_BLOCK GENMASK(26, 24) +#define CDNS_XSPI_SEQ_PAGE_CA_SIZE BIT(16) +#define CDNS_XSPI_SEQ_PAGE_SIZE_EXT GENMASK(8, 0) + +#define CDNS_XSPI_XIP_MODE_CFG 0x0388 +#define CDNS_XSPI_XIP_EN BIT(0) + #define MODE_NO_OF_BYTES GENMASK(25, 24) #define MODEBYTES_COUNT 1 =20 @@ -305,6 +450,7 @@ enum cdns_xspi_stig_cmd_dir { =20 struct cdns_xspi_driver_data { bool mrvl_hw_overlay; + bool use_acmd; u32 dll_phy_ctrl; u32 ctb_rfile_phy_ctrl; u32 rfile_phy_tsel; @@ -313,12 +459,26 @@ struct cdns_xspi_driver_data { u32 rfile_phy_gate_lpbk_ctrl; u32 rfile_phy_dll_master_ctrl; u32 rfile_phy_dll_slave_ctrl; + u8 flash_type; }; =20 static struct cdns_xspi_driver_data cdns_driver_data =3D { .mrvl_hw_overlay =3D false, }; =20 +static struct cdns_xspi_driver_data cdns_nand_driver_data =3D { + .mrvl_hw_overlay =3D false, + .use_acmd =3D true, + .flash_type =3D CDNS_XSPI_FLASH_TYPE_NAND, +}; + +struct cdns_xspi_acmd_info { + u64 row_addr; + u64 column_addr; + size_t data_nbytes; + bool row_addr_valid; + bool initialized; +}; struct cdns_xspi_dev { struct platform_device *pdev; struct spi_controller *host; @@ -351,6 +511,13 @@ struct cdns_xspi_dev { =20 bool xfer_in_progress; int current_xfer_qword; + u32 work_mode; + u8 flash_type; + + struct cdns_xspi_acmd_info acmd_info; + void *dma_buf; + dma_addr_t dma_addr; + u32 dma_buf_len; }; =20 static int cdns_xspi_wait_for_controller_idle(struct cdns_xspi_dev *cdns_x= spi) @@ -425,6 +592,268 @@ static void cdns_xspi_set_interrupts(struct cdns_xspi= _dev *cdns_xspi, writel(intr_enable, cdns_xspi->iobase + CDNS_XSPI_INTR_ENABLE_REG); } =20 +static void cdns_xspi_nand_cfg_seq_init(struct cdns_xspi_dev *cdns_xspi, + struct spinand_device *spinand) +{ + u32 seq_cfg, seq_cfg1; + + seq_cfg =3D readl(cdns_xspi->iobase + CDNS_XSPI_GLOBAL_SEQ_CFG); + seq_cfg1 =3D readl(cdns_xspi->iobase + CDNS_XSPI_GLOBAL_SEQ_CFG_1); + seq_cfg =3D u32_replace_bits(seq_cfg, CDNS_XSPI_SEQ_SPI_NAND, + CDNS_XSPI_SEQ_TYPE); + seq_cfg =3D u32_replace_bits(seq_cfg, + ilog2(spinand->base.memorg.pagesize), + CDNS_XSPI_SEQ_PAGE_SIZE_PGM); + seq_cfg =3D u32_replace_bits(seq_cfg, + ilog2(spinand->base.memorg.pagesize), + CDNS_XSPI_SEQ_PAGE_SIZE_RD); + seq_cfg1 =3D u32_replace_bits(seq_cfg1, + ilog2(spinand->base.memorg.planes_per_lun), + CDNS_XSPI_SEQ_PLANE_CNT); + seq_cfg1 =3D u32_replace_bits(seq_cfg1, + ilog2(spinand->base.memorg.pages_per_eraseblock), + CDNS_XSPI_SEQ_PAGE_PER_BLOCK); + seq_cfg1 =3D u32_replace_bits(seq_cfg1, + !!(spinand->base.memorg.pagesize & BIT(12)), + CDNS_XSPI_SEQ_PAGE_CA_SIZE); + seq_cfg1 =3D u32_replace_bits(seq_cfg1, spinand->base.memorg.oobsize, + CDNS_XSPI_SEQ_PAGE_SIZE_EXT); + + writel(seq_cfg, cdns_xspi->iobase + CDNS_XSPI_GLOBAL_SEQ_CFG); + writel(seq_cfg1, cdns_xspi->iobase + CDNS_XSPI_GLOBAL_SEQ_CFG_1); +} + +static void cdns_xspi_nand_read_seq_init(struct cdns_xspi_dev *cdns_xspi, + struct spinand_device *spinand) +{ + const struct spi_mem_op *op =3D spinand->op_templates->read_cache; + u32 dummy_cycles =3D 0; + u32 read_seq_cfg0; + u32 read_seq_cfg1; + + if (op->dummy.nbytes && op->dummy.buswidth) + dummy_cycles =3D op->dummy.nbytes * BITS_PER_BYTE / + (op->dummy.buswidth * (op->dummy.dtr + 1)); + + read_seq_cfg0 =3D + FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_CMD_VAL, op->cmd.opcode) | + FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_CMD_IOS, + ilog2(op->cmd.buswidth)) | + FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_CMD_EDGE, op->cmd.dtr) | + FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_ADDR_CNT, + op->addr.nbytes) | + FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_ADDR_IOS, + ilog2(op->addr.buswidth)) | + FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_ADDR_EDGE, op->addr.dtr) | + FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_DATA_IOS, + ilog2(op->data.buswidth)) | + FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_DATA_EDGE, op->data.dtr) | + FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_DUMMY_CNT, dummy_cycles); + read_seq_cfg1 =3D FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_CMD_EXT_EN, + op->cmd.nbytes > 1) | + CDNS_XSPI_READ_SEQ_P1_CACHE_RANDOM_READ_EN; + + writel(read_seq_cfg0, cdns_xspi->iobase + CDNS_XSPI_READ_SEQ_CFG_0); + writel(read_seq_cfg1, cdns_xspi->iobase + CDNS_XSPI_READ_SEQ_CFG_1); + writel(0, cdns_xspi->iobase + CDNS_XSPI_READ_SEQ_CFG_2); + + dev_dbg(cdns_xspi->dev, + "ACMD read: op=3D%02x %u-%u-%u%s addr=3D%u dummy=3D%u cfg=3D%08x/%08x\n", + op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, + op->data.buswidth, op->data.dtr ? " DTR" : " SDR", + op->addr.nbytes, dummy_cycles, read_seq_cfg0, read_seq_cfg1); +} + +static void cdns_xspi_nand_write_seq_init(struct cdns_xspi_dev *cdns_xspi, + struct spinand_device *spinand) +{ + const struct spi_mem_op *op =3D spinand->op_templates->write_cache; + u32 dummy_cycles =3D 0; + u32 write_seq_cfg0; + u32 write_seq_cfg1; + + if (op->dummy.nbytes && op->dummy.buswidth) + dummy_cycles =3D op->dummy.nbytes * BITS_PER_BYTE / + (op->dummy.buswidth * (op->dummy.dtr + 1)); + + write_seq_cfg0 =3D + FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_CMD_VAL, op->cmd.opcode) | + FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_CMD_IOS, + ilog2(op->cmd.buswidth)) | + FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_CMD_EDGE, op->cmd.dtr) | + FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_ADDR_CNT, + op->addr.nbytes) | + FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_ADDR_IOS, + ilog2(op->addr.buswidth)) | + FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_ADDR_EDGE, op->addr.dtr) | + FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_DATA_IOS, + ilog2(op->data.buswidth)) | + FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_DATA_EDGE, op->data.dtr) | + FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_DUMMY_CNT, dummy_cycles); + write_seq_cfg1 =3D FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_CMD_EXT_EN, + op->cmd.nbytes > 1); + + writel(write_seq_cfg0, cdns_xspi->iobase + CDNS_XSPI_PROG_SEQ_CFG_0); + writel(write_seq_cfg1, cdns_xspi->iobase + CDNS_XSPI_PROG_SEQ_CFG_1); + writel(0, cdns_xspi->iobase + CDNS_XSPI_PROG_SEQ_CFG_2); + + dev_dbg(cdns_xspi->dev, + "ACMD program: op=3D%02x %u-%u-%u%s addr=3D%u dummy=3D%u cfg=3D%08x/%08x= \n", + op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, + op->data.buswidth, op->data.dtr ? " DTR" : " SDR", + op->addr.nbytes, dummy_cycles, write_seq_cfg0, write_seq_cfg1); +} + +static void cdns_xspi_nand_erase_seq_init(struct cdns_xspi_dev *cdns_xspi, + struct spinand_device *spinand) +{ + u32 erase_seq_cfg0; + u32 erase_seq_cfg1; + + /* SPI-NAND block erase is always D8h with a 3-byte row address. */ + erase_seq_cfg0 =3D + FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_VAL, 0xd8) | + FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_IOS, 0) | + FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_EDGE, 0) | + FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_ADDR_CNT, 3) | + FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_EXT_EN, 0) | + FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_EXT_VAL, 0) | + FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_ADDR_IOS, 0) | + FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_ADDR_EDGE, 0); + erase_seq_cfg1 =3D FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_SECT_SIZE, + ilog2(nanddev_eraseblock_size(&spinand->base))); + + writel(erase_seq_cfg0, + cdns_xspi->iobase + CDNS_XSPI_ERSS_SEQ_CFG_0); + writel(erase_seq_cfg1, + cdns_xspi->iobase + CDNS_XSPI_ERSS_SEQ_CFG_1); + writel(0, cdns_xspi->iobase + CDNS_XSPI_ERSS_SEQ_CFG_2); + + dev_dbg(cdns_xspi->dev, + "ACMD erase sequence: cfg0=3D%08x cfg1=3D%08x\n", + erase_seq_cfg0, erase_seq_cfg1); +} + +static void cdns_xspi_nand_status_seq_init(struct cdns_xspi_dev *cdns_xspi) +{ + u32 stat_seq_cfg1; + u32 stat_seq_cfg2; + u32 stat_seq_cfg5; + + writel(FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_ADDR_CNT, 0), + cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_0); + + stat_seq_cfg1 =3D CDNS_XSPI_P1_DEV_RDY_ADDR_EN | + CDNS_XSPI_P1_PROG_FAIL_ADDR_EN | + CDNS_XSPI_P1_ERS_FAIL_ADDR_EN; + writel(stat_seq_cfg1, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_1); + + stat_seq_cfg2 =3D FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_DEV_RDY_CMD_VAL, + CDNS_XSPI_NAND_OP_GET_FEATURE) | + FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_PROG_FAIL_CMD_VAL, + CDNS_XSPI_NAND_OP_GET_FEATURE) | + FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_ERS_FAIL_CMD_VAL, + CDNS_XSPI_NAND_OP_GET_FEATURE); + writel(stat_seq_cfg2, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_2); + writel(0, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_3); + writel(0, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_4); + + stat_seq_cfg5 =3D + FIELD_PREP(CDNS_XSPI_STAT_SEQ_DEV_RDY_IDX, + __ffs(STATUS_BUSY)) | + CDNS_XSPI_STAT_SEQ_DEV_RDY_EN | + FIELD_PREP(CDNS_XSPI_STAT_SEQ_ERS_FAIL_IDX, + __ffs(STATUS_ERASE_FAILED)) | + CDNS_XSPI_STAT_SEQ_ERS_FAIL_VAL | + CDNS_XSPI_STAT_SEQ_ERS_FAIL_EN | + FIELD_PREP(CDNS_XSPI_STAT_SEQ_PROG_FAIL_IDX, + __ffs(STATUS_PROG_FAILED)) | + CDNS_XSPI_STAT_SEQ_PROG_FAIL_VAL | + CDNS_XSPI_STAT_SEQ_PROG_FAIL_EN; + writel(stat_seq_cfg5, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_5); + writel(CDNS_XSPI_NAND_STATUS_REG, + cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_7); + writel(CDNS_XSPI_NAND_STATUS_REG, + cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_8); + writel(CDNS_XSPI_NAND_STATUS_REG, + cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_9); + + writel(CDNS_XSPI_STAT_SEQ_ECC_FAIL_EN | + FIELD_PREP(CDNS_XSPI_STAT_SEQ_CRDY_IDX, __ffs(STATUS_BUSY)) | + FIELD_PREP(CDNS_XSPI_STAT_SEQ_CRDY_VAL, 0) | + FIELD_PREP(CDNS_XSPI_STAT_SEQ_ECC_CORR_VAL, + STATUS_ECC_HAS_BITFLIPS) | + FIELD_PREP(CDNS_XSPI_STAT_SEQ_ECC_FAIL_VAL, + STATUS_ECC_UNCOR_ERROR) | + FIELD_PREP(CDNS_XSPI_STAT_SEQ_ECC_FAIL_MASK, + STATUS_ECC_MASK), + cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_10); +} + +static void cdns_xspi_nand_write_enable_seq_init(struct cdns_xspi_dev *cdn= s_xspi) +{ + u32 cfg; + + cfg =3D readl(cdns_xspi->iobase + CDNS_XSPI_WE_SEQ_CFG_0); + cfg =3D u32_replace_bits(cfg, 1, CDNS_XSPI_WE_SEQ_P1_EN); + cfg =3D u32_replace_bits(cfg, CDNS_XSPI_NAND_OP_WRITE_ENABLE, + CDNS_XSPI_WE_SEQ_P1_CMD_VAL); + cfg =3D u32_replace_bits(cfg, 0, CDNS_XSPI_WE_SEQ_P1_CMD_IOS); + cfg =3D u32_replace_bits(cfg, 0, CDNS_XSPI_WE_SEQ_P1_CMD_EDGE); + writel(cfg, cdns_xspi->iobase + CDNS_XSPI_WE_SEQ_CFG_0); +} + +static void cdns_xspi_set_mode_acmd(struct cdns_xspi_dev *cdns_xspi) +{ + u32 reg_val; + + reg_val =3D readl(cdns_xspi->iobase + CDNS_XSPI_CTRL_CONFIG_REG); + reg_val =3D u32_replace_bits(reg_val, CDNS_XSPI_WORK_MODE_ACMD, + CDNS_XSPI_CTRL_WORK_MODE); + writel(reg_val, cdns_xspi->iobase + CDNS_XSPI_CTRL_CONFIG_REG); +} + +static void cdns_xspi_nand_reset_seq_init(struct cdns_xspi_dev *cdns_xspi) +{ + u32 cfg; + + cfg =3D FIELD_PREP(CDNS_XSPI_RST_SEQ_P1_CMD1_VAL, + CDNS_XSPI_NAND_OP_RESET); + writel(cfg, cdns_xspi->iobase + CDNS_XSPI_RST_SEQ_CFG_0); +} + +static int cdns_xspi_nand_init(struct cdns_xspi_dev *cdns_xspi, + struct spinand_device *spinand) +{ + u32 reg_val; + + cdns_xspi_nand_cfg_seq_init(cdns_xspi, spinand); + cdns_xspi_nand_read_seq_init(cdns_xspi, spinand); + cdns_xspi_nand_reset_seq_init(cdns_xspi); + cdns_xspi_nand_write_seq_init(cdns_xspi, spinand); + cdns_xspi_nand_write_enable_seq_init(cdns_xspi); + cdns_xspi_nand_status_seq_init(cdns_xspi); + cdns_xspi_nand_erase_seq_init(cdns_xspi, spinand); + + reg_val =3D readl(cdns_xspi->iobase + CDNS_XSPI_XIP_MODE_CFG); + if (reg_val & CDNS_XSPI_XIP_EN) { + reg_val &=3D ~CDNS_XSPI_XIP_EN; + writel(reg_val, cdns_xspi->iobase + CDNS_XSPI_XIP_MODE_CFG); + } + cdns_xspi->dma_buf_len =3D spinand->base.memorg.pagesize + + spinand->base.memorg.oobsize; + cdns_xspi->dma_buf =3D dmam_alloc_coherent(cdns_xspi->dev, + cdns_xspi->dma_buf_len, + &cdns_xspi->dma_addr, + GFP_KERNEL); + if (!cdns_xspi->dma_buf) + return -ENOMEM; + + cdns_xspi->acmd_info.initialized =3D true; + + return 0; +} + static int cdns_xspi_controller_init(struct cdns_xspi_dev *cdns_xspi) { u32 ctrl_ver; @@ -580,6 +1009,349 @@ static int cdns_xspi_send_stig_command(struct cdns_x= spi_dev *cdns_xspi, return 0; } =20 +static int cdns_xspi_acmd_get_thread_status(struct cdns_xspi_dev *cdns_xsp= i, + u32 thread) +{ + writel(thread, cdns_xspi->iobase + CDNS_XSPI_CMD_STATUS_PTR_REG); + + return cdns_xspi_check_command_status(cdns_xspi); +} + +static int cdns_xspi_acmd_run(struct cdns_xspi_dev *cdns_xspi, u32 cmd_reg= s[6], + u32 thread) +{ + unsigned long timeout; + int ret; + + cdns_xspi_set_mode_acmd(cdns_xspi); + reinit_completion(&cdns_xspi->auto_cmd_complete); + cdns_xspi_set_interrupts(cdns_xspi, true); + cdns_xspi_trigger_command(cdns_xspi, cmd_regs); + + timeout =3D msecs_to_jiffies(CDNS_XSPI_ACMD_TIMEOUT_MS); + if (!wait_for_completion_timeout(&cdns_xspi->auto_cmd_complete, + timeout)) { + dev_err(cdns_xspi->dev, "ACMD command timed out\n"); + ret =3D -ETIMEDOUT; + } else { + ret =3D cdns_xspi_acmd_get_thread_status(cdns_xspi, thread); + } + + cdns_xspi_set_interrupts(cdns_xspi, false); + + return ret; +} + +static int cdns_xspi_nand_addr(struct spinand_device *spinand, u64 row, + u64 column, u64 *xspi_addr) +{ + const struct nand_memory_organization *memorg =3D &spinand->base.memorg; + u64 column_limit, page_stride; + + page_stride =3D roundup_pow_of_two(memorg->pagesize + memorg->oobsize); + if (check_mul_overflow(page_stride, memorg->planes_per_lun, + &column_limit) || + column >=3D column_limit || + check_mul_overflow(row, page_stride, xspi_addr) || + check_add_overflow(*xspi_addr, column, xspi_addr)) + return -ERANGE; + + return 0; +} + +static u32 cdns_xspi_acmd_cmd(struct cdns_xspi_dev *cdns_xspi, u32 type, + u32 thread, bool use_dma) +{ + return FIELD_PREP(CDNS_XSPI_ACMD_MODE, CDNS_XSPI_ACMD_MODE_PIO) | + FIELD_PREP(CDNS_XSPI_ACMD_TRD_NUM, thread) | + FIELD_PREP(CDNS_XSPI_ACMD_BANK_NUM, cdns_xspi->cur_cs) | + (use_dma ? CDNS_XSPI_ACMD_DMA_SEL : 0) | + CDNS_XSPI_ACMD_INT_EN | + FIELD_PREP(CDNS_XSPI_ACMD_CMD_TYPE, type); +} + +static int cdns_xspi_pio_mdma_erase(struct cdns_xspi_dev *cdns_xspi, + struct spinand_device *spinand, + const struct spi_mem_op *op) +{ + u32 cmd_regs[6] =3D { 0 }; + u64 xspi_addr; + int ret; + + ret =3D cdns_xspi_wait_for_controller_idle(cdns_xspi); + if (ret) { + dev_err(cdns_xspi->dev, + "controller did not become idle before erase\n"); + return ret; + } + + ret =3D cdns_xspi_nand_addr(spinand, op->addr.val, 0, &xspi_addr); + if (ret) + return ret; + + if (upper_32_bits(xspi_addr)) { + dev_err(cdns_xspi->dev, + "erase address 0x%llx exceeds the ACMD PIO range\n", + xspi_addr); + return -ERANGE; + } + + cmd_regs[1] =3D lower_32_bits(xspi_addr); + cmd_regs[0] =3D cdns_xspi_acmd_cmd(cdns_xspi, + CDNS_XSPI_ACMD_ERASE_OP, + CDNS_XSPI_ACMD_ERASE_THREAD, false); + + ret =3D cdns_xspi_acmd_run(cdns_xspi, cmd_regs, + CDNS_XSPI_ACMD_ERASE_THREAD); + if (ret) + dev_err(cdns_xspi->dev, "ACMD erase failed: %d\n", ret); + + return ret; +} + +static int cdns_xspi_pio_reset(struct cdns_xspi_dev *cdns_xspi) +{ + u32 cmd_regs[6] =3D { 0 }; + int ret; + + ret =3D cdns_xspi_wait_for_controller_idle(cdns_xspi); + if (ret) { + dev_err(cdns_xspi->dev, + "controller did not become idle before reset\n"); + return ret; + } + cmd_regs[0] =3D cdns_xspi_acmd_cmd(cdns_xspi, + CDNS_XSPI_ACMD_RESET_OP, + CDNS_XSPI_ACMD_DATA_THREAD, false); + ret =3D cdns_xspi_acmd_run(cdns_xspi, cmd_regs, + CDNS_XSPI_ACMD_DATA_THREAD); + if (ret) + return ret; + + cdns_xspi->acmd_info.row_addr_valid =3D false; + cdns_xspi->acmd_info.column_addr =3D 0; + cdns_xspi->acmd_info.data_nbytes =3D 0; + cdns_xspi->out_buffer =3D NULL; + + return 0; +} + +static int cdns_xspi_pio_mdma_program(struct cdns_xspi_dev *cdns_xspi, + struct spinand_device *spinand, + const struct spi_mem_op *op) +{ + u32 cmd_regs[6] =3D { 0 }; + u64 xspi_addr; + int ret; + + if (!cdns_xspi->out_buffer || !cdns_xspi->acmd_info.data_nbytes) { + dev_err(cdns_xspi->dev, "missing program-load data\n"); + ret =3D -EINVAL; + goto out_clear_program_state; + } + + if (cdns_xspi->acmd_info.data_nbytes > cdns_xspi->dma_buf_len) { + ret =3D -EMSGSIZE; + goto out_clear_program_state; + } + + memcpy(cdns_xspi->dma_buf, cdns_xspi->out_buffer, + cdns_xspi->acmd_info.data_nbytes); + + ret =3D cdns_xspi_wait_for_controller_idle(cdns_xspi); + if (ret) { + dev_err(cdns_xspi->dev, + "controller did not become idle before program\n"); + goto out_clear_program_state; + } + + ret =3D cdns_xspi_nand_addr(spinand, op->addr.val, + cdns_xspi->acmd_info.column_addr, + &xspi_addr); + if (ret) + goto out_clear_program_state; + + if (upper_32_bits(xspi_addr)) { + dev_err(cdns_xspi->dev, + "program address 0x%llx exceeds the ACMD PIO range\n", + xspi_addr); + ret =3D -ERANGE; + goto out_clear_program_state; + } + + cmd_regs[1] =3D lower_32_bits(xspi_addr); + cmd_regs[2] =3D lower_32_bits(cdns_xspi->dma_addr); + cmd_regs[3] =3D upper_32_bits(cdns_xspi->dma_addr); + cmd_regs[4] =3D cdns_xspi->acmd_info.data_nbytes - 1; + cmd_regs[0] =3D cdns_xspi_acmd_cmd(cdns_xspi, + CDNS_XSPI_ACMD_PROG_OP, + CDNS_XSPI_ACMD_DATA_THREAD, true); + + ret =3D cdns_xspi_acmd_run(cdns_xspi, cmd_regs, + CDNS_XSPI_ACMD_DATA_THREAD); + +out_clear_program_state: + cdns_xspi->acmd_info.column_addr =3D 0; + cdns_xspi->acmd_info.data_nbytes =3D 0; + cdns_xspi->out_buffer =3D NULL; + return ret; +} + +static int cdns_xspi_pio_mdma_read(struct cdns_xspi_dev *cdns_xspi, + struct spinand_device *spinand, + const struct spi_mem_op *op) +{ + u32 cmd_regs[6] =3D { 0 }; + u64 xspi_addr; + int ret; + + if (!cdns_xspi->acmd_info.row_addr_valid) { + dev_err(cdns_xspi->dev, + "read cache command without a pending page read\n"); + return -EINVAL; + } + + if (op->data.dir !=3D SPI_MEM_DATA_IN || !op->data.nbytes || + !op->data.buf.in) { + ret =3D -EINVAL; + goto out_clear_read_state; + } + + if (op->data.nbytes > cdns_xspi->dma_buf_len) { + ret =3D -EMSGSIZE; + goto out_clear_read_state; + } + + ret =3D cdns_xspi_wait_for_controller_idle(cdns_xspi); + if (ret) { + dev_err(cdns_xspi->dev, + "controller did not become idle before read\n"); + goto out_clear_read_state; + } + + ret =3D cdns_xspi_nand_addr(spinand, + cdns_xspi->acmd_info.row_addr, + op->addr.val, &xspi_addr); + if (ret) + goto out_clear_read_state; + + if (upper_32_bits(xspi_addr)) { + dev_err(cdns_xspi->dev, + "read address 0x%llx exceeds the ACMD PIO range\n", + xspi_addr); + ret =3D -ERANGE; + goto out_clear_read_state; + } + + cmd_regs[1] =3D lower_32_bits(xspi_addr); + cmd_regs[2] =3D lower_32_bits(cdns_xspi->dma_addr); + cmd_regs[3] =3D upper_32_bits(cdns_xspi->dma_addr); + cmd_regs[4] =3D op->data.nbytes - 1; + cmd_regs[0] =3D cdns_xspi_acmd_cmd(cdns_xspi, + CDNS_XSPI_ACMD_READ_OP, + CDNS_XSPI_ACMD_DATA_THREAD, true); + + ret =3D cdns_xspi_acmd_run(cdns_xspi, cmd_regs, + CDNS_XSPI_ACMD_DATA_THREAD); + if (ret) { + dev_err(cdns_xspi->dev, "ACMD read failed: %d\n", ret); + goto out_clear_read_state; + } + + memcpy(op->data.buf.in, cdns_xspi->dma_buf, op->data.nbytes); + +out_clear_read_state: + cdns_xspi->acmd_info.row_addr_valid =3D false; + cdns_xspi->acmd_info.row_addr =3D 0; + return ret; +} + +static int cdns_xspi_send_pio_command(struct cdns_xspi_dev *cdns_xspi, + struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct spinand_device *spinand; + const struct spi_mem_op *read_cache; + const struct spi_mem_op *write_cache; + const struct spi_mem_op *update_cache; + int ret; + + if (cdns_xspi->flash_type !=3D CDNS_XSPI_FLASH_TYPE_NAND) + goto use_stig; + + spinand =3D spi_mem_get_drvdata(mem); + if (!spinand || !spinand->op_templates || + !spinand->op_templates->read_cache || + !spinand->op_templates->write_cache || + !spinand->op_templates->update_cache) + goto use_stig; + + if (!cdns_xspi->acmd_info.initialized) { + ret =3D cdns_xspi_nand_init(cdns_xspi, spinand); + if (ret) { + dev_err(cdns_xspi->dev, + "failed to initialize ACMD: %d\n", ret); + return ret; + } + } + + read_cache =3D spinand->op_templates->read_cache; + write_cache =3D spinand->op_templates->write_cache; + update_cache =3D spinand->op_templates->update_cache; + + if ((write_cache && op->cmd.opcode =3D=3D write_cache->cmd.opcode) || + (update_cache && op->cmd.opcode =3D=3D update_cache->cmd.opcode)) { + if (op->data.dir !=3D SPI_MEM_DATA_OUT || !op->data.nbytes || + !op->data.buf.out) + return -EINVAL; + + cdns_xspi_nand_write_enable_seq_init(cdns_xspi); + cdns_xspi->out_buffer =3D op->data.buf.out; + cdns_xspi->acmd_info.column_addr =3D op->addr.val; + cdns_xspi->acmd_info.data_nbytes =3D op->data.nbytes; + return 0; + } + + switch (op->cmd.opcode) { + case CDNS_XSPI_NAND_OP_PAGE_READ: + cdns_xspi->acmd_info.row_addr =3D op->addr.val; + cdns_xspi->acmd_info.row_addr_valid =3D true; + return 0; + + case CDNS_XSPI_NAND_OP_GET_FEATURE: + if (op->addr.val !=3D CDNS_XSPI_NAND_STATUS_REG || + !cdns_xspi->acmd_info.row_addr_valid) + break; + + if (op->data.dir !=3D SPI_MEM_DATA_IN || !op->data.nbytes || + !op->data.buf.in) + return -EINVAL; + + memset(op->data.buf.in, 0, op->data.nbytes); + return 0; + + case CDNS_XSPI_NAND_OP_RESET: + return cdns_xspi_pio_reset(cdns_xspi); + + case CDNS_XSPI_NAND_OP_BLOCK_ERASE: + return cdns_xspi_pio_mdma_erase(cdns_xspi, spinand, op); + + case CDNS_XSPI_NAND_OP_PROGRAM_EXECUTE: + return cdns_xspi_pio_mdma_program(cdns_xspi, spinand, op); + + default: + break; + } + + if (read_cache && op->cmd.opcode =3D=3D read_cache->cmd.opcode) + return cdns_xspi_pio_mdma_read(cdns_xspi, spinand, op); + +use_stig: + /* ACMD does not consume this operation; execute it through STIG. */ + return cdns_xspi_send_stig_command(cdns_xspi, op, + op->data.dir !=3D SPI_MEM_NO_DATA); +} + static int cdns_xspi_mem_op(struct cdns_xspi_dev *cdns_xspi, struct spi_mem *mem, const struct spi_mem_op *op) @@ -589,6 +1361,9 @@ static int cdns_xspi_mem_op(struct cdns_xspi_dev *cdns= _xspi, if (cdns_xspi->cur_cs !=3D spi_get_chipselect(mem->spi, 0)) cdns_xspi->cur_cs =3D spi_get_chipselect(mem->spi, 0); =20 + if (cdns_xspi->work_mode =3D=3D CDNS_XSPI_WORK_MODE_ACMD) + return cdns_xspi_send_pio_command(cdns_xspi, mem, op); + return cdns_xspi_send_stig_command(cdns_xspi, op, (dir !=3D SPI_MEM_NO_DATA)); } @@ -1217,6 +1992,9 @@ static int cdns_xspi_probe(struct platform_device *pd= ev) cdns_xspi->host =3D host; cdns_xspi->dev =3D &pdev->dev; cdns_xspi->cur_cs =3D 0; + cdns_xspi->flash_type =3D cdns_xspi->driver_data->flash_type; + cdns_xspi->work_mode =3D cdns_xspi->driver_data->use_acmd ? + CDNS_XSPI_WORK_MODE_ACMD : CDNS_XSPI_WORK_MODE_STIG; =20 init_completion(&cdns_xspi->cmd_complete); init_completion(&cdns_xspi->auto_cmd_complete); @@ -1344,6 +2122,10 @@ static const struct of_device_id cdns_xspi_of_match[= ] =3D { .data =3D &marvell_driver_data, }, #endif + { + .compatible =3D "cdns,xspi-nand", + .data =3D &cdns_nand_driver_data, + }, { /* end of table */} }; MODULE_DEVICE_TABLE(of, cdns_xspi_of_match); --=20 2.34.1 From nobody Thu Sep 24 20:04:10 2026 Received: from spam01.horizon.auto (spam01.horizon.auto [42.62.85.75]) (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 BA52E46E01D; Mon, 21 Sep 2026 10:28:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=42.62.85.75 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789986498; cv=none; b=IiEQCwPxT+UTHSuLLERgHlkJLop1VXaNhUYv7aGTUg3toIXQp3VVcIR0kFSNmIJbxI+1q7D9Wd7MLexLx+jgLToPfg060EENFYnrw0kvxM9aCwsDoPUcta9nSosZGJMXbZ6C7+vn84nb2gCwcxSVk2uz4ZVcFd7bR4/xhGeamI0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789986498; c=relaxed/simple; bh=GcSjeCEg4EifetYnW8w4MqDaNAd6lARMlHDXjbziGbQ=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=VLYm1CNlGktBg7Y1HbV72tnEiZOeWB0CPPJDWok0tpm59w93PQq4eUq7GTc9o9tUcuR7DiN+0REOOwWRvlV+XrbRQvmWpyGQFsPr1zvYc+GLcT8idr5thIWXA96gR/E4ZZUxhviapgtzBXSJzQKEW5wjh9VUxJhfjPOBM//096o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=horizon.auto; spf=pass smtp.mailfrom=horizon.auto; dkim=fail (2048-bit key) header.d=horizon.auto header.i=@horizon.auto header.b=QWnT/mDe reason="signature verification failed"; arc=none smtp.client-ip=42.62.85.75 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=horizon.auto Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=horizon.auto Authentication-Results: smtp.subspace.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=horizon.auto header.i=@horizon.auto header.b="QWnT/mDe" Received: from spam01.horizon.auto (localhost [127.0.0.2] (may be forged)) by spam01.horizon.auto with ESMTP id 68L9csFU069410; Mon, 21 Sep 2026 17:38:54 +0800 (+08) (envelope-from fei.xie@horizon.auto) Received: from spam01.horizon.auto (exchange003.hobot.cc [10.69.1.10]) by spam01.horizon.auto with ESMTPS id 68L9c36m068860 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NO); Mon, 21 Sep 2026 17:38:03 +0800 (+08) (envelope-from fei.xie@horizon.auto) Received: from server227.horizon.auto (10.9.0.252) by exchange003.hobot.cc (10.69.1.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_RSA_WITH_AES_128_CBC_SHA256) id 15.1.2308.27; Mon, 21 Sep 2026 17:38:02 +0800 From: Fei Xie To: Mark Brown CC: Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Parshuram Thombare , , , , , "fei.xie" Subject: [RFC PATCH 3/4] spi: cadence-xspi: factor out reusable ACMD helpers Date: Mon, 21 Sep 2026 17:37:00 +0800 Message-ID: <20260921093701.1341766-4-fei.xie@horizon.auto> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260921093701.1341766-1-fei.xie@horizon.auto> References: <20260921093701.1341766-1-fei.xie@horizon.auto> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: exchange004.hobot.cc (10.9.15.112) To exchange003.hobot.cc (10.69.1.10) X-DKIM-Results: exchange003.hobot.cc; dkim=none; X-DNSRBL: X-SPAM-SOURCE-CHECK: pass X-MAIL: spam01.horizon.auto 68L9csFU069410 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=horizon.auto; s=auto; t=1789983498; bh=1b1haRp//ZE7fcCGYJ0hGUgE8n+xITtgL9GI45T07h8=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=QWnT/mDeQvfT6OKpP2lYYoygkJByifjObIVo58BFETHmo1l4xdWt0U1fibOlWQc8P jkgi2JQMO90mP/H5wrA5hdErLtoQavAQSWNJLOZ2ilP0RtkpSGxQGHSEFCq13iBZmD ntrieEA9g4Q7y2Krtj9fofbniga0dEFCEBL8tAYsG06wHsERv9g6LOwdv8kvw7Hr1k SKLKoMIOHaoxrKFhXcOf0b9klaKRRfOqJZ7Qjqx/5lYsS32mAf5Q/pou17jgsUIwxn 5qssNBpXhxZfDNJaq2pK7vsn1BaeEkXR+9Mi1yaxqFOG9kMnXpWwlAI26GArYbEpnR d/aAnPXa6zQuQ== Content-Type: text/plain; charset="utf-8" From: "fei.xie" Move DMA buffer allocation, XIP setup and PIO command execution into common helpers. Keep SPI NAND address conversion and operation state handling in NAND-specific wrappers. This is a behavior-neutral preparation for sharing the ACMD PIO and MDMA path with other flash types. Signed-off-by: fei.xie --- drivers/spi/spi-cadence-xspi.c | 223 ++++++++++++++++++++------------- 1 file changed, 136 insertions(+), 87 deletions(-) diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c index 0fedf6cd6a590..114c1e0fd79fb 100644 --- a/drivers/spi/spi-cadence-xspi.c +++ b/drivers/spi/spi-cadence-xspi.c @@ -822,10 +822,35 @@ static void cdns_xspi_nand_reset_seq_init(struct cdns= _xspi_dev *cdns_xspi) writel(cfg, cdns_xspi->iobase + CDNS_XSPI_RST_SEQ_CFG_0); } =20 +static int cdns_xspi_acmd_alloc_dma_buf(struct cdns_xspi_dev *cdns_xspi, + size_t len) +{ + cdns_xspi->dma_buf =3D dmam_alloc_coherent(cdns_xspi->dev, len, + &cdns_xspi->dma_addr, + GFP_KERNEL); + if (!cdns_xspi->dma_buf) + return -ENOMEM; + + cdns_xspi->dma_buf_len =3D len; + + return 0; +} + +static void cdns_xspi_acmd_disable_xip(struct cdns_xspi_dev *cdns_xspi) +{ + u32 reg_val; + + reg_val =3D readl(cdns_xspi->iobase + CDNS_XSPI_XIP_MODE_CFG); + if (reg_val & CDNS_XSPI_XIP_EN) { + reg_val &=3D ~CDNS_XSPI_XIP_EN; + writel(reg_val, cdns_xspi->iobase + CDNS_XSPI_XIP_MODE_CFG); + } +} + static int cdns_xspi_nand_init(struct cdns_xspi_dev *cdns_xspi, struct spinand_device *spinand) { - u32 reg_val; + int ret; =20 cdns_xspi_nand_cfg_seq_init(cdns_xspi, spinand); cdns_xspi_nand_read_seq_init(cdns_xspi, spinand); @@ -835,19 +860,12 @@ static int cdns_xspi_nand_init(struct cdns_xspi_dev *= cdns_xspi, cdns_xspi_nand_status_seq_init(cdns_xspi); cdns_xspi_nand_erase_seq_init(cdns_xspi, spinand); =20 - reg_val =3D readl(cdns_xspi->iobase + CDNS_XSPI_XIP_MODE_CFG); - if (reg_val & CDNS_XSPI_XIP_EN) { - reg_val &=3D ~CDNS_XSPI_XIP_EN; - writel(reg_val, cdns_xspi->iobase + CDNS_XSPI_XIP_MODE_CFG); - } - cdns_xspi->dma_buf_len =3D spinand->base.memorg.pagesize + - spinand->base.memorg.oobsize; - cdns_xspi->dma_buf =3D dmam_alloc_coherent(cdns_xspi->dev, - cdns_xspi->dma_buf_len, - &cdns_xspi->dma_addr, - GFP_KERNEL); - if (!cdns_xspi->dma_buf) - return -ENOMEM; + cdns_xspi_acmd_disable_xip(cdns_xspi); + ret =3D cdns_xspi_acmd_alloc_dma_buf(cdns_xspi, + spinand->base.memorg.pagesize + + spinand->base.memorg.oobsize); + if (ret) + return ret; =20 cdns_xspi->acmd_info.initialized =3D true; =20 @@ -1070,12 +1088,10 @@ static u32 cdns_xspi_acmd_cmd(struct cdns_xspi_dev = *cdns_xspi, u32 type, FIELD_PREP(CDNS_XSPI_ACMD_CMD_TYPE, type); } =20 -static int cdns_xspi_pio_mdma_erase(struct cdns_xspi_dev *cdns_xspi, - struct spinand_device *spinand, - const struct spi_mem_op *op) +static int cdns_xspi_pio_erase(struct cdns_xspi_dev *cdns_xspi, + u64 xspi_addr) { u32 cmd_regs[6] =3D { 0 }; - u64 xspi_addr; int ret; =20 ret =3D cdns_xspi_wait_for_controller_idle(cdns_xspi); @@ -1085,10 +1101,6 @@ static int cdns_xspi_pio_mdma_erase(struct cdns_xspi= _dev *cdns_xspi, return ret; } =20 - ret =3D cdns_xspi_nand_addr(spinand, op->addr.val, 0, &xspi_addr); - if (ret) - return ret; - if (upper_32_bits(xspi_addr)) { dev_err(cdns_xspi->dev, "erase address 0x%llx exceeds the ACMD PIO range\n", @@ -1137,71 +1149,141 @@ static int cdns_xspi_pio_reset(struct cdns_xspi_de= v *cdns_xspi) } =20 static int cdns_xspi_pio_mdma_program(struct cdns_xspi_dev *cdns_xspi, - struct spinand_device *spinand, - const struct spi_mem_op *op) + u64 xspi_addr, const void *buf, + size_t nbytes) { u32 cmd_regs[6] =3D { 0 }; - u64 xspi_addr; int ret; =20 - if (!cdns_xspi->out_buffer || !cdns_xspi->acmd_info.data_nbytes) { - dev_err(cdns_xspi->dev, "missing program-load data\n"); - ret =3D -EINVAL; - goto out_clear_program_state; - } + if (!buf || !nbytes) + return -EINVAL; =20 - if (cdns_xspi->acmd_info.data_nbytes > cdns_xspi->dma_buf_len) { - ret =3D -EMSGSIZE; - goto out_clear_program_state; + if (nbytes > cdns_xspi->dma_buf_len) + return -EMSGSIZE; + + if (upper_32_bits(xspi_addr)) { + dev_err(cdns_xspi->dev, + "program address 0x%llx exceeds the ACMD PIO range\n", + xspi_addr); + return -ERANGE; } =20 - memcpy(cdns_xspi->dma_buf, cdns_xspi->out_buffer, - cdns_xspi->acmd_info.data_nbytes); + memcpy(cdns_xspi->dma_buf, buf, nbytes); =20 ret =3D cdns_xspi_wait_for_controller_idle(cdns_xspi); if (ret) { dev_err(cdns_xspi->dev, "controller did not become idle before program\n"); - goto out_clear_program_state; + return ret; } =20 - ret =3D cdns_xspi_nand_addr(spinand, op->addr.val, - cdns_xspi->acmd_info.column_addr, - &xspi_addr); - if (ret) - goto out_clear_program_state; + cmd_regs[1] =3D lower_32_bits(xspi_addr); + cmd_regs[2] =3D lower_32_bits(cdns_xspi->dma_addr); + cmd_regs[3] =3D upper_32_bits(cdns_xspi->dma_addr); + cmd_regs[4] =3D nbytes - 1; + cmd_regs[0] =3D cdns_xspi_acmd_cmd(cdns_xspi, + CDNS_XSPI_ACMD_PROG_OP, + CDNS_XSPI_ACMD_DATA_THREAD, true); + + return cdns_xspi_acmd_run(cdns_xspi, cmd_regs, + CDNS_XSPI_ACMD_DATA_THREAD); +} + +static int cdns_xspi_pio_mdma_read(struct cdns_xspi_dev *cdns_xspi, + u64 xspi_addr, void *buf, size_t nbytes) +{ + u32 cmd_regs[6] =3D { 0 }; + int ret; + + if (!buf || !nbytes) + return -EINVAL; + + if (nbytes > cdns_xspi->dma_buf_len) + return -EMSGSIZE; + + ret =3D cdns_xspi_wait_for_controller_idle(cdns_xspi); + if (ret) { + dev_err(cdns_xspi->dev, + "controller did not become idle before read\n"); + return ret; + } =20 if (upper_32_bits(xspi_addr)) { dev_err(cdns_xspi->dev, - "program address 0x%llx exceeds the ACMD PIO range\n", + "read address 0x%llx exceeds the ACMD PIO range\n", xspi_addr); - ret =3D -ERANGE; - goto out_clear_program_state; + return -ERANGE; } =20 cmd_regs[1] =3D lower_32_bits(xspi_addr); cmd_regs[2] =3D lower_32_bits(cdns_xspi->dma_addr); cmd_regs[3] =3D upper_32_bits(cdns_xspi->dma_addr); - cmd_regs[4] =3D cdns_xspi->acmd_info.data_nbytes - 1; + cmd_regs[4] =3D nbytes - 1; cmd_regs[0] =3D cdns_xspi_acmd_cmd(cdns_xspi, - CDNS_XSPI_ACMD_PROG_OP, + CDNS_XSPI_ACMD_READ_OP, CDNS_XSPI_ACMD_DATA_THREAD, true); =20 ret =3D cdns_xspi_acmd_run(cdns_xspi, cmd_regs, CDNS_XSPI_ACMD_DATA_THREAD); + if (ret) { + dev_err(cdns_xspi->dev, "ACMD read failed: %d\n", ret); + return ret; + } + + memcpy(buf, cdns_xspi->dma_buf, nbytes); + + return 0; +} + +static int cdns_xspi_nand_pio_erase(struct cdns_xspi_dev *cdns_xspi, + struct spinand_device *spinand, + const struct spi_mem_op *op) +{ + u64 xspi_addr; + int ret; + + ret =3D cdns_xspi_nand_addr(spinand, op->addr.val, 0, &xspi_addr); + if (ret) + return ret; + + return cdns_xspi_pio_erase(cdns_xspi, xspi_addr); +} + +static int cdns_xspi_nand_pio_program(struct cdns_xspi_dev *cdns_xspi, + struct spinand_device *spinand, + const struct spi_mem_op *op) +{ + u64 xspi_addr; + int ret; + + if (!cdns_xspi->out_buffer || !cdns_xspi->acmd_info.data_nbytes) { + dev_err(cdns_xspi->dev, "missing program-load data\n"); + ret =3D -EINVAL; + goto out_clear_program_state; + } + + ret =3D cdns_xspi_nand_addr(spinand, op->addr.val, + cdns_xspi->acmd_info.column_addr, + &xspi_addr); + if (ret) + goto out_clear_program_state; + + ret =3D cdns_xspi_pio_mdma_program(cdns_xspi, xspi_addr, + cdns_xspi->out_buffer, + cdns_xspi->acmd_info.data_nbytes); =20 out_clear_program_state: cdns_xspi->acmd_info.column_addr =3D 0; cdns_xspi->acmd_info.data_nbytes =3D 0; cdns_xspi->out_buffer =3D NULL; + return ret; } =20 -static int cdns_xspi_pio_mdma_read(struct cdns_xspi_dev *cdns_xspi, +static int cdns_xspi_nand_pio_read(struct cdns_xspi_dev *cdns_xspi, struct spinand_device *spinand, const struct spi_mem_op *op) { - u32 cmd_regs[6] =3D { 0 }; u64 xspi_addr; int ret; =20 @@ -1217,52 +1299,19 @@ static int cdns_xspi_pio_mdma_read(struct cdns_xspi= _dev *cdns_xspi, goto out_clear_read_state; } =20 - if (op->data.nbytes > cdns_xspi->dma_buf_len) { - ret =3D -EMSGSIZE; - goto out_clear_read_state; - } - - ret =3D cdns_xspi_wait_for_controller_idle(cdns_xspi); - if (ret) { - dev_err(cdns_xspi->dev, - "controller did not become idle before read\n"); - goto out_clear_read_state; - } - ret =3D cdns_xspi_nand_addr(spinand, cdns_xspi->acmd_info.row_addr, op->addr.val, &xspi_addr); if (ret) goto out_clear_read_state; =20 - if (upper_32_bits(xspi_addr)) { - dev_err(cdns_xspi->dev, - "read address 0x%llx exceeds the ACMD PIO range\n", - xspi_addr); - ret =3D -ERANGE; - goto out_clear_read_state; - } - - cmd_regs[1] =3D lower_32_bits(xspi_addr); - cmd_regs[2] =3D lower_32_bits(cdns_xspi->dma_addr); - cmd_regs[3] =3D upper_32_bits(cdns_xspi->dma_addr); - cmd_regs[4] =3D op->data.nbytes - 1; - cmd_regs[0] =3D cdns_xspi_acmd_cmd(cdns_xspi, - CDNS_XSPI_ACMD_READ_OP, - CDNS_XSPI_ACMD_DATA_THREAD, true); - - ret =3D cdns_xspi_acmd_run(cdns_xspi, cmd_regs, - CDNS_XSPI_ACMD_DATA_THREAD); - if (ret) { - dev_err(cdns_xspi->dev, "ACMD read failed: %d\n", ret); - goto out_clear_read_state; - } - - memcpy(op->data.buf.in, cdns_xspi->dma_buf, op->data.nbytes); + ret =3D cdns_xspi_pio_mdma_read(cdns_xspi, xspi_addr, + op->data.buf.in, op->data.nbytes); =20 out_clear_read_state: cdns_xspi->acmd_info.row_addr_valid =3D false; cdns_xspi->acmd_info.row_addr =3D 0; + return ret; } =20 @@ -1334,17 +1383,17 @@ static int cdns_xspi_send_pio_command(struct cdns_x= spi_dev *cdns_xspi, return cdns_xspi_pio_reset(cdns_xspi); =20 case CDNS_XSPI_NAND_OP_BLOCK_ERASE: - return cdns_xspi_pio_mdma_erase(cdns_xspi, spinand, op); + return cdns_xspi_nand_pio_erase(cdns_xspi, spinand, op); =20 case CDNS_XSPI_NAND_OP_PROGRAM_EXECUTE: - return cdns_xspi_pio_mdma_program(cdns_xspi, spinand, op); + return cdns_xspi_nand_pio_program(cdns_xspi, spinand, op); =20 default: break; } =20 if (read_cache && op->cmd.opcode =3D=3D read_cache->cmd.opcode) - return cdns_xspi_pio_mdma_read(cdns_xspi, spinand, op); + return cdns_xspi_nand_pio_read(cdns_xspi, spinand, op); =20 use_stig: /* ACMD does not consume this operation; execute it through STIG. */ --=20 2.34.1 From nobody Thu Sep 24 20:04:10 2026 Received: from spam01.horizon.auto (spam01.horizon.auto [42.62.85.75]) (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 47C8047277C; Mon, 21 Sep 2026 10:28:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=42.62.85.75 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789986509; cv=none; b=QYdN98OMBwiCs2fFG4K1QXHNHJfbrYxjin4y43n2/ZcdhFbpUGXZktuBEcJIfjaCq4TprJieEGefuIdxPrV99hNJSIiTHxnQW+9QT8rqWZm8nLBg26kifFeAnjKcT8G2DiwIIHxMJG+rSnOkylOfOAGoP4Rn1F0dmoZQGTeHBnI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789986509; c=relaxed/simple; bh=C/cijtAZYyQdBEf0M/dJNxv6j+pvRLaav2ADrktA3Vg=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Sh1nxHQrvk/5wCCSy0nBxl+dX0l+TWFaQhBy06G4tuBDbh07V5gaHVuKjG4ctQCjiHHWT1b9nxFaXaqhx0QSwHqHo3l8v05apyPJyoBj9vLX7VDqLAGEOZOJTlddeJXAlDAh4qoAXZCvV0wxCCw+MTxMdqjMtUkntlgUue1oR0I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=horizon.auto; spf=pass smtp.mailfrom=horizon.auto; dkim=fail (2048-bit key) header.d=horizon.auto header.i=@horizon.auto header.b=KsAgJfnv reason="signature verification failed"; arc=none smtp.client-ip=42.62.85.75 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=horizon.auto Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=horizon.auto Authentication-Results: smtp.subspace.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=horizon.auto header.i=@horizon.auto header.b="KsAgJfnv" Received: from spam01.horizon.auto (localhost [127.0.0.2] (may be forged)) by spam01.horizon.auto with ESMTP id 68L9csUL069412; Mon, 21 Sep 2026 17:38:54 +0800 (+08) (envelope-from fei.xie@horizon.auto) Received: from spam01.horizon.auto (exchange003.hobot.cc [10.69.1.10]) by spam01.horizon.auto with ESMTPS id 68L9c4Sh068891 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NO); Mon, 21 Sep 2026 17:38:04 +0800 (+08) (envelope-from fei.xie@horizon.auto) Received: from server227.horizon.auto (10.9.0.252) by exchange003.hobot.cc (10.69.1.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_RSA_WITH_AES_128_CBC_SHA256) id 15.1.2308.27; Mon, 21 Sep 2026 17:38:04 +0800 From: Fei Xie To: Mark Brown CC: Miquel Raynal , Richard Weinberger , Vignesh Raghavendra , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Parshuram Thombare , , , , , "fei.xie" Subject: [RFC PATCH 4/4] spi: cadence-xspi: add ACMD support for SPI NOR Date: Mon, 21 Sep 2026 17:37:01 +0800 Message-ID: <20260921093701.1341766-5-fei.xie@horizon.auto> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260921093701.1341766-1-fei.xie@horizon.auto> References: <20260921093701.1341766-1-fei.xie@horizon.auto> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: exchange004.hobot.cc (10.9.15.112) To exchange003.hobot.cc (10.69.1.10) X-DKIM-Results: exchange003.hobot.cc; dkim=none; X-DNSRBL: X-SPAM-SOURCE-CHECK: pass X-MAIL: spam01.horizon.auto 68L9csUL069412 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=horizon.auto; s=auto; t=1789983503; bh=NIDiqsVmgrS3EXwLeiz3kru1Gjw4BQlfnJkDij+iwqc=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=KsAgJfnv2QHpF9krCruijVPJsVP4w8IqeFqrHdu2P6xt531EdAhAsLhC4xkShNkgR jB9I1O8fnj7c48pfr4B8QvpnlQXAY6GRXG+iosKUEXhUSax0No5AkwYYMa11km4kUo /bAXS2XwbxLh3MbUT5q1CdiIJpLE1cY4opkBs9hvHLnmBuFOPkDMK3+M3ytzjgMuXa 7EGLpMqfZ9oke1j2FWTYjV3dbNtQ+ZlV6tqQvlZDEObfFVNoj46lRkx4Y1s7T2Lj7w vn26aYMsKt4seFidU+9P8QzOnlZy0HB0mD1sTjq9ZVbRcr3fuRv3WDv2CJdB4Qr+j3 i+whB+T3PycUA== Content-Type: text/plain; charset="utf-8" From: "fei.xie" Program the automatic command sequences from the SPI NOR operation templates. Use the PIO command path with MDMA data transfers for reads, page programs and erases. Unsupported command layouts continue to use STIG mode. Signed-off-by: fei.xie --- drivers/spi/spi-cadence-xspi.c | 440 ++++++++++++++++++++++++++++++++- 1 file changed, 433 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c index 114c1e0fd79fb..5900d4cbffb5c 100644 --- a/drivers/spi/spi-cadence-xspi.c +++ b/drivers/spi/spi-cadence-xspi.c @@ -12,11 +12,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -73,6 +75,7 @@ #define CDNS_XSPI_ACMD_ERASE_THREAD 5 #define CDNS_XSPI_ACMD_TIMEOUT_MS 1000 #define CDNS_XSPI_ACMD_MODE_PIO 1 +#define CDNS_XSPI_NOR_DMA_BUF_SIZE SZ_32K =20 #define CDNS_XSPI_NAND_OP_GET_FEATURE 0x0f #define CDNS_XSPI_NAND_OP_WRITE_ENABLE 0x06 @@ -225,6 +228,7 @@ #define CDNS_XSPI_READ_SEQ_CFG_1 0x0434 #define CDNS_XSPI_READ_SEQ_P1_CMD_EXT_EN BIT(0) #define CDNS_XSPI_READ_SEQ_P1_CACHE_RANDOM_READ_EN BIT(4) +#define CDNS_XSPI_READ_SEQ_P1_CMD_EXT_VAL GENMASK(15, 8) #define CDNS_XSPI_READ_SEQ_CFG_2 0x0438 =20 /* Write enable sequence config register */ @@ -232,12 +236,22 @@ #define CDNS_XSPI_WE_SEQ_P1_CMD_VAL GENMASK(7, 0) #define CDNS_XSPI_WE_SEQ_P1_CMD_IOS GENMASK(9, 8) #define CDNS_XSPI_WE_SEQ_P1_CMD_EDGE BIT(11) +#define CDNS_XSPI_WE_SEQ_P1_CMD_EXT_EN BIT(15) +#define CDNS_XSPI_WE_SEQ_P1_CMD_EXT_VAL GENMASK(23, 16) #define CDNS_XSPI_WE_SEQ_P1_EN BIT(24) =20 /* Status sequence config registers */ #define CDNS_XSPI_STAT_SEQ_CFG_0 0x0450 +#define CDNS_XSPI_STAT_SEQ_P1_CMD_IOS GENMASK(1, 0) +#define CDNS_XSPI_STAT_SEQ_P1_CMD_EDGE BIT(4) +#define CDNS_XSPI_STAT_SEQ_P1_CMD_EXT_EN BIT(5) #define CDNS_XSPI_STAT_SEQ_P1_ADDR_CNT GENMASK(9, 8) +#define CDNS_XSPI_STAT_SEQ_P1_ADDR_IOS GENMASK(11, 10) +#define CDNS_XSPI_STAT_SEQ_P1_ADDR_EDGE BIT(12) +#define CDNS_XSPI_STAT_SEQ_P1_DATA_IOS GENMASK(21, 20) +#define CDNS_XSPI_STAT_SEQ_P1_DATA_EDGE BIT(22) #define CDNS_XSPI_STAT_SEQ_CFG_1 0x0454 +#define CDNS_XSPI_STAT_SEQ_P1_DEV_RDY_DUMMY_CNT GENMASK(5, 0) #define CDNS_XSPI_P1_DEV_RDY_ADDR_EN BIT(6) #define CDNS_XSPI_P1_PROG_FAIL_ADDR_EN BIT(22) #define CDNS_XSPI_P1_ERS_FAIL_ADDR_EN BIT(30) @@ -248,9 +262,11 @@ #define CDNS_XSPI_STAT_SEQ_P1_PROG_FAIL_CMD_VAL GENMASK(31, 24) =20 #define CDNS_XSPI_STAT_SEQ_CFG_3 0x045c +#define CDNS_XSPI_STAT_SEQ_P1_DEV_RDY_CMD_EXT_VAL GENMASK(7, 0) #define CDNS_XSPI_STAT_SEQ_CFG_4 0x0460 #define CDNS_XSPI_STAT_SEQ_CFG_5 0x0464 #define CDNS_XSPI_STAT_SEQ_DEV_RDY_IDX GENMASK(3, 0) +#define CDNS_XSPI_STAT_SEQ_DEV_RDY_SIZE BIT(5) #define CDNS_XSPI_STAT_SEQ_DEV_RDY_EN BIT(6) #define CDNS_XSPI_STAT_SEQ_ERS_FAIL_IDX GENMASK(11, 8) #define CDNS_XSPI_STAT_SEQ_ERS_FAIL_VAL BIT(12) @@ -288,6 +304,7 @@ #define CDNS_XSPI_SEQ_PAGE_PER_BLOCK GENMASK(26, 24) #define CDNS_XSPI_SEQ_PAGE_CA_SIZE BIT(16) #define CDNS_XSPI_SEQ_PAGE_SIZE_EXT GENMASK(8, 0) +#define CDNS_XSPI_SEQ_SPI_NOR 0 =20 #define CDNS_XSPI_XIP_MODE_CFG 0x0388 #define CDNS_XSPI_XIP_EN BIT(0) @@ -476,7 +493,13 @@ struct cdns_xspi_acmd_info { u64 row_addr; u64 column_addr; size_t data_nbytes; + u8 nor_status_buswidth; + u8 nor_write_enable_buswidth; bool row_addr_valid; + bool nor_status_dtr; + bool nor_write_enable_dtr; + bool nor_status_initialized; + bool nor_write_enable_initialized; bool initialized; }; struct cdns_xspi_dev { @@ -592,6 +615,31 @@ static void cdns_xspi_set_interrupts(struct cdns_xspi_= dev *cdns_xspi, writel(intr_enable, cdns_xspi->iobase + CDNS_XSPI_INTR_ENABLE_REG); } =20 +static u8 cdns_xspi_get_opcode(const struct spi_mem_op *op) +{ + if (op->cmd.nbytes =3D=3D 2) + return op->cmd.opcode >> 8; + + return op->cmd.opcode; +} + +static u8 cdns_xspi_get_opcode_extension(const struct spi_mem_op *op) +{ + if (op->cmd.nbytes =3D=3D 2) + return op->cmd.opcode; + + return 0; +} + +static u32 cdns_xspi_get_dummy_cycles(const struct spi_mem_op *op) +{ + if (!op->dummy.nbytes || !op->dummy.buswidth) + return 0; + + return op->dummy.nbytes * BITS_PER_BYTE / + (op->dummy.buswidth * (op->dummy.dtr + 1)); +} + static void cdns_xspi_nand_cfg_seq_init(struct cdns_xspi_dev *cdns_xspi, struct spinand_device *spinand) { @@ -872,6 +920,268 @@ static int cdns_xspi_nand_init(struct cdns_xspi_dev *= cdns_xspi, return 0; } =20 +static int cdns_xspi_nor_cfg_seq_init(struct cdns_xspi_dev *cdns_xspi, + struct spi_nor *nor) +{ + unsigned int page_size; + u32 seq_cfg; + + if (!is_power_of_2(nor->mtd.writebufsize)) + return -EOPNOTSUPP; + + page_size =3D ilog2(nor->mtd.writebufsize); + if (!FIELD_FIT(CDNS_XSPI_SEQ_PAGE_SIZE_PGM, page_size)) + return -EOPNOTSUPP; + + seq_cfg =3D readl(cdns_xspi->iobase + CDNS_XSPI_GLOBAL_SEQ_CFG); + seq_cfg =3D u32_replace_bits(seq_cfg, CDNS_XSPI_SEQ_SPI_NOR, + CDNS_XSPI_SEQ_TYPE); + seq_cfg =3D u32_replace_bits(seq_cfg, page_size, + CDNS_XSPI_SEQ_PAGE_SIZE_PGM); + seq_cfg =3D u32_replace_bits(seq_cfg, + ilog2(CDNS_XSPI_NOR_DMA_BUF_SIZE), + CDNS_XSPI_SEQ_PAGE_SIZE_RD); + writel(seq_cfg, cdns_xspi->iobase + CDNS_XSPI_GLOBAL_SEQ_CFG); + writel(0, cdns_xspi->iobase + CDNS_XSPI_GLOBAL_SEQ_CFG_1); + + return 0; +} + +static int cdns_xspi_nor_read_seq_init(struct cdns_xspi_dev *cdns_xspi, + const struct spi_mem_op *op) +{ + u32 dummy_cycles =3D cdns_xspi_get_dummy_cycles(op); + u32 cfg0; + u32 cfg1; + + if (!FIELD_FIT(CDNS_XSPI_READ_SEQ_P1_ADDR_CNT, op->addr.nbytes) || + !FIELD_FIT(CDNS_XSPI_READ_SEQ_P1_DUMMY_CNT, dummy_cycles)) + return -EOPNOTSUPP; + + cfg0 =3D FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_CMD_VAL, + cdns_xspi_get_opcode(op)) | + FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_CMD_IOS, + ilog2(op->cmd.buswidth)) | + FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_CMD_EDGE, op->cmd.dtr) | + FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_ADDR_CNT, op->addr.nbytes) | + FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_ADDR_IOS, + ilog2(op->addr.buswidth)) | + FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_ADDR_EDGE, op->addr.dtr) | + FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_DATA_IOS, + ilog2(op->data.buswidth)) | + FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_DATA_EDGE, op->data.dtr) | + FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_DUMMY_CNT, dummy_cycles); + cfg1 =3D FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_CMD_EXT_EN, + op->cmd.nbytes =3D=3D 2) | + FIELD_PREP(CDNS_XSPI_READ_SEQ_P1_CMD_EXT_VAL, + cdns_xspi_get_opcode_extension(op)); + + writel(cfg0, cdns_xspi->iobase + CDNS_XSPI_READ_SEQ_CFG_0); + writel(cfg1, cdns_xspi->iobase + CDNS_XSPI_READ_SEQ_CFG_1); + writel(0, cdns_xspi->iobase + CDNS_XSPI_READ_SEQ_CFG_2); + + return 0; +} + +static int cdns_xspi_nor_program_seq_init(struct cdns_xspi_dev *cdns_xspi, + const struct spi_mem_op *op) +{ + u32 dummy_cycles =3D cdns_xspi_get_dummy_cycles(op); + u32 cfg0; + u32 cfg1; + + if (!FIELD_FIT(CDNS_XSPI_PROG_SEQ_P1_ADDR_CNT, op->addr.nbytes) || + !FIELD_FIT(CDNS_XSPI_PROG_SEQ_P1_DUMMY_CNT, dummy_cycles)) + return -EOPNOTSUPP; + + cfg0 =3D FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_CMD_VAL, + cdns_xspi_get_opcode(op)) | + FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_CMD_IOS, + ilog2(op->cmd.buswidth)) | + FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_CMD_EDGE, op->cmd.dtr) | + FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_ADDR_CNT, op->addr.nbytes) | + FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_ADDR_IOS, + ilog2(op->addr.buswidth)) | + FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_ADDR_EDGE, op->addr.dtr) | + FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_DATA_IOS, + ilog2(op->data.buswidth)) | + FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_DATA_EDGE, op->data.dtr) | + FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_DUMMY_CNT, dummy_cycles); + cfg1 =3D FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_CMD_EXT_EN, + op->cmd.nbytes =3D=3D 2) | + FIELD_PREP(CDNS_XSPI_PROG_SEQ_P1_CMD_EXT_VAL, + cdns_xspi_get_opcode_extension(op)); + + writel(cfg0, cdns_xspi->iobase + CDNS_XSPI_PROG_SEQ_CFG_0); + writel(cfg1, cdns_xspi->iobase + CDNS_XSPI_PROG_SEQ_CFG_1); + writel(0, cdns_xspi->iobase + CDNS_XSPI_PROG_SEQ_CFG_2); + + return 0; +} + +static int cdns_xspi_nor_erase_seq_init(struct cdns_xspi_dev *cdns_xspi, + struct spi_nor *nor, + const struct spi_mem_op *op) +{ + unsigned int erase_size; + u32 cfg0; + + if (!is_power_of_2(nor->mtd.erasesize)) + return -EOPNOTSUPP; + + erase_size =3D ilog2(nor->mtd.erasesize); + if (!FIELD_FIT(CDNS_XSPI_ERSS_SEQ_P1_ADDR_CNT, op->addr.nbytes) || + !FIELD_FIT(CDNS_XSPI_ERSS_SEQ_P1_SECT_SIZE, erase_size)) + return -EOPNOTSUPP; + + cfg0 =3D FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_VAL, + cdns_xspi_get_opcode(op)) | + FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_IOS, + ilog2(op->cmd.buswidth)) | + FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_EDGE, op->cmd.dtr) | + FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_ADDR_CNT, op->addr.nbytes) | + FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_EXT_EN, + op->cmd.nbytes =3D=3D 2) | + FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_CMD_EXT_VAL, + cdns_xspi_get_opcode_extension(op)) | + FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_ADDR_IOS, + ilog2(op->addr.buswidth)) | + FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_ADDR_EDGE, op->addr.dtr); + + writel(cfg0, cdns_xspi->iobase + CDNS_XSPI_ERSS_SEQ_CFG_0); + writel(FIELD_PREP(CDNS_XSPI_ERSS_SEQ_P1_SECT_SIZE, erase_size), + cdns_xspi->iobase + CDNS_XSPI_ERSS_SEQ_CFG_1); + writel(0, cdns_xspi->iobase + CDNS_XSPI_ERSS_SEQ_CFG_2); + + return 0; +} + +static int cdns_xspi_nor_status_seq_init(struct cdns_xspi_dev *cdns_xspi, + const struct spi_mem_op *op) +{ + u32 dummy_cycles =3D cdns_xspi_get_dummy_cycles(op); + u32 addr_count =3D op->addr.nbytes ? op->addr.nbytes - 1 : 0; + u32 cfg0; + u32 cfg1; + u32 cfg5; + + if (op->data.dir !=3D SPI_MEM_DATA_IN || !op->data.nbytes || + !FIELD_FIT(CDNS_XSPI_STAT_SEQ_P1_ADDR_CNT, addr_count) || + !FIELD_FIT(CDNS_XSPI_STAT_SEQ_P1_DEV_RDY_DUMMY_CNT, + dummy_cycles) || upper_32_bits(op->addr.val)) + return -EOPNOTSUPP; + + cfg0 =3D FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_CMD_IOS, + ilog2(op->cmd.buswidth)) | + FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_CMD_EDGE, op->cmd.dtr) | + FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_CMD_EXT_EN, + op->cmd.nbytes =3D=3D 2) | + FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_ADDR_CNT, addr_count) | + FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_ADDR_IOS, + op->addr.buswidth ? ilog2(op->addr.buswidth) : 0) | + FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_ADDR_EDGE, op->addr.dtr) | + FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_DATA_IOS, + ilog2(op->data.buswidth)) | + FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_DATA_EDGE, op->data.dtr); + cfg1 =3D FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_DEV_RDY_DUMMY_CNT, + dummy_cycles) | + (op->addr.nbytes ? CDNS_XSPI_P1_DEV_RDY_ADDR_EN : 0); + cfg5 =3D FIELD_PREP(CDNS_XSPI_STAT_SEQ_DEV_RDY_IDX, __ffs(SR_WIP)) | + (op->data.dtr ? CDNS_XSPI_STAT_SEQ_DEV_RDY_SIZE : 0) | + CDNS_XSPI_STAT_SEQ_DEV_RDY_EN; + + writel(cfg0, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_0); + writel(cfg1, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_1); + writel(FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_DEV_RDY_CMD_VAL, + cdns_xspi_get_opcode(op)), + cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_2); + writel(FIELD_PREP(CDNS_XSPI_STAT_SEQ_P1_DEV_RDY_CMD_EXT_VAL, + cdns_xspi_get_opcode_extension(op)), + cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_3); + writel(0, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_4); + writel(cfg5, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_5); + writel(lower_32_bits(op->addr.val), + cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_7); + writel(0, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_8); + writel(0, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_9); + writel(0, cdns_xspi->iobase + CDNS_XSPI_STAT_SEQ_CFG_10); + + cdns_xspi->acmd_info.nor_status_initialized =3D true; + cdns_xspi->acmd_info.nor_status_buswidth =3D op->cmd.buswidth; + cdns_xspi->acmd_info.nor_status_dtr =3D op->cmd.dtr; + + return 0; +} + +static void +cdns_xspi_nor_write_enable_seq_init(struct cdns_xspi_dev *cdns_xspi, + const struct spi_mem_op *op) +{ + u32 cfg; + + cfg =3D FIELD_PREP(CDNS_XSPI_WE_SEQ_P1_CMD_VAL, + cdns_xspi_get_opcode(op)) | + FIELD_PREP(CDNS_XSPI_WE_SEQ_P1_CMD_IOS, + ilog2(op->cmd.buswidth)) | + FIELD_PREP(CDNS_XSPI_WE_SEQ_P1_CMD_EDGE, op->cmd.dtr) | + FIELD_PREP(CDNS_XSPI_WE_SEQ_P1_CMD_EXT_EN, + op->cmd.nbytes =3D=3D 2) | + FIELD_PREP(CDNS_XSPI_WE_SEQ_P1_CMD_EXT_VAL, + cdns_xspi_get_opcode_extension(op)) | + CDNS_XSPI_WE_SEQ_P1_EN; + writel(cfg, cdns_xspi->iobase + CDNS_XSPI_WE_SEQ_CFG_0); + cdns_xspi->acmd_info.nor_write_enable_initialized =3D true; + cdns_xspi->acmd_info.nor_write_enable_buswidth =3D op->cmd.buswidth; + cdns_xspi->acmd_info.nor_write_enable_dtr =3D op->cmd.dtr; +} + +static int cdns_xspi_nor_init(struct cdns_xspi_dev *cdns_xspi, + struct spi_nor *nor) +{ + struct spi_mem_op status_op =3D + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_IN(1, NULL, 1)); + int ret; + + ret =3D cdns_xspi_nor_cfg_seq_init(cdns_xspi, nor); + if (ret) + return ret; + + cdns_xspi_acmd_disable_xip(cdns_xspi); + ret =3D cdns_xspi_acmd_alloc_dma_buf(cdns_xspi, + CDNS_XSPI_NOR_DMA_BUF_SIZE); + if (ret) + return ret; + + if (!spi_nor_protocol_is_dtr(nor->reg_proto) && + !cdns_xspi->acmd_info.nor_status_initialized) { + ret =3D cdns_xspi_nor_status_seq_init(cdns_xspi, &status_op); + if (ret) + return ret; + } + + cdns_xspi->acmd_info.initialized =3D true; + + return 0; +} + +static bool +cdns_xspi_nor_helper_sequences_ready(const struct cdns_xspi_dev *cdns_xspi, + const struct spi_nor *nor) +{ + u8 buswidth =3D spi_nor_get_protocol_inst_nbits(nor->reg_proto); + bool dtr =3D spi_nor_protocol_is_dtr(nor->reg_proto); + + return cdns_xspi->acmd_info.nor_status_initialized && + cdns_xspi->acmd_info.nor_status_buswidth =3D=3D buswidth && + cdns_xspi->acmd_info.nor_status_dtr =3D=3D dtr && + cdns_xspi->acmd_info.nor_write_enable_initialized && + cdns_xspi->acmd_info.nor_write_enable_buswidth =3D=3D buswidth && + cdns_xspi->acmd_info.nor_write_enable_dtr =3D=3D dtr; +} + static int cdns_xspi_controller_init(struct cdns_xspi_dev *cdns_xspi) { u32 ctrl_ver; @@ -1315,9 +1625,9 @@ static int cdns_xspi_nand_pio_read(struct cdns_xspi_d= ev *cdns_xspi, return ret; } =20 -static int cdns_xspi_send_pio_command(struct cdns_xspi_dev *cdns_xspi, - struct spi_mem *mem, - const struct spi_mem_op *op) +static int cdns_xspi_send_nand_pio_command(struct cdns_xspi_dev *cdns_xspi, + struct spi_mem *mem, + const struct spi_mem_op *op) { struct spinand_device *spinand; const struct spi_mem_op *read_cache; @@ -1325,9 +1635,6 @@ static int cdns_xspi_send_pio_command(struct cdns_xsp= i_dev *cdns_xspi, const struct spi_mem_op *update_cache; int ret; =20 - if (cdns_xspi->flash_type !=3D CDNS_XSPI_FLASH_TYPE_NAND) - goto use_stig; - spinand =3D spi_mem_get_drvdata(mem); if (!spinand || !spinand->op_templates || !spinand->op_templates->read_cache || @@ -1401,6 +1708,115 @@ static int cdns_xspi_send_pio_command(struct cdns_x= spi_dev *cdns_xspi, op->data.dir !=3D SPI_MEM_NO_DATA); } =20 +static int cdns_xspi_send_nor_pio_command(struct cdns_xspi_dev *cdns_xspi, + struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct spi_nor *nor =3D spi_mem_get_drvdata(mem); + u8 opcode =3D cdns_xspi_get_opcode(op); + int ret; + + if (!nor) + goto use_stig; + + if (opcode =3D=3D SPINOR_OP_WREN) { + cdns_xspi_nor_write_enable_seq_init(cdns_xspi, op); + goto use_stig; + } + + if (opcode =3D=3D SPINOR_OP_RDSR && op->data.dir =3D=3D SPI_MEM_DATA_IN) { + cdns_xspi->acmd_info.nor_status_initialized =3D false; + ret =3D cdns_xspi_nor_status_seq_init(cdns_xspi, op); + if (ret) + dev_dbg(cdns_xspi->dev, + "cannot use RDSR operation for ACMD status: %d\n", + ret); + goto use_stig; + } + + if (!nor->mtd.size || !nor->mtd.writebufsize) + goto use_stig; + + if (opcode !=3D nor->read_opcode && opcode !=3D nor->program_opcode && + opcode !=3D nor->erase_opcode) + goto use_stig; + if (upper_32_bits(op->addr.val)) + goto use_stig; + + if (!cdns_xspi->acmd_info.initialized) { + ret =3D cdns_xspi_nor_init(cdns_xspi, nor); + if (ret =3D=3D -EOPNOTSUPP) + goto use_stig; + if (ret) { + dev_err(cdns_xspi->dev, + "failed to initialize NOR ACMD: %d\n", ret); + return ret; + } + } + + if (opcode =3D=3D nor->read_opcode && op->data.dir =3D=3D SPI_MEM_DATA_IN= ) { + ret =3D cdns_xspi_nor_read_seq_init(cdns_xspi, op); + if (ret =3D=3D -EOPNOTSUPP) + goto use_stig; + if (ret) + return ret; + + return cdns_xspi_pio_mdma_read(cdns_xspi, op->addr.val, + op->data.buf.in, + op->data.nbytes); + } + + if (opcode =3D=3D nor->program_opcode && + op->data.dir =3D=3D SPI_MEM_DATA_OUT) { + if (!cdns_xspi_nor_helper_sequences_ready(cdns_xspi, nor)) + goto use_stig; + + ret =3D cdns_xspi_nor_program_seq_init(cdns_xspi, op); + if (ret =3D=3D -EOPNOTSUPP) + goto use_stig; + if (ret) + return ret; + + return cdns_xspi_pio_mdma_program(cdns_xspi, op->addr.val, + op->data.buf.out, + op->data.nbytes); + } + + if (opcode =3D=3D nor->erase_opcode && op->data.dir =3D=3D SPI_MEM_NO_DAT= A) { + if (nor->mtd.numeraseregions || + !cdns_xspi_nor_helper_sequences_ready(cdns_xspi, nor)) + goto use_stig; + + ret =3D cdns_xspi_nor_erase_seq_init(cdns_xspi, nor, op); + if (ret =3D=3D -EOPNOTSUPP) + goto use_stig; + if (ret) + return ret; + + return cdns_xspi_pio_erase(cdns_xspi, op->addr.val); + } + +use_stig: + return cdns_xspi_send_stig_command(cdns_xspi, op, + op->data.dir !=3D SPI_MEM_NO_DATA); +} + +static int cdns_xspi_send_pio_command(struct cdns_xspi_dev *cdns_xspi, + struct spi_mem *mem, + const struct spi_mem_op *op) +{ + switch (cdns_xspi->flash_type) { + case CDNS_XSPI_FLASH_TYPE_NAND: + return cdns_xspi_send_nand_pio_command(cdns_xspi, mem, op); + + case CDNS_XSPI_FLASH_TYPE_NOR: + return cdns_xspi_send_nor_pio_command(cdns_xspi, mem, op); + + default: + return -EOPNOTSUPP; + } +} + static int cdns_xspi_mem_op(struct cdns_xspi_dev *cdns_xspi, struct spi_mem *mem, const struct spi_mem_op *op) @@ -1484,8 +1900,18 @@ static int cdns_xspi_adjust_mem_op_size(struct spi_m= em *mem, struct spi_mem_op * { struct cdns_xspi_dev *cdns_xspi =3D spi_controller_get_devdata(mem->spi->controller); + size_t max_len =3D cdns_xspi->sdmasize; + + if (cdns_xspi->work_mode =3D=3D CDNS_XSPI_WORK_MODE_ACMD) { + if (cdns_xspi->acmd_info.initialized) + max_len =3D min_t(size_t, max_len, + cdns_xspi->dma_buf_len); + else if (cdns_xspi->flash_type =3D=3D CDNS_XSPI_FLASH_TYPE_NOR) + max_len =3D min_t(size_t, max_len, + CDNS_XSPI_NOR_DMA_BUF_SIZE); + } =20 - op->data.nbytes =3D clamp_val(op->data.nbytes, 0, cdns_xspi->sdmasize); + op->data.nbytes =3D min_t(size_t, op->data.nbytes, max_len); =20 return 0; } --=20 2.34.1