Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933752AbdCWRjJ (ORCPT ); Thu, 23 Mar 2017 13:39:09 -0400 Received: from 20.mo1.mail-out.ovh.net ([188.165.45.168]:53394 "EHLO 20.mo1.mail-out.ovh.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753670AbdCWRjH (ORCPT ); Thu, 23 Mar 2017 13:39:07 -0400 X-Greylist: delayed 4179 seconds by postgrey-1.27 at vger.kernel.org; Thu, 23 Mar 2017 13:39:06 EDT Subject: Re: [PATCH v5 1/6] mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode To: Cyrille Pitchen , marek.vasut@gmail.com, linux-mtd@lists.infradead.org, jartur@cadence.com, kdasu.kdev@gmail.com, mar.krzeminski@gmail.com References: <65619c23078469af03e4d53d781a8cffa92d5a61.1490220411.git.cyrille.pitchen@atmel.com> Cc: boris.brezillon@free-electrons.com, richard@nod.at, nicolas.ferre@microchip.com, linux-kernel@vger.kernel.org, computersforpeace@gmail.com, dwmw2@infradead.org From: =?UTF-8?Q?C=c3=a9dric_Le_Goater?= Message-ID: Date: Thu, 23 Mar 2017 16:13:48 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.8.0 MIME-Version: 1.0 In-Reply-To: <65619c23078469af03e4d53d781a8cffa92d5a61.1490220411.git.cyrille.pitchen@atmel.com> Content-Type: text/plain; charset=windows-1252 Content-Transfer-Encoding: 7bit X-Ovh-Tracer-Id: 16590135129625234277 X-VR-SPAMSTATE: OK X-VR-SPAMSCORE: -100 X-VR-SPAMCAUSE: gggruggvucftvghtrhhoucdtuddrfeelhedrjeefgdejgecutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfqggfjpdevjffgvefmvefgnecuuegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmd Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 45068 Lines: 1357 On 03/23/2017 12:33 AM, Cyrille Pitchen wrote: > This patch changes the prototype of spi_nor_scan(): its 3rd parameter > is replaced by a 'struct spi_nor_hwcaps' pointer, which tells the spi-nor > framework about the actual hardware capabilities supported by the SPI > controller and its driver. > > Besides, this patch also introduces a new 'struct spi_nor_flash_parameter' > telling the spi-nor framework about the hardware capabilities supported by > the SPI flash memory and the associated settings required to use those > hardware caps. > > Currently the 'struct spi_nor_flash_parameter' is filled with legacy > values but a later patch will allow to fill it dynamically by reading the > JESD216 Serial Flash Discoverable Parameter (SFDP) tables from the SPI > memory. > > With both structures, the spi-nor framework can now compute the best > match between hardware caps supported by both the (Q)SPI memory and > controller hence selecting the relevant SPI protocols and op codes for > (Fast) Read, Page Program and Sector Erase operations. > > The 'struct spi_nor_flash_parameter' also provides the spi-nor framework > with the number of dummy cycles to be used with each Fast Read commands > and the erase block size associated to the erase block op codes. > > Finally the 'struct spi_nor_flash_parameter', through the optional > .enable_quad_io() hook, tells the spi-nor framework how to set the Quad > Enable (QE) bit of the QSPI memory to enable its Quad SPI features. The Aspeed controller only supports Dual I/O and a helper similar to the one for Quad I/O would be needed to setup the chip for multiple I/O: int (*dual_enable)(struct spi_nor *nor); Is the approach correct ? or maybe rename the current 'quad_enable()' to 'multiple_enable()' and add an extra parameter. I have a bunch of patches queued for Dual I/O data support but I think they will conflict with your patches. I will wait for this one to be merged. Then, I can look at Dual I/O address + data support. Thanks, C. > Signed-off-by: Cyrille Pitchen > --- > drivers/mtd/devices/m25p80.c | 16 +- > drivers/mtd/spi-nor/aspeed-smc.c | 23 +- > drivers/mtd/spi-nor/atmel-quadspi.c | 80 +++--- > drivers/mtd/spi-nor/cadence-quadspi.c | 18 +- > drivers/mtd/spi-nor/fsl-quadspi.c | 8 +- > drivers/mtd/spi-nor/hisi-sfc.c | 31 ++- > drivers/mtd/spi-nor/intel-spi.c | 7 +- > drivers/mtd/spi-nor/mtk-quadspi.c | 16 +- > drivers/mtd/spi-nor/nxp-spifi.c | 22 +- > drivers/mtd/spi-nor/spi-nor.c | 441 +++++++++++++++++++++++++++------- > include/linux/mtd/spi-nor.h | 158 +++++++++++- > 11 files changed, 643 insertions(+), 177 deletions(-) > > diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c > index c4df3b1bded0..68986a26c8fe 100644 > --- a/drivers/mtd/devices/m25p80.c > +++ b/drivers/mtd/devices/m25p80.c > @@ -111,10 +111,10 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len, > > static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor) > { > - switch (nor->flash_read) { > - case SPI_NOR_DUAL: > + switch (nor->read_proto) { > + case SNOR_PROTO_1_1_2: > return 2; > - case SPI_NOR_QUAD: > + case SNOR_PROTO_1_1_4: > return 4; > default: > return 0; > @@ -196,7 +196,9 @@ static int m25p_probe(struct spi_device *spi) > struct flash_platform_data *data; > struct m25p *flash; > struct spi_nor *nor; > - enum read_mode mode = SPI_NOR_NORMAL; > + struct spi_nor_hwcaps hwcaps = { > + .mask = (SNOR_HWCAPS_READ | SNOR_HWCAPS_PP), > + }; > char *flash_name; > int ret; > > @@ -222,9 +224,9 @@ static int m25p_probe(struct spi_device *spi) > flash->spi = spi; > > if (spi->mode & SPI_RX_QUAD) > - mode = SPI_NOR_QUAD; > + hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4; > else if (spi->mode & SPI_RX_DUAL) > - mode = SPI_NOR_DUAL; > + hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2; > > if (data && data->name) > nor->mtd.name = data->name; > @@ -241,7 +243,7 @@ static int m25p_probe(struct spi_device *spi) > else > flash_name = spi->modalias; > > - ret = spi_nor_scan(nor, flash_name, mode); > + ret = spi_nor_scan(nor, flash_name, &hwcaps); > if (ret) > return ret; > > diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c > index 56051d30f000..723026d9cf0c 100644 > --- a/drivers/mtd/spi-nor/aspeed-smc.c > +++ b/drivers/mtd/spi-nor/aspeed-smc.c > @@ -585,14 +585,12 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip) > * TODO: Adjust clocks if fast read is supported and interpret > * SPI-NOR flags to adjust controller settings. > */ > - switch (chip->nor.flash_read) { > - case SPI_NOR_NORMAL: > - cmd = CONTROL_COMMAND_MODE_NORMAL; > - break; > - case SPI_NOR_FAST: > - cmd = CONTROL_COMMAND_MODE_FREAD; > - break; > - default: > + if (chip->nor.read_proto == SNOR_PROTO_1_1_1) { > + if (chip->nor.read_dummy == 0) > + cmd = CONTROL_COMMAND_MODE_NORMAL; > + else > + cmd = CONTROL_COMMAND_MODE_FREAD; > + } else { > dev_err(chip->nor.dev, "unsupported SPI read mode\n"); > return -EINVAL; > } > @@ -608,6 +606,11 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip) > static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller, > struct device_node *np, struct resource *r) > { > + struct spi_nor_hwcaps hwcaps = { > + .mask = (SNOR_HWCAPS_READ | > + SNOR_HWCAPS_READ_FAST | > + SNOR_HWCAPS_PP), > + }; > const struct aspeed_smc_info *info = controller->info; > struct device *dev = controller->dev; > struct device_node *child; > @@ -671,11 +674,11 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller, > break; > > /* > - * TODO: Add support for SPI_NOR_QUAD and SPI_NOR_DUAL > + * TODO: Add support for Dual and Quad SPI protocols > > * attach when board support is present as determined > * by of property. > */ > - ret = spi_nor_scan(nor, NULL, SPI_NOR_NORMAL); > + ret = spi_nor_scan(nor, NULL, &hwcaps); > if (ret) > break; > > diff --git a/drivers/mtd/spi-nor/atmel-quadspi.c b/drivers/mtd/spi-nor/atmel-quadspi.c > index 47937d9beec6..9f579f7c1733 100644 > --- a/drivers/mtd/spi-nor/atmel-quadspi.c > +++ b/drivers/mtd/spi-nor/atmel-quadspi.c > @@ -275,14 +275,48 @@ static void atmel_qspi_debug_command(struct atmel_qspi *aq, > > static int atmel_qspi_run_command(struct atmel_qspi *aq, > const struct atmel_qspi_command *cmd, > - u32 ifr_tfrtyp, u32 ifr_width) > + u32 ifr_tfrtyp, enum spi_nor_protocol proto) > { > u32 iar, icr, ifr, sr; > int err = 0; > > iar = 0; > icr = 0; > - ifr = ifr_tfrtyp | ifr_width; > + ifr = ifr_tfrtyp; > + > + /* Set the SPI protocol */ > + switch (proto) { > + case SNOR_PROTO_1_1_1: > + ifr |= QSPI_IFR_WIDTH_SINGLE_BIT_SPI; > + break; > + > + case SNOR_PROTO_1_1_2: > + ifr |= QSPI_IFR_WIDTH_DUAL_OUTPUT; > + break; > + > + case SNOR_PROTO_1_1_4: > + ifr |= QSPI_IFR_WIDTH_QUAD_OUTPUT; > + break; > + > + case SNOR_PROTO_1_2_2: > + ifr |= QSPI_IFR_WIDTH_DUAL_IO; > + break; > + > + case SNOR_PROTO_1_4_4: > + ifr |= QSPI_IFR_WIDTH_QUAD_IO; > + break; > + > + case SNOR_PROTO_2_2_2: > + ifr |= QSPI_IFR_WIDTH_DUAL_CMD; > + break; > + > + case SNOR_PROTO_4_4_4: > + ifr |= QSPI_IFR_WIDTH_QUAD_CMD; > + break; > + > + default: > + return -EINVAL; > + } > > /* Compute instruction parameters */ > if (cmd->enable.bits.instruction) { > @@ -434,7 +468,7 @@ static int atmel_qspi_read_reg(struct spi_nor *nor, u8 opcode, > cmd.rx_buf = buf; > cmd.buf_len = len; > return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ, > - QSPI_IFR_WIDTH_SINGLE_BIT_SPI); > + nor->reg_proto); > } > > static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode, > @@ -450,7 +484,7 @@ static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode, > cmd.tx_buf = buf; > cmd.buf_len = len; > return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE, > - QSPI_IFR_WIDTH_SINGLE_BIT_SPI); > + nor->reg_proto); > } > > static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len, > @@ -469,7 +503,7 @@ static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len, > cmd.tx_buf = write_buf; > cmd.buf_len = len; > ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM, > - QSPI_IFR_WIDTH_SINGLE_BIT_SPI); > + nor->write_proto); > return (ret < 0) ? ret : len; > } > > @@ -484,7 +518,7 @@ static int atmel_qspi_erase(struct spi_nor *nor, loff_t offs) > cmd.instruction = nor->erase_opcode; > cmd.address = (u32)offs; > return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE, > - QSPI_IFR_WIDTH_SINGLE_BIT_SPI); > + nor->reg_proto); > } > > static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len, > @@ -493,27 +527,8 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len, > struct atmel_qspi *aq = nor->priv; > struct atmel_qspi_command cmd; > u8 num_mode_cycles, num_dummy_cycles; > - u32 ifr_width; > ssize_t ret; > > - switch (nor->flash_read) { > - case SPI_NOR_NORMAL: > - case SPI_NOR_FAST: > - ifr_width = QSPI_IFR_WIDTH_SINGLE_BIT_SPI; > - break; > - > - case SPI_NOR_DUAL: > - ifr_width = QSPI_IFR_WIDTH_DUAL_OUTPUT; > - break; > - > - case SPI_NOR_QUAD: > - ifr_width = QSPI_IFR_WIDTH_QUAD_OUTPUT; > - break; > - > - default: > - return -EINVAL; > - } > - > if (nor->read_dummy >= 2) { > num_mode_cycles = 2; > num_dummy_cycles = nor->read_dummy - 2; > @@ -536,7 +551,7 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len, > cmd.rx_buf = read_buf; > cmd.buf_len = len; > ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ_MEM, > - ifr_width); > + nor->read_proto); > return (ret < 0) ? ret : len; > } > > @@ -590,6 +605,17 @@ static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id) > > static int atmel_qspi_probe(struct platform_device *pdev) > { > + struct spi_nor_hwcaps hwcaps = { > + .mask = (SNOR_HWCAPS_READ | > + SNOR_HWCAPS_READ_FAST | > + SNOR_HWCAPS_READ_1_1_2 | > + SNOR_HWCAPS_READ_1_2_2 | > + SNOR_HWCAPS_READ_1_1_4 | > + SNOR_HWCAPS_READ_1_4_4 | > + SNOR_HWCAPS_PP | > + SNOR_HWCAPS_PP_1_1_4 | > + SNOR_HWCAPS_PP_1_4_4), > + }; > struct device_node *child, *np = pdev->dev.of_node; > struct atmel_qspi *aq; > struct resource *res; > @@ -679,7 +705,7 @@ static int atmel_qspi_probe(struct platform_device *pdev) > if (err) > goto disable_clk; > > - err = spi_nor_scan(nor, NULL, SPI_NOR_QUAD); > + err = spi_nor_scan(nor, NULL, &hwcaps); > if (err) > goto disable_clk; > > diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c > index 9f8102de1b16..3f91a3e97892 100644 > --- a/drivers/mtd/spi-nor/cadence-quadspi.c > +++ b/drivers/mtd/spi-nor/cadence-quadspi.c > @@ -855,15 +855,14 @@ static int cqspi_set_protocol(struct spi_nor *nor, const int read) > f_pdata->data_width = CQSPI_INST_TYPE_SINGLE; > > if (read) { > - switch (nor->flash_read) { > - case SPI_NOR_NORMAL: > - case SPI_NOR_FAST: > + switch (nor->read_proto) { > + case SNOR_PROTO_1_1_1: > f_pdata->data_width = CQSPI_INST_TYPE_SINGLE; > break; > - case SPI_NOR_DUAL: > + case SNOR_PROTO_1_1_2: > f_pdata->data_width = CQSPI_INST_TYPE_DUAL; > break; > - case SPI_NOR_QUAD: > + case SNOR_PROTO_1_1_4: > f_pdata->data_width = CQSPI_INST_TYPE_QUAD; > break; > default: > @@ -1069,6 +1068,13 @@ static void cqspi_controller_init(struct cqspi_st *cqspi) > > static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np) > { > + struct spi_nor_hwcaps hwcaps = { > + .mask = (SNOR_HWCAPS_READ | > + SNOR_HWCAPS_READ_FAST | > + SNOR_HWCAPS_READ_1_1_2 | > + SNOR_HWCAPS_READ_1_1_4 | > + SNOR_HWCAPS_PP), > + }; > struct platform_device *pdev = cqspi->pdev; > struct device *dev = &pdev->dev; > struct cqspi_flash_pdata *f_pdata; > @@ -1123,7 +1129,7 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np) > goto err; > } > > - ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD); > + ret = spi_nor_scan(nor, NULL, &hwcaps); > if (ret) > goto err; > > diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c > index 1476135e0d50..ec9c8e960fd2 100644 > --- a/drivers/mtd/spi-nor/fsl-quadspi.c > +++ b/drivers/mtd/spi-nor/fsl-quadspi.c > @@ -957,6 +957,12 @@ static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops) > > static int fsl_qspi_probe(struct platform_device *pdev) > { > + struct spi_nor_hwcaps hwcaps = { > + .mask = (SNOR_HWCAPS_READ | > + SNOR_HWCAPS_READ_FAST | > + SNOR_HWCAPS_READ_1_1_4 | > + SNOR_HWCAPS_PP), > + }; > struct device_node *np = pdev->dev.of_node; > struct device *dev = &pdev->dev; > struct fsl_qspi *q; > @@ -1065,7 +1071,7 @@ static int fsl_qspi_probe(struct platform_device *pdev) > /* set the chip address for READID */ > fsl_qspi_set_base_addr(q, nor); > > - ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD); > + ret = spi_nor_scan(nor, NULL, &hwcaps); > if (ret) > goto mutex_failed; > > diff --git a/drivers/mtd/spi-nor/hisi-sfc.c b/drivers/mtd/spi-nor/hisi-sfc.c > index a286350627a6..80e2d173abdd 100644 > --- a/drivers/mtd/spi-nor/hisi-sfc.c > +++ b/drivers/mtd/spi-nor/hisi-sfc.c > @@ -120,19 +120,24 @@ static inline int wait_op_finish(struct hifmc_host *host) > (reg & FMC_INT_OP_DONE), 0, FMC_WAIT_TIMEOUT); > } > > -static int get_if_type(enum read_mode flash_read) > +static int get_if_type(enum spi_nor_protocol proto) > { > enum hifmc_iftype if_type; > > - switch (flash_read) { > - case SPI_NOR_DUAL: > + switch (proto) { > + case SNOR_PROTO_1_1_2: > if_type = IF_TYPE_DUAL; > break; > - case SPI_NOR_QUAD: > + case SNOR_PROTO_1_2_2: > + if_type = IF_TYPE_DIO; > + break; > + case SNOR_PROTO_1_1_4: > if_type = IF_TYPE_QUAD; > break; > - case SPI_NOR_NORMAL: > - case SPI_NOR_FAST: > + case SNOR_PROTO_1_4_4: > + if_type = IF_TYPE_QIO; > + break; > + case SNOR_PROTO_1_1_1: > default: > if_type = IF_TYPE_STD; > break; > @@ -253,7 +258,10 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off, > writel(FMC_DMA_LEN_SET(len), host->regbase + FMC_DMA_LEN); > > reg = OP_CFG_FM_CS(priv->chipselect); > - if_type = get_if_type(nor->flash_read); > + if (op_type == FMC_OP_READ) > + if_type = get_if_type(nor->read_proto); > + else > + if_type = get_if_type(nor->write_proto); > reg |= OP_CFG_MEM_IF_TYPE(if_type); > if (op_type == FMC_OP_READ) > reg |= OP_CFG_DUMMY_NUM(nor->read_dummy >> 3); > @@ -321,6 +329,13 @@ static ssize_t hisi_spi_nor_write(struct spi_nor *nor, loff_t to, > static int hisi_spi_nor_register(struct device_node *np, > struct hifmc_host *host) > { > + struct spi_nor_hwcaps hwcaps = { > + .mask = (SNOR_HWCAPS_READ | > + SNOR_HWCAPS_READ_FAST | > + SNOR_HWCAPS_READ_1_1_2 | > + SNOR_HWCAPS_READ_1_1_4 | > + SNOR_HWCAPS_PP), > + }; > struct device *dev = host->dev; > struct spi_nor *nor; > struct hifmc_priv *priv; > @@ -362,7 +377,7 @@ static int hisi_spi_nor_register(struct device_node *np, > nor->read = hisi_spi_nor_read; > nor->write = hisi_spi_nor_write; > nor->erase = NULL; > - ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD); > + ret = spi_nor_scan(nor, NULL, &hwcaps); > if (ret) > return ret; > > diff --git a/drivers/mtd/spi-nor/intel-spi.c b/drivers/mtd/spi-nor/intel-spi.c > index 986a3d020a3a..515aa1f7f4f1 100644 > --- a/drivers/mtd/spi-nor/intel-spi.c > +++ b/drivers/mtd/spi-nor/intel-spi.c > @@ -715,6 +715,11 @@ static void intel_spi_fill_partition(struct intel_spi *ispi, > struct intel_spi *intel_spi_probe(struct device *dev, > struct resource *mem, const struct intel_spi_boardinfo *info) > { > + struct spi_nor_hwcaps hwcaps = { > + .mask = (SNOR_HWCAPS_READ | > + SNOR_HWCAPS_READ_FAST | > + SNOR_HWCAPS_PP), > + }; > struct mtd_partition part; > struct intel_spi *ispi; > int ret; > @@ -746,7 +751,7 @@ struct intel_spi *intel_spi_probe(struct device *dev, > ispi->nor.write = intel_spi_write; > ispi->nor.erase = intel_spi_erase; > > - ret = spi_nor_scan(&ispi->nor, NULL, SPI_NOR_NORMAL); > + ret = spi_nor_scan(&ispi->nor, NULL, &hwcaps); > if (ret) { > dev_info(dev, "failed to locate the chip\n"); > return ERR_PTR(ret); > diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c > index e661877c23de..615e258866f7 100644 > --- a/drivers/mtd/spi-nor/mtk-quadspi.c > +++ b/drivers/mtd/spi-nor/mtk-quadspi.c > @@ -121,20 +121,20 @@ static void mt8173_nor_set_read_mode(struct mt8173_nor *mt8173_nor) > { > struct spi_nor *nor = &mt8173_nor->nor; > > - switch (nor->flash_read) { > - case SPI_NOR_FAST: > + switch (nor->read_proto) { > + case SNOR_PROTO_1_1_1: > writeb(nor->read_opcode, mt8173_nor->base + > MTK_NOR_PRGDATA3_REG); > writeb(MTK_NOR_FAST_READ, mt8173_nor->base + > MTK_NOR_CFG1_REG); > break; > - case SPI_NOR_DUAL: > + case SNOR_PROTO_1_1_2: > writeb(nor->read_opcode, mt8173_nor->base + > MTK_NOR_PRGDATA3_REG); > writeb(MTK_NOR_DUAL_READ_EN, mt8173_nor->base + > MTK_NOR_DUAL_REG); > break; > - case SPI_NOR_QUAD: > + case SNOR_PROTO_1_1_4: > writeb(nor->read_opcode, mt8173_nor->base + > MTK_NOR_PRGDATA4_REG); > writeb(MTK_NOR_QUAD_READ_EN, mt8173_nor->base + > @@ -381,6 +381,12 @@ static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, > static int mtk_nor_init(struct mt8173_nor *mt8173_nor, > struct device_node *flash_node) > { > + struct spi_nor_hwcaps hwcaps = { > + .mask = (SNOR_HWCAPS_READ | > + SNOR_HWCAPS_READ_FAST | > + SNOR_HWCAPS_READ_1_1_2 | > + SNOR_HWCAPS_PP), > + }; > int ret; > struct spi_nor *nor; > > @@ -399,7 +405,7 @@ static int mtk_nor_init(struct mt8173_nor *mt8173_nor, > nor->write_reg = mt8173_nor_write_reg; > nor->mtd.name = "mtk_nor"; > /* initialized with NULL */ > - ret = spi_nor_scan(nor, NULL, SPI_NOR_DUAL); > + ret = spi_nor_scan(nor, NULL, &hwcaps); > if (ret) > return ret; > > diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c > index 73a14f40928b..c5992e099542 100644 > --- a/drivers/mtd/spi-nor/nxp-spifi.c > +++ b/drivers/mtd/spi-nor/nxp-spifi.c > @@ -240,13 +240,12 @@ static int nxp_spifi_erase(struct spi_nor *nor, loff_t offs) > > static int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi) > { > - switch (spifi->nor.flash_read) { > - case SPI_NOR_NORMAL: > - case SPI_NOR_FAST: > + switch (spifi->nor.read_proto) { > + case SNOR_PROTO_1_1_1: > spifi->mcmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL; > break; > - case SPI_NOR_DUAL: > - case SPI_NOR_QUAD: > + case SNOR_PROTO_1_1_2: > + case SNOR_PROTO_1_1_4: > spifi->mcmd = SPIFI_CMD_FIELDFORM_QUAD_DUAL_DATA; > break; > default: > @@ -274,7 +273,11 @@ static void nxp_spifi_dummy_id_read(struct spi_nor *nor) > static int nxp_spifi_setup_flash(struct nxp_spifi *spifi, > struct device_node *np) > { > - enum read_mode flash_read; > + struct spi_nor_hwcaps hwcaps = { > + .mask = (SNOR_HWCAPS_READ | > + SNOR_HWCAPS_READ_FAST | > + SNOR_HWCAPS_PP), > + }; > u32 ctrl, property; > u16 mode = 0; > int ret; > @@ -308,13 +311,12 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi, > > if (mode & SPI_RX_DUAL) { > ctrl |= SPIFI_CTRL_DUAL; > - flash_read = SPI_NOR_DUAL; > + hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2; > } else if (mode & SPI_RX_QUAD) { > ctrl &= ~SPIFI_CTRL_DUAL; > - flash_read = SPI_NOR_QUAD; > + hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4; > } else { > ctrl |= SPIFI_CTRL_DUAL; > - flash_read = SPI_NOR_NORMAL; > } > > switch (mode & (SPI_CPHA | SPI_CPOL)) { > @@ -351,7 +353,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi, > */ > nxp_spifi_dummy_id_read(&spifi->nor); > > - ret = spi_nor_scan(&spifi->nor, NULL, flash_read); > + ret = spi_nor_scan(&spifi->nor, NULL, &hwcaps); > if (ret) { > dev_err(spifi->dev, "device scan failed\n"); > return ret; > diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c > index d3cb44b28490..cc443c6cbae8 100644 > --- a/drivers/mtd/spi-nor/spi-nor.c > +++ b/drivers/mtd/spi-nor/spi-nor.c > @@ -150,24 +150,6 @@ static int read_cr(struct spi_nor *nor) > } > > /* > - * Dummy Cycle calculation for different type of read. > - * It can be used to support more commands with > - * different dummy cycle requirements. > - */ > -static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor) > -{ > - switch (nor->flash_read) { > - case SPI_NOR_FAST: > - case SPI_NOR_DUAL: > - case SPI_NOR_QUAD: > - return 8; > - case SPI_NOR_NORMAL: > - return 0; > - } > - return 0; > -} > - > -/* > * Write status register 1 byte > * Returns negative if error occurred. > */ > @@ -221,6 +203,10 @@ static inline u8 spi_nor_convert_3to4_read(u8 opcode) > { SPINOR_OP_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B }, > { SPINOR_OP_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B }, > { SPINOR_OP_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B }, > + > + { SPINOR_OP_READ_1_1_1_DTR, SPINOR_OP_READ_1_1_1_DTR_4B }, > + { SPINOR_OP_READ_1_2_2_DTR, SPINOR_OP_READ_1_2_2_DTR_4B }, > + { SPINOR_OP_READ_1_4_4_DTR, SPINOR_OP_READ_1_4_4_DTR_4B }, > }; > > return spi_nor_convert_opcode(opcode, spi_nor_3to4_read, > @@ -1459,30 +1445,6 @@ static int spansion_quad_enable(struct spi_nor *nor) > return 0; > } > > -static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) > -{ > - int status; > - > - switch (JEDEC_MFR(info)) { > - case SNOR_MFR_MACRONIX: > - status = macronix_quad_enable(nor); > - if (status) { > - dev_err(nor->dev, "Macronix quad-read not enabled\n"); > - return -EINVAL; > - } > - return status; > - case SNOR_MFR_MICRON: > - return 0; > - default: > - status = spansion_quad_enable(nor); > - if (status) { > - dev_err(nor->dev, "Spansion quad-read not enabled\n"); > - return -EINVAL; > - } > - return status; > - } > -} > - > static int spi_nor_check(struct spi_nor *nor) > { > if (!nor->dev || !nor->read || !nor->write || > @@ -1535,8 +1497,322 @@ static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor) > return 0; > } > > -int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) > + > +struct spi_nor_read_command { > + u8 num_mode_clocks; > + u8 num_wait_states; > + u8 opcode; > + enum spi_nor_protocol proto; > +}; > + > +struct spi_nor_pp_command { > + u8 opcode; > + enum spi_nor_protocol proto; > +}; > + > +enum spi_nor_read_command_index { > + SNOR_CMD_READ, > + SNOR_CMD_READ_FAST, > + SNOR_CMD_READ_1_1_1_DTR, > + > + /* Dual SPI */ > + SNOR_CMD_READ_1_1_2, > + SNOR_CMD_READ_1_2_2, > + SNOR_CMD_READ_2_2_2, > + SNOR_CMD_READ_1_2_2_DTR, > + > + /* Quad SPI */ > + SNOR_CMD_READ_1_1_4, > + SNOR_CMD_READ_1_4_4, > + SNOR_CMD_READ_4_4_4, > + SNOR_CMD_READ_1_4_4_DTR, > + > + /* Octo SPI */ > + SNOR_CMD_READ_1_1_8, > + SNOR_CMD_READ_1_8_8, > + SNOR_CMD_READ_8_8_8, > + SNOR_CMD_READ_1_8_8_DTR, > + > + SNOR_CMD_READ_MAX > +}; > + > +enum spi_nor_pp_command_index { > + SNOR_CMD_PP, > + > + /* Quad SPI */ > + SNOR_CMD_PP_1_1_4, > + SNOR_CMD_PP_1_4_4, > + SNOR_CMD_PP_4_4_4, > + > + /* Octo SPI */ > + SNOR_CMD_PP_1_1_8, > + SNOR_CMD_PP_1_8_8, > + SNOR_CMD_PP_8_8_8, > + > + SNOR_CMD_PP_MAX > +}; > + > +struct spi_nor_flash_parameter { > + u64 size; > + u32 page_size; > + > + struct spi_nor_hwcaps hwcaps; > + struct spi_nor_read_command reads[SNOR_CMD_READ_MAX]; > + struct spi_nor_pp_command page_programs[SNOR_CMD_PP_MAX]; > + > + int (*quad_enable)(struct spi_nor *nor); > +}; > + > + > +static inline void > +spi_nor_set_read_settings(struct spi_nor_read_command *read, > + u8 num_mode_clocks, > + u8 num_wait_states, > + u8 opcode, > + enum spi_nor_protocol proto) > +{ > + read->num_mode_clocks = num_mode_clocks; > + read->num_wait_states = num_wait_states; > + read->opcode = opcode; > + read->proto = proto; > +} > + > +static inline void > +spi_nor_set_pp_settings(struct spi_nor_pp_command *pp, > + u8 opcode, > + enum spi_nor_protocol proto) > +{ > + pp->opcode = opcode; > + pp->proto = proto; > +} > + > +static int spi_nor_init_params(struct spi_nor *nor, > + const struct flash_info *info, > + struct spi_nor_flash_parameter *params) > +{ > + /* Set legacy flash parameters as default. */ > + memset(params, 0, sizeof(*params)); > + > + /* Set SPI NOR sizes. */ > + params->size = info->sector_size * info->n_sectors; > + params->page_size = info->page_size; > + > + /* (Fast) Read settings. */ > + params->hwcaps.mask |= SNOR_HWCAPS_READ; > + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ], > + 0, 0, SPINOR_OP_READ, > + SNOR_PROTO_1_1_1); > + if (!(info->flags & SPI_NOR_NO_FR)) { > + params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST; > + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_FAST], > + 0, 8, SPINOR_OP_READ_FAST, > + SNOR_PROTO_1_1_1); > + } > + if (info->flags & SPI_NOR_DUAL_READ) { > + params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2; > + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_1_2], > + 0, 8, SPINOR_OP_READ_1_1_2, > + SNOR_PROTO_1_1_2); > + } > + if (info->flags & SPI_NOR_QUAD_READ) { > + params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4; > + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_1_4], > + 0, 8, SPINOR_OP_READ_1_1_4, > + SNOR_PROTO_1_1_4); > + } > + > + /* Page Program settings. */ > + params->hwcaps.mask |= SNOR_HWCAPS_PP; > + spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP], > + SPINOR_OP_PP, SNOR_PROTO_1_1_1); > + > + /* Select the procedure to set the Quad Enable bit. */ > + if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD | > + SNOR_HWCAPS_PP_QUAD)) { > + switch (JEDEC_MFR(info)) { > + case SNOR_MFR_MACRONIX: > + params->quad_enable = macronix_quad_enable; > + break; > + > + case SNOR_MFR_MICRON: > + break; > + > + default: > + params->quad_enable = spansion_quad_enable; > + break; > + } > + } > + > + return 0; > +} > + > +static int spi_nor_hwcaps2cmd(u32 hwcaps) > { > + switch (hwcaps) { > + case SNOR_HWCAPS_READ: return SNOR_CMD_READ; > + case SNOR_HWCAPS_READ_FAST: return SNOR_CMD_READ_FAST; > + case SNOR_HWCAPS_READ_1_1_1_DTR: return SNOR_CMD_READ_1_1_1_DTR; > + case SNOR_HWCAPS_READ_1_1_2: return SNOR_CMD_READ_1_1_2; > + case SNOR_HWCAPS_READ_1_2_2: return SNOR_CMD_READ_1_2_2; > + case SNOR_HWCAPS_READ_2_2_2: return SNOR_CMD_READ_2_2_2; > + case SNOR_HWCAPS_READ_1_2_2_DTR: return SNOR_CMD_READ_1_2_2_DTR; > + case SNOR_HWCAPS_READ_1_1_4: return SNOR_CMD_READ_1_1_4; > + case SNOR_HWCAPS_READ_1_4_4: return SNOR_CMD_READ_1_4_4; > + case SNOR_HWCAPS_READ_4_4_4: return SNOR_CMD_READ_4_4_4; > + case SNOR_HWCAPS_READ_1_4_4_DTR: return SNOR_CMD_READ_1_4_4_DTR; > + case SNOR_HWCAPS_READ_1_1_8: return SNOR_CMD_READ_1_1_8; > + case SNOR_HWCAPS_READ_1_8_8: return SNOR_CMD_READ_1_8_8; > + case SNOR_HWCAPS_READ_8_8_8: return SNOR_CMD_READ_8_8_8; > + case SNOR_HWCAPS_READ_1_8_8_DTR: return SNOR_CMD_READ_1_8_8_DTR; > + > + case SNOR_HWCAPS_PP: return SNOR_CMD_PP; > + case SNOR_HWCAPS_PP_1_1_4: return SNOR_CMD_PP_1_1_4; > + case SNOR_HWCAPS_PP_1_4_4: return SNOR_CMD_PP_1_4_4; > + case SNOR_HWCAPS_PP_4_4_4: return SNOR_CMD_PP_4_4_4; > + case SNOR_HWCAPS_PP_1_1_8: return SNOR_CMD_PP_1_1_8; > + case SNOR_HWCAPS_PP_1_8_8: return SNOR_CMD_PP_1_8_8; > + case SNOR_HWCAPS_PP_8_8_8: return SNOR_CMD_PP_8_8_8; > + } > + > + return -EINVAL; > +} > + > +static int spi_nor_select_read(struct spi_nor *nor, > + const struct spi_nor_flash_parameter *params, > + u32 shared_hwcaps) > +{ > + int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1; > + const struct spi_nor_read_command *read; > + > + if (best_match < 0) > + return -EINVAL; > + > + cmd = spi_nor_hwcaps2cmd(BIT(best_match)); > + if (cmd < 0) > + return -EINVAL; > + > + read = ¶ms->reads[cmd]; > + nor->read_opcode = read->opcode; > + nor->read_proto = read->proto; > + > + /* > + * In the spi-nor framework, we don't need to make the difference > + * between mode clock cycles and wait state clock cycles. > + * Indeed, the value of the mode clock cycles is used by a QSPI > + * flash memory to know whether it should enter or leave its 0-4-4 > + * (Continuous Read / XIP) mode. > + * eXecution In Place is out of the scope of the mtd sub-system. > + * Hence we choose to merge both mode and wait state clock cycles > + * into the so called dummy clock cycles. > + */ > + nor->read_dummy = read->num_mode_clocks + read->num_wait_states; > + return 0; > +} > + > +static int spi_nor_select_pp(struct spi_nor *nor, > + const struct spi_nor_flash_parameter *params, > + u32 shared_hwcaps) > +{ > + int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1; > + const struct spi_nor_pp_command *pp; > + > + if (best_match < 0) > + return -EINVAL; > + > + cmd = spi_nor_hwcaps2cmd(BIT(best_match)); > + if (cmd < 0) > + return -EINVAL; > + > + pp = ¶ms->page_programs[cmd]; > + nor->program_opcode = pp->opcode; > + nor->write_proto = pp->proto; > + return 0; > +} > + > +static int spi_nor_select_erase(struct spi_nor *nor, > + const struct flash_info *info) > +{ > + struct mtd_info *mtd = &nor->mtd; > + > +#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS > + /* prefer "small sector" erase if possible */ > + if (info->flags & SECT_4K) { > + nor->erase_opcode = SPINOR_OP_BE_4K; > + mtd->erasesize = 4096; > + } else if (info->flags & SECT_4K_PMC) { > + nor->erase_opcode = SPINOR_OP_BE_4K_PMC; > + mtd->erasesize = 4096; > + } else > +#endif > + { > + nor->erase_opcode = SPINOR_OP_SE; > + mtd->erasesize = info->sector_size; > + } > + return 0; > +} > + > +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) > +{ > + u32 ignored_mask, 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 protocol classes N-N-N 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 protocol classes N-N-N are not supported yet.\n"); > + shared_mask &= ~ignored_mask; > + } > + > + /* Select the (Fast) Read command. */ > + err = spi_nor_select_read(nor, params, shared_mask); > + if (err) { > + dev_err(nor->dev, "invalid (fast) read\n"); > + return err; > + } > + > + /* Select the Page Program command. */ > + err = spi_nor_select_pp(nor, params, shared_mask); > + if (err) { > + dev_err(nor->dev, "invalid page program\n"); > + return err; > + } > + > + /* Select the Sector Erase command. */ > + err = spi_nor_select_erase(nor, info); > + if (err) { > + dev_err(nor->dev, "invalid sector/block erase\n"); > + return err; > + } > + > + /* Enable Quad I/O if needed. */ > + enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 || > + spi_nor_get_protocol_width(nor->write_proto) == 4); > + if (enable_quad_io && params->quad_enable) > + nor->flash_quad_enable = params->quad_enable; > + else > + nor->flash_quad_enable = NULL; > + > + return 0; > +} > + > +int spi_nor_scan(struct spi_nor *nor, const char *name, > + const struct spi_nor_hwcaps *hwcaps) > +{ > + struct spi_nor_flash_parameter params; > const struct flash_info *info = NULL; > struct device *dev = nor->dev; > struct mtd_info *mtd = &nor->mtd; > @@ -1548,6 +1824,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) > if (ret) > return ret; > > + /* Reset SPI protocol for all commands */ > + nor->reg_proto = SNOR_PROTO_1_1_1; > + nor->read_proto = SNOR_PROTO_1_1_1; > + nor->write_proto = SNOR_PROTO_1_1_1; > + > if (name) > info = spi_nor_match_id(name); > /* Try to auto-detect if chip name wasn't specified or not found */ > @@ -1580,6 +1861,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) > } > } > > + /* Parse the Serial Flash Discoverable Parameters table */ > + ret = spi_nor_init_params(nor, info, ¶ms); > + if (ret) > + return ret; > + > mutex_init(&nor->lock); > > /* > @@ -1610,7 +1896,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) > mtd->type = MTD_NORFLASH; > mtd->writesize = 1; > mtd->flags = MTD_CAP_NORFLASH; > - mtd->size = info->sector_size * info->n_sectors; > + mtd->size = params.size; > mtd->_erase = spi_nor_erase; > mtd->_read = spi_nor_read; > > @@ -1641,76 +1927,47 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) > if (info->flags & NO_CHIP_ERASE) > nor->flags |= SNOR_F_NO_OP_CHIP_ERASE; > > -#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS > - /* prefer "small sector" erase if possible */ > - if (info->flags & SECT_4K) { > - nor->erase_opcode = SPINOR_OP_BE_4K; > - mtd->erasesize = 4096; > - } else if (info->flags & SECT_4K_PMC) { > - nor->erase_opcode = SPINOR_OP_BE_4K_PMC; > - mtd->erasesize = 4096; > - } else > -#endif > - { > - nor->erase_opcode = SPINOR_OP_SE; > - mtd->erasesize = info->sector_size; > - } > - > if (info->flags & SPI_NOR_NO_ERASE) > mtd->flags |= MTD_NO_ERASE; > > mtd->dev.parent = dev; > - nor->page_size = info->page_size; > + nor->page_size = params.page_size; > mtd->writebufsize = nor->page_size; > > if (np) { > /* If we were instantiated by DT, use it */ > if (of_property_read_bool(np, "m25p,fast-read")) > - nor->flash_read = SPI_NOR_FAST; > + params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST; > else > - nor->flash_read = SPI_NOR_NORMAL; > + params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST; > } else { > /* If we weren't instantiated by DT, default to fast-read */ > - nor->flash_read = SPI_NOR_FAST; > + params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST; > } > > /* Some devices cannot do fast-read, no matter what DT tells us */ > if (info->flags & SPI_NOR_NO_FR) > - nor->flash_read = SPI_NOR_NORMAL; > + params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST; > + > + /* > + * Configure the SPI memory: > + * - select op codes for (Fast) Read, Page Program and Sector Erase. > + * - set the number of dummy cycles (mode cycles + wait states). > + * - 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); > + if (ret) > + return ret; > > - /* Quad/Dual-read mode takes precedence over fast/normal */ > - if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) { > - ret = set_quad_mode(nor, info); > + if (nor->flash_quad_enable) { > + ret = nor->flash_quad_enable(nor); > if (ret) { > dev_err(dev, "quad mode not supported\n"); > return ret; > } > - nor->flash_read = SPI_NOR_QUAD; > - } else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) { > - nor->flash_read = SPI_NOR_DUAL; > } > > - /* Default commands */ > - switch (nor->flash_read) { > - case SPI_NOR_QUAD: > - nor->read_opcode = SPINOR_OP_READ_1_1_4; > - break; > - case SPI_NOR_DUAL: > - nor->read_opcode = SPINOR_OP_READ_1_1_2; > - break; > - case SPI_NOR_FAST: > - nor->read_opcode = SPINOR_OP_READ_FAST; > - break; > - case SPI_NOR_NORMAL: > - nor->read_opcode = SPINOR_OP_READ; > - break; > - default: > - dev_err(dev, "No Read opcode defined\n"); > - return -EINVAL; > - } > - > - nor->program_opcode = SPINOR_OP_PP; > - > if (info->addr_width) > nor->addr_width = info->addr_width; > else if (mtd->size > 0x1000000) { > @@ -1731,8 +1988,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) > return -EINVAL; > } > > - nor->read_dummy = spi_nor_read_dummy_cycles(nor); > - > if (info->flags & SPI_S3AN) { > ret = s3an_nor_scan(info, nor); > if (ret) > diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h > index f2a718030476..732ee6cd5330 100644 > --- a/include/linux/mtd/spi-nor.h > +++ b/include/linux/mtd/spi-nor.h > @@ -73,6 +73,15 @@ > #define SPINOR_OP_BE_32K_4B 0x5c /* Erase 32KiB block */ > #define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */ > > +/* Double Transfer Rate opcodes - defined in JEDEC JESD216B. */ > +#define SPINOR_OP_READ_1_1_1_DTR 0x0d > +#define SPINOR_OP_READ_1_2_2_DTR 0xbd > +#define SPINOR_OP_READ_1_4_4_DTR 0xed > + > +#define SPINOR_OP_READ_1_1_1_DTR_4B 0x0e > +#define SPINOR_OP_READ_1_2_2_DTR_4B 0xbe > +#define SPINOR_OP_READ_1_4_4_DTR_4B 0xee > + > /* Used for SST flashes only. */ > #define SPINOR_OP_BP 0x02 /* Byte program */ > #define SPINOR_OP_WRDI 0x04 /* Write disable */ > @@ -119,13 +128,75 @@ > /* Configuration Register bits. */ > #define CR_QUAD_EN_SPAN BIT(1) /* Spansion Quad I/O */ > > -enum read_mode { > - SPI_NOR_NORMAL = 0, > - SPI_NOR_FAST, > - SPI_NOR_DUAL, > - SPI_NOR_QUAD, > + > +/* Supported SPI protocols */ > +#define SNOR_PROTO_WIDTH_MASK GENMASK(7, 0) > + > +#define SNOR_PROTO_CLASS_MASK GENMASK(9, 8) > +#define SNOR_PROTO_CLASS_1_1_N (0x0u << 8) > +#define SNOR_PROTO_CLASS_1_N_N (0x1u << 8) > +#define SNOR_PROTO_CLASS_N_N_N (0x2u << 8) > + > +#define SNOR_PROTO_IS_DTR BIT(10) /* Double Transfer Rate */ > + > +#define SNOR_PROTO_STR(_pclass, _pwidth) \ > + ((_pclass) | (_pwidth)) > +#define SNOR_PROTO_DTR(_pclass, _pwidth) \ > + (SNOR_PROTO_IS_DTR | (_pclass) | (_pwidth)) > + > +enum spi_nor_protocol { > + SNOR_PROTO_1_1_1 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 1), > + SNOR_PROTO_1_1_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 2), > + SNOR_PROTO_1_1_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 4), > + SNOR_PROTO_1_1_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 8), > + SNOR_PROTO_1_2_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 2), > + SNOR_PROTO_1_4_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 4), > + SNOR_PROTO_1_8_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 8), > + SNOR_PROTO_2_2_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 2), > + SNOR_PROTO_4_4_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 4), > + SNOR_PROTO_8_8_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 8), > + > + SNOR_PROTO_1_1_1_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_1_N, 1), > + SNOR_PROTO_1_2_2_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 2), > + SNOR_PROTO_1_4_4_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 4), > + SNOR_PROTO_1_8_8_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 8), > }; > > +static inline bool spi_nor_protocol_is_dtr(enum spi_nor_protocol proto) > +{ > + return (proto & SNOR_PROTO_IS_DTR) == SNOR_PROTO_IS_DTR; > +} > + > +static inline u32 spi_nor_get_protocol_class(enum spi_nor_protocol proto) > +{ > + return proto & SNOR_PROTO_CLASS_MASK; > +} > + > +static inline u8 spi_nor_get_protocol_width(enum spi_nor_protocol proto) > +{ > + return proto & SNOR_PROTO_WIDTH_MASK; > +} > + > +static inline u8 spi_nor_get_protocol_inst_width(enum spi_nor_protocol proto) > +{ > + return (spi_nor_get_protocol_class(proto) == SNOR_PROTO_CLASS_N_N_N) ? > + spi_nor_get_protocol_width(proto) : > + 1u; > +} > + > +static inline u8 spi_nor_get_protocol_addr_width(enum spi_nor_protocol proto) > +{ > + return (spi_nor_get_protocol_class(proto) != SNOR_PROTO_CLASS_1_1_N) ? > + spi_nor_get_protocol_width(proto) : > + 1u; > +} > + > +static inline u8 spi_nor_get_protocol_data_width(enum spi_nor_protocol proto) > +{ > + return spi_nor_get_protocol_width(proto); > +} > + > + > #define SPI_NOR_MAX_CMD_SIZE 8 > enum spi_nor_ops { > SPI_NOR_OPS_READ = 0, > @@ -154,9 +225,11 @@ enum spi_nor_option_flags { > * @read_opcode: the read opcode > * @read_dummy: the dummy needed by the read operation > * @program_opcode: the program opcode > - * @flash_read: the mode of the read > * @sst_write_second: used by the SST write operation > * @flags: flag options for the current SPI-NOR (SNOR_F_*) > + * @read_proto: the SPI protocol for read operations > + * @write_proto: the SPI protocol for write operations > + * @reg_proto the SPI protocol for read_reg/write_reg/erase operations > * @cmd_buf: used by the write_reg > * @prepare: [OPTIONAL] do some preparations for the > * read/write/erase/lock/unlock operations > @@ -173,6 +246,7 @@ enum spi_nor_option_flags { > * @flash_unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR > * @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is > * completely locked > + * @flash_quad_enable: [FLASH-SPECIFIC] set the Quad Enable bit of the SPI NOR > * @priv: the private data > */ > struct spi_nor { > @@ -185,7 +259,9 @@ struct spi_nor { > u8 read_opcode; > u8 read_dummy; > u8 program_opcode; > - enum read_mode flash_read; > + enum spi_nor_protocol read_proto; > + enum spi_nor_protocol write_proto; > + enum spi_nor_protocol reg_proto; > bool sst_write_second; > u32 flags; > u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE]; > @@ -204,6 +280,7 @@ struct spi_nor { > int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len); > int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len); > int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len); > + int (*flash_quad_enable)(struct spi_nor *nor); > > void *priv; > }; > @@ -219,11 +296,73 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor) > return mtd_get_of_node(&nor->mtd); > } > > + > +/** > + * struct spi_nor_hwcaps - Structure for describing the hardware capabilies > + * supported by the SPI controller (bus master). > + * @mask: the bitmask listing all the supported hw capabilies > + */ > +struct spi_nor_hwcaps { > + u32 mask; > +}; > + > +/* > + *(Fast) Read capabilities. > + * MUST be ordered by priority: the higher bit position, the higher priority. > + * As a matter of performances, it is relevant to use Octo SPI protocols first, > + * then Quad SPI protocols before Dual SPI protocols, Fast Read and lastly > + * (Slow) Read. > + */ > +#define SNOR_HWCAPS_READ_MASK GENMASK(15, 0) > +#define SNOR_HWCAPS_READ BIT(0) > +#define SNOR_HWCAPS_READ_FAST BIT(1) > +#define SNOR_HWCAPS_READ_1_1_1_DTR BIT(2) > + > +#define SNOR_HWCAPS_READ_DUAL GENMASK(7, 4) > +#define SNOR_HWCAPS_READ_1_1_2 BIT(4) > +#define SNOR_HWCAPS_READ_1_2_2 BIT(5) > +#define SNOR_HWCAPS_READ_2_2_2 BIT(6) > +#define SNOR_HWCAPS_READ_1_2_2_DTR BIT(7) > + > +#define SNOR_HWCAPS_READ_QUAD GENMASK(11, 8) > +#define SNOR_HWCAPS_READ_1_1_4 BIT(8) > +#define SNOR_HWCAPS_READ_1_4_4 BIT(9) > +#define SNOR_HWCAPS_READ_4_4_4 BIT(10) > +#define SNOR_HWCAPS_READ_1_4_4_DTR BIT(11) > + > +#define SNOR_HWCPAS_READ_OCTO GENMASK(15, 12) > +#define SNOR_HWCAPS_READ_1_1_8 BIT(12) > +#define SNOR_HWCAPS_READ_1_8_8 BIT(13) > +#define SNOR_HWCAPS_READ_8_8_8 BIT(14) > +#define SNOR_HWCAPS_READ_1_8_8_DTR BIT(15) > + > +/* > + * Page Program capabilities. > + * MUST be ordered by priority: the higher bit position, the higher priority. > + * Like (Fast) Read capabilities, Octo/Quad SPI protocols are preferred to the > + * legacy SPI 1-1-1 protocol. > + * Note that Dual Page Programs are not supported because there is no existing > + * JEDEC/SFDP standard to define them. Also at this moment no SPI flash memory > + * implements such commands. > + */ > +#define SNOR_HWCAPS_PP_MASK GENMASK(22, 16) > +#define SNOR_HWCAPS_PP BIT(16) > + > +#define SNOR_HWCAPS_PP_QUAD GENMASK(19, 17) > +#define SNOR_HWCAPS_PP_1_1_4 BIT(17) > +#define SNOR_HWCAPS_PP_1_4_4 BIT(18) > +#define SNOR_HWCAPS_PP_4_4_4 BIT(19) > + > +#define SNOR_HWCAPS_PP_OCTO GENMASK(22, 20) > +#define SNOR_HWCAPS_PP_1_1_8 BIT(20) > +#define SNOR_HWCAPS_PP_1_8_8 BIT(21) > +#define SNOR_HWCAPS_PP_8_8_8 BIT(22) > + > /** > * spi_nor_scan() - scan the SPI NOR > * @nor: the spi_nor structure > * @name: the chip type name > - * @mode: the read mode supported by the driver > + * @hwcaps: the hardware capabilities supported by the controller driver > * > * The drivers can use this fuction to scan the SPI NOR. > * In the scanning, it will try to get all the necessary information to > @@ -233,6 +372,7 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor) > * > * Return: 0 for success, others for failure. > */ > -int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode); > +int spi_nor_scan(struct spi_nor *nor, const char *name, > + const struct spi_nor_hwcaps *hwcaps); > > #endif >