From mboxrd@z Thu Jan 1 00:00:00 1970 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.1 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 D1440C2B9F4 for ; Fri, 25 Jun 2021 19:19:57 +0000 (UTC) Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 4A25C61976 for ; Fri, 25 Jun 2021 19:19:57 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 4A25C61976 Authentication-Results: mail.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=ti.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 8D55C82C4F; Fri, 25 Jun 2021 21:18:51 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (1024-bit key; unprotected) header.d=ti.com header.i=@ti.com header.b="KFy2MAQZ"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 5883082C2A; Fri, 25 Jun 2021 21:18:19 +0200 (CEST) Received: from fllv0016.ext.ti.com (fllv0016.ext.ti.com [198.47.19.142]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 62C3582C2D for ; Fri, 25 Jun 2021 21:18:07 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=p.yadav@ti.com Received: from fllv0034.itg.ti.com ([10.64.40.246]) by fllv0016.ext.ti.com (8.15.2/8.15.2) with ESMTP id 15PJI5gO078368; Fri, 25 Jun 2021 14:18:05 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1624648685; bh=/C0v5dOZEjfB1oZ2rWE+jJtfxh8dRau+AVFdsSPpHTc=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=KFy2MAQZVDInG+i8WLW76re3k0cHnSH93ycOVezjK/LgvfNzX68ONBw01VnmXV0tL YXSMr6yEDSesumudcZCCxrBTig1XZg4ZWXRsKvn9U3/imXgc7W2Dn9TDccpnMZkUGZ 2RUKBnnXttUXF9cph4g8oTsBbL/Q5jb2G/OlzZ60= Received: from DLEE108.ent.ti.com (dlee108.ent.ti.com [157.170.170.38]) by fllv0034.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 15PJI5RT123342 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL); Fri, 25 Jun 2021 14:18:05 -0500 Received: from DLEE109.ent.ti.com (157.170.170.41) by DLEE108.ent.ti.com (157.170.170.38) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2176.2; Fri, 25 Jun 2021 14:18:05 -0500 Received: from lelv0327.itg.ti.com (10.180.67.183) by DLEE109.ent.ti.com (157.170.170.41) 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; Fri, 25 Jun 2021 14:18:05 -0500 Received: from pratyush-OptiPlex-790.dhcp.ti.com (ileax41-snat.itg.ti.com [10.172.224.153]) by lelv0327.itg.ti.com (8.15.2/8.15.2) with ESMTP id 15PJHURS076530; Fri, 25 Jun 2021 14:18:03 -0500 From: Pratyush Yadav To: Jagan Teki , Vignesh R , Ryder Lee , Weijie Gao , Chunfeng Yun , GSS_MTK_Uboot_upstream , CC: Pratyush Yadav Subject: [PATCH v10 12/27] mtd: spi-nor-core: Rework hwcaps selection Date: Sat, 26 Jun 2021 00:47:14 +0530 Message-ID: <20210625191729.31798-13-p.yadav@ti.com> X-Mailer: git-send-email 2.30.0 In-Reply-To: <20210625191729.31798-1-p.yadav@ti.com> References: <20210625191729.31798-1-p.yadav@ti.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean The spi-mem layer provides a spi_mem_supports_op() function to check whether a specific operation is supported by the controller or not. This is much more accurate than the hwcaps selection logic based on SPI_{RX,TX}_ flags. Rework the hwcaps selection logic to use spi_mem_supports_op(). To make sure the build doesn't break for boards not using CONFIG_DM_SPI, add a simple SPI_{RX,TX}_ based hwcaps selection logic in spi-mem-nodm similar to spi_mem_default_supports_op(). This change is only compile-tested. To avoid SPL size problems on the x530 board, the old hwcaps selection is still kept around. Leaving the code in-place was getting difficult to read and understand, so the code is restructured to have it all in one isolated function. As a result of this, the parameter hwcaps to spi_nor_setup() is no longer needed. Remove it. Based on the Linux commit c76f5089796a (mtd: spi-nor: Rework hwcaps selection for the spi-mem case, 2019-08-06) Signed-off-by: Pratyush Yadav --- drivers/mtd/spi/Kconfig | 9 ++ drivers/mtd/spi/spi-nor-core.c | 244 ++++++++++++++++++++++++++------- drivers/spi/spi-mem-nodm.c | 62 +++++++++ include/linux/mtd/spi-nor.h | 17 ++- 4 files changed, 280 insertions(+), 52 deletions(-) diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig index f8db8e5213..a701167dcc 100644 --- a/drivers/mtd/spi/Kconfig +++ b/drivers/mtd/spi/Kconfig @@ -88,6 +88,15 @@ config SPI_FLASH_SFDP_SUPPORT SPI NOR flashes using Serial Flash Discoverable Parameters (SFDP) tables as per JESD216 standard. +config SPI_FLASH_SMART_HWCAPS + bool "Smart hardware capability detection based on SPI MEM supports_op() hook" + default y + help + Enable support for smart hardware capability detection based on SPI + MEM supports_op() hook that lets controllers express whether they + can support a type of operation in a much more refined way compared + to using flags like SPI_RX_DUAL, SPI_TX_QUAD, etc. + config SPI_FLASH_BAR bool "SPI flash Bank/Extended address register support" help diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c index 013a48d2ef..24c6b8c4a3 100644 --- a/drivers/mtd/spi/spi-nor-core.c +++ b/drivers/mtd/spi/spi-nor-core.c @@ -2299,6 +2299,194 @@ static int spi_nor_hwcaps_pp2cmd(u32 hwcaps) ARRAY_SIZE(hwcaps_pp2cmd)); } +#ifdef CONFIG_SPI_FLASH_SMART_HWCAPS +/** + * spi_nor_check_op - check if the operation is supported by controller + * @nor: pointer to a 'struct spi_nor' + * @op: pointer to op template to be checked + * + * Returns 0 if operation is supported, -ENOTSUPP otherwise. + */ +static int spi_nor_check_op(struct spi_nor *nor, + struct spi_mem_op *op) +{ + /* + * First test with 4 address bytes. The opcode itself might be a 3B + * addressing opcode but we don't care, because SPI controller + * implementation should not check the opcode, but just the sequence. + */ + op->addr.nbytes = 4; + if (!spi_mem_supports_op(nor->spi, op)) { + if (nor->mtd.size > SZ_16M) + return -ENOTSUPP; + + /* If flash size <= 16MB, 3 address bytes are sufficient */ + op->addr.nbytes = 3; + if (!spi_mem_supports_op(nor->spi, op)) + return -ENOTSUPP; + } + + return 0; +} + +/** + * spi_nor_check_readop - check if the read op is supported by controller + * @nor: pointer to a 'struct spi_nor' + * @read: pointer to op template to be checked + * + * Returns 0 if operation is supported, -ENOTSUPP otherwise. + */ +static int spi_nor_check_readop(struct spi_nor *nor, + const struct spi_nor_read_command *read) +{ + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(read->opcode, 1), + SPI_MEM_OP_ADDR(3, 0, 1), + SPI_MEM_OP_DUMMY(0, 1), + SPI_MEM_OP_DATA_IN(0, NULL, 1)); + + op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(read->proto); + op.addr.buswidth = spi_nor_get_protocol_addr_nbits(read->proto); + op.data.buswidth = spi_nor_get_protocol_data_nbits(read->proto); + op.dummy.buswidth = op.addr.buswidth; + op.dummy.nbytes = (read->num_mode_clocks + read->num_wait_states) * + op.dummy.buswidth / 8; + + return spi_nor_check_op(nor, &op); +} + +/** + * spi_nor_check_pp - check if the page program op is supported by controller + * @nor: pointer to a 'struct spi_nor' + * @pp: pointer to op template to be checked + * + * Returns 0 if operation is supported, -ENOTSUPP otherwise. + */ +static int spi_nor_check_pp(struct spi_nor *nor, + const struct spi_nor_pp_command *pp) +{ + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(pp->opcode, 1), + SPI_MEM_OP_ADDR(3, 0, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(0, NULL, 1)); + + op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(pp->proto); + op.addr.buswidth = spi_nor_get_protocol_addr_nbits(pp->proto); + op.data.buswidth = spi_nor_get_protocol_data_nbits(pp->proto); + + return spi_nor_check_op(nor, &op); +} + +/** + * spi_nor_adjust_hwcaps - Find optimal Read/Write protocol based on SPI + * controller capabilities + * @nor: pointer to a 'struct spi_nor' + * @params: pointer to the 'struct spi_nor_flash_parameter' + * representing SPI NOR flash capabilities + * @hwcaps: pointer to resulting capabilities after adjusting + * according to controller and flash's capability + * + * Discard caps based on what the SPI controller actually supports (using + * spi_mem_supports_op()). + */ +static void +spi_nor_adjust_hwcaps(struct spi_nor *nor, + const struct spi_nor_flash_parameter *params, + u32 *hwcaps) +{ + unsigned int cap; + + /* + * Enable all caps by default. We will mask them after checking what's + * really supported using spi_mem_supports_op(). + */ + *hwcaps = SNOR_HWCAPS_ALL; + + /* DTR modes are not supported yet, mask them all. */ + *hwcaps &= ~SNOR_HWCAPS_DTR; + + /* X-X-X modes are not supported yet, mask them all. */ + *hwcaps &= ~SNOR_HWCAPS_X_X_X; + + for (cap = 0; cap < sizeof(*hwcaps) * BITS_PER_BYTE; cap++) { + int rdidx, ppidx; + + if (!(*hwcaps & BIT(cap))) + continue; + + rdidx = spi_nor_hwcaps_read2cmd(BIT(cap)); + if (rdidx >= 0 && + spi_nor_check_readop(nor, ¶ms->reads[rdidx])) + *hwcaps &= ~BIT(cap); + + ppidx = spi_nor_hwcaps_pp2cmd(BIT(cap)); + if (ppidx < 0) + continue; + + if (spi_nor_check_pp(nor, ¶ms->page_programs[ppidx])) + *hwcaps &= ~BIT(cap); + } +} +#else +/** + * spi_nor_adjust_hwcaps - Find optimal Read/Write protocol based on SPI + * controller capabilities + * @nor: pointer to a 'struct spi_nor' + * @params: pointer to the 'struct spi_nor_flash_parameter' + * representing SPI NOR flash capabilities + * @hwcaps: pointer to resulting capabilities after adjusting + * according to controller and flash's capability + * + * Select caps based on what the SPI controller and SPI flash both support. + */ +static void +spi_nor_adjust_hwcaps(struct spi_nor *nor, + const struct spi_nor_flash_parameter *params, + u32 *hwcaps) +{ + struct spi_slave *spi = nor->spi; + u32 ignored_mask = (SNOR_HWCAPS_READ_2_2_2 | + SNOR_HWCAPS_READ_4_4_4 | + SNOR_HWCAPS_READ_8_8_8 | + SNOR_HWCAPS_PP_4_4_4 | + SNOR_HWCAPS_PP_8_8_8); + u32 spi_hwcaps = (SNOR_HWCAPS_READ | SNOR_HWCAPS_READ_FAST | + SNOR_HWCAPS_PP); + + /* Get the hardware capabilities the SPI controller supports. */ + if (spi->mode & SPI_RX_OCTAL) { + spi_hwcaps |= SNOR_HWCAPS_READ_1_1_8; + + if (spi->mode & SPI_TX_OCTAL) + spi_hwcaps |= (SNOR_HWCAPS_READ_1_8_8 | + SNOR_HWCAPS_PP_1_1_8 | + SNOR_HWCAPS_PP_1_8_8); + } else if (spi->mode & SPI_RX_QUAD) { + spi_hwcaps |= SNOR_HWCAPS_READ_1_1_4; + + if (spi->mode & SPI_TX_QUAD) + spi_hwcaps |= (SNOR_HWCAPS_READ_1_4_4 | + SNOR_HWCAPS_PP_1_1_4 | + SNOR_HWCAPS_PP_1_4_4); + } else if (spi->mode & SPI_RX_DUAL) { + spi_hwcaps |= SNOR_HWCAPS_READ_1_1_2; + + if (spi->mode & SPI_TX_DUAL) + spi_hwcaps |= SNOR_HWCAPS_READ_1_2_2; + } + + /* + * Keep only the hardware capabilities supported by both the SPI + * controller and the SPI flash memory. + */ + *hwcaps = spi_hwcaps & params->hwcaps.mask; + if (*hwcaps & ignored_mask) { + dev_dbg(nor->dev, + "SPI n-n-n protocols are not supported yet.\n"); + *hwcaps &= ~ignored_mask; + } +} +#endif /* CONFIG_SPI_FLASH_SMART_HWCAPS */ + static int spi_nor_select_read(struct spi_nor *nor, const struct spi_nor_flash_parameter *params, u32 shared_hwcaps) @@ -2379,30 +2567,13 @@ static int spi_nor_select_erase(struct spi_nor *nor, static int spi_nor_default_setup(struct spi_nor *nor, const struct flash_info *info, - const struct spi_nor_flash_parameter *params, - const struct spi_nor_hwcaps *hwcaps) + const struct spi_nor_flash_parameter *params) { - u32 ignored_mask, shared_mask; + u32 shared_mask; bool enable_quad_io; int err; - /* - * Keep only the hardware capabilities supported by both the SPI - * controller and the SPI flash memory. - */ - shared_mask = hwcaps->mask & params->hwcaps.mask; - - /* SPI n-n-n protocols are not supported yet. */ - ignored_mask = (SNOR_HWCAPS_READ_2_2_2 | - SNOR_HWCAPS_READ_4_4_4 | - SNOR_HWCAPS_READ_8_8_8 | - SNOR_HWCAPS_PP_4_4_4 | - SNOR_HWCAPS_PP_8_8_8); - if (shared_mask & ignored_mask) { - dev_dbg(nor->dev, - "SPI n-n-n protocols are not supported yet.\n"); - shared_mask &= ~ignored_mask; - } + spi_nor_adjust_hwcaps(nor, params, &shared_mask); /* Select the (Fast) Read command. */ err = spi_nor_select_read(nor, params, shared_mask); @@ -2440,13 +2611,12 @@ static int spi_nor_default_setup(struct spi_nor *nor, } static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info, - const struct spi_nor_flash_parameter *params, - const struct spi_nor_hwcaps *hwcaps) + const struct spi_nor_flash_parameter *params) { if (!nor->setup) return 0; - return nor->setup(nor, info, params, hwcaps); + return nor->setup(nor, info, params); } static int spi_nor_init(struct spi_nor *nor) @@ -2502,11 +2672,6 @@ int spi_nor_scan(struct spi_nor *nor) struct spi_nor_flash_parameter params; const struct flash_info *info = NULL; struct mtd_info *mtd = &nor->mtd; - struct spi_nor_hwcaps hwcaps = { - .mask = SNOR_HWCAPS_READ | - SNOR_HWCAPS_READ_FAST | - SNOR_HWCAPS_PP, - }; struct spi_slave *spi = nor->spi; int ret; @@ -2521,27 +2686,6 @@ int spi_nor_scan(struct spi_nor *nor) nor->setup = spi_nor_default_setup; - if (spi->mode & SPI_RX_OCTAL) { - hwcaps.mask |= SNOR_HWCAPS_READ_1_1_8; - - if (spi->mode & SPI_TX_OCTAL) - hwcaps.mask |= (SNOR_HWCAPS_READ_1_8_8 | - SNOR_HWCAPS_PP_1_1_8 | - SNOR_HWCAPS_PP_1_8_8); - } else if (spi->mode & SPI_RX_QUAD) { - hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4; - - if (spi->mode & SPI_TX_QUAD) - hwcaps.mask |= (SNOR_HWCAPS_READ_1_4_4 | - SNOR_HWCAPS_PP_1_1_4 | - SNOR_HWCAPS_PP_1_4_4); - } else if (spi->mode & SPI_RX_DUAL) { - hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2; - - if (spi->mode & SPI_TX_DUAL) - hwcaps.mask |= SNOR_HWCAPS_READ_1_2_2; - } - info = spi_nor_read_id(nor); if (IS_ERR_OR_NULL(info)) return -ENOENT; @@ -2616,7 +2760,7 @@ int spi_nor_scan(struct spi_nor *nor) * - set the SPI protocols for register and memory accesses. * - set the Quad Enable bit if needed (required by SPI x-y-4 protos). */ - ret = spi_nor_setup(nor, info, ¶ms, &hwcaps); + ret = spi_nor_setup(nor, info, ¶ms); if (ret) return ret; diff --git a/drivers/spi/spi-mem-nodm.c b/drivers/spi/spi-mem-nodm.c index db54101383..a228c808c7 100644 --- a/drivers/spi/spi-mem-nodm.c +++ b/drivers/spi/spi-mem-nodm.c @@ -105,3 +105,65 @@ int spi_mem_adjust_op_size(struct spi_slave *slave, return 0; } + +static int spi_check_buswidth_req(struct spi_slave *slave, u8 buswidth, bool tx) +{ + u32 mode = slave->mode; + + switch (buswidth) { + case 1: + return 0; + + case 2: + if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) || + (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD)))) + return 0; + + break; + + case 4: + if ((tx && (mode & SPI_TX_QUAD)) || + (!tx && (mode & SPI_RX_QUAD))) + return 0; + + break; + case 8: + if ((tx && (mode & SPI_TX_OCTAL)) || + (!tx && (mode & SPI_RX_OCTAL))) + return 0; + + break; + + default: + break; + } + + return -ENOTSUPP; +} + +bool spi_mem_supports_op(struct spi_slave *slave, const struct spi_mem_op *op) +{ + if (spi_check_buswidth_req(slave, op->cmd.buswidth, true)) + return false; + + if (op->addr.nbytes && + spi_check_buswidth_req(slave, op->addr.buswidth, true)) + return false; + + if (op->dummy.nbytes && + spi_check_buswidth_req(slave, op->dummy.buswidth, true)) + return false; + + if (op->data.nbytes && + spi_check_buswidth_req(slave, op->data.buswidth, + op->data.dir == SPI_MEM_DATA_OUT)) + return false; + + if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr) + return false; + + if (op->cmd.nbytes != 1) + return false; + + return true; +} diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index b2e9e0895b..90b75ec845 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -312,6 +312,20 @@ struct spi_nor_hwcaps { #define SNOR_HWCAPS_PP_1_8_8 BIT(21) #define SNOR_HWCAPS_PP_8_8_8 BIT(22) +#define SNOR_HWCAPS_X_X_X (SNOR_HWCAPS_READ_2_2_2 | \ + SNOR_HWCAPS_READ_4_4_4 | \ + SNOR_HWCAPS_READ_8_8_8 | \ + SNOR_HWCAPS_PP_4_4_4 | \ + SNOR_HWCAPS_PP_8_8_8) + +#define SNOR_HWCAPS_DTR (SNOR_HWCAPS_READ_1_1_1_DTR | \ + SNOR_HWCAPS_READ_1_2_2_DTR | \ + SNOR_HWCAPS_READ_1_4_4_DTR | \ + SNOR_HWCAPS_READ_1_8_8_DTR) + +#define SNOR_HWCAPS_ALL (SNOR_HWCAPS_READ_MASK | \ + SNOR_HWCAPS_PP_MASK) + struct spi_nor_read_command { u8 num_mode_clocks; u8 num_wait_states; @@ -461,8 +475,7 @@ struct spi_nor { struct spi_nor_fixups *fixups; int (*setup)(struct spi_nor *nor, const struct flash_info *info, - const struct spi_nor_flash_parameter *params, - const struct spi_nor_hwcaps *hwcaps); + const struct spi_nor_flash_parameter *params); int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops); void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len); -- 2.30.0