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>; + }; + }; + }; }; };