Re: [PATCH v5 2/3] mtd: spi-nor: Move m25p80 code in spi-nor.c

From: Vignesh Raghavendra
Date: Fri Aug 09 2019 - 07:47:15 EST


Hi Tomer,

On 08/08/19 3:35 PM, Tomer Maimon wrote:
>
> Hi Vignesh,
>
> Thanks for working on the spi-nor patches.
>
> I have tested your latest patches with NPCM FIU driver on two different
> Flashes:
>
> 1. Winbond w25q256 - 32MB
> 2. Macronix mx25l3205d- 4MB
>
> Sorry I going to a vacation soon (on tight schedule) so I haven't had a
> time to do full stress test on the spi-nor driver (only erase,write, read).
>

Thanks for testing!

> There is an issue with erase sector function, the address should send as
> a data. (see the modification below).
>

As Boris pointed out, Erase sequence does not have data phase, spi-mem
controller driver should handle SPI_MEM_NO_DATA flag accordingly.
I guess you have fixed this in v2 of NPCM FIU controller driver support
seriesâ and this is no longer an issue right?

> Are you intend to add a direct spi-mem support to the spi-nor driver?

May not be for this merge window, moreover I don't have a spi-mem driver
to exercise this dirmap APIs (I am in the process of converting one).
But, feel free to submit a patch if you have one.

Please keep in mind the of changes done by Tudor here:
https://patchwork.ozlabs.org/project/linux-mtd/list/?series=122420
https://patchwork.ozlabs.org/project/linux-mtd/list/?series=122423
https://patchwork.ozlabs.org/project/linux-mtd/list/?series=122424

There may be potential conflicts and rebasing needed.

Regards
Vignesh

>
> On Tue, 6 Aug 2019 at 08:10, Vignesh Raghavendra <vigneshr@xxxxxx
> <mailto:vigneshr@xxxxxx>> wrote:
>
> From: Boris Brezillon <boris.brezillon@xxxxxxxxxxx
> <mailto:boris.brezillon@xxxxxxxxxxx>>
>
> The m25p80 driver is actually a generic wrapper around the spi-mem
> layer. Not only the driver name is misleading, but we'd expect such a
> common logic to be directly available in the core. Another reason for
> moving this code is that SPI NOR controller drivers should
> progressively be replaced by SPI controller drivers implementing the
> spi_mem_ops interface, and when the conversion is done, we should have
> a single spi-nor driver directly interfacing with the spi-mem layer.
>
> While moving the code we also fix a longstanding issue when
> non-DMA-able buffers are passed by the MTD layer.
>
> Signed-off-by: Boris Brezillon <boris.brezillon@xxxxxxxxxxx
> <mailto:boris.brezillon@xxxxxxxxxxx>>
> Signed-off-by: Vignesh Raghavendra <vigneshr@xxxxxx
> <mailto:vigneshr@xxxxxx>>
> ---
> v5:
> Fix comments by Tudor in spi_nor_spimem_read_data() and
> spi_nor_read_sr2()
>
> v4:
> Fix a bug in write_sr()
> Update comments around allocation of bounce buffer
>
> v3:
> Simplify register read/write by dropping spi_nor_exec_op() and using
> spi_mem_exec_op() directly
> Modify spi_nor_spimem_xfer_data() to drop "enum spi_nor_protocol proto"
> Fix misc coding style comments by Tudor
>
> v2:
> Add docs for new functions added
> Add spi_nor_ prefix to new functions
> Incorporate Andrey's patches https://lkml.org/lkml/2019/4/1/32
> to avoid looping spi_nor_spimem_* APIs
>
> Âdrivers/mtd/devices/Kconfig Â| 18 -
> Âdrivers/mtd/devices/Makefile | Â1 -
> Âdrivers/mtd/devices/m25p80.c | 347 -------------------
> Âdrivers/mtd/spi-nor/Kconfig Â| Â2 +
> Âdrivers/mtd/spi-nor/spi-nor.c | 628 ++++++++++++++++++++++++++++++++--
> Âinclude/linux/mtd/spi-nor.h Â| Â3 +
> Â6 files changed, 605 insertions(+), 394 deletions(-)
> Âdelete mode 100644 drivers/mtd/devices/m25p80.c
>
> diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
> index 49abbc52457d..f96287c4b789 100644
> --- a/drivers/mtd/devices/Kconfig
> +++ b/drivers/mtd/devices/Kconfig
> @@ -79,24 +79,6 @@ config MTD_DATAFLASH_OTP
>      other key product data. The second half is programmed with a
> Â Â Â Â Â unique-to-each-chip bit pattern at the factory.
>
> -config MTD_M25P80
> -Â Â Â Âtristate "Support most SPI Flash chips (AT26DF, M25P, W25X,
> ...)"
> -Â Â Â Âdepends on SPI_MASTER && MTD_SPI_NOR
> -Â Â Â Âselect SPI_MEM
> -Â Â Â Âhelp
> -Â Â Â Â ÂThis enables access to most modern SPI flash chips, used for
> -    Âprogram and data storage. ÂSeries supported include Atmel
> AT26DF,
> -Â Â Â Â ÂSpansion S25SL, SST 25VF, ST M25P, and Winbond W25X.Â
> Other chips
> -    Âare supported as well. See the driver source for the
> current list,
> -Â Â Â Â Âor to add other chips.
> -
> -Â Â Â Â ÂNote that the original DataFlash chips (AT45 series, not
> AT26DF),
> -Â Â Â Â Âneed an entirely different driver.
> -
> -Â Â Â Â ÂSet up your spi devices with the right board-specific
> platform data,
> -Â Â Â Â Âif you want to specify device partitioning or to use a
> device which
> -Â Â Â Â Âdoesn't support the JEDEC ID instruction.
> -
> Âconfig MTD_MCHP23K256
> Â Â Â Â tristate "Microchip 23K256 SRAM"
> Â Â Â Â depends on SPI_MASTER
> diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
> index 94895eab3066..991c8d12c016 100644
> --- a/drivers/mtd/devices/Makefile
> +++ b/drivers/mtd/devices/Makefile
> @@ -12,7 +12,6 @@ obj-$(CONFIG_MTD_MTDRAM)Â Â Â += mtdram.o
> Âobj-$(CONFIG_MTD_LART)Â Â Â Â Â+= lart.o
> Âobj-$(CONFIG_MTD_BLOCK2MTD)Â Â += block2mtd.o
> Âobj-$(CONFIG_MTD_DATAFLASH)Â Â += mtd_dataflash.o
> -obj-$(CONFIG_MTD_M25P80)Â Â Â Â+= m25p80.o
> Âobj-$(CONFIG_MTD_MCHP23K256)Â Â+= mchp23k256.o
> Âobj-$(CONFIG_MTD_SPEAR_SMI)Â Â += spear_smi.o
> Âobj-$(CONFIG_MTD_SST25L)Â Â Â Â+= sst25l.o
> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
> deleted file mode 100644
> index c50888670250..000000000000
> --- a/drivers/mtd/devices/m25p80.c
> +++ /dev/null
> @@ -1,347 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0-only
> -/*
> - * MTD SPI driver for ST M25Pxx (and similar) serial flash chips
> - *
> - * Author: Mike Lavender, mike@xxxxxxxxxxxxxxxxx
> <mailto:mike@xxxxxxxxxxxxxxxxx>
> - *
> - * Copyright (c) 2005, Intec Automation Inc.
> - *
> - * Some parts are based on lart.c by Abraham Van Der Merwe
> - *
> - * Cleaned up and generalized based on mtd_dataflash.c
> - */
> -
> -#include <linux/err.h>
> -#include <linux/errno.h>
> -#include <linux/module.h>
> -#include <linux/device.h>
> -
> -#include <linux/mtd/mtd.h>
> -#include <linux/mtd/partitions.h>
> -
> -#include <linux/spi/spi.h>
> -#include <linux/spi/spi-mem.h>
> -#include <linux/spi/flash.h>
> -#include <linux/mtd/spi-nor.h>
> -
> -struct m25p {
> -   Âstruct spi_mem     *spimem;
> -   Âstruct spi_nor     spi_nor;
> -};
> -
> -static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val,
> int len)
> -{
> -Â Â Â Âstruct m25p *flash = nor->priv;
> -Â Â Â Âstruct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(code, 1),
> -Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP_NO_ADDR,
> -Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP_NO_DUMMY,
> -Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP_DATA_IN(len,
> NULL, 1));
> -Â Â Â Âvoid *scratchbuf;
> -Â Â Â Âint ret;
> -
> -Â Â Â Âscratchbuf = kmalloc(len, GFP_KERNEL);
> -Â Â Â Âif (!scratchbuf)
> -Â Â Â Â Â Â Â Âreturn -ENOMEM;
> -
> -Â Â Â Âop.data.buf.in <http://op.data.buf.in> = scratchbuf;
> -Â Â Â Âret = spi_mem_exec_op(flash->spimem, &op);
> -Â Â Â Âif (ret < 0)
> -Â Â Â Â Â Â Â Âdev_err(&flash->spimem->spi->dev, "error %d reading
> %x\n", ret,
> -Â Â Â Â Â Â Â Â Â Â Â Âcode);
> -Â Â Â Âelse
> -Â Â Â Â Â Â Â Âmemcpy(val, scratchbuf, len);
> -
> -Â Â Â Âkfree(scratchbuf);
> -
> -Â Â Â Âreturn ret;
> -}
> -
> -static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8
> *buf, int len)
> -{
> -Â Â Â Âstruct m25p *flash = nor->priv;
> -Â Â Â Âstruct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 1),
> -Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP_NO_ADDR,
> -Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP_NO_DUMMY,
> -Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP_DATA_OUT(len,
> NULL, 1));
> -Â Â Â Âvoid *scratchbuf;
> -Â Â Â Âint ret;
> -
> -Â Â Â Âscratchbuf = kmemdup(buf, len, GFP_KERNEL);
> -Â Â Â Âif (!scratchbuf)
> -Â Â Â Â Â Â Â Âreturn -ENOMEM;
> -
> -Â Â Â Âop.data.buf.out = scratchbuf;
> -Â Â Â Âret = spi_mem_exec_op(flash->spimem, &op);
> -Â Â Â Âkfree(scratchbuf);
> -
> -Â Â Â Âreturn ret;
> -}
> -
> -static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
> -Â Â Â Â Â Â Â Â Â Â Â Â Â Âconst u_char *buf)
> -{
> -Â Â Â Âstruct m25p *flash = nor->priv;
> -Â Â Â Âstruct spi_mem_op op =
> -Â Â Â Â Â Â Â Â Â Â Â
> ÂSPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
> -Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_ADDR(nor->addr_width,
> to, 1),
> -Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DUMMY,
> -Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_DATA_OUT(len, buf, 1));
> -Â Â Â Âint ret;
> -
> -Â Â Â Â/* get transfer protocols. */
> -Â Â Â Âop.cmd.buswidth =
> spi_nor_get_protocol_inst_nbits(nor->write_proto);
> -Â Â Â Âop.addr.buswidth =
> spi_nor_get_protocol_addr_nbits(nor->write_proto);
> -Â Â Â Âop.data.buswidth =
> spi_nor_get_protocol_data_nbits(nor->write_proto);
> -
> -Â Â Â Âif (nor->program_opcode == SPINOR_OP_AAI_WP &&
> nor->sst_write_second)
> -Â Â Â Â Â Â Â Âop.addr.nbytes = 0;
> -
> -Â Â Â Âret = spi_mem_adjust_op_size(flash->spimem, &op);
> -Â Â Â Âif (ret)
> -Â Â Â Â Â Â Â Âreturn ret;
> -Â Â Â Âop.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes;
> -
> -Â Â Â Âret = spi_mem_exec_op(flash->spimem, &op);
> -Â Â Â Âif (ret)
> -Â Â Â Â Â Â Â Âreturn ret;
> -
> -Â Â Â Âreturn op.data.nbytes;
> -}
> -
> -/*
> - * Read an address range from the nor chip. The address range
> - * may be any size provided it is within the physical boundaries.
> - */
> -static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t
> len,
> -Â Â Â Â Â Â Â Â Â Â Â Â Â u_char *buf)
> -{
> -Â Â Â Âstruct m25p *flash = nor->priv;
> -Â Â Â Âstruct spi_mem_op op =
> -Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
> -Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_ADDR(nor->addr_width,
> from, 1),
> -Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
> -Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_DATA_IN(len, buf, 1));
> -Â Â Â Âsize_t remaining = len;
> -Â Â Â Âint ret;
> -
> -Â Â Â Â/* get transfer protocols. */
> -Â Â Â Âop.cmd.buswidth =
> spi_nor_get_protocol_inst_nbits(nor->read_proto);
> -Â Â Â Âop.addr.buswidth =
> spi_nor_get_protocol_addr_nbits(nor->read_proto);
> -Â Â Â Âop.dummy.buswidth = op.addr.buswidth;
> -Â Â Â Âop.data.buswidth =
> spi_nor_get_protocol_data_nbits(nor->read_proto);
> -
> -Â Â Â Â/* convert the dummy cycles to the number of bytes */
> -Â Â Â Âop.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
> -
> -Â Â Â Âwhile (remaining) {
> -Â Â Â Â Â Â Â Âop.data.nbytes = remaining < UINT_MAX ? remaining :
> UINT_MAX;
> -Â Â Â Â Â Â Â Âret = spi_mem_adjust_op_size(flash->spimem, &op);
> -Â Â Â Â Â Â Â Âif (ret)
> -Â Â Â Â Â Â Â Â Â Â Â Âreturn ret;
> -
> -Â Â Â Â Â Â Â Âret = spi_mem_exec_op(flash->spimem, &op);
> -Â Â Â Â Â Â Â Âif (ret)
> -Â Â Â Â Â Â Â Â Â Â Â Âreturn ret;
> -
> -Â Â Â Â Â Â Â Âop.addr.val += op.data.nbytes;
> -Â Â Â Â Â Â Â Âremaining -= op.data.nbytes;
> -Â Â Â Â Â Â Â Âop.data.buf.in <http://op.data.buf.in> +=
> op.data.nbytes;
> -Â Â Â Â}
> -
> -Â Â Â Âreturn len;
> -}
> -
> -/*
> - * board specific setup should have ensured the SPI clock used here
> - * matches what the READ command supports, at least until this driver
> - * understands FAST_READ (for clocks over 25 MHz).
> - */
> -static int m25p_probe(struct spi_mem *spimem)
> -{
> -Â Â Â Âstruct spi_device *spi = spimem->spi;
> -   Âstruct flash_platform_data   *data;
> -Â Â Â Âstruct m25p *flash;
> -Â Â Â Âstruct spi_nor *nor;
> -Â Â Â Âstruct spi_nor_hwcaps hwcaps = {
> -Â Â Â Â Â Â Â Â.mask = SNOR_HWCAPS_READ |
> -Â Â Â Â Â Â Â Â Â Â Â ÂSNOR_HWCAPS_READ_FAST |
> -Â Â Â Â Â Â Â Â Â Â Â ÂSNOR_HWCAPS_PP,
> -Â Â Â Â};
> -Â Â Â Âchar *flash_name;
> -Â Â Â Âint ret;
> -
> -Â Â Â Âdata = dev_get_platdata(&spimem->spi->dev);
> -
> -Â Â Â Âflash = devm_kzalloc(&spimem->spi->dev, sizeof(*flash),
> GFP_KERNEL);
> -Â Â Â Âif (!flash)
> -Â Â Â Â Â Â Â Âreturn -ENOMEM;
> -
> -Â Â Â Ânor = &flash->spi_nor;
> -
> -Â Â Â Â/* install the hooks */
> -Â Â Â Ânor->read = m25p80_read;
> -Â Â Â Ânor->write = m25p80_write;
> -Â Â Â Ânor->write_reg = m25p80_write_reg;
> -Â Â Â Ânor->read_reg = m25p80_read_reg;
> -
> -Â Â Â Ânor->dev = &spimem->spi->dev;
> -Â Â Â Âspi_nor_set_flash_node(nor, spi->dev.of_node);
> -Â Â Â Ânor->priv = flash;
> -
> -Â Â Â Âspi_mem_set_drvdata(spimem, flash);
> -Â Â Â Âflash->spimem = spimem;
> -
> -Â Â Â Â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;
> -Â Â Â Â}
> -
> -Â Â Â Âif (data && data->name)
> -Â Â Â Â Â Â Â Ânor->mtd.name <http://mtd.name> = data->name;
> -
> -Â Â Â Âif (!nor->mtd.name <http://mtd.name>)
> -Â Â Â Â Â Â Â Ânor->mtd.name <http://mtd.name> =
> spi_mem_get_name(spimem);
> -
> -Â Â Â Â/* For some (historical?) reason many platforms provide two
> different
> -Â Â Â Â * names in flash_platform_data: "name" and "type". Quite
> often name is
> -Â Â Â Â * set to "m25p80" and then "type" provides a real chip name.
> -Â Â Â Â * If that's the case, respect "type" and ignore a "name".
> -Â Â Â Â */
> -Â Â Â Âif (data && data->type)
> -Â Â Â Â Â Â Â Âflash_name = data->type;
> -Â Â Â Âelse if (!strcmp(spi->modalias, "spi-nor"))
> -Â Â Â Â Â Â Â Âflash_name = NULL; /* auto-detect */
> -Â Â Â Âelse
> -Â Â Â Â Â Â Â Âflash_name = spi->modalias;
> -
> -Â Â Â Âret = spi_nor_scan(nor, flash_name, &hwcaps);
> -Â Â Â Âif (ret)
> -Â Â Â Â Â Â Â Âreturn ret;
> -
> -Â Â Â Âreturn mtd_device_register(&nor->mtd, data ? data->parts : NULL,
> -Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â data ? data->nr_parts : 0);
> -}
> -
> -
> -static int m25p_remove(struct spi_mem *spimem)
> -{
> -   Âstruct m25p  Â*flash = spi_mem_get_drvdata(spimem);
> -
> -Â Â Â Âspi_nor_restore(&flash->spi_nor);
> -
> -Â Â Â Â/* Clean up MTD stuff. */
> -Â Â Â Âreturn mtd_device_unregister(&flash->spi_nor.mtd);
> -}
> -
> -static void m25p_shutdown(struct spi_mem *spimem)
> -{
> -Â Â Â Âstruct m25p *flash = spi_mem_get_drvdata(spimem);
> -
> -Â Â Â Âspi_nor_restore(&flash->spi_nor);
> -}
> -/*
> - * Do NOT add to this array without reading the following:
> - *
> - * Historically, many flash devices are bound to this driver by
> their name. But
> - * since most of these flash are compatible to some extent, and their
> - * differences can often be differentiated by the JEDEC read-ID
> command, we
> - * encourage new users to add support to the spi-nor library, and
> simply bind
> - * against a generic string here (e.g., "jedec,spi-nor").
> - *
> - * Many flash names are kept here in this list (as well as in
> spi-nor.c) to
> - * keep them available as module aliases for existing platforms.
> - */
> -static const struct spi_device_id m25p_ids[] = {
> -Â Â Â Â/*
> -Â Â Â Â * Allow non-DT platform devices to bind to the "spi-nor"
> modalias, and
> -Â Â Â Â * hack around the fact that the SPI core does not provide
> uevent
> -Â Â Â Â * matching for .of_match_table
> -Â Â Â Â */
> -Â Â Â Â{"spi-nor"},
> -
> -Â Â Â Â/*
> -Â Â Â Â * Entries not used in DTs that should be safe to drop after
> replacing
> -Â Â Â Â * them with "spi-nor" in platform data.
> -Â Â Â Â */
> -   Â{"s25sl064a"}, {"w25x16"},  Â{"m25p10"},  Â{"m25px64"},
> -
> -Â Â Â Â/*
> -Â Â Â Â * Entries that were used in DTs without "jedec,spi-nor"
> fallback and
> -Â Â Â Â * should be kept for backward compatibility.
> -Â Â Â Â */
> -   Â{"at25df321a"}, {"at25df641"}, {"at26df081a"},
> -Â Â Â Â{"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"},
> -Â Â Â Â{"mx25l25635e"},{"mx66l51235l"},
> -   Â{"n25q064"},  {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"},
> -   Â{"s25fl256s1"}, {"s25fl512s"}, {"s25sl12801"}, {"s25fl008k"},
> -Â Â Â Â{"s25fl064k"},
> -Â Â Â Â{"sst25vf040b"},{"sst25vf016b"},{"sst25vf032b"},{"sst25wf040"},
> -   Â{"m25p40"},  Â{"m25p80"},  Â{"m25p16"},  Â{"m25p32"},
> -   Â{"m25p64"},  Â{"m25p128"},
> -   Â{"w25x80"},  Â{"w25x32"},  Â{"w25q32"},  Â{"w25q32dw"},
> -   Â{"w25q80bl"}, Â{"w25q128"},  {"w25q256"},
> -
> -Â Â Â Â/* Flashes that can't be detected using JEDEC */
> -   Â{"m25p05-nonjedec"},  {"m25p10-nonjedec"}, Â
> {"m25p20-nonjedec"},
> -   Â{"m25p40-nonjedec"},  {"m25p80-nonjedec"}, Â
> {"m25p16-nonjedec"},
> -   Â{"m25p32-nonjedec"},  {"m25p64-nonjedec"}, Â
> {"m25p128-nonjedec"},
> -
> -Â Â Â Â/* Everspin MRAMs (non-JEDEC) */
> -Â Â Â Â{ "mr25h128" }, /* 128 Kib, 40 MHz */
> -Â Â Â Â{ "mr25h256" }, /* 256 Kib, 40 MHz */
> -   Â{ "mr25h10" }, /* Â1 Mib, 40 MHz */
> -   Â{ "mr25h40" }, /* Â4 Mib, 40 MHz */
> -
> -Â Â Â Â{ },
> -};
> -MODULE_DEVICE_TABLE(spi, m25p_ids);
> -
> -static const struct of_device_id m25p_of_table[] = {
> -Â Â Â Â/*
> -Â Â Â Â * Generic compatibility for SPI NOR that can be identified
> by the
> -Â Â Â Â * JEDEC READ ID opcode (0x9F). Use this, if possible.
> -Â Â Â Â */
> -Â Â Â Â{ .compatible = "jedec,spi-nor" },
> -Â Â Â Â{}
> -};
> -MODULE_DEVICE_TABLE(of, m25p_of_table);
> -
> -static struct spi_mem_driver m25p80_driver = {
> -Â Â Â Â.spidrv = {
> -Â Â Â Â Â Â Â Â.driver = {
> -           Â.name Â= "m25p80",
> -Â Â Â Â Â Â Â Â Â Â Â Â.of_match_table = m25p_of_table,
> -Â Â Â Â Â Â Â Â},
> -       Â.id_table   Â= m25p_ids,
> -Â Â Â Â},
> -   Â.probe = m25p_probe,
> -Â Â Â Â.remove = m25p_remove,
> -   Â.shutdown   Â= m25p_shutdown,
> -
> -Â Â Â Â/* REVISIT: many of these chips have deep power-down modes,
> which
> -Â Â Â Â * should clearly be entered on suspend() to minimize power use.
> -Â Â Â Â * And also when they're otherwise idle...
> -Â Â Â Â */
> -};
> -
> -module_spi_mem_driver(m25p80_driver);
> -
> -MODULE_LICENSE("GPL");
> -MODULE_AUTHOR("Mike Lavender");
> -MODULE_DESCRIPTION("MTD SPI driver for ST M25Pxx flash chips");
> diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
> index 6de83277ce8b..f237fcdf7f86 100644
> --- a/drivers/mtd/spi-nor/Kconfig
> +++ b/drivers/mtd/spi-nor/Kconfig
> @@ -2,6 +2,8 @@
> Âmenuconfig MTD_SPI_NOR
> Â Â Â Â tristate "SPI-NOR device support"
> Â Â Â Â depends on MTD
> +Â Â Â Âdepends on MTD && SPI_MASTER
> +Â Â Â Âselect SPI_MEM
> Â Â Â Â help
> Â Â Â Â Â This is the framework for the SPI NOR which can be used by
> the SPI
> Â Â Â Â Â device drivers and the SPI-NOR device driver.
> diff --git a/drivers/mtd/spi-nor/spi-nor.c
> b/drivers/mtd/spi-nor/spi-nor.c
> index 7f0831be90a0..de906ab907c7 100644
> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -19,6 +19,7 @@
>
> Â#include <linux/mtd/mtd.h>
> Â#include <linux/of_platform.h>
> +#include <linux/sched/task_stack.h>
> Â#include <linux/spi/flash.h>
> Â#include <linux/mtd/spi-nor.h>
>
> @@ -288,6 +289,154 @@ struct flash_info {
>
> Â#define JEDEC_MFR(info)Â Â Â Â ((info)->id[0])
>
> +/**
> + * spi_nor_spimem_xfer_data() - helper function to read/write data to
> + *Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â flash's memory region
> + * @nor:Â Â Â Â pointer to 'struct spi_nor'
> + * @op:Â Â Â Â Âpointer to 'struct spi_mem_op' template for transfer
> + *
> + * Return: number of bytes transferred on success, -errno otherwise
> + */
> +static ssize_t spi_nor_spimem_xfer_data(struct spi_nor *nor,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct spi_mem_op *op)
> +{
> +Â Â Â Âbool usebouncebuf = false;
> +Â Â Â Âvoid *rdbuf = NULL;
> +Â Â Â Âconst void *buf;
> +Â Â Â Âint ret;
> +
> +Â Â Â Âif (op->data.dir == SPI_MEM_DATA_IN)
> +Â Â Â Â Â Â Â Âbuf = op->data.buf.in <http://data.buf.in>;
> +Â Â Â Âelse
> +Â Â Â Â Â Â Â Âbuf = op->data.buf.out;
> +
> +Â Â Â Âif (object_is_on_stack(buf) || !virt_addr_valid(buf))
> +Â Â Â Â Â Â Â Âusebouncebuf = true;
> +
> +Â Â Â Âif (usebouncebuf) {
> +Â Â Â Â Â Â Â Âif (op->data.nbytes > nor->bouncebuf_size)
> +Â Â Â Â Â Â Â Â Â Â Â Âop->data.nbytes = nor->bouncebuf_size;
> +
> +Â Â Â Â Â Â Â Âif (op->data.dir == SPI_MEM_DATA_IN) {
> +Â Â Â Â Â Â Â Â Â Â Â Ârdbuf = op->data.buf.in <http://data.buf.in>;
> +Â Â Â Â Â Â Â Â Â Â Â Âop->data.buf.in <http://data.buf.in> =
> nor->bouncebuf;
> +Â Â Â Â Â Â Â Â} else {
> +Â Â Â Â Â Â Â Â Â Â Â Âop->data.buf.out = nor->bouncebuf;
> +Â Â Â Â Â Â Â Â Â Â Â Âmemcpy(nor->bouncebuf, buf,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â op->data.nbytes);
> +Â Â Â Â Â Â Â Â}
> +Â Â Â Â}
> +
> +Â Â Â Âret = spi_mem_adjust_op_size(nor->spimem, op);
> +Â Â Â Âif (ret)
> +Â Â Â Â Â Â Â Âreturn ret;
> +
> +Â Â Â Âret = spi_mem_exec_op(nor->spimem, op);
> +Â Â Â Âif (ret)
> +Â Â Â Â Â Â Â Âreturn ret;
> +
> +Â Â Â Âif (usebouncebuf && op->data.dir == SPI_MEM_DATA_IN)
> +Â Â Â Â Â Â Â Âmemcpy(rdbuf, nor->bouncebuf, op->data.nbytes);
> +
> +Â Â Â Âreturn op->data.nbytes;
> +}
> +
> +/**
> + * spi_nor_spimem_read_data() - read data from flash's memory
> region via
> + *Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â spi-mem
> + * @nor:Â Â Â Â pointer to 'struct spi_nor'
> + * @from:Â Â Â Âoffset to read from
> + * @len:Â Â Â Â number of bytes to read
> + * @buf:Â Â Â Â pointer to dst buffer
> + *
> + * Return: number of bytes read successfully, -errno otherwise
> + */
> +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, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_ADDR(nor->addr_width, from, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_DATA_IN(len, buf, 1));
> +
> +Â Â Â Â/* get transfer protocols. */
> +Â Â Â Âop.cmd.buswidth =
> spi_nor_get_protocol_inst_nbits(nor->read_proto);
> +Â Â Â Âop.addr.buswidth =
> spi_nor_get_protocol_addr_nbits(nor->read_proto);
> +Â Â Â Âop.dummy.buswidth = op.addr.buswidth;
> +Â Â Â Âop.data.buswidth =
> spi_nor_get_protocol_data_nbits(nor->read_proto);
> +
> +Â Â Â Â/* convert the dummy cycles to the number of bytes */
> +Â Â Â Âop.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
> +
> +Â Â Â Âreturn spi_nor_spimem_xfer_data(nor, &op);
> +}
> +
> +/**
> + * spi_nor_read_data() - read data from flash memory
> + * @nor:Â Â Â Â pointer to 'struct spi_nor'
> + * @from:Â Â Â Âoffset to read from
> + * @len:Â Â Â Â number of bytes to read
> + * @buf:Â Â Â Â pointer to dst buffer
> + *
> + * Return: number of bytes read successfully, -errno otherwise
> + */
> +static ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from,
> size_t len,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â u8 *buf)
> +{
> +Â Â Â Âif (nor->spimem)
> +Â Â Â Â Â Â Â Âreturn spi_nor_spimem_read_data(nor, from, len, buf);
> +
> +Â Â Â Âreturn nor->read(nor, from, len, buf);
> +}
> +
> +/**
> + * spi_nor_spimem_write_data() - write data to flash memory via
> + *Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âspi-mem
> + * @nor:Â Â Â Â pointer to 'struct spi_nor'
> + * @to:Â Â Â Â Âoffset to write to
> + * @len:Â Â Â Â number of bytes to write
> + * @buf:Â Â Â Â pointer to src buffer
> + *
> + * Return: number of bytes written successfully, -errno otherwise
> + */
> +static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor,
> loff_t to,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â size_t len, const u8 *buf)
> +{
> +Â Â Â Âstruct spi_mem_op op =
> +Â Â Â Â Â Â Â ÂSPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_ADDR(nor->addr_width, to, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DUMMY,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_DATA_OUT(len, buf, 1));
> +
> +Â Â Â Âop.cmd.buswidth =
> spi_nor_get_protocol_inst_nbits(nor->write_proto);
> +Â Â Â Âop.addr.buswidth =
> spi_nor_get_protocol_addr_nbits(nor->write_proto);
> +Â Â Â Âop.data.buswidth =
> spi_nor_get_protocol_data_nbits(nor->write_proto);
> +
> +Â Â Â Âif (nor->program_opcode == SPINOR_OP_AAI_WP &&
> nor->sst_write_second)
> +Â Â Â Â Â Â Â Âop.addr.nbytes = 0;
> +
> +Â Â Â Âreturn spi_nor_spimem_xfer_data(nor, &op);
> +}
> +
> +/**
> + * spi_nor_write_data() - write data to flash memory
> + * @nor:Â Â Â Â pointer to 'struct spi_nor'
> + * @to:Â Â Â Â Âoffset to write to
> + * @len:Â Â Â Â number of bytes to write
> + * @buf:Â Â Â Â pointer to src buffer
> + *
> + * Return: number of bytes written successfully, -errno otherwise
> + */
> +static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to,
> size_t len,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âconst u8 *buf)
> +{
> +Â Â Â Âif (nor->spimem)
> +Â Â Â Â Â Â Â Âreturn spi_nor_spimem_write_data(nor, to, len, buf);
> +
> +Â Â Â Âreturn nor->write(nor, to, len, buf);
> +}
> +
> Â/*
> Â * Read the status register, returning its value in the location
> Â * Return the status register value.
> @@ -297,7 +446,18 @@ static int read_sr(struct spi_nor *nor)
> Â{
> Â Â Â Â int ret;
>
> -Â Â Â Âret = nor->read_reg(nor, SPINOR_OP_RDSR, nor->bouncebuf, 1);
> +Â Â Â Âif (nor->spimem) {
> +Â Â Â Â Â Â Â Âstruct spi_mem_op op =
> +Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_ADDR,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DUMMY,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_DATA_IN(1,
> nor->bouncebuf, 1));
> +
> +Â Â Â Â Â Â Â Âret = spi_mem_exec_op(nor->spimem, &op);
> +Â Â Â Â} else {
> +Â Â Â Â Â Â Â Âret = nor->read_reg(nor, SPINOR_OP_RDSR,
> nor->bouncebuf, 1);
> +Â Â Â Â}
> +
> Â Â Â Â if (ret < 0) {
> Â Â Â Â Â Â Â Â pr_err("error %d reading SR\n", (int) ret);
> Â Â Â Â Â Â Â Â return ret;
> @@ -315,7 +475,18 @@ static int read_fsr(struct spi_nor *nor)
> Â{
> Â Â Â Â int ret;
>
> -Â Â Â Âret = nor->read_reg(nor, SPINOR_OP_RDFSR, nor->bouncebuf, 1);
> +Â Â Â Âif (nor->spimem) {
> +Â Â Â Â Â Â Â Âstruct spi_mem_op op =
> +Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_ADDR,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DUMMY,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_DATA_IN(1,
> nor->bouncebuf, 1));
> +
> +Â Â Â Â Â Â Â Âret = spi_mem_exec_op(nor->spimem, &op);
> +Â Â Â Â} else {
> +Â Â Â Â Â Â Â Âret = nor->read_reg(nor, SPINOR_OP_RDFSR,
> nor->bouncebuf, 1);
> +Â Â Â Â}
> +
> Â Â Â Â if (ret < 0) {
> Â Â Â Â Â Â Â Â pr_err("error %d reading FSR\n", ret);
> Â Â Â Â Â Â Â Â return ret;
> @@ -333,7 +504,18 @@ static int read_cr(struct spi_nor *nor)
> Â{
> Â Â Â Â int ret;
>
> -Â Â Â Âret = nor->read_reg(nor, SPINOR_OP_RDCR, nor->bouncebuf, 1);
> +Â Â Â Âif (nor->spimem) {
> +Â Â Â Â Â Â Â Âstruct spi_mem_op op =
> +Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_ADDR,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DUMMY,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_DATA_IN(1,
> nor->bouncebuf, 1));
> +
> +Â Â Â Â Â Â Â Âret = spi_mem_exec_op(nor->spimem, &op);
> +Â Â Â Â} else {
> +Â Â Â Â Â Â Â Âret = nor->read_reg(nor, SPINOR_OP_RDCR,
> nor->bouncebuf, 1);
> +Â Â Â Â}
> +
> Â Â Â Â if (ret < 0) {
> Â Â Â Â Â Â Â Â dev_err(nor->dev, "error %d reading CR\n", ret);
> Â Â Â Â Â Â Â Â return ret;
> @@ -349,6 +531,16 @@ static int read_cr(struct spi_nor *nor)
> Âstatic int write_sr(struct spi_nor *nor, u8 val)
> Â{
> Â Â Â Â nor->bouncebuf[0] = val;
> +Â Â Â Âif (nor->spimem) {
> +Â Â Â Â Â Â Â Âstruct spi_mem_op op =
> +Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_ADDR,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DUMMY,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_DATA_IN(1,
> nor->bouncebuf, 1));
> +
> +Â Â Â Â Â Â Â Âreturn spi_mem_exec_op(nor->spimem, &op);
> +Â Â Â Â}
> +
> Â Â Â Â return nor->write_reg(nor, SPINOR_OP_WRSR, nor->bouncebuf, 1);
> Â}
>
> @@ -358,6 +550,16 @@ static int write_sr(struct spi_nor *nor, u8 val)
> Â */
> Âstatic int write_enable(struct spi_nor *nor)
> Â{
> +Â Â Â Âif (nor->spimem) {
> +Â Â Â Â Â Â Â Âstruct spi_mem_op op =
> +Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_ADDR,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DUMMY,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DATA);
> +
> +Â Â Â Â Â Â Â Âreturn spi_mem_exec_op(nor->spimem, &op);
> +Â Â Â Â}
> +
> Â Â Â Â return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0);
> Â}
>
> @@ -366,6 +568,16 @@ static int write_enable(struct spi_nor *nor)
> Â */
> Âstatic int write_disable(struct spi_nor *nor)
> Â{
> +Â Â Â Âif (nor->spimem) {
> +Â Â Â Â Â Â Â Âstruct spi_mem_op op =
> +Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_ADDR,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DUMMY,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DATA);
> +
> +Â Â Â Â Â Â Â Âreturn spi_mem_exec_op(nor->spimem, &op);
> +Â Â Â Â}
> +
> Â Â Â Â return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0);
> Â}
>
> @@ -465,12 +677,64 @@ static void spi_nor_set_4byte_opcodes(struct
> spi_nor *nor)
> Â Â Â Â }
> Â}
>
> +static int macronix_set_4byte(struct spi_nor *nor, bool enable)
> +{
> +Â Â Â Âif (nor->spimem) {
> +Â Â Â Â Â Â Â Âstruct spi_mem_op op =
> +Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP(SPI_MEM_OP_CMD(enable ?
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂSPINOR_OP_EN4B :
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂSPINOR_OP_EX4B,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP_NO_ADDR,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP_NO_DUMMY,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP_NO_DATA);
> +
> +Â Â Â Â Â Â Â Âreturn spi_mem_exec_op(nor->spimem, &op);
> +Â Â Â Â}
> +
> +Â Â Â Âreturn nor->write_reg(nor, enable ? SPINOR_OP_EN4B :
> SPINOR_OP_EX4B,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂNULL, 0);
> +}
> +
> +static int spansion_set_4byte(struct spi_nor *nor, bool enable)
> +{
> +Â Â Â Ânor->bouncebuf[0] = enable << 7;
> +
> +Â Â Â Âif (nor->spimem) {
> +Â Â Â Â Â Â Â Âstruct spi_mem_op op =
> +Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_BRWR, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_ADDR,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DUMMY,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_DATA_OUT(1,
> nor->bouncebuf, 1));
> +
> +Â Â Â Â Â Â Â Âreturn spi_mem_exec_op(nor->spimem, &op);
> +Â Â Â Â}
> +
> +Â Â Â Âreturn nor->write_reg(nor, SPINOR_OP_BRWR, nor->bouncebuf, 1);
> +}
> +
> +static int spi_nor_write_ear(struct spi_nor *nor, u8 ear)
> +{
> +Â Â Â Ânor->bouncebuf[0] = ear;
> +
> +Â Â Â Âif (nor->spimem) {
> +Â Â Â Â Â Â Â Âstruct spi_mem_op op =
> +Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREAR, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_ADDR,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DUMMY,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_DATA_OUT(1,
> nor->bouncebuf, 1));
> +
> +Â Â Â Â Â Â Â Âreturn spi_mem_exec_op(nor->spimem, &op);
> +Â Â Â Â}
> +
> +Â Â Â Âreturn nor->write_reg(nor, SPINOR_OP_WREAR, nor->bouncebuf, 1);
> +}
> +
> Â/* Enable/disable 4-byte addressing mode. */
> Âstatic int set_4byte(struct spi_nor *nor, bool enable)
> Â{
> Â Â Â Â int status;
> Â Â Â Â bool need_wren = false;
> -Â Â Â Âu8 cmd;
>
> Â Â Â Â switch (JEDEC_MFR(nor->info)) {
> Â Â Â Â case SNOR_MFR_ST:
> @@ -483,8 +747,7 @@ static int set_4byte(struct spi_nor *nor, bool
> enable)
> Â Â Â Â Â Â Â Â if (need_wren)
> Â Â Â Â Â Â Â Â Â Â Â Â write_enable(nor);
>
> -Â Â Â Â Â Â Â Âcmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
> -Â Â Â Â Â Â Â Âstatus = nor->write_reg(nor, cmd, NULL, 0);
> +Â Â Â Â Â Â Â Âstatus = macronix_set_4byte(nor, enable);
> Â Â Â Â Â Â Â Â if (need_wren)
> Â Â Â Â Â Â Â Â Â Â Â Â write_disable(nor);
>
> @@ -497,25 +760,37 @@ static int set_4byte(struct spi_nor *nor, bool
> enable)
> Â Â Â Â Â Â Â Â Â Â Â Â Â* We must clear the register to enable
> normal behavior.
> Â Â Â Â Â Â Â Â Â Â Â Â Â*/
> Â Â Â Â Â Â Â Â Â Â Â Â write_enable(nor);
> -Â Â Â Â Â Â Â Â Â Â Â Ânor->bouncebuf[0] = 0;
> -Â Â Â Â Â Â Â Â Â Â Â Ânor->write_reg(nor, SPINOR_OP_WREAR,
> -Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â nor->bouncebuf, 1);
> +Â Â Â Â Â Â Â Â Â Â Â Âspi_nor_write_ear(nor, 0);
> Â Â Â Â Â Â Â Â Â Â Â Â write_disable(nor);
> Â Â Â Â Â Â Â Â }
>
> Â Â Â Â Â Â Â Â return status;
> Â Â Â Â default:
> Â Â Â Â Â Â Â Â /* Spansion style */
> -Â Â Â Â Â Â Â Ânor->bouncebuf[0] = enable << 7;
> -Â Â Â Â Â Â Â Âreturn nor->write_reg(nor, SPINOR_OP_BRWR,
> nor->bouncebuf, 1);
> +Â Â Â Â Â Â Â Âreturn spansion_set_4byte(nor, enable);
> Â Â Â Â }
> Â}
>
> +static int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr)
> +{
> +Â Â Â Âif (nor->spimem) {
> +Â Â Â Â Â Â Â Âstruct spi_mem_op op =
> +Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_XRDSR, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_ADDR,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DUMMY,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_DATA_IN(1, sr, 1));
> +
> +Â Â Â Â Â Â Â Âreturn spi_mem_exec_op(nor->spimem, &op);
> +Â Â Â Â}
> +
> +Â Â Â Âreturn nor->read_reg(nor, SPINOR_OP_XRDSR, sr, 1);
> +}
> +
> Âstatic int s3an_sr_ready(struct spi_nor *nor)
> Â{
> Â Â Â Â int ret;
>
> -Â Â Â Âret = nor->read_reg(nor, SPINOR_OP_XRDSR, nor->bouncebuf, 1);
> +Â Â Â Âret = spi_nor_xread_sr(nor, nor->bouncebuf);
> Â Â Â Â if (ret < 0) {
> Â Â Â Â Â Â Â Â dev_err(nor->dev, "error %d reading XRDSR\n", (int)
> ret);
> Â Â Â Â Â Â Â Â return ret;
> @@ -524,6 +799,21 @@ static int s3an_sr_ready(struct spi_nor *nor)
> Â Â Â Â return !!(nor->bouncebuf[0] & XSR_RDY);
> Â}
>
> +static int spi_nor_clear_sr(struct spi_nor *nor)
> +{
> +Â Â Â Âif (nor->spimem) {
> +Â Â Â Â Â Â Â Âstruct spi_mem_op op =
> +Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_ADDR,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DUMMY,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DATA);
> +
> +Â Â Â Â Â Â Â Âreturn spi_mem_exec_op(nor->spimem, &op);
> +Â Â Â Â}
> +
> +Â Â Â Âreturn nor->write_reg(nor, SPINOR_OP_CLSR, NULL, 0);
> +}
> +
> Âstatic int spi_nor_sr_ready(struct spi_nor *nor)
> Â{
> Â Â Â Â int sr = read_sr(nor);
> @@ -536,13 +826,28 @@ static int spi_nor_sr_ready(struct spi_nor *nor)
> Â Â Â Â Â Â Â Â else
> Â Â Â Â Â Â Â Â Â Â Â Â dev_err(nor->dev, "Programming Error
> occurred\n");
>
> -Â Â Â Â Â Â Â Ânor->write_reg(nor, SPINOR_OP_CLSR, NULL, 0);
> +Â Â Â Â Â Â Â Âspi_nor_clear_sr(nor);
> Â Â Â Â Â Â Â Â return -EIO;
> Â Â Â Â }
>
> Â Â Â Â return !(sr & SR_WIP);
> Â}
>
> +static int spi_nor_clear_fsr(struct spi_nor *nor)
> +{
> +Â Â Â Âif (nor->spimem) {
> +Â Â Â Â Â Â Â Âstruct spi_mem_op op =
> +Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_ADDR,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DUMMY,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DATA);
> +
> +Â Â Â Â Â Â Â Âreturn spi_mem_exec_op(nor->spimem, &op);
> +Â Â Â Â}
> +
> +Â Â Â Âreturn nor->write_reg(nor, SPINOR_OP_CLFSR, NULL, 0);
> +}
> +
> Âstatic int spi_nor_fsr_ready(struct spi_nor *nor)
> Â{
> Â Â Â Â int fsr = read_fsr(nor);
> @@ -559,7 +864,7 @@ static int spi_nor_fsr_ready(struct spi_nor *nor)
> Â Â Â Â Â Â Â Â Â Â Â Â dev_err(nor->dev,
> Â Â Â Â Â Â Â Â Â Â Â Â "Attempted to modify a protected sector.\n");
>
> -Â Â Â Â Â Â Â Ânor->write_reg(nor, SPINOR_OP_CLFSR, NULL, 0);
> +Â Â Â Â Â Â Â Âspi_nor_clear_fsr(nor);
> Â Â Â Â Â Â Â Â return -EIO;
> Â Â Â Â }
>
> @@ -627,6 +932,16 @@ static int erase_chip(struct spi_nor *nor)
> Â{
> Â Â Â Â dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >>
> 10));
>
> +Â Â Â Âif (nor->spimem) {
> +Â Â Â Â Â Â Â Âstruct spi_mem_op op =
> +Â Â Â Â Â Â Â Â Â Â Â
> ÂSPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_ADDR,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DUMMY,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DATA);
> +
> +Â Â Â Â Â Â Â Âreturn spi_mem_exec_op(nor->spimem, &op);
> +Â Â Â Â}
> +
> Â Â Â Â return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0);
> Â}Â
>
> @@ -688,6 +1003,16 @@ static int spi_nor_erase_sector(struct spi_nor
> *nor, u32 addr)
> Â Â Â Â if (nor->erase)
> Â Â Â Â Â Â Â Â return nor->erase(nor, addr);
>
> +Â Â Â Âif (nor->spimem) {
> +Â Â Â Â Â Â Â Âstruct spi_mem_op op =
> +Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_ADDR(nor->addr_width,
> addr, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DUMMY,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DATA);
> +
> +Â Â Â Â Â Â Â Âreturn spi_mem_exec_op(nor->spimem, &op);
> +Â Â Â Â}
> +
>
>
> static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
> {
>
> int i;
>
> if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
>
> addr = spi_nor_s3an_addr_convert(nor, addr);
>
>
> if (nor->erase)
>
> return nor->erase(nor, addr);
>
>
> /*
> * Default implementation, if driver doesn't have a specialized HW
> * control
> */
> for (i = nor->addr_width - 1; i >= 0; i--) {
>
> nor->bouncebuf[i] = addr & 0xff;
>
> addr >>= 8;
>
> }
>
> if (nor->spimem) {
>
> struct spi_mem_op op =
>
> SPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 1),
>
> Â SPI_MEM_OP_NO_ADDR,
>
> Â SPI_MEM_OP_NO_DUMMY,
>
> Â SPI_MEM_OP_DATA_OUT(nor->addr_width,
>
> Â Â Â nor->bouncebuf, 1));
>
>
> return spi_mem_exec_op(nor->spimem, &op);
>
> }
>
> return nor->write_reg(nor, nor->erase_opcode, nor->bouncebuf,
>
> nor->addr_width);
>
> }
> Â
>
> Â Â Â Â /*
> Â Â Â Â Â* Default implementation, if driver doesn't have a
> specialized HW
> Â Â Â Â Â* control
> @@ -1403,7 +1728,18 @@ static int write_sr_cr(struct spi_nor *nor,
> u8 *sr_cr)
>
> Â Â Â Â write_enable(nor);
>
> -Â Â Â Âret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2);
> +Â Â Â Âif (nor->spimem) {
> +Â Â Â Â Â Â Â Âstruct spi_mem_op op =
> +Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_ADDR,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DUMMY,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_DATA_OUT(2, sr_cr, 1));
> +
> +Â Â Â Â Â Â Â Âret = spi_mem_exec_op(nor->spimem, &op);
> +Â Â Â Â} else {
> +Â Â Â Â Â Â Â Âret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2);
> +Â Â Â Â}
> +
> Â Â Â Â if (ret < 0) {
> Â Â Â Â Â Â Â Â dev_err(nor->dev,
> Â Â Â Â Â Â Â Â Â Â Â Â "error while writing configuration register\n");
> @@ -1584,6 +1920,36 @@ static int
> spansion_read_cr_quad_enable(struct spi_nor *nor)
> Â Â Â Â return 0;
> Â}
>
> +static int spi_nor_write_sr2(struct spi_nor *nor, u8 *sr2)
> +{
> +Â Â Â Âif (nor->spimem) {
> +Â Â Â Â Â Â Â Âstruct spi_mem_op op =
> +Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_ADDR,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DUMMY,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_DATA_OUT(1, sr2, 1));
> +
> +Â Â Â Â Â Â Â Âreturn spi_mem_exec_op(nor->spimem, &op);
> +Â Â Â Â}
> +
> +Â Â Â Âreturn nor->write_reg(nor, SPINOR_OP_WRSR2, sr2, 1);
> +}
> +
> +static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2)
> +{
> +Â Â Â Âif (nor->spimem) {
> +Â Â Â Â Â Â Â Âstruct spi_mem_op op =
> +Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_ADDR,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DUMMY,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_DATA_IN(1, sr2, 1));
> +
> +Â Â Â Â Â Â Â Âreturn spi_mem_exec_op(nor->spimem, &op);
> +Â Â Â Â}
> +
> +Â Â Â Âreturn nor->read_reg(nor, SPINOR_OP_RDSR2, sr2, 1);
> +}
> +
> Â/**
> Â * sr2_bit7_quad_enable() - set QE bit in Status Register 2.
> Â * @nor:Â Â Â Âpointer to a 'struct spi_nor'
> @@ -1602,7 +1968,7 @@ static int sr2_bit7_quad_enable(struct spi_nor
> *nor)
> Â Â Â Â int ret;
>
> Â Â Â Â /* Check current Quad Enable bit value. */
> -Â Â Â Âret = nor->read_reg(nor, SPINOR_OP_RDSR2, sr2, 1);
> +Â Â Â Âret = spi_nor_read_sr2(nor, sr2);
> Â Â Â Â if (ret)
> Â Â Â Â Â Â Â Â return ret;
> Â Â Â Â if (*sr2 & SR2_QUAD_EN_BIT7)
> @@ -1613,7 +1979,7 @@ static int sr2_bit7_quad_enable(struct spi_nor
> *nor)
>
> Â Â Â Â write_enable(nor);
>
> -Â Â Â Âret = nor->write_reg(nor, SPINOR_OP_WRSR2, sr2, 1);
> +Â Â Â Âret = spi_nor_write_sr2(nor, sr2);
> Â Â Â Â if (ret < 0) {
> Â Â Â Â Â Â Â Â dev_err(nor->dev, "error while writing status
> register 2\n");
> Â Â Â Â Â Â Â Â return -EINVAL;
> @@ -1626,7 +1992,7 @@ static int sr2_bit7_quad_enable(struct spi_nor
> *nor)
> Â Â Â Â }
>
> Â Â Â Â /* Read back and check it. */
> -Â Â Â Âret = nor->read_reg(nor, SPINOR_OP_RDSR2, sr2, 1);
> +Â Â Â Âret = spi_nor_read_sr2(nor, sr2);
> Â Â Â Â if (!(ret > 0 && (*sr2 & SR2_QUAD_EN_BIT7))) {
> Â Â Â Â Â Â Â Â dev_err(nor->dev, "SR2 Quad bit not set\n");
> Â Â Â Â Â Â Â Â return -EINVAL;
> @@ -2179,7 +2545,18 @@ static const struct flash_info
> *spi_nor_read_id(struct spi_nor *nor)
> Â Â Â Â u8Â Â Â Â Â Â Â Â Â Â Â *id = nor->bouncebuf;
> Â Â Â Â const struct flash_info *info;
>
> -Â Â Â Âtmp = nor->read_reg(nor, SPINOR_OP_RDID, id,
> SPI_NOR_MAX_ID_LEN);
> +Â Â Â Âif (nor->spimem) {
> +Â Â Â Â Â Â Â Âstruct spi_mem_op op =
> +Â Â Â Â Â Â Â Â Â Â Â ÂSPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1),
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_ADDR,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â SPI_MEM_OP_NO_DUMMY,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â
> SPI_MEM_OP_DATA_IN(SPI_NOR_MAX_ID_LEN, id, 1));
> +
> +Â Â Â Â Â Â Â Âtmp = spi_mem_exec_op(nor->spimem, &op);
> +Â Â Â Â} else {
> +Â Â Â Â Â Â Â Âtmp = nor->read_reg(nor, SPINOR_OP_RDID, id,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂSPI_NOR_MAX_ID_LEN);
> +Â Â Â Â}
> Â Â Â Â if (tmp < 0) {
> Â Â Â Â Â Â Â Â dev_err(nor->dev, "error %d reading JEDEC ID\n", tmp);
> Â Â Â Â Â Â Â Â return ERR_PTR(tmp);
> @@ -2215,7 +2592,7 @@ static int spi_nor_read(struct mtd_info *mtd,
> loff_t from, size_t len,
> Â Â Â Â Â Â Â Â if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
> Â Â Â Â Â Â Â Â Â Â Â Â addr = spi_nor_s3an_addr_convert(nor, addr);
>
> -Â Â Â Â Â Â Â Âret = nor->read(nor, addr, len, buf);
> +Â Â Â Â Â Â Â Âret = spi_nor_read_data(nor, addr, len, buf);
> Â Â Â Â Â Â Â Â if (ret == 0) {
> Â Â Â Â Â Â Â Â Â Â Â Â /* We shouldn't see 0-length reads */
> Â Â Â Â Â Â Â Â Â Â Â Â ret = -EIO;
> @@ -2260,7 +2637,7 @@ static int sst_write(struct mtd_info *mtd,
> loff_t to, size_t len,
> Â Â Â Â Â Â Â Â nor->program_opcode = SPINOR_OP_BP;
>
> Â Â Â Â Â Â Â Â /* write one byte. */
> -Â Â Â Â Â Â Â Âret = nor->write(nor, to, 1, buf);
> +Â Â Â Â Â Â Â Âret = spi_nor_write_data(nor, to, 1, buf);
> Â Â Â Â Â Â Â Â if (ret < 0)
> Â Â Â Â Â Â Â Â Â Â Â Â goto sst_write_err;
> Â Â Â Â Â Â Â Â WARN(ret != 1, "While writing 1 byte written %i
> bytes\n",
> @@ -2276,7 +2653,7 @@ static int sst_write(struct mtd_info *mtd,
> loff_t to, size_t len,
> Â Â Â Â Â Â Â Â nor->program_opcode = SPINOR_OP_AAI_WP;
>
> Â Â Â Â Â Â Â Â /* write two bytes. */
> -Â Â Â Â Â Â Â Âret = nor->write(nor, to, 2, buf + actual);
> +Â Â Â Â Â Â Â Âret = spi_nor_write_data(nor, to, 2, buf + actual);
> Â Â Â Â Â Â Â Â if (ret < 0)
> Â Â Â Â Â Â Â Â Â Â Â Â goto sst_write_err;
> Â Â Â Â Â Â Â Â WARN(ret != 2, "While writing 2 bytes written %i
> bytes\n",
> @@ -2299,7 +2676,7 @@ static int sst_write(struct mtd_info *mtd,
> loff_t to, size_t len,
> Â Â Â Â Â Â Â Â write_enable(nor);
>
> Â Â Â Â Â Â Â Â nor->program_opcode = SPINOR_OP_BP;
> -Â Â Â Â Â Â Â Âret = nor->write(nor, to, 1, buf + actual);
> +Â Â Â Â Â Â Â Âret = spi_nor_write_data(nor, to, 1, buf + actual);
> Â Â Â Â Â Â Â Â if (ret < 0)
> Â Â Â Â Â Â Â Â Â Â Â Â goto sst_write_err;
> Â Â Â Â Â Â Â Â WARN(ret != 1, "While writing 1 byte written %i
> bytes\n",
> @@ -2361,7 +2738,7 @@ static int spi_nor_write(struct mtd_info *mtd,
> loff_t to, size_t len,
> Â Â Â Â Â Â Â Â Â Â Â Â addr = spi_nor_s3an_addr_convert(nor, addr);
>
> Â Â Â Â Â Â Â Â write_enable(nor);
> -Â Â Â Â Â Â Â Âret = nor->write(nor, addr, page_remain, buf + i);
> +Â Â Â Â Â Â Â Âret = spi_nor_write_data(nor, addr, page_remain, buf
> + i);
> Â Â Â Â Â Â Â Â if (ret < 0)
> Â Â Â Â Â Â Â Â Â Â Â Â goto write_err;
> Â Â Â Â Â Â Â Â written = ret;
> @@ -2380,8 +2757,10 @@ static int spi_nor_write(struct mtd_info
> *mtd, loff_t to, size_t len,
>
> Âstatic int spi_nor_check(struct spi_nor *nor)
> Â{
> -Â Â Â Âif (!nor->dev || !nor->read || !nor->write ||
> -Â Â Â Â Â Â Â Â!nor->read_reg || !nor->write_reg) {
> +Â Â Â Âif (!nor->dev ||
> +Â Â Â Â Â Â(!nor->spimem &&
> +Â Â Â Â Â Â(!nor->read || !nor->write || !nor->read_reg ||
> +Â Â Â Â Â Â Â!nor->write_reg))) {
> Â Â Â Â Â Â Â Â pr_err("spi-nor: please fill all the necessary
> fields!\n");
> Â Â Â Â Â Â Â Â return -EINVAL;
> Â Â Â Â }
> @@ -2393,7 +2772,7 @@ static int s3an_nor_scan(struct spi_nor *nor)
> Â{
> Â Â Â Â int ret;
>
> -Â Â Â Âret = nor->read_reg(nor, SPINOR_OP_XRDSR, nor->bouncebuf, 1);
> +Â Â Â Âret = spi_nor_xread_sr(nor, nor->bouncebuf);
> Â Â Â Â if (ret < 0) {
> Â Â Â Â Â Â Â Â dev_err(nor->dev, "error %d reading XRDSR\n", (int)
> ret);
> Â Â Â Â Â Â Â Â return ret;
> @@ -2523,7 +2902,7 @@ static int spi_nor_read_raw(struct spi_nor
> *nor, u32 addr, size_t len, u8 *buf)
> Â Â Â Â int ret;
>
> Â Â Â Â while (len) {
> -Â Â Â Â Â Â Â Âret = nor->read(nor, addr, len, buf);
> +Â Â Â Â Â Â Â Âret = spi_nor_read_data(nor, addr, len, buf);
> Â Â Â Â Â Â Â Â if (!ret || ret > len)
> Â Â Â Â Â Â Â Â Â Â Â Â return -EIO;
> Â Â Â Â Â Â Â Â if (ret < 0)
> @@ -4122,6 +4501,10 @@ int spi_nor_scan(struct spi_nor *nor, const
> char *name,
> Â Â Â Â /*
> Â Â Â Â Â* We need the bounce buffer early to read/write registers
> when going
> Â Â Â Â Â* through the spi-mem layer (buffers have to be DMA-able).
> +Â Â Â Â * For spi-mem drivers, we'll reallocate a new buffer if
> +Â Â Â Â * nor->page_size turns out to be greater than PAGE_SIZE (which
> +Â Â Â Â * shouldn't happen before long since NOR pages are usually less
> +Â Â Â Â * than 1KB) after spi_nor_scan() returns.
> Â Â Â Â Â*/
> Â Â Â Â nor->bouncebuf_size = PAGE_SIZE;
> Â Â Â Â nor->bouncebuf = devm_kmalloc(dev, nor->bouncebuf_size,
> @@ -4324,6 +4707,195 @@ int spi_nor_scan(struct spi_nor *nor, const
> char *name,
> Â}
> ÂEXPORT_SYMBOL_GPL(spi_nor_scan);
>
> +static int spi_nor_probe(struct spi_mem *spimem)
> +{
> +Â Â Â Âstruct spi_device *spi = spimem->spi;
> +Â Â Â Âstruct flash_platform_data *data = dev_get_platdata(&spi->dev);
> +Â Â Â Âstruct spi_nor *nor;
> +Â Â Â Âstruct spi_nor_hwcaps hwcaps = {
> +Â Â Â Â Â Â Â Â.mask = SNOR_HWCAPS_READ |
> +Â Â Â Â Â Â Â Â Â Â Â ÂSNOR_HWCAPS_READ_FAST |
> +Â Â Â Â Â Â Â Â Â Â Â ÂSNOR_HWCAPS_PP,
> +Â Â Â Â};
> +Â Â Â Âchar *flash_name;
> +Â Â Â Âint ret;
> +
> +Â Â Â Ânor = devm_kzalloc(&spi->dev, sizeof(*nor), GFP_KERNEL);
> +Â Â Â Âif (!nor)
> +Â Â Â Â Â Â Â Âreturn -ENOMEM;
> +
> +Â Â Â Ânor->spimem = spimem;
> +Â Â Â Ânor->dev = &spi->dev;
> +Â Â Â Âspi_nor_set_flash_node(nor, spi->dev.of_node);
> +
> +Â Â Â Âspi_mem_set_drvdata(spimem, nor);
> +
> +Â Â Â Â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;
> +Â Â Â Â}
> +
> +Â Â Â Âif (data && data->name)
> +Â Â Â Â Â Â Â Ânor->mtd.name <http://mtd.name> = data->name;
> +
> +Â Â Â Âif (!nor->mtd.name <http://mtd.name>)
> +Â Â Â Â Â Â Â Ânor->mtd.name <http://mtd.name> =
> spi_mem_get_name(spimem);
> +
> +Â Â Â Â/*
> +Â Â Â Â * For some (historical?) reason many platforms provide two
> different
> +Â Â Â Â * names in flash_platform_data: "name" and "type". Quite
> often name is
> +Â Â Â Â * set to "m25p80" and then "type" provides a real chip name.
> +Â Â Â Â * If that's the case, respect "type" and ignore a "name".
> +Â Â Â Â */
> +Â Â Â Âif (data && data->type)
> +Â Â Â Â Â Â Â Âflash_name = data->type;
> +Â Â Â Âelse if (!strcmp(spi->modalias, "spi-nor"))
> +Â Â Â Â Â Â Â Âflash_name = NULL; /* auto-detect */
> +Â Â Â Âelse
> +Â Â Â Â Â Â Â Âflash_name = spi->modalias;
> +
> +Â Â Â Âret = spi_nor_scan(nor, flash_name, &hwcaps);
> +Â Â Â Âif (ret)
> +Â Â Â Â Â Â Â Âreturn ret;
> +
> +Â Â Â Â/*
> +Â Â Â Â * None of the existing parts have > 512B pages, but let's
> play safe
> +Â Â Â Â * and add this logic so that if anyone ever adds support
> for such
> +Â Â Â Â * a NOR we don't end up with buffer overflows.
> +Â Â Â Â */
> +Â Â Â Âif (nor->page_size > PAGE_SIZE) {
> +Â Â Â Â Â Â Â Ânor->bouncebuf_size = nor->page_size;
> +Â Â Â Â Â Â Â Âdevm_kfree(nor->dev, nor->bouncebuf);
> +Â Â Â Â Â Â Â Ânor->bouncebuf = devm_kmalloc(nor->dev,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Ânor->bouncebuf_size,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂGFP_KERNEL);
> +Â Â Â Â Â Â Â Âif (!nor->bouncebuf)
> +Â Â Â Â Â Â Â Â Â Â Â Âreturn -ENOMEM;
> +Â Â Â Â}
> +
> +Â Â Â Âreturn mtd_device_register(&nor->mtd, data ? data->parts : NULL,
> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â data ? data->nr_parts : 0);
> +}
> +
> +static int spi_nor_remove(struct spi_mem *spimem)
> +{
> +Â Â Â Âstruct spi_nor *nor = spi_mem_get_drvdata(spimem);
> +
> +Â Â Â Âspi_nor_restore(nor);
> +
> +Â Â Â Â/* Clean up MTD stuff. */
> +Â Â Â Âreturn mtd_device_unregister(&nor->mtd);
> +}
> +
> +static void spi_nor_shutdown(struct spi_mem *spimem)
> +{
> +Â Â Â Âstruct spi_nor *nor = spi_mem_get_drvdata(spimem);
> +
> +Â Â Â Âspi_nor_restore(nor);
> +}
> +
> +/*
> + * Do NOT add to this array without reading the following:
> + *
> + * Historically, many flash devices are bound to this driver by
> their name. But
> + * since most of these flash are compatible to some extent, and their
> + * differences can often be differentiated by the JEDEC read-ID
> command, we
> + * encourage new users to add support to the spi-nor library, and
> simply bind
> + * against a generic string here (e.g., "jedec,spi-nor").
> + *
> + * Many flash names are kept here in this list (as well as in
> spi-nor.c) to
> + * keep them available as module aliases for existing platforms.
> + */
> +static const struct spi_device_id spi_nor_dev_ids[] = {
> +Â Â Â Â/*
> +Â Â Â Â * Allow non-DT platform devices to bind to the "spi-nor"
> modalias, and
> +Â Â Â Â * hack around the fact that the SPI core does not provide
> uevent
> +Â Â Â Â * matching for .of_match_table
> +Â Â Â Â */
> +Â Â Â Â{"spi-nor"},
> +
> +Â Â Â Â/*
> +Â Â Â Â * Entries not used in DTs that should be safe to drop after
> replacing
> +Â Â Â Â * them with "spi-nor" in platform data.
> +Â Â Â Â */
> +   Â{"s25sl064a"}, {"w25x16"},  Â{"m25p10"},  Â{"m25px64"},
> +
> +Â Â Â Â/*
> +Â Â Â Â * Entries that were used in DTs without "jedec,spi-nor"
> fallback and
> +Â Â Â Â * should be kept for backward compatibility.
> +Â Â Â Â */
> +   Â{"at25df321a"}, {"at25df641"}, {"at26df081a"},
> +Â Â Â Â{"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"},
> +Â Â Â Â{"mx25l25635e"},{"mx66l51235l"},
> +   Â{"n25q064"},  {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"},
> +   Â{"s25fl256s1"}, {"s25fl512s"}, {"s25sl12801"}, {"s25fl008k"},
> +Â Â Â Â{"s25fl064k"},
> +Â Â Â Â{"sst25vf040b"},{"sst25vf016b"},{"sst25vf032b"},{"sst25wf040"},
> +   Â{"m25p40"},  Â{"m25p80"},  Â{"m25p16"},  Â{"m25p32"},
> +   Â{"m25p64"},  Â{"m25p128"},
> +   Â{"w25x80"},  Â{"w25x32"},  Â{"w25q32"},  Â{"w25q32dw"},
> +   Â{"w25q80bl"}, Â{"w25q128"},  {"w25q256"},
> +
> +Â Â Â Â/* Flashes that can't be detected using JEDEC */
> +   Â{"m25p05-nonjedec"},  {"m25p10-nonjedec"}, Â
> {"m25p20-nonjedec"},
> +   Â{"m25p40-nonjedec"},  {"m25p80-nonjedec"}, Â
> {"m25p16-nonjedec"},
> +   Â{"m25p32-nonjedec"},  {"m25p64-nonjedec"}, Â
> {"m25p128-nonjedec"},
> +
> +Â Â Â Â/* Everspin MRAMs (non-JEDEC) */
> +Â Â Â Â{ "mr25h128" }, /* 128 Kib, 40 MHz */
> +Â Â Â Â{ "mr25h256" }, /* 256 Kib, 40 MHz */
> +   Â{ "mr25h10" }, /* Â1 Mib, 40 MHz */
> +   Â{ "mr25h40" }, /* Â4 Mib, 40 MHz */
> +
> +Â Â Â Â{ },
> +};
> +MODULE_DEVICE_TABLE(spi, spi_nor_dev_ids);
> +
> +static const struct of_device_id spi_nor_of_table[] = {
> +Â Â Â Â/*
> +Â Â Â Â * Generic compatibility for SPI NOR that can be identified
> by the
> +Â Â Â Â * JEDEC READ ID opcode (0x9F). Use this, if possible.
> +Â Â Â Â */
> +Â Â Â Â{ .compatible = "jedec,spi-nor" },
> +Â Â Â Â{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, spi_nor_of_table);
> +
> +/*
> + * REVISIT: many of these chips have deep power-down modes, which
> + * should clearly be entered on suspend() to minimize power use.
> + * And also when they're otherwise idle...
> + */
> +static struct spi_mem_driver spi_nor_driver = {
> +Â Â Â Â.spidrv = {
> +Â Â Â Â Â Â Â Â.driver = {
> +Â Â Â Â Â Â Â Â Â Â Â Â.name = "spi-nor",
> +Â Â Â Â Â Â Â Â Â Â Â Â.of_match_table = spi_nor_of_table,
> +Â Â Â Â Â Â Â Â},
> +Â Â Â Â Â Â Â Â.id_table = spi_nor_dev_ids,
> +Â Â Â Â},
> +Â Â Â Â.probe = spi_nor_probe,
> +Â Â Â Â.remove = spi_nor_remove,
> +Â Â Â Â.shutdown = spi_nor_shutdown,
> +};
> +module_spi_mem_driver(spi_nor_driver);
> +
> ÂMODULE_LICENSE("GPL v2");
> ÂMODULE_AUTHOR("Huang Shijie <shijie8@xxxxxxxxx
> <mailto:shijie8@xxxxxxxxx>>");
> ÂMODULE_AUTHOR("Mike Lavender");
> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
> index 6b5956a7a65a..4f35b1877889 100644
> --- a/include/linux/mtd/spi-nor.h
> +++ b/include/linux/mtd/spi-nor.h
> @@ -9,6 +9,7 @@
> Â#include <linux/bitops.h>
> Â#include <linux/mtd/cfi.h>
> Â#include <linux/mtd/mtd.h>
> +#include <linux/spi/spi-mem.h>
>
> Â/*
> Â * Manufacturer IDs
> @@ -344,6 +345,7 @@ struct flash_info;
> Â * @mtd:Â Â Â Â Â Â Â Âpoint to a mtd_info structure
> Â * @lock:Â Â Â Â Â Â Â the lock for the
> read/write/erase/lock/unlock operations
> Â * @dev:Â Â Â Â Â Â Â Âpoint to a spi device, or a spi nor
> controller device.
> + * @spimem:Â Â Â Â Â Â point to the spi mem device
> Â * @bouncebuf:Â Â Â Â Âbounce buffer used when the buffer passed by
> the MTD
> Â *Â Â Â Â Â Â Â Â Â Â Â layer is not DMA-able
> Â * @bouncebuf_size:Â Â size of the bounce buffer
> @@ -384,6 +386,7 @@ struct spi_nor {
>     struct mtd_info    Âmtd;
>     struct mutex      lock;
>     struct device     Â*dev;
> +   Âstruct spi_mem     *spimem;
> Â Â Â Â u8Â Â Â Â Â Â Â Â Â Â Â *bouncebuf;
>     size_t         bouncebuf_size;
> Â Â Â Â const struct flash_info *info;
> --
> 2.22.0
>
>
> Thanks,
>
> TomerÂ

--
Regards
Vignesh