From patchwork Thu Mar 11 19:12:11 2021
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Pratyush Yadav
X-Patchwork-Id: 12132555
Return-Path:
X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
aws-us-west-2-korg-lkml-1.web.codeaurora.org
X-Spam-Level:
X-Spam-Status: No, score=-19.0 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH,
DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,
INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,
URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0
Received: from mail.kernel.org (mail.kernel.org [198.145.29.99])
by smtp.lore.kernel.org (Postfix) with ESMTP id 2B940C4332E
for ; Thu, 11 Mar 2021 19:13:28 +0000 (UTC)
Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
by mail.kernel.org (Postfix) with ESMTP id E004664EFE
for ; Thu, 11 Mar 2021 19:13:27 +0000 (UTC)
Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand
id S229809AbhCKTMw (ORCPT );
Thu, 11 Mar 2021 14:12:52 -0500
Received: from fllv0016.ext.ti.com ([198.47.19.142]:44248 "EHLO
fllv0016.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
with ESMTP id S229490AbhCKTMq (ORCPT
); Thu, 11 Mar 2021 14:12:46 -0500
Received: from lelv0265.itg.ti.com ([10.180.67.224])
by fllv0016.ext.ti.com (8.15.2/8.15.2) with ESMTP id 12BJCR4m072855;
Thu, 11 Mar 2021 13:12:27 -0600
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com;
s=ti-com-17Q1; t=1615489947;
bh=TFMbG/HskxSKnHoLH+CdmpXxIxc/DBKy8fugLnBgIIQ=;
h=From:To:CC:Subject:Date:In-Reply-To:References;
b=E+B4oiStGriEF7CnmhCLKl4uVexnuAYuBP7aDkkQqhdRG6jso4Mr3ZEurqVwyHFiL
lOViZaAKaRcUmjN+ZT1nB0QOKE5F3L8ZaM9Nx074O2kYxalzXFHnNcyMEMkJi1oBD8
Zyi6ScQdJ2/mOOgI96rf6NR0I+j7kh1SxSTqcOmg=
Received: from DLEE114.ent.ti.com (dlee114.ent.ti.com [157.170.170.25])
by lelv0265.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 12BJCQaS110401
(version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL);
Thu, 11 Mar 2021 13:12:26 -0600
Received: from DLEE103.ent.ti.com (157.170.170.33) by DLEE114.ent.ti.com
(157.170.170.25) with Microsoft SMTP Server (version=TLS1_2,
cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2176.2; Thu, 11
Mar 2021 13:12:26 -0600
Received: from fllv0039.itg.ti.com (10.64.41.19) by DLEE103.ent.ti.com
(157.170.170.33) with Microsoft SMTP Server (version=TLS1_2,
cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2176.2 via
Frontend Transport; Thu, 11 Mar 2021 13:12:26 -0600
Received: from pratyush-OptiPlex-790.dhcp.ti.com (ileax41-snat.itg.ti.com
[10.172.224.153])
by fllv0039.itg.ti.com (8.15.2/8.15.2) with ESMTP id 12BJCHvS080816;
Thu, 11 Mar 2021 13:12:22 -0600
From: Pratyush Yadav
To: Nishanth Menon , Tero Kristo ,
Rob Herring ,
Tudor Ambarus ,
Michael Walle ,
Miquel Raynal ,
Richard Weinberger ,
Vignesh Raghavendra ,
Mark Brown ,
,
, ,
,
CC: Pratyush Yadav , Lokesh Vutla
Subject: [RFC PATCH 1/6] spi: spi-mem: Tell controller when device is ready
for calibration
Date: Fri, 12 Mar 2021 00:42:11 +0530
Message-ID: <20210311191216.7363-2-p.yadav@ti.com>
X-Mailer: git-send-email 2.30.0
In-Reply-To: <20210311191216.7363-1-p.yadav@ti.com>
References: <20210311191216.7363-1-p.yadav@ti.com>
MIME-Version: 1.0
X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180
Precedence: bulk
List-ID:
X-Mailing-List: linux-spi@vger.kernel.org
Some controllers like the Cadence OSPI controller need to perform a
calibration sequence to operate at high clock speeds. This calibration
should happen after the flash is fully initialized otherwise the
calibration might happen in a different SPI mode from the one the flash
is finally set to. Add a hook that can be used to tell the controller
when the flash is ready for calibration. Whether calibration is needed
depends on the controller.
Signed-off-by: Pratyush Yadav
---
drivers/spi/spi-mem.c | 12 ++++++++++++
include/linux/spi/spi-mem.h | 8 ++++++++
2 files changed, 20 insertions(+)
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index dc713b0c3c4d..e2f05ad3f4dc 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -464,6 +464,18 @@ int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
}
EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
+int spi_mem_do_calibration(struct spi_mem *mem, struct spi_mem_op *op)
+{
+ struct spi_controller *ctlr = mem->spi->controller;
+
+ if (!ctlr->mem_ops || !ctlr->mem_ops->do_calibration)
+ return -EOPNOTSUPP;
+
+ ctlr->mem_ops->do_calibration(mem, op);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(spi_mem_do_calibration);
+
static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, void *buf)
{
diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
index 2b65c9edc34e..97a2d280f2d0 100644
--- a/include/linux/spi/spi-mem.h
+++ b/include/linux/spi/spi-mem.h
@@ -250,6 +250,12 @@ static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
* the currently mapped area), and the caller of
* spi_mem_dirmap_write() is responsible for calling it again in
* this case.
+ * @do_calibration: perform calibration needed for high SPI clock speed
+ * operation. Should be called after the SPI memory device has
+ * been completely initialized. The op passed should contain
+ * a template for the read operation used for the device so
+ * the controller can decide what type of calibration is
+ * required for this type of read.
*
* This interface should be implemented by SPI controllers providing an
* high-level interface to execute SPI memory operation, which is usually the
@@ -274,6 +280,7 @@ struct spi_controller_mem_ops {
u64 offs, size_t len, void *buf);
ssize_t (*dirmap_write)(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, const void *buf);
+ void (*do_calibration)(struct spi_mem *mem, struct spi_mem_op *op);
};
/**
@@ -346,6 +353,7 @@ bool spi_mem_dtr_supports_op(struct spi_mem *mem,
#endif /* CONFIG_SPI_MEM */
int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op);
+int spi_mem_do_calibration(struct spi_mem *mem, struct spi_mem_op *op);
bool spi_mem_supports_op(struct spi_mem *mem,
const struct spi_mem_op *op);
From patchwork Thu Mar 11 19:12:12 2021
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Pratyush Yadav
X-Patchwork-Id: 12132557
Return-Path:
X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
aws-us-west-2-korg-lkml-1.web.codeaurora.org
X-Spam-Level:
X-Spam-Status: No, score=-19.0 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH,
DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,
INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,
URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0
Received: from mail.kernel.org (mail.kernel.org [198.145.29.99])
by smtp.lore.kernel.org (Postfix) with ESMTP id 74205C43332
for ; Thu, 11 Mar 2021 19:13:28 +0000 (UTC)
Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
by mail.kernel.org (Postfix) with ESMTP id 3481C64F42
for ; Thu, 11 Mar 2021 19:13:28 +0000 (UTC)
Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand
id S229944AbhCKTMw (ORCPT );
Thu, 11 Mar 2021 14:12:52 -0500
Received: from fllv0016.ext.ti.com ([198.47.19.142]:44262 "EHLO
fllv0016.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
with ESMTP id S229970AbhCKTMs (ORCPT
); Thu, 11 Mar 2021 14:12:48 -0500
Received: from lelv0265.itg.ti.com ([10.180.67.224])
by fllv0016.ext.ti.com (8.15.2/8.15.2) with ESMTP id 12BJCVCb072885;
Thu, 11 Mar 2021 13:12:31 -0600
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com;
s=ti-com-17Q1; t=1615489951;
bh=D5mUrZ0LNwosvF5ATru7HapQyEd98umXGOgLCaoP3sw=;
h=From:To:CC:Subject:Date:In-Reply-To:References;
b=uuc5i95sn4KgiTb/lI14qNmWTK3ywbnDfpsffrVSKfIeXKzkU3D8y9QeVH9OfS0Vj
45Ka76C5qkhfyRgWs0H2yvHUmd9qQP4+LXNL9vRPA5CnVZvhIIaIxf7SYw52NtPQkb
hdeyZ0IB9CAoeSAcNYOMdL1CitHsRnkRkoH+YEGo=
Received: from DLEE113.ent.ti.com (dlee113.ent.ti.com [157.170.170.24])
by lelv0265.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 12BJCVUV110439
(version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL);
Thu, 11 Mar 2021 13:12:31 -0600
Received: from DLEE111.ent.ti.com (157.170.170.22) by DLEE113.ent.ti.com
(157.170.170.24) with Microsoft SMTP Server (version=TLS1_2,
cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2176.2; Thu, 11
Mar 2021 13:12:31 -0600
Received: from fllv0039.itg.ti.com (10.64.41.19) by DLEE111.ent.ti.com
(157.170.170.22) with Microsoft SMTP Server (version=TLS1_2,
cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2176.2 via
Frontend Transport; Thu, 11 Mar 2021 13:12:31 -0600
Received: from pratyush-OptiPlex-790.dhcp.ti.com (ileax41-snat.itg.ti.com
[10.172.224.153])
by fllv0039.itg.ti.com (8.15.2/8.15.2) with ESMTP id 12BJCHvT080816;
Thu, 11 Mar 2021 13:12:27 -0600
From: Pratyush Yadav
To: Nishanth Menon , Tero Kristo ,
Rob Herring ,
Tudor Ambarus ,
Michael Walle ,
Miquel Raynal ,
Richard Weinberger ,
Vignesh Raghavendra ,
Mark Brown ,
,
, ,
,
CC: Pratyush Yadav , Lokesh Vutla
Subject: [RFC PATCH 2/6] mtd: spi-nor: core: consolidate read op creation
Date: Fri, 12 Mar 2021 00:42:12 +0530
Message-ID: <20210311191216.7363-3-p.yadav@ti.com>
X-Mailer: git-send-email 2.30.0
In-Reply-To: <20210311191216.7363-1-p.yadav@ti.com>
References: <20210311191216.7363-1-p.yadav@ti.com>
MIME-Version: 1.0
X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180
Precedence: bulk
List-ID:
X-Mailing-List: linux-spi@vger.kernel.org
Currently the spi_mem_op to read from the flash is used in two places:
spi_nor_create_read_dirmap() and spi_nor_spimem_read_data(). In a later
commit this number will increase to three. Instead of repeating the same
code thrice, add a function that returns a template of the read op. The
callers can then fill in the details like address, data length, data
buffer location.
Signed-off-by: Pratyush Yadav
Reviewed-by: Michael Walle
---
drivers/mtd/spi-nor/core.c | 62 ++++++++++++++++++++------------------
1 file changed, 32 insertions(+), 30 deletions(-)
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 4a315cb1c4db..88888df009f0 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -183,6 +183,33 @@ static int spi_nor_controller_ops_erase(struct spi_nor *nor, loff_t offs)
return nor->controller_ops->erase(nor, offs);
}
+/**
+ * spi_nor_spimem_get_read_op() - return a template for the spi_mem_op used for
+ * reading data from the flash via spi-mem.
+ * @nor: pointer to 'struct spi_nor'
+ *
+ * Return: A template of the 'struct spi_mem_op' for used for reading data from
+ * the flash. The caller is expected to fill in the address, data length, and
+ * the data buffer.
+ */
+static struct spi_mem_op spi_nor_spimem_get_read_op(struct spi_nor *nor)
+{
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0),
+ SPI_MEM_OP_ADDR(nor->addr_width, 0, 0),
+ SPI_MEM_OP_DUMMY(nor->read_dummy, 0),
+ SPI_MEM_OP_DATA_IN(1, NULL, 0));
+
+ spi_nor_spimem_setup_op(nor, &op, nor->read_proto);
+
+ /* convert the dummy cycles to the number of bytes */
+ op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
+ if (spi_nor_protocol_is_dtr(nor->read_proto))
+ op.dummy.nbytes *= 2;
+
+ return op;
+}
+
/**
* spi_nor_spimem_read_data() - read data from flash's memory region via
* spi-mem
@@ -196,21 +223,14 @@ static int spi_nor_controller_ops_erase(struct spi_nor *nor, loff_t offs)
static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from,
size_t len, u8 *buf)
{
- struct spi_mem_op op =
- SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0),
- SPI_MEM_OP_ADDR(nor->addr_width, from, 0),
- SPI_MEM_OP_DUMMY(nor->read_dummy, 0),
- SPI_MEM_OP_DATA_IN(len, buf, 0));
+ struct spi_mem_op op = spi_nor_spimem_get_read_op(nor);
bool usebouncebuf;
ssize_t nbytes;
int error;
- spi_nor_spimem_setup_op(nor, &op, nor->read_proto);
-
- /* convert the dummy cycles to the number of bytes */
- op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
- if (spi_nor_protocol_is_dtr(nor->read_proto))
- op.dummy.nbytes *= 2;
+ op.addr.val = from;
+ op.data.nbytes = len;
+ op.data.buf.in = buf;
usebouncebuf = spi_nor_spimem_bounce(nor, &op);
@@ -3581,28 +3601,10 @@ EXPORT_SYMBOL_GPL(spi_nor_scan);
static int spi_nor_create_read_dirmap(struct spi_nor *nor)
{
struct spi_mem_dirmap_info info = {
- .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0),
- SPI_MEM_OP_ADDR(nor->addr_width, 0, 0),
- SPI_MEM_OP_DUMMY(nor->read_dummy, 0),
- SPI_MEM_OP_DATA_IN(0, NULL, 0)),
+ .op_tmpl = spi_nor_spimem_get_read_op(nor),
.offset = 0,
.length = nor->mtd.size,
};
- struct spi_mem_op *op = &info.op_tmpl;
-
- spi_nor_spimem_setup_op(nor, op, nor->read_proto);
-
- /* convert the dummy cycles to the number of bytes */
- op->dummy.nbytes = (nor->read_dummy * op->dummy.buswidth) / 8;
- if (spi_nor_protocol_is_dtr(nor->read_proto))
- op->dummy.nbytes *= 2;
-
- /*
- * Since spi_nor_spimem_setup_op() only sets buswidth when the number
- * of data bytes is non-zero, the data buswidth won't be set here. So,
- * do it explicitly.
- */
- op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
nor->dirmap.rdesc = devm_spi_mem_dirmap_create(nor->dev, nor->spimem,
&info);
From patchwork Thu Mar 11 19:12:13 2021
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Pratyush Yadav
X-Patchwork-Id: 12132551
Return-Path:
X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
aws-us-west-2-korg-lkml-1.web.codeaurora.org
X-Spam-Level:
X-Spam-Status: No, score=-19.0 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH,
DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,
INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,
USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0
Received: from mail.kernel.org (mail.kernel.org [198.145.29.99])
by smtp.lore.kernel.org (Postfix) with ESMTP id 5A78FC433E0
for ; Thu, 11 Mar 2021 19:13:25 +0000 (UTC)
Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
by mail.kernel.org (Postfix) with ESMTP id 20E9164EC0
for ; Thu, 11 Mar 2021 19:13:25 +0000 (UTC)
Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand
id S230182AbhCKTMv (ORCPT );
Thu, 11 Mar 2021 14:12:51 -0500
Received: from fllv0016.ext.ti.com ([198.47.19.142]:44252 "EHLO
fllv0016.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
with ESMTP id S229921AbhCKTMq (ORCPT
); Thu, 11 Mar 2021 14:12:46 -0500
Received: from fllv0034.itg.ti.com ([10.64.40.246])
by fllv0016.ext.ti.com (8.15.2/8.15.2) with ESMTP id 12BJCanU072953;
Thu, 11 Mar 2021 13:12:36 -0600
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com;
s=ti-com-17Q1; t=1615489956;
bh=n3DRjCff2C4yzirXTzPuZEGe7eZhKiV8GXaya6kJgQM=;
h=From:To:CC:Subject:Date:In-Reply-To:References;
b=b5kYp6YSfZpL/Ff+Z70SDQnez/A3FB8SSfTxS0cqixG2nzCWjraR+Btzj1a7B7m9V
WvAflg74iWVQhjGQLXFA1IhaT3XT1o8HOdWeqqCwA/mFZavToyue+bBayuh1cgbsNU
BfrQEdYAThM7P2qi7edmn0cztlpke12CwcNMW550=
Received: from DFLE100.ent.ti.com (dfle100.ent.ti.com [10.64.6.21])
by fllv0034.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 12BJCaog113136
(version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL);
Thu, 11 Mar 2021 13:12:36 -0600
Received: from DFLE107.ent.ti.com (10.64.6.28) by DFLE100.ent.ti.com
(10.64.6.21) with Microsoft SMTP Server (version=TLS1_2,
cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2176.2; Thu, 11
Mar 2021 13:12:35 -0600
Received: from fllv0039.itg.ti.com (10.64.41.19) by DFLE107.ent.ti.com
(10.64.6.28) with Microsoft SMTP Server (version=TLS1_2,
cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2176.2 via
Frontend Transport; Thu, 11 Mar 2021 13:12:35 -0600
Received: from pratyush-OptiPlex-790.dhcp.ti.com (ileax41-snat.itg.ti.com
[10.172.224.153])
by fllv0039.itg.ti.com (8.15.2/8.15.2) with ESMTP id 12BJCHvU080816;
Thu, 11 Mar 2021 13:12:31 -0600
From: Pratyush Yadav
To: Nishanth Menon , Tero Kristo ,
Rob Herring ,
Tudor Ambarus ,
Michael Walle ,
Miquel Raynal ,
Richard Weinberger ,
Vignesh Raghavendra ,
Mark Brown ,
,
, ,
,
CC: Pratyush Yadav , Lokesh Vutla
Subject: [RFC PATCH 3/6] mtd: spi-nor: core: run calibration when
initialization is done
Date: Fri, 12 Mar 2021 00:42:13 +0530
Message-ID: <20210311191216.7363-4-p.yadav@ti.com>
X-Mailer: git-send-email 2.30.0
In-Reply-To: <20210311191216.7363-1-p.yadav@ti.com>
References: <20210311191216.7363-1-p.yadav@ti.com>
MIME-Version: 1.0
X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180
Precedence: bulk
List-ID:
X-Mailing-List: linux-spi@vger.kernel.org
Once the flash is initialized tell the controller it can run
calibration procedures if needed. This can be useful when calibration is
needed to run at higher clock speeds.
Signed-off-by: Pratyush Yadav
---
drivers/mtd/spi-nor/core.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 88888df009f0..e0cbcaf1be89 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -3650,6 +3650,7 @@ static int spi_nor_probe(struct spi_mem *spimem)
* checking what's really supported using spi_mem_supports_op().
*/
const struct spi_nor_hwcaps hwcaps = { .mask = SNOR_HWCAPS_ALL };
+ struct spi_mem_op op;
char *flash_name;
int ret;
@@ -3709,8 +3710,15 @@ static int spi_nor_probe(struct spi_mem *spimem)
if (ret)
return ret;
- return mtd_device_register(&nor->mtd, data ? data->parts : NULL,
- data ? data->nr_parts : 0);
+ ret = mtd_device_register(&nor->mtd, data ? data->parts : NULL,
+ data ? data->nr_parts : 0);
+ if (ret)
+ return ret;
+
+ op = spi_nor_spimem_get_read_op(nor);
+ spi_mem_do_calibration(nor->spimem, &op);
+
+ return 0;
}
static int spi_nor_remove(struct spi_mem *spimem)
From patchwork Thu Mar 11 19:12:14 2021
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Pratyush Yadav
X-Patchwork-Id: 12132559
Return-Path:
X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
aws-us-west-2-korg-lkml-1.web.codeaurora.org
X-Spam-Level:
X-Spam-Status: No, score=-16.2 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH,
DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,
INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,
UNWANTED_LANGUAGE_BODY,USER_AGENT_GIT autolearn=unavailable
autolearn_force=no version=3.4.0
Received: from mail.kernel.org (mail.kernel.org [198.145.29.99])
by smtp.lore.kernel.org (Postfix) with ESMTP id 8043FC433E6
for ; Thu, 11 Mar 2021 19:13:56 +0000 (UTC)
Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
by mail.kernel.org (Postfix) with ESMTP id 3F6D864EE9
for ; Thu, 11 Mar 2021 19:13:56 +0000 (UTC)
Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand
id S229705AbhCKTNY (ORCPT );
Thu, 11 Mar 2021 14:13:24 -0500
Received: from fllv0016.ext.ti.com ([198.47.19.142]:44276 "EHLO
fllv0016.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
with ESMTP id S230198AbhCKTMx (ORCPT
); Thu, 11 Mar 2021 14:12:53 -0500
Received: from lelv0265.itg.ti.com ([10.180.67.224])
by fllv0016.ext.ti.com (8.15.2/8.15.2) with ESMTP id 12BJCfkl072981;
Thu, 11 Mar 2021 13:12:41 -0600
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com;
s=ti-com-17Q1; t=1615489961;
bh=OhuGeU0Ap5BT7Sl9DsCYa3Ndt+hNk93fO4NJx/FnSKQ=;
h=From:To:CC:Subject:Date:In-Reply-To:References;
b=eh4R1oMJGkaqFg1L4275raVqJ9xVf1hbgZPsbamWqIk8KgE7V1NMVLV7l4w6FInhU
/JIJzZLvrn54QL9PKimr5fDbtS434AM70iLss7Nebgo+S5FMFs9A3kD7NslJ4APae8
wecIOPirp1su8cXZ+CEQ5eEFNuIug2+eEAyj7S48=
Received: from DFLE102.ent.ti.com (dfle102.ent.ti.com [10.64.6.23])
by lelv0265.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 12BJCf5p110575
(version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL);
Thu, 11 Mar 2021 13:12:41 -0600
Received: from DFLE113.ent.ti.com (10.64.6.34) by DFLE102.ent.ti.com
(10.64.6.23) with Microsoft SMTP Server (version=TLS1_2,
cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2176.2; Thu, 11
Mar 2021 13:12:40 -0600
Received: from fllv0039.itg.ti.com (10.64.41.19) by DFLE113.ent.ti.com
(10.64.6.34) with Microsoft SMTP Server (version=TLS1_2,
cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2176.2 via
Frontend Transport; Thu, 11 Mar 2021 13:12:40 -0600
Received: from pratyush-OptiPlex-790.dhcp.ti.com (ileax41-snat.itg.ti.com
[10.172.224.153])
by fllv0039.itg.ti.com (8.15.2/8.15.2) with ESMTP id 12BJCHvV080816;
Thu, 11 Mar 2021 13:12:36 -0600
From: Pratyush Yadav
To: Nishanth Menon , Tero Kristo ,
Rob Herring ,
Tudor Ambarus ,
Michael Walle ,
Miquel Raynal ,
Richard Weinberger ,
Vignesh Raghavendra ,
Mark Brown ,
,
, ,
,
CC: Pratyush Yadav , Lokesh Vutla
Subject: [RFC PATCH 4/6] spi: cadence-qspi: Use PHY for DAC reads if possible
Date: Fri, 12 Mar 2021 00:42:14 +0530
Message-ID: <20210311191216.7363-5-p.yadav@ti.com>
X-Mailer: git-send-email 2.30.0
In-Reply-To: <20210311191216.7363-1-p.yadav@ti.com>
References: <20210311191216.7363-1-p.yadav@ti.com>
MIME-Version: 1.0
X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180
Precedence: bulk
List-ID:
X-Mailing-List: linux-spi@vger.kernel.org
Check if a read is eligible for PHY and if it is, enable PHY and DQS.
Since PHY reads only work at an address that is 16-byte aligned and of
size that is a multiple of 16 bytes, read the starting and ending
unaligned portions without PHY, and only enable PHY for the middle part.
Signed-off-by: Pratyush Yadav
---
drivers/spi/spi-cadence-quadspi.c | 203 ++++++++++++++++++++++++++----
1 file changed, 182 insertions(+), 21 deletions(-)
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index e2d6ea833423..e64d8e125263 100644
--- a/drivers/spi/spi-cadence-quadspi.c
+++ b/drivers/spi/spi-cadence-quadspi.c
@@ -41,19 +41,27 @@
struct cqspi_st;
+struct phy_setting {
+ u8 rx;
+ u8 tx;
+ u8 read_delay;
+};
+
struct cqspi_flash_pdata {
- struct cqspi_st *cqspi;
- u32 clk_rate;
- u32 read_delay;
- u32 tshsl_ns;
- u32 tsd2d_ns;
- u32 tchsh_ns;
- u32 tslch_ns;
- u8 inst_width;
- u8 addr_width;
- u8 data_width;
- bool dtr;
- u8 cs;
+ struct cqspi_st *cqspi;
+ u32 clk_rate;
+ u32 read_delay;
+ u32 tshsl_ns;
+ u32 tsd2d_ns;
+ u32 tchsh_ns;
+ u32 tslch_ns;
+ u8 inst_width;
+ u8 addr_width;
+ u8 data_width;
+ bool dtr;
+ u8 cs;
+ bool use_phy;
+ struct phy_setting phy_setting;
};
struct cqspi_st {
@@ -108,12 +116,14 @@ struct cqspi_driver_platdata {
/* Register map */
#define CQSPI_REG_CONFIG 0x00
#define CQSPI_REG_CONFIG_ENABLE_MASK BIT(0)
+#define CQSPI_REG_CONFIG_PHY_EN BIT(3)
#define CQSPI_REG_CONFIG_ENB_DIR_ACC_CTRL BIT(7)
#define CQSPI_REG_CONFIG_DECODE_MASK BIT(9)
#define CQSPI_REG_CONFIG_CHIPSELECT_LSB 10
#define CQSPI_REG_CONFIG_DMA_MASK BIT(15)
#define CQSPI_REG_CONFIG_BAUD_LSB 19
#define CQSPI_REG_CONFIG_DTR_PROTO BIT(24)
+#define CQSPI_REG_CONFIG_PHY_PIPELINE BIT(25)
#define CQSPI_REG_CONFIG_DUAL_OPCODE BIT(30)
#define CQSPI_REG_CONFIG_IDLE_LSB 31
#define CQSPI_REG_CONFIG_CHIPSELECT_MASK 0xF
@@ -150,6 +160,7 @@ struct cqspi_driver_platdata {
#define CQSPI_REG_READCAPTURE_BYPASS_LSB 0
#define CQSPI_REG_READCAPTURE_DELAY_LSB 1
#define CQSPI_REG_READCAPTURE_DELAY_MASK 0xF
+#define CQSPI_REG_READCAPTURE_DQS_LSB 8
#define CQSPI_REG_SIZE 0x14
#define CQSPI_REG_SIZE_ADDRESS_LSB 0
@@ -999,6 +1010,7 @@ static void cqspi_config_baudrate_div(struct cqspi_st *cqspi)
static void cqspi_readdata_capture(struct cqspi_st *cqspi,
const bool bypass,
+ const bool dqs,
const unsigned int delay)
{
void __iomem *reg_base = cqspi->iobase;
@@ -1017,6 +1029,11 @@ static void cqspi_readdata_capture(struct cqspi_st *cqspi,
reg |= (delay & CQSPI_REG_READCAPTURE_DELAY_MASK)
<< CQSPI_REG_READCAPTURE_DELAY_LSB;
+ if (dqs)
+ reg |= (1 << CQSPI_REG_READCAPTURE_DQS_LSB);
+ else
+ reg &= ~(1 << CQSPI_REG_READCAPTURE_DQS_LSB);
+
writel(reg, reg_base + CQSPI_REG_READCAPTURE);
}
@@ -1035,6 +1052,64 @@ static void cqspi_controller_enable(struct cqspi_st *cqspi, bool enable)
writel(reg, reg_base + CQSPI_REG_CONFIG);
}
+static void cqspi_phy_enable(struct cqspi_flash_pdata *f_pdata, bool enable)
+{
+ struct cqspi_st *cqspi = f_pdata->cqspi;
+ void __iomem *reg_base = cqspi->iobase;
+ u32 reg;
+ u8 dummy;
+
+ if (enable) {
+ cqspi_readdata_capture(cqspi, 1, true,
+ f_pdata->phy_setting.read_delay);
+
+ reg = readl(reg_base + CQSPI_REG_CONFIG);
+ reg |= CQSPI_REG_CONFIG_PHY_EN |
+ CQSPI_REG_CONFIG_PHY_PIPELINE;
+ writel(reg, reg_base + CQSPI_REG_CONFIG);
+
+ /*
+ * Reduce dummy cycle by 1. This is a requirement of PHY mode
+ * operation for correctly reading the data.
+ */
+ reg = readl(reg_base + CQSPI_REG_RD_INSTR);
+ dummy = (reg >> CQSPI_REG_RD_INSTR_DUMMY_LSB) &
+ CQSPI_REG_RD_INSTR_DUMMY_MASK;
+ dummy--;
+ reg &= ~(CQSPI_REG_RD_INSTR_DUMMY_MASK <<
+ CQSPI_REG_RD_INSTR_DUMMY_LSB);
+
+ reg |= (dummy & CQSPI_REG_RD_INSTR_DUMMY_MASK)
+ << CQSPI_REG_RD_INSTR_DUMMY_LSB;
+ writel(reg, reg_base + CQSPI_REG_RD_INSTR);
+ } else {
+ cqspi_readdata_capture(cqspi, !cqspi->rclk_en, false,
+ f_pdata->read_delay);
+
+ reg = readl(reg_base + CQSPI_REG_CONFIG);
+ reg &= ~(CQSPI_REG_CONFIG_PHY_EN |
+ CQSPI_REG_CONFIG_PHY_PIPELINE);
+ writel(reg, reg_base + CQSPI_REG_CONFIG);
+
+ /*
+ * Dummy cycles were decremented when enabling PHY. Increment
+ * dummy cycle by 1 to restore the original value.
+ */
+ reg = readl(reg_base + CQSPI_REG_RD_INSTR);
+ dummy = (reg >> CQSPI_REG_RD_INSTR_DUMMY_LSB) &
+ CQSPI_REG_RD_INSTR_DUMMY_MASK;
+ dummy++;
+ reg &= ~(CQSPI_REG_RD_INSTR_DUMMY_MASK <<
+ CQSPI_REG_RD_INSTR_DUMMY_LSB);
+
+ reg |= (dummy & CQSPI_REG_RD_INSTR_DUMMY_MASK)
+ << CQSPI_REG_RD_INSTR_DUMMY_LSB;
+ writel(reg, reg_base + CQSPI_REG_RD_INSTR);
+ }
+
+ cqspi_wait_idle(cqspi);
+}
+
static void cqspi_configure(struct cqspi_flash_pdata *f_pdata,
unsigned long sclk)
{
@@ -1056,7 +1131,7 @@ static void cqspi_configure(struct cqspi_flash_pdata *f_pdata,
cqspi->sclk = sclk;
cqspi_config_baudrate_div(cqspi);
cqspi_delay(f_pdata);
- cqspi_readdata_capture(cqspi, !cqspi->rclk_en,
+ cqspi_readdata_capture(cqspi, !cqspi->rclk_en, false,
f_pdata->read_delay);
}
@@ -1098,6 +1173,39 @@ static ssize_t cqspi_write(struct cqspi_flash_pdata *f_pdata,
return cqspi_indirect_write_execute(f_pdata, to, buf, len);
}
+/*
+ * Check if PHY mode can be used on the given op. This is assuming it will be a
+ * DAC mode read, since PHY won't work on any other type of operation anyway.
+ */
+static bool cqspi_phy_op_eligible(const struct spi_mem_op *op)
+{
+ /* PHY is only tuned for 8D-8D-8D. */
+ if (!(op->cmd.dtr && op->addr.dtr && op->dummy.dtr && op->data.dtr))
+ return false;
+ if (op->cmd.buswidth != 8)
+ return false;
+ if (op->addr.nbytes && op->addr.buswidth != 8)
+ return false;
+ if (op->dummy.nbytes && op->dummy.buswidth != 8)
+ return false;
+ if (op->data.nbytes && op->data.buswidth != 8)
+ return false;
+
+ return true;
+}
+
+static bool cqspi_use_phy(struct cqspi_flash_pdata *f_pdata,
+ const struct spi_mem_op *op)
+{
+ if (!f_pdata->use_phy)
+ return false;
+
+ if (op->data.nbytes < 16)
+ return false;
+
+ return cqspi_phy_op_eligible(op);
+}
+
static void cqspi_rx_dma_callback(void *param)
{
struct cqspi_st *cqspi = param;
@@ -1105,8 +1213,8 @@ static void cqspi_rx_dma_callback(void *param)
complete(&cqspi->rx_dma_complete);
}
-static int cqspi_direct_read_execute(struct cqspi_flash_pdata *f_pdata,
- u_char *buf, loff_t from, size_t len)
+static int cqspi_direct_read_dma(struct cqspi_flash_pdata *f_pdata,
+ u_char *buf, loff_t from, size_t len)
{
struct cqspi_st *cqspi = f_pdata->cqspi;
struct device *dev = &cqspi->pdev->dev;
@@ -1118,11 +1226,6 @@ static int cqspi_direct_read_execute(struct cqspi_flash_pdata *f_pdata,
dma_addr_t dma_dst;
struct device *ddev;
- if (!cqspi->rx_chan || !virt_addr_valid(buf)) {
- memcpy_fromio(buf, cqspi->ahb_base + from, len);
- return 0;
- }
-
ddev = cqspi->rx_chan->device->dev;
dma_dst = dma_map_single(ddev, buf, len, DMA_FROM_DEVICE);
if (dma_mapping_error(ddev, dma_dst)) {
@@ -1164,6 +1267,64 @@ static int cqspi_direct_read_execute(struct cqspi_flash_pdata *f_pdata,
return ret;
}
+static int cqspi_direct_read_execute(struct cqspi_flash_pdata *f_pdata,
+ const struct spi_mem_op *op)
+{
+ struct cqspi_st *cqspi = f_pdata->cqspi;
+ loff_t from = op->addr.val;
+ loff_t from_aligned, to_aligned;
+ size_t len = op->data.nbytes;
+ size_t len_aligned;
+ u_char *buf = op->data.buf.in;
+ int ret;
+
+ if (!cqspi->rx_chan || !virt_addr_valid(buf)) {
+ memcpy_fromio(buf, cqspi->ahb_base + from, len);
+ return 0;
+ }
+
+ if (!cqspi_use_phy(f_pdata, op))
+ return cqspi_direct_read_dma(f_pdata, buf, from, len);
+
+ /*
+ * PHY reads must be 16-byte aligned, and they must be a multiple of 16
+ * bytes.
+ */
+ from_aligned = (from + 0xF) & ~0xF;
+ to_aligned = (from + len) & ~0xF;
+ len_aligned = to_aligned - from_aligned;
+
+ /* Read the unaligned part at the start. */
+ if (from != from_aligned) {
+ ret = cqspi_direct_read_dma(f_pdata, buf, from,
+ from_aligned - from);
+ if (ret)
+ return ret;
+ buf += from_aligned - from;
+ }
+
+ if (len_aligned) {
+ cqspi_phy_enable(f_pdata, true);
+ ret = cqspi_direct_read_dma(f_pdata, buf, from_aligned,
+ len_aligned);
+ cqspi_phy_enable(f_pdata, false);
+ if (ret)
+ return ret;
+ buf += len_aligned;
+ }
+
+ /* Now read the remaining part, if any. */
+ if (to_aligned != (from + len)) {
+ ret = cqspi_direct_read_dma(f_pdata, buf, to_aligned,
+ (from + len) - to_aligned);
+ if (ret)
+ return ret;
+ buf += (from + len) - to_aligned;
+ }
+
+ return 0;
+}
+
static ssize_t cqspi_read(struct cqspi_flash_pdata *f_pdata,
const struct spi_mem_op *op)
{
@@ -1182,7 +1343,7 @@ static ssize_t cqspi_read(struct cqspi_flash_pdata *f_pdata,
return ret;
if (cqspi->use_direct_mode && ((from + len) <= cqspi->ahb_size))
- return cqspi_direct_read_execute(f_pdata, buf, from, len);
+ return cqspi_direct_read_execute(f_pdata, op);
return cqspi_indirect_read_execute(f_pdata, buf, from, len);
}
From patchwork Thu Mar 11 19:12:15 2021
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Pratyush Yadav
X-Patchwork-Id: 12132563
Return-Path:
X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
aws-us-west-2-korg-lkml-1.web.codeaurora.org
X-Spam-Level:
X-Spam-Status: No, score=-19.0 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH,
DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,
INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,
USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0
Received: from mail.kernel.org (mail.kernel.org [198.145.29.99])
by smtp.lore.kernel.org (Postfix) with ESMTP id B8F6AC433E0
for ; Thu, 11 Mar 2021 19:13:56 +0000 (UTC)
Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
by mail.kernel.org (Postfix) with ESMTP id 7E92964F14
for ; Thu, 11 Mar 2021 19:13:56 +0000 (UTC)
Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand
id S230438AbhCKTN0 (ORCPT );
Thu, 11 Mar 2021 14:13:26 -0500
Received: from fllv0015.ext.ti.com ([198.47.19.141]:42350 "EHLO
fllv0015.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
with ESMTP id S230341AbhCKTM6 (ORCPT
); Thu, 11 Mar 2021 14:12:58 -0500
Received: from lelv0265.itg.ti.com ([10.180.67.224])
by fllv0015.ext.ti.com (8.15.2/8.15.2) with ESMTP id 12BJCjjR123950;
Thu, 11 Mar 2021 13:12:45 -0600
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com;
s=ti-com-17Q1; t=1615489965;
bh=TUi2WHK3M2rDM2J87pAt68xPOpINrGhVEr8Jkhg2JPY=;
h=From:To:CC:Subject:Date:In-Reply-To:References;
b=VUA1S0pZMh49E+FybBpMjIZq0wduLyW/Uxig+WKs+4CEHMcf4Ist7B0Jw81gP7u3T
LWhimlx5WuzQDEcB0MqTILgxo2ixiklwMRIGF3Ed9eAoAHE0kao0wpNg4RqUxkR/Mu
F/uaTCNd7hbFfVpmFEXqRmE4o2puFwZsGcPhBmo4=
Received: from DFLE104.ent.ti.com (dfle104.ent.ti.com [10.64.6.25])
by lelv0265.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 12BJCjZA110617
(version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL);
Thu, 11 Mar 2021 13:12:45 -0600
Received: from DFLE101.ent.ti.com (10.64.6.22) by DFLE104.ent.ti.com
(10.64.6.25) with Microsoft SMTP Server (version=TLS1_2,
cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2176.2; Thu, 11
Mar 2021 13:12:45 -0600
Received: from fllv0039.itg.ti.com (10.64.41.19) by DFLE101.ent.ti.com
(10.64.6.22) with Microsoft SMTP Server (version=TLS1_2,
cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2176.2 via
Frontend Transport; Thu, 11 Mar 2021 13:12:45 -0600
Received: from pratyush-OptiPlex-790.dhcp.ti.com (ileax41-snat.itg.ti.com
[10.172.224.153])
by fllv0039.itg.ti.com (8.15.2/8.15.2) with ESMTP id 12BJCHvW080816;
Thu, 11 Mar 2021 13:12:41 -0600
From: Pratyush Yadav
To: Nishanth Menon , Tero Kristo ,
Rob Herring ,
Tudor Ambarus ,
Michael Walle ,
Miquel Raynal ,
Richard Weinberger ,
Vignesh Raghavendra ,
Mark Brown ,
,
, ,
,
CC: Pratyush Yadav , Lokesh Vutla
Subject: [RFC PATCH 5/6] spi: cadence-qspi: Tune PHY to allow running at
higher frequencies
Date: Fri, 12 Mar 2021 00:42:15 +0530
Message-ID: <20210311191216.7363-6-p.yadav@ti.com>
X-Mailer: git-send-email 2.30.0
In-Reply-To: <20210311191216.7363-1-p.yadav@ti.com>
References: <20210311191216.7363-1-p.yadav@ti.com>
MIME-Version: 1.0
X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180
Precedence: bulk
List-ID:
X-Mailing-List: linux-spi@vger.kernel.org
The controller can only run at 1/8 ref clock speed without PHY. With
PHY, it can run at the ref clock speed. So, to enable higher speed
operations, perform the PHY tuning algorithm and determine the RX, TX,
and read delay values for optimal performance. The details of the tuning
algorithm can be found at [0].
To allow this tuning to happen, pre-determined data must be programmed
to the flash at some location. This location is then advertised via a
nvmem cell. Without this data being available, the tuning would fail.
The tuning algorithm is a multi-variable search. The RX and TX delays
need to be found, along with the read delay that would work across a
temperature range. To do that, first the upper and lower RX values at
which the tuning pattern is readable are looked for. This is called the
passing region. The search is performed with Tx = 16 incrementing the
read delay with each iteration. If the two RX values have the same read
delay, the same search is performed with TX = 48.
Once the RX boundaries are found, the TX boundaries are searched for in
a similar fashion with RX set to 1/4 of the RX window (the difference
between the highest and lowest values). And similarly, if the TX
boundaries have the same read delay, the same search is performed with
RX set to 3/4 of the RX window.
There is a region around the boundary of the two passing regions. It is
called the failing region. PHY reads will not work in this region so the
PHY should be tuned as far from it as possible to allow for temperature
variations. This region is found using binary search where the window is
progressively narrowed down until it arrives at the final boundary's
lower and upper limits.
Once PHY is successfully tuned, mark it as usable to allow eligible
operations to run at high speeds. PHY can only be used with DAC mode
reads, and only in chunks of 16 bytes. For all other operations, PHY
mode should be turned off.
[0] https://www.ti.com/lit/pdf/spract2/
Signed-off-by: Pratyush Yadav
---
drivers/spi/spi-cadence-quadspi.c | 617 ++++++++++++++++++++++++++++++
1 file changed, 617 insertions(+)
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index e64d8e125263..d304148a4722 100644
--- a/drivers/spi/spi-cadence-quadspi.c
+++ b/drivers/spi/spi-cadence-quadspi.c
@@ -28,6 +28,7 @@
#include
#include
#include
+#include
#define CQSPI_NAME "cadence-qspi"
#define CQSPI_MAX_CHIPSELECT 16
@@ -62,6 +63,9 @@ struct cqspi_flash_pdata {
u8 cs;
bool use_phy;
struct phy_setting phy_setting;
+ struct spi_mem_op phy_read_op;
+ u32 phy_tx_start;
+ u32 phy_tx_end;
};
struct cqspi_st {
@@ -237,6 +241,13 @@ struct cqspi_driver_platdata {
#define CQSPI_REG_POLLING_STATUS 0xB0
#define CQSPI_REG_POLLING_STATUS_DUMMY_LSB 16
+#define CQSPI_REG_PHY_CONFIG 0xB4
+#define CQSPI_REG_PHY_CONFIG_RX_DEL_LSB 0
+#define CQSPI_REG_PHY_CONFIG_RX_DEL_MASK 0x7F
+#define CQSPI_REG_PHY_CONFIG_TX_DEL_LSB 16
+#define CQSPI_REG_PHY_CONFIG_TX_DEL_MASK 0x7F
+#define CQSPI_REG_PHY_CONFIG_RESYNC BIT(31)
+
#define CQSPI_REG_OP_EXT_LOWER 0xE0
#define CQSPI_REG_OP_EXT_READ_LSB 24
#define CQSPI_REG_OP_EXT_WRITE_LSB 16
@@ -262,6 +273,570 @@ struct cqspi_driver_platdata {
#define CQSPI_IRQ_STATUS_MASK 0x1FFFF
+#define CQSPI_PHY_INIT_RD 1
+#define CQSPI_PHY_MAX_RD 4
+#define CQSPI_PHY_MAX_RX 63
+#define CQSPI_PHY_MAX_TX 63
+#define CQSPI_PHY_LOW_RX_BOUND 15
+#define CQSPI_PHY_HIGH_RX_BOUND 25
+#define CQSPI_PHY_LOW_TX_BOUND 32
+#define CQSPI_PHY_HIGH_TX_BOUND 48
+#define CQSPI_PHY_TX_LOOKUP_LOW_BOUND 24
+#define CQSPI_PHY_TX_LOOKUP_HIGH_BOUND 38
+
+#define CQSPI_PHY_DEFAULT_TEMP 45
+#define CQSPI_PHY_MIN_TEMP -45
+#define CQSPI_PHY_MAX_TEMP 130
+#define CQSPI_PHY_MID_TEMP (CQSPI_PHY_MIN_TEMP + \
+ ((CQSPI_PHY_MAX_TEMP - CQSPI_PHY_MIN_TEMP) / 2))
+
+static const u8 phy_tuning_pattern[] = {
+0xFE, 0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xFE, 0xFE, 0x01, 0x01,
+0x01, 0x01, 0x00, 0x00, 0xFE, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
+0x00, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFE, 0xFE, 0xFF, 0x01,
+0x01, 0x01, 0x01, 0x01, 0xFE, 0x00, 0xFE, 0xFE, 0x01, 0x01, 0x01, 0x01, 0xFE,
+0x00, 0xFE, 0xFE, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0xFE, 0xFE,
+0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0xFE, 0xFE, 0xFF, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x00, 0xFE, 0xFE, 0xFE, 0x01, 0x01, 0x01, 0x01, 0x00, 0xFE, 0xFE, 0xFE,
+0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF,
+0xFF, 0x00, 0xFE, 0xFE, 0xFE, 0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFE, 0xFE,
+0xFE, 0xFE, 0x01, 0x01, 0x01, 0x01, 0xFE, 0xFE, 0xFE, 0xFE, 0x01,
+};
+
+static void cqspi_set_tx_dll(void __iomem *reg_base, u8 dll)
+{
+ unsigned int reg;
+
+ reg = readl(reg_base + CQSPI_REG_PHY_CONFIG);
+ reg &= ~(CQSPI_REG_PHY_CONFIG_TX_DEL_MASK <<
+ CQSPI_REG_PHY_CONFIG_TX_DEL_LSB);
+ reg |= (dll & CQSPI_REG_PHY_CONFIG_TX_DEL_MASK) <<
+ CQSPI_REG_PHY_CONFIG_TX_DEL_LSB;
+ reg |= CQSPI_REG_PHY_CONFIG_RESYNC;
+ writel(reg, reg_base + CQSPI_REG_PHY_CONFIG);
+}
+
+static void cqspi_set_rx_dll(void __iomem *reg_base, u8 dll)
+{
+ unsigned int reg;
+
+ reg = readl(reg_base + CQSPI_REG_PHY_CONFIG);
+ reg &= ~(CQSPI_REG_PHY_CONFIG_RX_DEL_MASK <<
+ CQSPI_REG_PHY_CONFIG_RX_DEL_LSB);
+ reg |= (dll & CQSPI_REG_PHY_CONFIG_RX_DEL_MASK) <<
+ CQSPI_REG_PHY_CONFIG_RX_DEL_LSB;
+ reg |= CQSPI_REG_PHY_CONFIG_RESYNC;
+ writel(reg, reg_base + CQSPI_REG_PHY_CONFIG);
+}
+
+/* TODO: Figure out how to get the temperature here. */
+static int cqspi_get_temp(int *temp)
+{
+ return -EOPNOTSUPP;
+}
+
+static void cqspi_phy_apply_setting(struct cqspi_flash_pdata *f_pdata,
+ struct phy_setting *phy)
+{
+ struct cqspi_st *cqspi = f_pdata->cqspi;
+
+ cqspi_set_rx_dll(cqspi->iobase, phy->rx);
+ cqspi_set_tx_dll(cqspi->iobase, phy->tx);
+ f_pdata->phy_setting.read_delay = phy->read_delay;
+}
+
+static int cqspi_phy_check_pattern(struct cqspi_flash_pdata *f_pdata,
+ struct nvmem_cell *cell)
+{
+ u8 *read_data;
+ size_t len;
+ int ret;
+
+ read_data = nvmem_cell_read(cell, &len);
+ if (IS_ERR(read_data))
+ return PTR_ERR(read_data);
+ if (len != ARRAY_SIZE(phy_tuning_pattern))
+ return -EIO;
+
+ if (memcmp(read_data, phy_tuning_pattern,
+ ARRAY_SIZE(phy_tuning_pattern))) {
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ kfree(read_data);
+ return ret;
+}
+
+static int cqspi_find_rx_low(struct cqspi_flash_pdata *f_pdata,
+ struct nvmem_cell *cell, struct phy_setting *phy)
+{
+ struct device *dev = &f_pdata->cqspi->pdev->dev;
+ int ret;
+
+ do {
+ phy->rx = 0;
+ do {
+ cqspi_phy_apply_setting(f_pdata, phy);
+ ret = cqspi_phy_check_pattern(f_pdata, cell);
+ if (!ret)
+ return 0;
+
+ phy->rx++;
+ } while (phy->rx <= CQSPI_PHY_LOW_RX_BOUND);
+
+ phy->read_delay++;
+ } while (phy->read_delay <= CQSPI_PHY_MAX_RD);
+
+ dev_dbg(dev, "Unable to find RX low\n");
+ return -ENOENT;
+}
+
+static int cqspi_find_rx_high(struct cqspi_flash_pdata *f_pdata,
+ struct nvmem_cell *cell, struct phy_setting *phy)
+{
+ struct device *dev = &f_pdata->cqspi->pdev->dev;
+ int ret;
+
+ do {
+ phy->rx = CQSPI_PHY_MAX_RX;
+ do {
+ cqspi_phy_apply_setting(f_pdata, phy);
+ ret = cqspi_phy_check_pattern(f_pdata, cell);
+ if (!ret)
+ return 0;
+
+ phy->rx--;
+ } while (phy->rx >= CQSPI_PHY_HIGH_RX_BOUND);
+
+ phy->read_delay++;
+ } while (phy->read_delay <= CQSPI_PHY_MAX_RD);
+
+ dev_dbg(dev, "Unable to find RX high\n");
+ return -ENOENT;
+}
+
+static int cqspi_find_tx_low(struct cqspi_flash_pdata *f_pdata,
+ struct nvmem_cell *cell, struct phy_setting *phy)
+{
+ struct device *dev = &f_pdata->cqspi->pdev->dev;
+ int ret;
+
+ do {
+ phy->tx = 0;
+ do {
+ cqspi_phy_apply_setting(f_pdata, phy);
+ ret = cqspi_phy_check_pattern(f_pdata, cell);
+ if (!ret)
+ return 0;
+
+ phy->tx++;
+ } while (phy->tx <= CQSPI_PHY_LOW_TX_BOUND);
+
+ phy->read_delay++;
+ } while (phy->read_delay <= CQSPI_PHY_MAX_RD);
+
+ dev_dbg(dev, "Unable to find TX low\n");
+ return -ENOENT;
+}
+
+static int cqspi_find_tx_high(struct cqspi_flash_pdata *f_pdata,
+ struct nvmem_cell *cell, struct phy_setting *phy)
+{
+ struct device *dev = &f_pdata->cqspi->pdev->dev;
+ int ret;
+
+ do {
+ phy->tx = CQSPI_PHY_MAX_TX;
+ do {
+ cqspi_phy_apply_setting(f_pdata, phy);
+ ret = cqspi_phy_check_pattern(f_pdata, cell);
+ if (!ret)
+ return 0;
+
+ phy->tx--;
+ } while (phy->tx >= CQSPI_PHY_HIGH_TX_BOUND);
+
+ phy->read_delay++;
+ } while (phy->read_delay <= CQSPI_PHY_MAX_RD);
+
+ dev_dbg(dev, "Unable to find TX high\n");
+ return -ENOENT;
+}
+
+static int cqspi_phy_find_gaplow(struct cqspi_flash_pdata *f_pdata,
+ struct nvmem_cell *cell,
+ struct phy_setting *bottomleft,
+ struct phy_setting *topright,
+ struct phy_setting *gaplow)
+{
+ struct phy_setting left, right, mid;
+ int ret;
+
+ left = *bottomleft;
+ right = *topright;
+
+ mid.tx = left.tx + ((right.tx - left.tx) / 2);
+ mid.rx = left.rx + ((right.rx - left.rx) / 2);
+ mid.read_delay = left.read_delay;
+
+ do {
+ cqspi_phy_apply_setting(f_pdata, &mid);
+ ret = cqspi_phy_check_pattern(f_pdata, cell);
+ if (ret) {
+ /* The pattern was not found. Go to the lower half. */
+ right.tx = mid.tx;
+ right.rx = mid.rx;
+
+ mid.tx = left.tx + ((mid.tx - left.tx) / 2);
+ mid.rx = left.rx + ((mid.rx - left.rx) / 2);
+ } else {
+ /* The pattern was found. Go to the upper half. */
+ left.tx = mid.tx;
+ left.rx = mid.rx;
+
+ mid.tx = mid.tx + ((right.tx - mid.tx) / 2);
+ mid.rx = mid.rx + ((right.rx - mid.rx) / 2);
+ }
+
+ /* Break the loop if the window has closed. */
+ } while ((right.tx - left.tx >= 2) && (right.rx - left.rx >= 2));
+
+ *gaplow = mid;
+ return 0;
+}
+
+static int cqspi_phy_find_gaphigh(struct cqspi_flash_pdata *f_pdata,
+ struct nvmem_cell *cell,
+ struct phy_setting *bottomleft,
+ struct phy_setting *topright,
+ struct phy_setting *gaphigh)
+{
+ struct phy_setting left, right, mid;
+ int ret;
+
+ left = *bottomleft;
+ right = *topright;
+
+ mid.tx = left.tx + ((right.tx - left.tx) / 2);
+ mid.rx = left.rx + ((right.rx - left.rx) / 2);
+ mid.read_delay = right.read_delay;
+
+ do {
+ cqspi_phy_apply_setting(f_pdata, &mid);
+ ret = cqspi_phy_check_pattern(f_pdata, cell);
+ if (ret) {
+ /* The pattern was not found. Go to the upper half. */
+ left.tx = mid.tx;
+ left.rx = mid.rx;
+
+ mid.tx = mid.tx + ((right.tx - mid.tx) / 2);
+ mid.rx = mid.rx + ((right.rx - mid.rx) / 2);
+ } else {
+ /* The pattern was found. Go to the lower half. */
+ right.tx = mid.tx;
+ right.rx = mid.rx;
+
+ mid.tx = left.tx + ((mid.tx - left.tx) / 2);
+ mid.rx = left.rx + ((mid.rx - left.rx) / 2);
+ }
+
+ /* Break the loop if the window has closed. */
+ } while ((right.tx - left.tx >= 2) && (right.rx - left.rx >= 2));
+
+ *gaphigh = mid;
+ return 0;
+}
+
+static int cqspi_phy_calibrate(struct cqspi_flash_pdata *f_pdata,
+ struct nvmem_cell *cell)
+{
+ struct cqspi_st *cqspi = f_pdata->cqspi;
+ struct device *dev = &cqspi->pdev->dev;
+ struct phy_setting rxlow, rxhigh, txlow, txhigh, temp;
+ struct phy_setting bottomleft, topright, searchpoint, gaplow, gaphigh;
+ int ret, tmp;
+
+ f_pdata->use_phy = true;
+
+ /* Look for RX boundaries at lower TX range. */
+ rxlow.tx = f_pdata->phy_tx_start;
+
+ do {
+ dev_dbg(dev, "Searching for rxlow on TX = %d\n", rxlow.tx);
+ rxlow.read_delay = CQSPI_PHY_INIT_RD;
+ ret = cqspi_find_rx_low(f_pdata, cell, &rxlow);
+ } while (ret && ++rxlow.tx <= CQSPI_PHY_TX_LOOKUP_LOW_BOUND);
+
+ if (ret)
+ goto out;
+ dev_dbg(dev, "rxlow: RX: %d TX: %d RD: %d\n", rxlow.rx, rxlow.tx,
+ rxlow.read_delay);
+
+ rxhigh.tx = rxlow.tx;
+ rxhigh.read_delay = rxlow.read_delay;
+ cqspi_find_rx_high(f_pdata, cell, &rxhigh);
+ if (ret)
+ goto out;
+ dev_dbg(dev, "rxhigh: RX: %d TX: %d RD: %d\n", rxhigh.rx, rxhigh.tx,
+ rxhigh.read_delay);
+
+ /*
+ * Check a different point if rxlow and rxhigh are on the same read
+ * delay. This avoids mistaking the failing region for an RX boundary.
+ */
+ if (rxlow.read_delay == rxhigh.read_delay) {
+ dev_dbg(dev,
+ "rxlow and rxhigh at the same read delay.\n");
+
+ /* Look for RX boundaries at upper TX range. */
+ temp.tx = f_pdata->phy_tx_end;
+
+ do {
+ dev_dbg(dev, "Searching for rxlow on TX = %d\n",
+ temp.tx);
+ temp.read_delay = CQSPI_PHY_INIT_RD;
+ ret = cqspi_find_rx_low(f_pdata, cell, &temp);
+ } while (ret && --temp.tx >= CQSPI_PHY_TX_LOOKUP_HIGH_BOUND);
+
+ if (ret)
+ goto out;
+ dev_dbg(dev, "rxlow: RX: %d TX: %d RD: %d\n", temp.rx, temp.tx,
+ temp.read_delay);
+
+ if (temp.rx < rxlow.rx) {
+ rxlow = temp;
+ dev_dbg(dev, "Updating rxlow to the one at TX = 48\n");
+ }
+
+ /* Find RX max. */
+ ret = cqspi_find_rx_high(f_pdata, cell, &temp);
+ if (ret)
+ goto out;
+ dev_dbg(dev, "rxhigh: RX: %d TX: %d RD: %d\n", temp.rx, temp.tx,
+ temp.read_delay);
+
+ if (temp.rx < rxhigh.rx) {
+ rxhigh = temp;
+ dev_dbg(dev, "Updating rxhigh to the one at TX = 48\n");
+ }
+ }
+
+ /* Look for TX boundaries at 1/4 of RX window. */
+ txlow.rx = rxlow.rx + ((rxhigh.rx - rxlow.rx) / 4);
+ txhigh.rx = txlow.rx;
+
+ txlow.read_delay = CQSPI_PHY_INIT_RD;
+ ret = cqspi_find_tx_low(f_pdata, cell, &txlow);
+ if (ret)
+ goto out;
+ dev_dbg(dev, "txlow: RX: %d TX: %d RD: %d\n", txlow.rx, txlow.tx,
+ txlow.read_delay);
+
+ txhigh.read_delay = txlow.read_delay;
+ ret = cqspi_find_tx_high(f_pdata, cell, &txhigh);
+ if (ret)
+ goto out;
+ dev_dbg(dev, "txhigh: RX: %d TX: %d RD: %d\n", txhigh.rx, txhigh.tx,
+ txhigh.read_delay);
+
+ /*
+ * Check a different point if txlow and txhigh are on the same read
+ * delay. This avoids mistaking the failing region for an TX boundary.
+ */
+ if (txlow.read_delay == txhigh.read_delay) {
+ /* Look for TX boundaries at 3/4 of RX window. */
+ temp.rx = rxlow.rx + (3 * (rxhigh.rx - rxlow.rx) / 4);
+ temp.read_delay = CQSPI_PHY_INIT_RD;
+ dev_dbg(dev,
+ "txlow and txhigh at the same read delay. Searching at RX = %d\n",
+ temp.rx);
+
+ ret = cqspi_find_tx_low(f_pdata, cell, &temp);
+ if (ret)
+ goto out;
+ dev_dbg(dev, "txlow: RX: %d TX: %d RD: %d\n", temp.rx, temp.tx,
+ temp.read_delay);
+
+ if (temp.tx < txlow.tx) {
+ txlow = temp;
+ dev_dbg(dev, "Updating txlow with the one at RX = %d\n",
+ txlow.rx);
+ }
+
+ ret = cqspi_find_tx_high(f_pdata, cell, &temp);
+ if (ret)
+ goto out;
+ dev_dbg(dev, "txhigh: RX: %d TX: %d RD: %d\n", temp.rx, temp.tx,
+ temp.read_delay);
+
+ if (temp.tx < txhigh.tx) {
+ txhigh = temp;
+ dev_dbg(dev, "Updating txhigh with the one at RX = %d\n",
+ txhigh.rx);
+ }
+ }
+
+ /*
+ * Set bottom left and top right corners. These are theoretical
+ * corners. They may not actually be "good" points. But the longest
+ * diagonal will be between these corners.
+ */
+ bottomleft.tx = txlow.tx;
+ bottomleft.rx = rxlow.rx;
+ if (txlow.read_delay <= rxlow.read_delay)
+ bottomleft.read_delay = txlow.read_delay;
+ else
+ bottomleft.read_delay = rxlow.read_delay;
+
+ temp = bottomleft;
+ temp.tx += 4;
+ temp.rx += 4;
+ cqspi_phy_apply_setting(f_pdata, &temp);
+ ret = cqspi_phy_check_pattern(f_pdata, cell);
+ if (ret) {
+ temp.read_delay--;
+ cqspi_phy_apply_setting(f_pdata, &temp);
+ ret = cqspi_phy_check_pattern(f_pdata, cell);
+ }
+
+ if (!ret)
+ bottomleft.read_delay = temp.read_delay;
+
+ topright.tx = txhigh.tx;
+ topright.rx = rxhigh.rx;
+ if (txhigh.read_delay >= rxhigh.read_delay)
+ topright.read_delay = txhigh.read_delay;
+ else
+ topright.read_delay = rxhigh.read_delay;
+
+ temp = topright;
+ temp.tx -= 4;
+ temp.rx -= 4;
+ cqspi_phy_apply_setting(f_pdata, &temp);
+ ret = cqspi_phy_check_pattern(f_pdata, cell);
+ if (ret) {
+ temp.read_delay++;
+ cqspi_phy_apply_setting(f_pdata, &temp);
+ ret = cqspi_phy_check_pattern(f_pdata, cell);
+ }
+
+ if (!ret)
+ topright.read_delay = temp.read_delay;
+
+ dev_dbg(dev, "topright: RX: %d TX: %d RD: %d\n", topright.rx,
+ topright.tx, topright.read_delay);
+ dev_dbg(dev, "bottomleft: RX: %d TX: %d RD: %d\n", bottomleft.rx,
+ bottomleft.tx, bottomleft.read_delay);
+
+ ret = cqspi_phy_find_gaplow(f_pdata, cell, &bottomleft, &topright,
+ &gaplow);
+ if (ret)
+ goto out;
+ dev_dbg(dev, "gaplow: RX: %d TX: %d RD: %d\n", gaplow.rx, gaplow.tx,
+ gaplow.read_delay);
+
+ if (bottomleft.read_delay == topright.read_delay) {
+ /*
+ * If there is only one passing region, it means that the "true"
+ * topright is too small to find, so the start of the failing
+ * region is a good approximation. Put the tuning point in the
+ * middle and adjust for temperature.
+ */
+ topright = gaplow;
+ searchpoint.read_delay = bottomleft.read_delay;
+ searchpoint.tx = bottomleft.tx +
+ ((topright.tx - bottomleft.tx) / 2);
+ searchpoint.rx = bottomleft.rx +
+ ((topright.rx - bottomleft.rx) / 2);
+
+ ret = cqspi_get_temp(&tmp);
+ if (ret) {
+ /*
+ * Assume room temperature if it couldn't be obtained
+ * from the thermal sensor.
+ *
+ * TODO: Change it to dev_warn once support for finding
+ * out the temperature is added.
+ */
+ dev_dbg(dev,
+ "Unable to get temperature. Assuming room temperature\n");
+ tmp = CQSPI_PHY_DEFAULT_TEMP;
+ }
+
+ if (tmp < CQSPI_PHY_MIN_TEMP || tmp > CQSPI_PHY_MAX_TEMP) {
+ dev_err(dev,
+ "Temperature outside operating range: %dC\n",
+ tmp);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Avoid a divide-by-zero. */
+ if (tmp == CQSPI_PHY_MID_TEMP)
+ tmp++;
+ dev_dbg(dev, "Temperature: %dC\n", tmp);
+
+ searchpoint.tx += (topright.tx - bottomleft.tx) /
+ (330 / (tmp - CQSPI_PHY_MID_TEMP));
+ searchpoint.rx += (topright.rx - bottomleft.rx) /
+ (330 / (tmp - CQSPI_PHY_MID_TEMP));
+ } else {
+ /*
+ * If there are two passing regions, find the start and end of
+ * the second one.
+ */
+ ret = cqspi_phy_find_gaphigh(f_pdata, cell, &bottomleft,
+ &topright, &gaphigh);
+ if (ret)
+ goto out;
+ dev_dbg(dev, "gaphigh: RX: %d TX: %d RD: %d\n", gaphigh.rx,
+ gaphigh.tx, gaphigh.read_delay);
+
+ /*
+ * Place the final tuning point in the corner furthest from the
+ * failing region but leave some margin for temperature changes.
+ */
+ if ((abs(gaplow.tx - bottomleft.tx) +
+ abs(gaplow.rx - bottomleft.rx)) <
+ (abs(gaphigh.tx - topright.tx) +
+ abs(gaphigh.rx - topright.rx))) {
+ searchpoint = topright;
+ searchpoint.tx -= 16;
+ searchpoint.rx -= (16 * (topright.rx - bottomleft.rx)) /
+ (topright.tx - bottomleft.tx);
+ } else {
+ searchpoint = bottomleft;
+ searchpoint.tx += 16;
+ searchpoint.rx += (16 * (topright.rx - bottomleft.rx)) /
+ (topright.tx - bottomleft.tx);
+ }
+ }
+
+ /* Set the final PHY settings and check if they are working. */
+ cqspi_phy_apply_setting(f_pdata, &searchpoint);
+ dev_dbg(dev, "Final tuning point: RX: %d TX: %d RD: %d\n",
+ searchpoint.rx, searchpoint.tx, searchpoint.read_delay);
+
+ ret = cqspi_phy_check_pattern(f_pdata, cell);
+ if (ret) {
+ dev_err(dev,
+ "Failed to find pattern at final calibration point\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = 0;
+ f_pdata->phy_setting.read_delay = searchpoint.read_delay;
+out:
+ if (ret)
+ f_pdata->use_phy = false;
+ return ret;
+}
+
static int cqspi_wait_for_bit(void __iomem *reg, const u32 mask, bool clr)
{
u32 val;
@@ -1400,6 +1975,41 @@ static bool cqspi_supports_mem_op(struct spi_mem *mem,
return spi_mem_default_supports_op(mem, op);
}
+static void cqspi_mem_do_calibration(struct spi_mem *mem, struct spi_mem_op *op)
+{
+ struct cqspi_st *cqspi = spi_master_get_devdata(mem->spi->master);
+ struct cqspi_flash_pdata *f_pdata;
+ struct nvmem_cell *cell;
+ struct device *dev = &cqspi->pdev->dev;
+ int ret;
+
+ f_pdata = &cqspi->f_pdata[mem->spi->chip_select];
+
+ /* Check if the op is eligible for PHY mode operation. */
+ if (!cqspi_phy_op_eligible(op))
+ return;
+
+ cell = nvmem_cell_get(dev, "calibration");
+ if (IS_ERR_OR_NULL(cell)) {
+ dev_dbg(dev, "Failed to get calibration data nvmem: %ld\n",
+ PTR_ERR(cell));
+ return;
+ }
+
+ ret = cqspi_phy_check_pattern(f_pdata, cell);
+ if (ret) {
+ dev_dbg(dev, "Pattern not found. Skipping calibration.\n");
+ nvmem_cell_put(cell);
+ return;
+ }
+
+ ret = cqspi_phy_calibrate(f_pdata, cell);
+ if (ret)
+ dev_info(&cqspi->pdev->dev, "PHY calibration failed: %d\n", ret);
+
+ nvmem_cell_put(cell);
+}
+
static int cqspi_of_get_flash_pdata(struct platform_device *pdev,
struct cqspi_flash_pdata *f_pdata,
struct device_node *np)
@@ -1434,6 +2044,12 @@ static int cqspi_of_get_flash_pdata(struct platform_device *pdev,
return -ENXIO;
}
+ if (of_property_read_u32(np, "cdns,phy-tx-start", &f_pdata->phy_tx_start))
+ f_pdata->phy_tx_start = 16;
+
+ if (of_property_read_u32(np, "cdns,phy-tx-end", &f_pdata->phy_tx_end))
+ f_pdata->phy_tx_end = 48;
+
return 0;
}
@@ -1534,6 +2150,7 @@ static const struct spi_controller_mem_ops cqspi_mem_ops = {
.exec_op = cqspi_exec_mem_op,
.get_name = cqspi_get_name,
.supports_op = cqspi_supports_mem_op,
+ .do_calibration = cqspi_mem_do_calibration,
};
static int cqspi_setup_flash(struct cqspi_st *cqspi)
From patchwork Thu Mar 11 19:12:16 2021
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Pratyush Yadav
X-Patchwork-Id: 12132561
Return-Path:
X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
aws-us-west-2-korg-lkml-1.web.codeaurora.org
X-Spam-Level:
X-Spam-Status: No, score=-19.0 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH,
DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,
INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,
USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0
Received: from mail.kernel.org (mail.kernel.org [198.145.29.99])
by smtp.lore.kernel.org (Postfix) with ESMTP id E5FAAC43381
for ; Thu, 11 Mar 2021 19:13:56 +0000 (UTC)
Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
by mail.kernel.org (Postfix) with ESMTP id 9DE3A64F15
for ; Thu, 11 Mar 2021 19:13:56 +0000 (UTC)
Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand
id S230475AbhCKTN1 (ORCPT );
Thu, 11 Mar 2021 14:13:27 -0500
Received: from fllv0015.ext.ti.com ([198.47.19.141]:42362 "EHLO
fllv0015.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
with ESMTP id S230401AbhCKTNB (ORCPT
); Thu, 11 Mar 2021 14:13:01 -0500
Received: from fllv0035.itg.ti.com ([10.64.41.0])
by fllv0015.ext.ti.com (8.15.2/8.15.2) with ESMTP id 12BJCos3123972;
Thu, 11 Mar 2021 13:12:50 -0600
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com;
s=ti-com-17Q1; t=1615489970;
bh=O1hbhr2KTh0yRrzhm4nwwGvK8OQhgZuCTONHf0rb8Ag=;
h=From:To:CC:Subject:Date:In-Reply-To:References;
b=QfCzBFDoeuiSH69Phe/HCtElVibJmQm4ktfYSqvbFwlnuuDyahU5PIZ5g4Kn3uJt8
nxoJQDBpFwNKikAze0vS84/DXyHWtPHfzRtg//cM/T7TxWWwGLMIxKVBmKeIgeyIu0
4sEYRAqFlAW4koBwCmbmctPLqLOrJYPaFLVOpggc=
Received: from DFLE102.ent.ti.com (dfle102.ent.ti.com [10.64.6.23])
by fllv0035.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 12BJCok6005602
(version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL);
Thu, 11 Mar 2021 13:12:50 -0600
Received: from DFLE113.ent.ti.com (10.64.6.34) by DFLE102.ent.ti.com
(10.64.6.23) with Microsoft SMTP Server (version=TLS1_2,
cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2176.2; Thu, 11
Mar 2021 13:12:50 -0600
Received: from fllv0039.itg.ti.com (10.64.41.19) by DFLE113.ent.ti.com
(10.64.6.34) with Microsoft SMTP Server (version=TLS1_2,
cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2176.2 via
Frontend Transport; Thu, 11 Mar 2021 13:12:50 -0600
Received: from pratyush-OptiPlex-790.dhcp.ti.com (ileax41-snat.itg.ti.com
[10.172.224.153])
by fllv0039.itg.ti.com (8.15.2/8.15.2) with ESMTP id 12BJCHvX080816;
Thu, 11 Mar 2021 13:12:45 -0600
From: Pratyush Yadav
To: Nishanth Menon , Tero Kristo ,
Rob Herring ,
Tudor Ambarus ,
Michael Walle ,
Miquel Raynal ,
Richard Weinberger ,
Vignesh Raghavendra ,
Mark Brown ,
,
, ,
,
CC: Pratyush Yadav , Lokesh Vutla
Subject: [RFC PATCH 6/6] arm64: dts: ti: k3-j721e-som-p0: Enable PHY
calibration
Date: Fri, 12 Mar 2021 00:42:16 +0530
Message-ID: <20210311191216.7363-7-p.yadav@ti.com>
X-Mailer: git-send-email 2.30.0
In-Reply-To: <20210311191216.7363-1-p.yadav@ti.com>
References: <20210311191216.7363-1-p.yadav@ti.com>
MIME-Version: 1.0
X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180
Precedence: bulk
List-ID:
X-Mailing-List: linux-spi@vger.kernel.org
For running the flash in 8D-8D-8D mode at 166 MHz (controller ref clock
speed), PHY calibration procedure needs to run to calculate the proper
line delays.
To perform this calibration, the controller needs to know the location
of the pre-determined calibration pattern on the flash. Add a fixed
partition table that contains the calibration partition, along with the
rest of the partitions for the platform. Also add a nvmem cell that
points to the calibration partition.
Signed-off-by: Pratyush Yadav
---
Based on patch
https://patchwork.kernel.org/project/linux-arm-kernel/patch/20210305153926.3479-2-p.yadav@ti.com/
arch/arm64/boot/dts/ti/k3-j721e-som-p0.dtsi | 55 +++++++++++++++++++++
1 file changed, 55 insertions(+)
--
2.30.0
diff --git a/arch/arm64/boot/dts/ti/k3-j721e-som-p0.dtsi b/arch/arm64/boot/dts/ti/k3-j721e-som-p0.dtsi
index 2fee2906183d..6013ebb45517 100644
--- a/arch/arm64/boot/dts/ti/k3-j721e-som-p0.dtsi
+++ b/arch/arm64/boot/dts/ti/k3-j721e-som-p0.dtsi
@@ -170,6 +170,8 @@ J721E_WKUP_IOPAD(0x002c, PIN_OUTPUT, 0) /* MCU_OSPI0_CSn0 */
&ospi0 {
pinctrl-names = "default";
pinctrl-0 = <&mcu_fss0_ospi0_pins_default>;
+ nvmem-cells = <&ospi_calibration_data>;
+ nvmem-cell-names = "calibration";
flash@0{
compatible = "jedec,spi-nor";
@@ -184,6 +186,59 @@ flash@0{
cdns,read-delay = <0>;
#address-cells = <1>;
#size-cells = <1>;
+
+ partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ partiiton@0 {
+ label = "ospi.tiboot3";
+ reg = <0x0 0x80000>;
+ };
+
+ partition@80000 {
+ label = "ospi.tispl";
+ reg = <0x80000 0x200000>;
+ };
+
+ partition@280000 {
+ label = "ospi.u-boot";
+ reg = <0x280000 0x400000>;
+ };
+
+ partition@680000 {
+ label = "ospi.env";
+ reg = <0x680000 0x20000>;
+ };
+
+ partition@6a0000 {
+ label = "ospi.env.backup";
+ reg = <0x6a0000 0x20000>;
+ };
+
+ partition@0x6c0000 {
+ label = "ospi.sysfw";
+ reg = <0x6c0000 0x100000>;
+ };
+
+ partition@800000 {
+ label = "ospi.rootfs";
+ reg = <0x800000 0x37e0000>;
+ };
+
+ calibration_partition: partition@3fe0000 {
+ compatible = "nvmem-cells";
+ label = "ospi.phypattern";
+ reg = <0x3fe0000 0x20000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ ospi_calibration_data: ospi-calibration-cell@0 {
+ reg = <0x0 0x80>;
+ };
+ };
+ };
};
};