[U-Boot] [PATCH v2 04/11] mtd: spi: Port SPI NOR framework from Linux

Jagan Teki jagan at amarulasolutions.com
Sun Jan 27 18:48:05 UTC 2019


Do you have this whole series in some branch in github? I'm unable to
apply it on master.

On Fri, Dec 21, 2018 at 12:15 PM Vignesh R <vigneshr at ti.com> wrote:
>
> Current U-Boot SPI NOR support (sf layer) is quite outdated as it does not
> support 4 byte addressing opcodes, SFDP table parsing and different types of
> quad mode enable sequences. Many newer flashes no longer support BANK
> registers used by sf layer to a access >16MB space.
> Also, many SPI controllers have special MMIO interfaces which provide
> accelerated read/write access but require knowledge of flash parameters
> to make use of it. Recent spi-mem layer provides a way to support such
> flashes but sf layer isn't using that.
> So sync SPI NOR framework from Linux v4.19 and add spi-mem support on top.
> in order to gain 4 byte addressing support, SFDP support and a way to
> support SPI controllers with MMIO flash interface.

Understand the usage if direct complete sync, however it's difficult
for me to review whole stuff. Can you please break into few patches
like Basic sync, SFDP and other.

>
> Signed-off-by: Vignesh R <vigneshr at ti.com>
> ---
>  drivers/mtd/spi/spi-nor-core.c | 2590 ++++++++++++++++++++++++++++++++
>  include/linux/mtd/cfi.h        |   32 +
>  include/linux/mtd/spi-nor.h    |  410 +++++
>  3 files changed, 3032 insertions(+)
>  create mode 100644 drivers/mtd/spi/spi-nor-core.c
>  create mode 100644 include/linux/mtd/cfi.h
>  create mode 100644 include/linux/mtd/spi-nor.h
>
> diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
> new file mode 100644
> index 000000000000..597898d3b4af
> --- /dev/null
> +++ b/drivers/mtd/spi/spi-nor-core.c
> @@ -0,0 +1,2590 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Based on m25p80.c, by Mike Lavender (mike at steroidmicros.com), with
> + * influence from lart.c (Abraham Van Der Merwe) and mtd_dataflash.c
> + *
> + * Copyright (C) 2005, Intec Automation Inc.
> + * Copyright (C) 2014, Freescale Semiconductor, Inc.
> + *
> + * Synced from Linux v4.19
> + */
> +
> +#include <common.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/log2.h>
> +#include <linux/math64.h>
> +#include <linux/sizes.h>
> +
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/spi-nor.h>
> +#include <spi-mem.h>
> +#include <spi.h>
> +
> +/* Define max times to check status register before we give up. */
> +
> +/*
> + * For everything but full-chip erase; probably could be much smaller, but kept
> + * around for safety for now
> + */
> +
> +#define HZ                                     CONFIG_SYS_HZ
> +
> +#define DEFAULT_READY_WAIT_JIFFIES             (40UL * HZ)
> +
> +#define SPI_NOR_MAX_ID_LEN     6
> +#define SPI_NOR_MAX_ADDR_WIDTH 4
> +
> +struct flash_info {
> +       char            *name;
> +
> +       /*
> +        * This array stores the ID bytes.
> +        * The first three bytes are the JEDIC ID.
> +        * JEDEC ID zero means "no ID" (mostly older chips).
> +        */
> +       u8              id[SPI_NOR_MAX_ID_LEN];
> +       u8              id_len;
> +
> +       /* The size listed here is what works with SPINOR_OP_SE, which isn't
> +        * necessarily called a "sector" by the vendor.
> +        */
> +       unsigned int    sector_size;
> +       u16             n_sectors;
> +
> +       u16             page_size;
> +       u16             addr_width;
> +
> +       u16             flags;
> +#define SECT_4K                        BIT(0)  /* SPINOR_OP_BE_4K works uniformly */
> +#define SPI_NOR_NO_ERASE       BIT(1)  /* No erase command needed */
> +#define SST_WRITE              BIT(2)  /* use SST byte programming */
> +#define SPI_NOR_NO_FR          BIT(3)  /* Can't do fastread */
> +#define SECT_4K_PMC            BIT(4)  /* SPINOR_OP_BE_4K_PMC works uniformly */
> +#define SPI_NOR_DUAL_READ      BIT(5)  /* Flash supports Dual Read */
> +#define SPI_NOR_QUAD_READ      BIT(6)  /* Flash supports Quad Read */
> +#define USE_FSR                        BIT(7)  /* use flag status register */
> +#define SPI_NOR_HAS_LOCK       BIT(8)  /* Flash supports lock/unlock via SR */
> +#define SPI_NOR_HAS_TB         BIT(9)  /*
> +                                        * Flash SR has Top/Bottom (TB) protect
> +                                        * bit. Must be used with
> +                                        * SPI_NOR_HAS_LOCK.
> +                                        */
> +#define        SPI_S3AN                BIT(10) /*
> +                                        * Xilinx Spartan 3AN In-System Flash
> +                                        * (MFR cannot be used for probing
> +                                        * because it has the same value as
> +                                        * ATMEL flashes)
> +                                        */
> +#define SPI_NOR_4B_OPCODES     BIT(11) /*
> +                                        * Use dedicated 4byte address op codes
> +                                        * to support memory size above 128Mib.
> +                                        */
> +#define NO_CHIP_ERASE          BIT(12) /* Chip does not support chip erase */
> +#define SPI_NOR_SKIP_SFDP      BIT(13) /* Skip parsing of SFDP tables */
> +#define USE_CLSR               BIT(14) /* use CLSR command */
> +
> +       int     (*quad_enable)(struct spi_nor *nor);
> +};
> +
> +#define JEDEC_MFR(info)        ((info)->id[0])
> +
> +static int spi_nor_read_write_reg(struct spi_nor *nor, struct spi_mem_op
> +               *op, void *buf)
> +{
> +       if (op->data.dir == SPI_MEM_DATA_IN)
> +               op->data.buf.in = buf;
> +       else
> +               op->data.buf.out = buf;
> +       return spi_mem_exec_op(nor->spi, op);
> +}
> +
> +static int spi_nor_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
> +{
> +       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));
> +       int ret;
> +
> +       ret = spi_nor_read_write_reg(nor, &op, val);
> +       if (ret < 0)
> +               dev_dbg(&flash->spimem->spi->dev, "error %d reading %x\n", ret,
> +                       code);
> +
> +       return ret;
> +}
> +
> +static int spi_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
> +{
> +       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));
> +
> +       return spi_nor_read_write_reg(nor, &op, buf);
> +}
> +
> +static ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
> +                                u_char *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));
> +       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(nor->spi, &op);
> +               if (ret)
> +                       return ret;
> +
> +               ret = spi_mem_exec_op(nor->spi, &op);
> +               if (ret)
> +                       return ret;
> +
> +               op.addr.val += op.data.nbytes;
> +               remaining -= op.data.nbytes;
> +               op.data.buf.in += op.data.nbytes;
> +       }
> +
> +       return len;
> +}
> +
> +static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,
> +                                 const u_char *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));
> +       size_t remaining = len;
> +       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;
> +
> +       while (remaining) {
> +               op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
> +               ret = spi_mem_adjust_op_size(nor->spi, &op);
> +               if (ret)
> +                       return ret;
> +
> +               ret = spi_mem_exec_op(nor->spi, &op);
> +               if (ret)
> +                       return ret;
> +
> +               op.addr.val += op.data.nbytes;
> +               remaining -= op.data.nbytes;
> +               op.data.buf.out += op.data.nbytes;
> +       }
> +
> +       return len;
> +}
> +
> +/*
> + * Read the status register, returning its value in the location
> + * Return the status register value.
> + * Returns negative if error occurred.
> + */
> +static int read_sr(struct spi_nor *nor)
> +{
> +       int ret;
> +       u8 val;
> +
> +       ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val, 1);
> +       if (ret < 0) {
> +               pr_debug("error %d reading SR\n", (int)ret);
> +               return ret;
> +       }
> +
> +       return val;
> +}
> +
> +/*
> + * Read the flag status register, returning its value in the location
> + * Return the status register value.
> + * Returns negative if error occurred.
> + */
> +static int read_fsr(struct spi_nor *nor)
> +{
> +       int ret;
> +       u8 val;
> +
> +       ret = nor->read_reg(nor, SPINOR_OP_RDFSR, &val, 1);
> +       if (ret < 0) {
> +               pr_debug("error %d reading FSR\n", ret);
> +               return ret;
> +       }
> +
> +       return val;
> +}
> +
> +/*
> + * Read configuration register, returning its value in the
> + * location. Return the configuration register value.
> + * Returns negative if error occurred.
> + */
> +#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
> +static int read_cr(struct spi_nor *nor)
> +{
> +       int ret;
> +       u8 val;
> +
> +       ret = nor->read_reg(nor, SPINOR_OP_RDCR, &val, 1);
> +       if (ret < 0) {
> +               dev_dbg(nor->dev, "error %d reading CR\n", ret);
> +               return ret;
> +       }
> +
> +       return val;
> +}
> +#endif
> +
> +/*
> + * Write status register 1 byte
> + * Returns negative if error occurred.
> + */
> +static int write_sr(struct spi_nor *nor, u8 val)
> +{
> +       nor->cmd_buf[0] = val;
> +       return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1);
> +}
> +
> +/*
> + * Set write enable latch with Write Enable command.
> + * Returns negative if error occurred.
> + */
> +static int write_enable(struct spi_nor *nor)
> +{
> +       return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0);
> +}
> +
> +/*
> + * Send write disable instruction to the chip.
> + */
> +static int write_disable(struct spi_nor *nor)
> +{
> +       return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0);
> +}
> +
> +static struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
> +{
> +       return mtd->priv;
> +}
> +
> +static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size)
> +{
> +       size_t i;
> +
> +       for (i = 0; i < size; i++)
> +               if (table[i][0] == opcode)
> +                       return table[i][1];
> +
> +       /* No conversion found, keep input op code. */
> +       return opcode;
> +}
> +
> +static u8 spi_nor_convert_3to4_read(u8 opcode)
> +{
> +       static const u8 spi_nor_3to4_read[][2] = {
> +               { SPINOR_OP_READ,       SPINOR_OP_READ_4B },
> +               { SPINOR_OP_READ_FAST,  SPINOR_OP_READ_FAST_4B },
> +               { SPINOR_OP_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B },
> +               { 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,
> +                                     ARRAY_SIZE(spi_nor_3to4_read));
> +}
> +
> +static u8 spi_nor_convert_3to4_program(u8 opcode)
> +{
> +       static const u8 spi_nor_3to4_program[][2] = {
> +               { SPINOR_OP_PP,         SPINOR_OP_PP_4B },
> +               { SPINOR_OP_PP_1_1_4,   SPINOR_OP_PP_1_1_4_4B },
> +               { SPINOR_OP_PP_1_4_4,   SPINOR_OP_PP_1_4_4_4B },
> +       };
> +
> +       return spi_nor_convert_opcode(opcode, spi_nor_3to4_program,
> +                                     ARRAY_SIZE(spi_nor_3to4_program));
> +}
> +
> +static u8 spi_nor_convert_3to4_erase(u8 opcode)
> +{
> +       static const u8 spi_nor_3to4_erase[][2] = {
> +               { SPINOR_OP_BE_4K,      SPINOR_OP_BE_4K_4B },
> +               { SPINOR_OP_BE_32K,     SPINOR_OP_BE_32K_4B },
> +               { SPINOR_OP_SE,         SPINOR_OP_SE_4B },
> +       };
> +
> +       return spi_nor_convert_opcode(opcode, spi_nor_3to4_erase,
> +                                     ARRAY_SIZE(spi_nor_3to4_erase));
> +}
> +
> +static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
> +                                     const struct flash_info *info)
> +{
> +       /* Do some manufacturer fixups first */
> +       switch (JEDEC_MFR(info)) {
> +       case SNOR_MFR_SPANSION:
> +               /* No small sector erase for 4-byte command set */
> +               nor->erase_opcode = SPINOR_OP_SE;
> +               nor->mtd.erasesize = info->sector_size;
> +               break;
> +
> +       default:
> +               break;
> +       }
> +
> +       nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
> +       nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode);
> +       nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
> +}
> +
> +/* Enable/disable 4-byte addressing mode. */
> +static int set_4byte(struct spi_nor *nor, const struct flash_info *info,
> +                    int enable)
> +{
> +       int status;
> +       bool need_wren = false;
> +       u8 cmd;
> +
> +       switch (JEDEC_MFR(info)) {
> +       case SNOR_MFR_ST:
> +       case SNOR_MFR_MICRON:
> +               /* Some Micron need WREN command; all will accept it */
> +               need_wren = true;
> +       case SNOR_MFR_MACRONIX:
> +       case SNOR_MFR_WINBOND:
> +               if (need_wren)
> +                       write_enable(nor);
> +
> +               cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
> +               status = nor->write_reg(nor, cmd, NULL, 0);
> +               if (need_wren)
> +                       write_disable(nor);
> +
> +               if (!status && !enable &&
> +                   JEDEC_MFR(info) == SNOR_MFR_WINBOND) {
> +                       /*
> +                        * On Winbond W25Q256FV, leaving 4byte mode causes
> +                        * the Extended Address Register to be set to 1, so all
> +                        * 3-byte-address reads come from the second 16M.
> +                        * We must clear the register to enable normal behavior.
> +                        */
> +                       write_enable(nor);
> +                       nor->cmd_buf[0] = 0;
> +                       nor->write_reg(nor, SPINOR_OP_WREAR, nor->cmd_buf, 1);
> +                       write_disable(nor);
> +               }
> +
> +               return status;
> +       default:
> +               /* Spansion style */
> +               nor->cmd_buf[0] = enable << 7;
> +               return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);
> +       }
> +}
> +
> +static int spi_nor_sr_ready(struct spi_nor *nor)
> +{
> +       int sr = read_sr(nor);
> +
> +       if (sr < 0)
> +               return sr;
> +
> +#ifndef CONFIG_SPL_BUILD
> +       if (nor->flags & SNOR_F_USE_CLSR && sr & (SR_E_ERR | SR_P_ERR)) {
> +               if (sr & SR_E_ERR)
> +                       dev_dbg(nor->dev, "Erase Error occurred\n");
> +               else
> +                       dev_dbg(nor->dev, "Programming Error occurred\n");
> +
> +               nor->write_reg(nor, SPINOR_OP_CLSR, NULL, 0);
> +               return -EIO;
> +       }
> +#endif

Does it increase SPL size? or do we always assume SPL can't access
flash that would require CLSR?

> +
> +       return !(sr & SR_WIP);
> +}
> +
> +static int spi_nor_fsr_ready(struct spi_nor *nor)
> +{
> +       int fsr = read_fsr(nor);
> +
> +       if (fsr < 0)
> +               return fsr;
> +#ifndef CONFIG_SPL_BUILD
> +       if (fsr & (FSR_E_ERR | FSR_P_ERR)) {
> +               if (fsr & FSR_E_ERR)
> +                       dev_dbg(nor->dev, "Erase operation failed.\n");
> +               else
> +                       dev_dbg(nor->dev, "Program operation failed.\n");
> +
> +               if (fsr & FSR_PT_ERR)
> +                       dev_dbg(nor->dev,
> +                               "Attempted to modify a protected sector.\n");
> +
> +               nor->write_reg(nor, SPINOR_OP_CLFSR, NULL, 0);
> +               return -EIO;
> +       }
> +#endif
> +       return fsr & FSR_READY;
> +}
> +
> +static int spi_nor_ready(struct spi_nor *nor)
> +{
> +       int sr, fsr;
> +
> +       sr = spi_nor_sr_ready(nor);
> +       if (sr < 0)
> +               return sr;
> +       fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1;
> +       if (fsr < 0)
> +               return fsr;
> +       return sr && fsr;
> +}
> +
> +/*
> + * Service routine to read status register until ready, or timeout occurs.
> + * Returns non-zero if error.
> + */
> +static int spi_nor_wait_till_ready_with_timeout(struct spi_nor *nor,
> +                                               unsigned long timeout)
> +{
> +       unsigned long timebase;
> +       int ret;
> +
> +       timebase = get_timer(0);
> +
> +       while (get_timer(timebase) < timeout) {
> +               ret = spi_nor_ready(nor);
> +               if (ret < 0)
> +                       return ret;
> +               if (ret)
> +                       return 0;
> +       }
> +
> +       dev_err(nor->dev, "flash operation timed out\n");
> +
> +       return -ETIMEDOUT;
> +}
> +
> +static int spi_nor_wait_till_ready(struct spi_nor *nor)
> +{
> +       return spi_nor_wait_till_ready_with_timeout(nor,
> +                                                   DEFAULT_READY_WAIT_JIFFIES);
> +}
> +
> +/*
> + * Initiate the erasure of a single sector
> + */
> +static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
> +{
> +       u8 buf[SPI_NOR_MAX_ADDR_WIDTH];
> +       int i;
> +
> +       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--) {
> +               buf[i] = addr & 0xff;
> +               addr >>= 8;
> +       }
> +
> +       return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width);
> +}
> +
> +/*
> + * Erase an address range on the nor chip.  The address range may extend
> + * one or more erase sectors.  Return an error is there is a problem erasing.
> + */
> +static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
> +{
> +       struct spi_nor *nor = mtd_to_spi_nor(mtd);
> +       u32 addr, len, rem;
> +       int ret;
> +
> +       dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
> +               (long long)instr->len);
> +
> +       div_u64_rem(instr->len, mtd->erasesize, &rem);
> +       if (rem)
> +               return -EINVAL;
> +
> +       addr = instr->addr;
> +       len = instr->len;
> +
> +       while (len) {
> +               write_enable(nor);
> +
> +               ret = spi_nor_erase_sector(nor, addr);
> +               if (ret)
> +                       goto erase_err;
> +
> +               addr += mtd->erasesize;
> +               len -= mtd->erasesize;
> +
> +               ret = spi_nor_wait_till_ready(nor);
> +               if (ret)
> +                       goto erase_err;
> +       }
> +
> +       write_disable(nor);
> +
> +erase_err:
> +       return ret;
> +}
> +
> +#if defined(CONFIG_SPI_FLASH_STMICRO) || defined(CONFIG_SPI_FLASH_SST)
> +/* Write status register and ensure bits in mask match written values */
> +static int write_sr_and_check(struct spi_nor *nor, u8 status_new, u8 mask)
> +{
> +       int ret;
> +
> +       write_enable(nor);
> +       ret = write_sr(nor, status_new);
> +       if (ret)
> +               return ret;
> +
> +       ret = spi_nor_wait_till_ready(nor);
> +       if (ret)
> +               return ret;
> +
> +       ret = read_sr(nor);
> +       if (ret < 0)
> +               return ret;
> +
> +       return ((ret & mask) != (status_new & mask)) ? -EIO : 0;
> +}
> +
> +static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs,
> +                                uint64_t *len)
> +{
> +       struct mtd_info *mtd = &nor->mtd;
> +       u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
> +       int shift = ffs(mask) - 1;
> +       int pow;
> +
> +       if (!(sr & mask)) {
> +               /* No protection */
> +               *ofs = 0;
> +               *len = 0;
> +       } else {
> +               pow = ((sr & mask) ^ mask) >> shift;
> +               *len = mtd->size >> pow;
> +               if (nor->flags & SNOR_F_HAS_SR_TB && sr & SR_TB)
> +                       *ofs = 0;
> +               else
> +                       *ofs = mtd->size - *len;
> +       }
> +}
> +
> +/*
> + * Return 1 if the entire region is locked (if @locked is true) or unlocked (if
> + * @locked is false); 0 otherwise
> + */
> +static int stm_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, u64 len,
> +                                   u8 sr, bool locked)
> +{
> +       loff_t lock_offs;
> +       uint64_t lock_len;
> +
> +       if (!len)
> +               return 1;
> +
> +       stm_get_locked_range(nor, sr, &lock_offs, &lock_len);
> +
> +       if (locked)
> +               /* Requested range is a sub-range of locked range */
> +               return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);
> +       else
> +               /* Requested range does not overlap with locked range */
> +               return (ofs >= lock_offs + lock_len) || (ofs + len <= lock_offs);
> +}
> +
> +static int stm_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
> +                           u8 sr)
> +{
> +       return stm_check_lock_status_sr(nor, ofs, len, sr, true);
> +}
> +
> +static int stm_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
> +                             u8 sr)
> +{
> +       return stm_check_lock_status_sr(nor, ofs, len, sr, false);
> +}
> +
> +/*
> + * Lock a region of the flash. Compatible with ST Micro and similar flash.
> + * Supports the block protection bits BP{0,1,2} in the status register
> + * (SR). Does not support these features found in newer SR bitfields:
> + *   - SEC: sector/block protect - only handle SEC=0 (block protect)
> + *   - CMP: complement protect - only support CMP=0 (range is not complemented)
> + *
> + * Support for the following is provided conditionally for some flash:
> + *   - TB: top/bottom protect
> + *
> + * Sample table portion for 8MB flash (Winbond w25q64fw):
> + *
> + *   SEC  |  TB   |  BP2  |  BP1  |  BP0  |  Prot Length  | Protected Portion
> + *  --------------------------------------------------------------------------
> + *    X   |   X   |   0   |   0   |   0   |  NONE         | NONE
> + *    0   |   0   |   0   |   0   |   1   |  128 KB       | Upper 1/64
> + *    0   |   0   |   0   |   1   |   0   |  256 KB       | Upper 1/32
> + *    0   |   0   |   0   |   1   |   1   |  512 KB       | Upper 1/16
> + *    0   |   0   |   1   |   0   |   0   |  1 MB         | Upper 1/8
> + *    0   |   0   |   1   |   0   |   1   |  2 MB         | Upper 1/4
> + *    0   |   0   |   1   |   1   |   0   |  4 MB         | Upper 1/2
> + *    X   |   X   |   1   |   1   |   1   |  8 MB         | ALL
> + *  ------|-------|-------|-------|-------|---------------|-------------------
> + *    0   |   1   |   0   |   0   |   1   |  128 KB       | Lower 1/64
> + *    0   |   1   |   0   |   1   |   0   |  256 KB       | Lower 1/32
> + *    0   |   1   |   0   |   1   |   1   |  512 KB       | Lower 1/16
> + *    0   |   1   |   1   |   0   |   0   |  1 MB         | Lower 1/8
> + *    0   |   1   |   1   |   0   |   1   |  2 MB         | Lower 1/4
> + *    0   |   1   |   1   |   1   |   0   |  4 MB         | Lower 1/2
> + *
> + * Returns negative on errors, 0 on success.
> + */
> +static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
> +{
> +       struct mtd_info *mtd = &nor->mtd;
> +       int status_old, status_new;
> +       u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
> +       u8 shift = ffs(mask) - 1, pow, val;
> +       loff_t lock_len;
> +       bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;
> +       bool use_top;
> +
> +       status_old = read_sr(nor);
> +       if (status_old < 0)
> +               return status_old;
> +
> +       /* If nothing in our range is unlocked, we don't need to do anything */
> +       if (stm_is_locked_sr(nor, ofs, len, status_old))
> +               return 0;
> +
> +       /* If anything below us is unlocked, we can't use 'bottom' protection */
> +       if (!stm_is_locked_sr(nor, 0, ofs, status_old))
> +               can_be_bottom = false;
> +
> +       /* If anything above us is unlocked, we can't use 'top' protection */
> +       if (!stm_is_locked_sr(nor, ofs + len, mtd->size - (ofs + len),
> +                             status_old))
> +               can_be_top = false;
> +
> +       if (!can_be_bottom && !can_be_top)
> +               return -EINVAL;
> +
> +       /* Prefer top, if both are valid */
> +       use_top = can_be_top;
> +
> +       /* lock_len: length of region that should end up locked */
> +       if (use_top)
> +               lock_len = mtd->size - ofs;
> +       else
> +               lock_len = ofs + len;
> +
> +       /*
> +        * Need smallest pow such that:
> +        *
> +        *   1 / (2^pow) <= (len / size)
> +        *
> +        * so (assuming power-of-2 size) we do:
> +        *
> +        *   pow = ceil(log2(size / len)) = log2(size) - floor(log2(len))
> +        */
> +       pow = ilog2(mtd->size) - ilog2(lock_len);
> +       val = mask - (pow << shift);
> +       if (val & ~mask)
> +               return -EINVAL;
> +       /* Don't "lock" with no region! */
> +       if (!(val & mask))
> +               return -EINVAL;
> +
> +       status_new = (status_old & ~mask & ~SR_TB) | val;
> +
> +       /* Disallow further writes if WP pin is asserted */
> +       status_new |= SR_SRWD;
> +
> +       if (!use_top)
> +               status_new |= SR_TB;
> +
> +       /* Don't bother if they're the same */
> +       if (status_new == status_old)
> +               return 0;
> +
> +       /* Only modify protection if it will not unlock other areas */
> +       if ((status_new & mask) < (status_old & mask))
> +               return -EINVAL;
> +
> +       return write_sr_and_check(nor, status_new, mask);
> +}
> +
> +/*
> + * Unlock a region of the flash. See stm_lock() for more info
> + *
> + * Returns negative on errors, 0 on success.
> + */
> +static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
> +{
> +       struct mtd_info *mtd = &nor->mtd;
> +       int status_old, status_new;
> +       u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
> +       u8 shift = ffs(mask) - 1, pow, val;
> +       loff_t lock_len;
> +       bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;
> +       bool use_top;
> +
> +       status_old = read_sr(nor);
> +       if (status_old < 0)
> +               return status_old;
> +
> +       /* If nothing in our range is locked, we don't need to do anything */
> +       if (stm_is_unlocked_sr(nor, ofs, len, status_old))
> +               return 0;
> +
> +       /* If anything below us is locked, we can't use 'top' protection */
> +       if (!stm_is_unlocked_sr(nor, 0, ofs, status_old))
> +               can_be_top = false;
> +
> +       /* If anything above us is locked, we can't use 'bottom' protection */
> +       if (!stm_is_unlocked_sr(nor, ofs + len, mtd->size - (ofs + len),
> +                               status_old))
> +               can_be_bottom = false;
> +
> +       if (!can_be_bottom && !can_be_top)
> +               return -EINVAL;
> +
> +       /* Prefer top, if both are valid */
> +       use_top = can_be_top;
> +
> +       /* lock_len: length of region that should remain locked */
> +       if (use_top)
> +               lock_len = mtd->size - (ofs + len);
> +       else
> +               lock_len = ofs;
> +
> +       /*
> +        * Need largest pow such that:
> +        *
> +        *   1 / (2^pow) >= (len / size)
> +        *
> +        * so (assuming power-of-2 size) we do:
> +        *
> +        *   pow = floor(log2(size / len)) = log2(size) - ceil(log2(len))
> +        */
> +       pow = ilog2(mtd->size) - order_base_2(lock_len);
> +       if (lock_len == 0) {
> +               val = 0; /* fully unlocked */
> +       } else {
> +               val = mask - (pow << shift);
> +               /* Some power-of-two sizes are not supported */
> +               if (val & ~mask)
> +                       return -EINVAL;
> +       }
> +
> +       status_new = (status_old & ~mask & ~SR_TB) | val;
> +
> +       /* Don't protect status register if we're fully unlocked */
> +       if (lock_len == 0)
> +               status_new &= ~SR_SRWD;
> +
> +       if (!use_top)
> +               status_new |= SR_TB;
> +
> +       /* Don't bother if they're the same */
> +       if (status_new == status_old)
> +               return 0;
> +
> +       /* Only modify protection if it will not lock other areas */
> +       if ((status_new & mask) > (status_old & mask))
> +               return -EINVAL;
> +
> +       return write_sr_and_check(nor, status_new, mask);
> +}
> +
> +/*
> + * Check if a region of the flash is (completely) locked. See stm_lock() for
> + * more info.
> + *
> + * Returns 1 if entire region is locked, 0 if any portion is unlocked, and
> + * negative on errors.
> + */
> +static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
> +{
> +       int status;
> +
> +       status = read_sr(nor);
> +       if (status < 0)
> +               return status;
> +
> +       return stm_is_locked_sr(nor, ofs, len, status);
> +}
> +#endif /* CONFIG_SPI_FLASH_STMICRO */
> +
> +/* Used when the "_ext_id" is two bytes at most */
> +#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)     \
> +               .id = {                                                 \
> +                       ((_jedec_id) >> 16) & 0xff,                     \
> +                       ((_jedec_id) >> 8) & 0xff,                      \
> +                       (_jedec_id) & 0xff,                             \
> +                       ((_ext_id) >> 8) & 0xff,                        \
> +                       (_ext_id) & 0xff,                               \
> +                       },                                              \
> +               .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))),       \
> +               .sector_size = (_sector_size),                          \
> +               .n_sectors = (_n_sectors),                              \
> +               .page_size = 256,                                       \
> +               .flags = (_flags),
> +
> +#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)    \
> +               .id = {                                                 \
> +                       ((_jedec_id) >> 16) & 0xff,                     \
> +                       ((_jedec_id) >> 8) & 0xff,                      \
> +                       (_jedec_id) & 0xff,                             \
> +                       ((_ext_id) >> 16) & 0xff,                       \
> +                       ((_ext_id) >> 8) & 0xff,                        \
> +                       (_ext_id) & 0xff,                               \
> +                       },                                              \
> +               .id_len = 6,                                            \
> +               .sector_size = (_sector_size),                          \
> +               .n_sectors = (_n_sectors),                              \
> +               .page_size = 256,                                       \
> +               .flags = (_flags),
> +
> +/* NOTE: double check command sets and memory organization when you add
> + * more nor chips.  This current list focusses on newer chips, which
> + * have been converging on command sets which including JEDEC ID.
> + *
> + * All newly added entries should describe *hardware* and should use SECT_4K
> + * (or SECT_4K_PMC) if hardware supports erasing 4 KiB sectors. For usage
> + * scenarios excluding small sectors there is config option that can be
> + * disabled: CONFIG_MTD_SPI_NOR_USE_4K_SECTORS.
> + * For historical (and compatibility) reasons (before we got above config) some
> + * old entries may be missing 4K flag.
> + */
> +const struct flash_info spi_nor_ids[] = {
> +#ifdef CONFIG_SPI_FLASH_ATMEL          /* ATMEL */
> +       /* Atmel -- some are (confusingly) marketed as "DataFlash" */
> +       { "at26df321",  INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) },
> +       { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) },
> +
> +       { "at45db011d", INFO(0x1f2200, 0, 64 * 1024,   4, SECT_4K) },
> +       { "at45db021d", INFO(0x1f2300, 0, 64 * 1024,   8, SECT_4K) },
> +       { "at45db041d", INFO(0x1f2400, 0, 64 * 1024,   8, SECT_4K) },
> +       { "at45db081d", INFO(0x1f2500, 0, 64 * 1024,  16, SECT_4K) },
> +       { "at45db161d", INFO(0x1f2600, 0, 64 * 1024,  32, SECT_4K) },
> +       { "at45db321d", INFO(0x1f2700, 0, 64 * 1024,  64, SECT_4K) },
> +       { "at45db641d", INFO(0x1f2800, 0, 64 * 1024, 128, SECT_4K) },
> +       { "at26df081a", INFO(0x1f4501, 0, 64 * 1024,  16, SECT_4K) },
> +#endif
> +#ifdef CONFIG_SPI_FLASH_EON            /* EON */
> +       /* EON -- en25xxx */
> +       { "en25q32b",   INFO(0x1c3016, 0, 64 * 1024,   64, 0) },
> +       { "en25q64",    INFO(0x1c3017, 0, 64 * 1024,  128, SECT_4K) },
> +       { "en25qh128",  INFO(0x1c7018, 0, 64 * 1024,  256, 0) },
> +       { "en25s64",    INFO(0x1c3817, 0, 64 * 1024,  128, SECT_4K) },
> +#endif
> +#ifdef CONFIG_SPI_FLASH_GIGADEVICE     /* GIGADEVICE */
> +       /* GigaDevice */
> +       {
> +               "gd25q16", INFO(0xc84015, 0, 64 * 1024,  32,
> +                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
> +                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
> +       },
> +       {
> +               "gd25q32", INFO(0xc84016, 0, 64 * 1024,  64,
> +                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
> +                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
> +       },
> +       {
> +               "gd25lq32", INFO(0xc86016, 0, 64 * 1024, 64,
> +                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
> +                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
> +       },
> +       {
> +               "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128,
> +                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
> +                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
> +       },
> +#endif
> +#ifdef CONFIG_SPI_FLASH_ISSI           /* ISSI */
> +       /* ISSI */
> +       { "is25lq040b", INFO(0x9d4013, 0, 64 * 1024,   8,
> +                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
> +       { "is25lp032",  INFO(0x9d6016, 0, 64 * 1024,  64, 0) },
> +       { "is25lp064",  INFO(0x9d6017, 0, 64 * 1024, 128, 0) },
> +       { "is25lp128",  INFO(0x9d6018, 0, 64 * 1024, 256,
> +                       SECT_4K | SPI_NOR_DUAL_READ) },
> +       { "is25lp256",  INFO(0x9d6019, 0, 64 * 1024, 512,
> +                       SECT_4K | SPI_NOR_DUAL_READ) },
> +       { "is25wp032",  INFO(0x9d7016, 0, 64 * 1024,  64,
> +                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
> +       { "is25wp064",  INFO(0x9d7017, 0, 64 * 1024, 128,
> +                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
> +       { "is25wp128",  INFO(0x9d7018, 0, 64 * 1024, 256,
> +                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
> +#endif
> +#ifdef CONFIG_SPI_FLASH_MACRONIX       /* MACRONIX */
> +       /* Macronix */
> +       { "mx25l2005a",  INFO(0xc22012, 0, 64 * 1024,   4, SECT_4K) },
> +       { "mx25l4005a",  INFO(0xc22013, 0, 64 * 1024,   8, SECT_4K) },
> +       { "mx25l8005",   INFO(0xc22014, 0, 64 * 1024,  16, 0) },
> +       { "mx25l1606e",  INFO(0xc22015, 0, 64 * 1024,  32, SECT_4K) },
> +       { "mx25l3205d",  INFO(0xc22016, 0, 64 * 1024,  64, SECT_4K) },
> +       { "mx25l6405d",  INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K) },
> +       { "mx25u2033e",  INFO(0xc22532, 0, 64 * 1024,   4, SECT_4K) },
> +       { "mx25u1635e",  INFO(0xc22535, 0, 64 * 1024,  32, SECT_4K) },
> +       { "mx25u6435f",  INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) },
> +       { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
> +       { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
> +       { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
> +       { "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_OPCODES) },
> +       { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
> +       { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
> +       { "mx66u51235f", INFO(0xc2253a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
> +       { "mx66l1g45g",  INFO(0xc2201b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
> +       { "mx25l1633e",  INFO(0xc22415, 0, 64 * 1024,   32, SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | SECT_4K) },
> +#endif
> +
> +#ifdef CONFIG_SPI_FLASH_STMICRO                /* STMICRO */
> +       /* Micron */
> +       { "n25q016a",    INFO(0x20bb15, 0, 64 * 1024,   32, SECT_4K | SPI_NOR_QUAD_READ) },
> +       { "n25q032",     INFO(0x20ba16, 0, 64 * 1024,   64, SPI_NOR_QUAD_READ) },
> +       { "n25q032a",    INFO(0x20bb16, 0, 64 * 1024,   64, SPI_NOR_QUAD_READ) },
> +       { "n25q064",     INFO(0x20ba17, 0, 64 * 1024,  128, SECT_4K | SPI_NOR_QUAD_READ) },
> +       { "n25q064a",    INFO(0x20bb17, 0, 64 * 1024,  128, SECT_4K | SPI_NOR_QUAD_READ) },
> +       { "n25q128a11",  INFO(0x20bb18, 0, 64 * 1024,  256, SECT_4K | SPI_NOR_QUAD_READ) },
> +       { "n25q128a13",  INFO(0x20ba18, 0, 64 * 1024,  256, SECT_4K | SPI_NOR_QUAD_READ) },
> +       { "n25q256a",    INFO(0x20ba19, 0, 64 * 1024,  512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
> +       { "n25q256ax1",  INFO(0x20bb19, 0, 64 * 1024,  512, SECT_4K | SPI_NOR_QUAD_READ) },
> +       { "n25q512a",    INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
> +       { "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
> +       { "n25q00",      INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
> +       { "n25q00a",     INFO(0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
> +       { "mt25qu02g",   INFO(0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
> +#endif
> +#ifdef CONFIG_SPI_FLASH_SPANSION       /* SPANSION */
> +       /* Spansion/Cypress -- single (large) sector size only, at least
> +        * for the chips listed here (without boot sectors).
> +        */
> +       { "s25sl032p",  INFO(0x010215, 0x4d00,  64 * 1024,  64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
> +       { "s25sl064p",  INFO(0x010216, 0x4d00,  64 * 1024, 128, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
> +       { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, USE_CLSR) },
> +       { "s25fl256s1", INFO(0x010219, 0x4d01,  64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
> +       { "s25fl512s",  INFO6(0x010220, 0x4d0081, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
> +       { "s25fl512s_256k",  INFO(0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
> +       { "s25fl512s_64k",  INFO(0x010220, 0x4d01, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
> +       { "s25fl512s_512k",  INFO(0x010220, 0x4f00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
> +       { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024,  64, 0) },
> +       { "s25sl12801", INFO(0x012018, 0x0301,  64 * 1024, 256, 0) },
> +       { "s25fl128s",  INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
> +       { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024,  64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
> +       { "s25fl129p1", INFO(0x012018, 0x4d01,  64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
> +       { "s25sl008a",  INFO(0x010213,      0,  64 * 1024,  16, 0) },
> +       { "s25sl016a",  INFO(0x010214,      0,  64 * 1024,  32, 0) },
> +       { "s25sl032a",  INFO(0x010215,      0,  64 * 1024,  64, 0) },
> +       { "s25sl064a",  INFO(0x010216,      0,  64 * 1024, 128, 0) },
> +       { "s25fl116k",  INFO(0x014015,      0,  64 * 1024,  32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
> +       { "s25fl164k",  INFO(0x014017,      0,  64 * 1024, 128, SECT_4K) },
> +       { "s25fl208k",  INFO(0x014014,      0,  64 * 1024,  16, SECT_4K | SPI_NOR_DUAL_READ) },
> +       { "s25fl128l",  INFO(0x016018,      0,  64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
> +#endif
> +#ifdef CONFIG_SPI_FLASH_SST            /* SST */
> +       /* SST -- large erase sizes are "overlays", "sectors" are 4K */
> +       { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) },
> +       { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
> +       { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) },
> +       { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) },
> +       { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) },
> +       { "sst25wf512",  INFO(0xbf2501, 0, 64 * 1024,  1, SECT_4K | SST_WRITE) },
> +       { "sst25wf010",  INFO(0xbf2502, 0, 64 * 1024,  2, SECT_4K | SST_WRITE) },
> +       { "sst25wf020",  INFO(0xbf2503, 0, 64 * 1024,  4, SECT_4K | SST_WRITE) },
> +       { "sst25wf020a", INFO(0x621612, 0, 64 * 1024,  4, SECT_4K) },
> +       { "sst25wf040b", INFO(0x621613, 0, 64 * 1024,  8, SECT_4K) },
> +       { "sst25wf040",  INFO(0xbf2504, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) },
> +       { "sst25wf080",  INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
> +       { "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
> +       { "sst26wf016",  INFO(0xbf2651, 0, 64 * 1024,  32, SECT_4K) },
> +       { "sst26wf032",  INFO(0xbf2622, 0, 64 * 1024,  64, SECT_4K) },
> +       { "sst26wf064",  INFO(0xbf2643, 0, 64 * 1024, 128, SECT_4K) },
> +#endif
> +#ifdef CONFIG_SPI_FLASH_STMICRO                /* STMICRO */
> +       /* ST Microelectronics -- newer production may have feature updates */
> +       { "m25p10",  INFO(0x202011,  0,  32 * 1024,   4, 0) },
> +       { "m25p20",  INFO(0x202012,  0,  64 * 1024,   4, 0) },
> +       { "m25p40",  INFO(0x202013,  0,  64 * 1024,   8, 0) },
> +       { "m25p80",  INFO(0x202014,  0,  64 * 1024,  16, 0) },
> +       { "m25p16",  INFO(0x202015,  0,  64 * 1024,  32, 0) },
> +       { "m25p32",  INFO(0x202016,  0,  64 * 1024,  64, 0) },
> +       { "m25p64",  INFO(0x202017,  0,  64 * 1024, 128, 0) },
> +       { "m25p128", INFO(0x202018,  0, 256 * 1024,  64, 0) },
> +       { "m25pe16", INFO(0x208015,  0, 64 * 1024, 32, SECT_4K) },
> +       { "m25px16",    INFO(0x207115,  0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
> +       { "m25px64",    INFO(0x207117,  0, 64 * 1024, 128, 0) },
> +#endif
> +#ifdef CONFIG_SPI_FLASH_WINBOND                /* WINBOND */
> +       /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
> +       { "w25x05", INFO(0xef3010, 0, 64 * 1024,  1,  SECT_4K) },
> +       { "w25x10", INFO(0xef3011, 0, 64 * 1024,  2,  SECT_4K) },
> +       { "w25x20", INFO(0xef3012, 0, 64 * 1024,  4,  SECT_4K) },
> +       { "w25x40", INFO(0xef3013, 0, 64 * 1024,  8,  SECT_4K) },
> +       { "w25x80", INFO(0xef3014, 0, 64 * 1024,  16, SECT_4K) },
> +       { "w25x16", INFO(0xef3015, 0, 64 * 1024,  32, SECT_4K) },
> +       {
> +               "w25q16dw", INFO(0xef6015, 0, 64 * 1024,  32,
> +                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
> +                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
> +       },
> +       { "w25x32", INFO(0xef3016, 0, 64 * 1024,  64, SECT_4K) },
> +       { "w25q20cl", INFO(0xef4012, 0, 64 * 1024,  4, SECT_4K) },
> +       { "w25q20bw", INFO(0xef5012, 0, 64 * 1024,  4, SECT_4K) },
> +       { "w25q20ew", INFO(0xef6012, 0, 64 * 1024,  4, SECT_4K) },
> +       { "w25q32", INFO(0xef4016, 0, 64 * 1024,  64, SECT_4K) },
> +       {
> +               "w25q32dw", INFO(0xef6016, 0, 64 * 1024,  64,
> +                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
> +                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
> +       },
> +       {
> +               "w25q32jv", INFO(0xef7016, 0, 64 * 1024,  64,
> +                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
> +                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
> +       },
> +       { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
> +       { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
> +       {
> +               "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128,
> +                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
> +                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
> +       },
> +       {
> +               "w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256,
> +                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
> +                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
> +       },
> +       { "w25q80", INFO(0xef5014, 0, 64 * 1024,  16, SECT_4K) },
> +       { "w25q80bl", INFO(0xef4014, 0, 64 * 1024,  16, SECT_4K) },
> +       { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
> +       { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
> +       { "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024,
> +                       SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ) },
> +#endif
> +#ifdef CONFIG_SPI_FLASH_XMC
> +       /* XMC (Wuhan Xinxin Semiconductor Manufacturing Corp.) */
> +       { "XM25QH64A", INFO(0x207017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
> +       { "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
> +#endif
> +       { },
> +};
> +
> +static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
> +{
> +       int                     tmp;
> +       u8                      id[SPI_NOR_MAX_ID_LEN];
> +       const struct flash_info *info;
> +
> +       if (!ARRAY_SIZE(spi_nor_ids))
> +               return ERR_PTR(-ENODEV);
> +
> +       tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
> +       if (tmp < 0) {
> +               dev_dbg(nor->dev, "error %d reading JEDEC ID\n", tmp);
> +               return ERR_PTR(tmp);
> +       }
> +
> +       for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) {
> +               info = &spi_nor_ids[tmp];
> +               if (info->id_len) {
> +                       if (!memcmp(info->id, id, info->id_len))
> +                               return &spi_nor_ids[tmp];
> +               }
> +       }
> +       dev_err(nor->dev, "unrecognized JEDEC id bytes: %02x, %02x, %02x\n",
> +               id[0], id[1], id[2]);
> +       return ERR_PTR(-ENODEV);
> +}
> +
> +static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
> +                       size_t *retlen, u_char *buf)
> +{
> +       struct spi_nor *nor = mtd_to_spi_nor(mtd);
> +       int ret;
> +
> +       dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
> +
> +       while (len) {
> +               loff_t addr = from;
> +
> +               ret = nor->read(nor, addr, len, buf);
> +               if (ret == 0) {
> +                       /* We shouldn't see 0-length reads */
> +                       ret = -EIO;
> +                       goto read_err;
> +               }
> +               if (ret < 0)
> +                       goto read_err;
> +
> +               *retlen += ret;
> +               buf += ret;
> +               from += ret;
> +               len -= ret;
> +       }
> +       ret = 0;
> +
> +read_err:
> +       return ret;
> +}
> +
> +#ifdef CONFIG_SPI_FLASH_SST
> +static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
> +                    size_t *retlen, const u_char *buf)
> +{
> +       struct spi_nor *nor = mtd_to_spi_nor(mtd);
> +       size_t actual;
> +       int ret;
> +
> +       dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
> +
> +       write_enable(nor);
> +
> +       nor->sst_write_second = false;
> +
> +       actual = to % 2;
> +       /* Start write from odd address. */
> +       if (actual) {
> +               nor->program_opcode = SPINOR_OP_BP;
> +
> +               /* write one byte. */
> +               ret = nor->write(nor, to, 1, buf);
> +               if (ret < 0)
> +                       goto sst_write_err;
> +               ret = spi_nor_wait_till_ready(nor);
> +               if (ret)
> +                       goto sst_write_err;
> +       }
> +       to += actual;
> +
> +       /* Write out most of the data here. */
> +       for (; actual < len - 1; actual += 2) {
> +               nor->program_opcode = SPINOR_OP_AAI_WP;
> +
> +               /* write two bytes. */
> +               ret = nor->write(nor, to, 2, buf + actual);
> +               if (ret < 0)
> +                       goto sst_write_err;
> +               ret = spi_nor_wait_till_ready(nor);
> +               if (ret)
> +                       goto sst_write_err;
> +               to += 2;
> +               nor->sst_write_second = true;
> +       }
> +       nor->sst_write_second = false;
> +
> +       write_disable(nor);
> +       ret = spi_nor_wait_till_ready(nor);
> +       if (ret)
> +               goto sst_write_err;
> +
> +       /* Write out trailing byte if it exists. */
> +       if (actual != len) {
> +               write_enable(nor);
> +
> +               nor->program_opcode = SPINOR_OP_BP;
> +               ret = nor->write(nor, to, 1, buf + actual);
> +               if (ret < 0)
> +                       goto sst_write_err;
> +               ret = spi_nor_wait_till_ready(nor);
> +               if (ret)
> +                       goto sst_write_err;
> +               write_disable(nor);
> +               actual += 1;
> +       }
> +sst_write_err:
> +       *retlen += actual;
> +       return ret;
> +}
> +#endif
> +/*
> + * Write an address range to the nor chip.  Data must be written in
> + * FLASH_PAGESIZE chunks.  The address range may be any size provided
> + * it is within the physical boundaries.
> + */
> +static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
> +       size_t *retlen, const u_char *buf)
> +{
> +       struct spi_nor *nor = mtd_to_spi_nor(mtd);
> +       size_t page_offset, page_remain, i;
> +       ssize_t ret;
> +
> +       dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
> +
> +       for (i = 0; i < len; ) {
> +               ssize_t written;
> +               loff_t addr = to + i;
> +
> +               /*
> +                * If page_size is a power of two, the offset can be quickly
> +                * calculated with an AND operation. On the other cases we
> +                * need to do a modulus operation (more expensive).
> +                * Power of two numbers have only one bit set and we can use
> +                * the instruction hweight32 to detect if we need to do a
> +                * modulus (do_div()) or not.
> +                */
> +               if (hweight32(nor->page_size) == 1) {
> +                       page_offset = addr & (nor->page_size - 1);
> +               } else {
> +                       u64 aux = addr;
> +
> +                       page_offset = do_div(aux, nor->page_size);
> +               }
> +               /* the size of data remaining on the first page */
> +               page_remain = min_t(size_t,
> +                                   nor->page_size - page_offset, len - i);
> +
> +               write_enable(nor);
> +               ret = nor->write(nor, addr, page_remain, buf + i);
> +               if (ret < 0)
> +                       goto write_err;
> +               written = ret;
> +
> +               ret = spi_nor_wait_till_ready(nor);
> +               if (ret)
> +                       goto write_err;
> +               *retlen += written;
> +               i += written;
> +               if (written != page_remain) {
> +                       ret = -EIO;
> +                       goto write_err;
> +               }
> +       }
> +
> +write_err:
> +       return ret;
> +}
> +
> +#ifdef CONFIG_SPI_FLASH_MACRONIX
> +/**
> + * macronix_quad_enable() - set QE bit in Status Register.
> + * @nor:       pointer to a 'struct spi_nor'
> + *
> + * Set the Quad Enable (QE) bit in the Status Register.
> + *
> + * bit 6 of the Status Register is the QE bit for Macronix like QSPI memories.
> + *
> + * Return: 0 on success, -errno otherwise.
> + */
> +static int macronix_quad_enable(struct spi_nor *nor)
> +{
> +       int ret, val;
> +
> +       val = read_sr(nor);
> +       if (val < 0)
> +               return val;
> +       if (val & SR_QUAD_EN_MX)
> +               return 0;
> +
> +       write_enable(nor);
> +
> +       write_sr(nor, val | SR_QUAD_EN_MX);
> +
> +       ret = spi_nor_wait_till_ready(nor);
> +       if (ret)
> +               return ret;
> +
> +       ret = read_sr(nor);
> +       if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) {
> +               dev_err(nor->dev, "Macronix Quad bit not set\n");
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +#endif
> +
> +#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
> +/*
> + * Write status Register and configuration register with 2 bytes
> + * The first byte will be written to the status register, while the
> + * second byte will be written to the configuration register.
> + * Return negative if error occurred.
> + */
> +static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr)
> +{
> +       int ret;
> +
> +       write_enable(nor);
> +
> +       ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2);
> +       if (ret < 0) {
> +               dev_dbg(nor->dev,
> +                       "error while writing configuration register\n");
> +               return -EINVAL;
> +       }
> +
> +       ret = spi_nor_wait_till_ready(nor);
> +       if (ret) {
> +               dev_dbg(nor->dev,
> +                       "timeout while writing configuration register\n");
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * spansion_read_cr_quad_enable() - set QE bit in Configuration Register.
> + * @nor:       pointer to a 'struct spi_nor'
> + *
> + * Set the Quad Enable (QE) bit in the Configuration Register.
> + * This function should be used with QSPI memories supporting the Read
> + * Configuration Register (35h) instruction.
> + *
> + * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI
> + * memories.
> + *
> + * Return: 0 on success, -errno otherwise.
> + */
> +static int spansion_read_cr_quad_enable(struct spi_nor *nor)
> +{
> +       u8 sr_cr[2];
> +       int ret;
> +
> +       /* Check current Quad Enable bit value. */
> +       ret = read_cr(nor);
> +       if (ret < 0) {
> +               dev_dbg(dev, "error while reading configuration register\n");
> +               return -EINVAL;
> +       }
> +
> +       if (ret & CR_QUAD_EN_SPAN)
> +               return 0;
> +
> +       sr_cr[1] = ret | CR_QUAD_EN_SPAN;
> +
> +       /* Keep the current value of the Status Register. */
> +       ret = read_sr(nor);
> +       if (ret < 0) {
> +               dev_dbg(dev, "error while reading status register\n");
> +               return -EINVAL;
> +       }
> +       sr_cr[0] = ret;
> +
> +       ret = write_sr_cr(nor, sr_cr);
> +       if (ret)
> +               return ret;
> +
> +       /* Read back and check it. */
> +       ret = read_cr(nor);
> +       if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {
> +               dev_dbg(nor->dev, "Spansion Quad bit not set\n");
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
> +#if CONFIG_IS_ENABLED(SPI_FLASH_SFDP_SUPPORT)
> +/**
> + * spansion_no_read_cr_quad_enable() - set QE bit in Configuration Register.
> + * @nor:       pointer to a 'struct spi_nor'
> + *
> + * Set the Quad Enable (QE) bit in the Configuration Register.
> + * This function should be used with QSPI memories not supporting the Read
> + * Configuration Register (35h) instruction.
> + *
> + * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI
> + * memories.
> + *
> + * Return: 0 on success, -errno otherwise.
> + */
> +static int spansion_no_read_cr_quad_enable(struct spi_nor *nor)
> +{
> +       u8 sr_cr[2];
> +       int ret;
> +
> +       /* Keep the current value of the Status Register. */
> +       ret = read_sr(nor);
> +       if (ret < 0) {
> +               dev_dbg(nor->dev, "error while reading status register\n");
> +               return -EINVAL;
> +       }
> +       sr_cr[0] = ret;
> +       sr_cr[1] = CR_QUAD_EN_SPAN;
> +
> +       return write_sr_cr(nor, sr_cr);
> +}
> +
> +#endif /* CONFIG_SPI_FLASH_SFDP_SUPPORT */
> +#endif /* CONFIG_SPI_FLASH_SPANSION */
> +
> +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
> +};

I'm afraid whether we teatsed all thse combinations? or we doing for
the sake of Linux sync?

> --
> 2.20.1
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> https://lists.denx.de/listinfo/u-boot



--
Jagan Teki
Senior Linux Kernel Engineer | Amarula Solutions
U-Boot, Linux | Upstream Maintainer
Hyderabad, India.


More information about the U-Boot mailing list