From patchwork Fri Oct 12 08:48:14 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris Brezillon X-Patchwork-Id: 10638067 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BA3DF3CF1 for ; Fri, 12 Oct 2018 08:48:52 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A9AE02B1A7 for ; Fri, 12 Oct 2018 08:48:52 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9D9932B74D; Fri, 12 Oct 2018 08:48:52 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0F35A2B1A7 for ; Fri, 12 Oct 2018 08:48:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727808AbeJLQUO (ORCPT ); Fri, 12 Oct 2018 12:20:14 -0400 Received: from mail.bootlin.com ([62.4.15.54]:43123 "EHLO mail.bootlin.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727880AbeJLQUO (ORCPT ); Fri, 12 Oct 2018 12:20:14 -0400 Received: by mail.bootlin.com (Postfix, from userid 110) id A5F09208C2; Fri, 12 Oct 2018 10:48:48 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-7-245.w90-88.abo.wanadoo.fr [90.88.129.245]) by mail.bootlin.com (Postfix) with ESMTPSA id B1F74208F4; Fri, 12 Oct 2018 10:48:29 +0200 (CEST) From: Boris Brezillon To: David Woodhouse , Brian Norris , Boris Brezillon , Marek Vasut , Richard Weinberger , linux-mtd@lists.infradead.org, Yogesh Gaur , Vignesh R , Cyrille Pitchen Cc: Julien Su , Mason Yang , , Mark Brown , linux-spi@vger.kernel.org Subject: [PATCH RFC 07/18] mtd: spi-nor: Rework hwcaps selection for the spi-mem case Date: Fri, 12 Oct 2018 10:48:14 +0200 Message-Id: <20181012084825.23697-8-boris.brezillon@bootlin.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20181012084825.23697-1-boris.brezillon@bootlin.com> References: <20181012084825.23697-1-boris.brezillon@bootlin.com> Sender: linux-spi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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() when nor->spimem != NULL. Signed-off-by: Boris Brezillon --- drivers/mtd/spi-nor/spi-nor.c | 157 ++++++++++++++++++++++++++++++++++-------- include/linux/mtd/spi-nor.h | 14 ++++ 2 files changed, 142 insertions(+), 29 deletions(-) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index d9b9dd76b987..46e5d5d4a423 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -2839,6 +2839,110 @@ static int spi_nor_hwcaps_pp2cmd(u32 hwcaps) ARRAY_SIZE(hwcaps_pp2cmd)); } +static int spi_nor_spimem_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; + + /* + * First test with 3 address bytes. The opcode itself might already + * be a 4B addressing opcode but we don't care, because SPI controller + * implementation should not check the opcode, but just the sequence. + */ + if (!spi_mem_supports_op(nor->spimem, &op)) + return -ENOTSUPP; + + /* Now test with 4 address bytes. */ + op.addr.nbytes = 4; + if (!spi_mem_supports_op(nor->spimem, &op)) + return -ENOTSUPP; + + return 0; +} + +static int spi_nor_spimem_check_progop(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); + + /* + * First test with 3 address bytes. The opcode itself might already + * be a 4B addressing opcode but we don't care, because SPI controller + * implementation should not check the opcode, but just the sequence. + */ + if (!spi_mem_supports_op(nor->spimem, &op)) + return -ENOTSUPP; + + /* Now test with 4 address bytes. */ + op.addr.nbytes = 4; + if (!spi_mem_supports_op(nor->spimem, &op)) + return -ENOTSUPP; + + return 0; +} + +static void +spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor, + const struct spi_nor_flash_parameter *params, + u32 *hwcaps) +{ + unsigned int cap; + + /* 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; + + /* Start with read commands. */ + for (cap = 0; cap < 32; cap++) { + int idx; + + if (!(*hwcaps & BIT(cap))) + continue; + + idx = spi_nor_hwcaps_read2cmd(BIT(cap)); + if (idx < 0) + continue; + + if (spi_nor_spimem_check_readop(nor, ¶ms->reads[idx])) + *hwcaps &= ~BIT(cap); + } + + /* Now check program commands. */ + for (cap = 0; cap < 32; cap++) { + int idx; + + if (!(*hwcaps & BIT(cap))) + continue; + + idx = spi_nor_hwcaps_pp2cmd(BIT(cap)); + if (idx < 0) + continue; + + if (spi_nor_spimem_check_progop(nor, + ¶ms->page_programs[idx])) + *hwcaps &= ~BIT(cap); + } +} + /** * spi_nor_set_erase_type() - set a SPI NOR erase type * @erase: pointer to a structure that describes a SPI NOR erase type @@ -3800,16 +3904,25 @@ static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info, */ 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; + if (nor->spimem) { + /* + * When called from spi_nor_probe(), all caps are set and we + * need to discard some of them based on what the SPI + * controller actually supports (using spi_mem_supports_op()). + */ + spi_nor_spimem_adjust_hwcaps(nor, params, &shared_mask); + } else { + /* + * SPI n-n-n protocols are not supported when the SPI + * controller directly implements the spi_nor interface. + * Yet another reason to switch to spi-mem. + */ + ignored_mask = SNOR_HWCAPS_X_X_X; + if (shared_mask & ignored_mask) { + dev_dbg(nor->dev, + "SPI n-n-n protocols are not supported.\n"); + shared_mask &= ~ignored_mask; + } } /* Select the (Fast) Read command. */ @@ -4241,11 +4354,11 @@ static int spi_nor_probe(struct spi_mem *spimem) struct spi_device *spi = spimem->spi; struct flash_platform_data *data; struct spi_nor *nor; - struct spi_nor_hwcaps hwcaps = { - .mask = SNOR_HWCAPS_READ | - SNOR_HWCAPS_READ_FAST | - SNOR_HWCAPS_PP, - }; + /* + * Enable all caps by default. The core will mask them after + * checking what's really supported using spi_mem_supports_op(). + */ + struct spi_nor_hwcaps hwcaps = { .mask = SNOR_HWCAPS_ALL }; char *flash_name; int ret; @@ -4276,20 +4389,6 @@ static int spi_nor_probe(struct spi_mem *spimem) spi_mem_set_drvdata(spimem, nor); - 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; - } - if (data && data->name) nor->mtd.name = data->name; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 7ab70651a0f8..93400b17fbd0 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -517,6 +517,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) + /** * spi_nor_scan() - scan the SPI NOR * @nor: the spi_nor structure