]> Git Repo - linux.git/commitdiff
Merge tag 'mtd/for-6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux
authorLinus Torvalds <[email protected]>
Wed, 26 Apr 2023 00:23:42 +0000 (17:23 -0700)
committerLinus Torvalds <[email protected]>
Wed, 26 Apr 2023 00:23:42 +0000 (17:23 -0700)
Pull mtd updates from Miquel Raynal:
 "Core MTD changes:
   - dt-bindings: Drop unneeded quotes
   - mtdblock: Tolerate corrected bit-flips
   - Use of_property_read_bool() for boolean properties
   - Avoid magic values
   - Avoid printing error messages on probe deferrals
   - Prepare mtd_otp_nvmem_add() to handle -EPROBE_DEFER
   - Fix error path for nvmem provider
   - Fix nvmem error reporting
   - Provide unique name for nvmem device

  MTD device changes:
   - lpddr_cmds: Remove unused words variable
   - bcm63xxpart: Remove MODULE_LICENSE in non-modules

  SPI NOR core changes:
   - Introduce Read While Write support for flashes featuring several
     banks
   - Set the 4-Byte Address Mode method based on SFDP data
   - Allow post_sfdp hook to return errors
   - Parse SCCR MC table and introduce support for multi-chip devices

  SPI NOR manufacturer drivers changes:
   - macronix: Add support for mx25uw51245g with RWW
   - spansion:
      - Determine current address mode at runtime as it can be changed
        in a non-volatile way and differ from factory defaults or from
        what SFDP advertises.
      - Enable JFFS2 write buffer mode for few ECC'd NOR flashes:
        S25FS256T, s25hx and s28hx
      - Add support for s25hl02gt and s25hs02gt

  Raw NAND core changes:
   - Convert to platform remove callback returning void
   - Fix spelling mistake waifunc() -> waitfunc()

  Raw NAND controller driver changes:
   - imx: Remove unused is_imx51_nfc and imx53_nfc functions
   - omap2: Drop obsolete dependency on COMPILE_TEST
   - orion: Use devm_platform_ioremap_resource()
   - qcom:
      - Use of_property_present() for testing DT property presence
      - Use devm_platform_get_and_ioremap_resource()
   - stm32_fmc2: Depends on ARCH_STM32 instead of MACH_STM32MP157
   - tmio: Remove reference to config MTD_NAND_TMIO in the parsers

  Raw NAND manufacturer driver changes:
   - hynix: Fix up bit 0 of sdr_timing_mode

  SPI-NAND changes:
   - Add support for ESMT F50x1G41LB"

* tag 'mtd/for-6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (55 commits)
  mtd: nand: Convert to platform remove callback returning void
  mtd: onenand: omap2: Drop obsolete dependency on COMPILE_TEST
  mtd: spi-nor: spansion: Add support for s25hl02gt and s25hs02gt
  mtd: spi-nor: spansion: Add a new ->ready() hook for multi-chip device
  mtd: spi-nor: spansion: Rework cypress_nor_quad_enable_volatile() for multi-chip device support
  mtd: spi-nor: spansion: Rework cypress_nor_get_page_size() for multi-chip device support
  mtd: spi-nor: sfdp: Add support for SCCR map for multi-chip device
  mtd: spi-nor: Extract volatile register offset from SCCR map
  mtd: spi-nor: Allow post_sfdp hook to return errors
  mtd: spi-nor: spansion: Rename method to cypress_nor_get_page_size
  mtd: spi-nor: spansion: Enable JFFS2 write buffer for S25FS256T
  mtd: spi-nor: spansion: Enable JFFS2 write buffer for Infineon s25hx SEMPER flash
  mtd: spi-nor: spansion: Enable JFFS2 write buffer for Infineon s28hx SEMPER flash
  mtd: spi-nor: spansion: Determine current address mode
  mtd: spi-nor: core: Introduce spi_nor_set_4byte_addr_mode()
  mtd: spi-nor: core: Update flash's current address mode when changing address mode
  mtd: spi-nor: Stop exporting spi_nor_restore()
  mtd: spi-nor: Set the 4-Byte Address Mode method based on SFDP data
  mtd: spi-nor: core: Make spi_nor_set_4byte_addr_mode_brwr public
  mtd: spi-nor: core: Update name and description of spi_nor_set_4byte_addr_mode
  ...

1  2 
Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml
drivers/mtd/nand/ecc-mxic.c
drivers/mtd/nand/raw/meson_nand.c
drivers/mtd/nand/raw/stm32_fmc2_nand.c
drivers/mtd/spi-nor/core.c
drivers/mtd/spi-nor/core.h
drivers/mtd/spi-nor/debugfs.c

index 54736362378eb5975712fccb9a6f73c058741533,2bece155699fd466f46528cce01388fe15c1e768..89959e5c47bae24917a7599fe989aa6c33a3c07a
@@@ -10,7 -10,7 +10,7 @@@ maintainers
    - Rob Herring <[email protected]>
  
  allOf:
-   - $ref: "mtd.yaml#"
+   - $ref: mtd.yaml#
    - $ref: /schemas/spi/spi-peripheral-props.yaml#
  
  properties:
        If "broken-flash-reset" is present then having this property does not
        make any difference.
  
 +  spi-cpol: true
 +  spi-cpha: true
 +
 +dependencies:
 +  spi-cpol: [ spi-cpha ]
 +  spi-cpha: [ spi-cpol ]
 +
  unevaluatedProperties: false
  
  examples:
index 6b487ffe2f2dc4dd6b0344080cf665ccaab4198b,915aadd1a170d261c78de2b8332085f37e4b4bec..22a760e6024effa2d7c2a4ce893eaa933d0ab326
@@@ -429,7 -429,6 +429,7 @@@ static int mxic_ecc_data_xfer_wait_for_
                mxic_ecc_enable_int(mxic);
                ret = wait_for_completion_timeout(&mxic->complete,
                                                  msecs_to_jiffies(1000));
 +              ret = ret ? 0 : -ETIMEDOUT;
                mxic_ecc_disable_int(mxic);
        } else {
                ret = readl_poll_timeout(mxic->regs + INTRPT_STS, val,
@@@ -848,13 -847,11 +848,11 @@@ static int mxic_ecc_probe(struct platfo
        return 0;
  }
  
- static int mxic_ecc_remove(struct platform_device *pdev)
+ static void mxic_ecc_remove(struct platform_device *pdev)
  {
        struct mxic_ecc_engine *mxic = platform_get_drvdata(pdev);
  
        nand_ecc_unregister_on_host_hw_engine(&mxic->external_engine);
-       return 0;
  }
  
  static const struct of_device_id mxic_ecc_of_ids[] = {
@@@ -871,7 -868,7 +869,7 @@@ static struct platform_driver mxic_ecc_
                .of_match_table = mxic_ecc_of_ids,
        },
        .probe = mxic_ecc_probe,
-       .remove = mxic_ecc_remove,
+       .remove_new = mxic_ecc_remove,
  };
  module_platform_driver(mxic_ecc_driver);
  
index 074e14225c06a07e0ceba491d14b5272472860d7,24f1ad025cda8d3a8cf45804c04b0498cd88a41b..1feea7d822520495f41a3971213cb7eabb243407
@@@ -176,7 -176,6 +176,7 @@@ struct meson_nfc 
  
        dma_addr_t daddr;
        dma_addr_t iaddr;
 +      u32 info_bytes;
  
        unsigned long assigned_cs;
  };
@@@ -280,7 -279,7 +280,7 @@@ static void meson_nfc_cmd_access(struc
  
        if (raw) {
                len = mtd->writesize + mtd->oobsize;
 -              cmd = (len & GENMASK(5, 0)) | scrambler | DMA_DIR(dir);
 +              cmd = (len & GENMASK(13, 0)) | scrambler | DMA_DIR(dir);
                writel(cmd, nfc->reg_base + NFC_REG_CMD);
                return;
        }
@@@ -504,7 -503,6 +504,7 @@@ static int meson_nfc_dma_buffer_setup(s
                                         nfc->daddr, datalen, dir);
                        return ret;
                }
 +              nfc->info_bytes = infolen;
                cmd = GENCMDIADDRL(NFC_CMD_AIL, nfc->iaddr);
                writel(cmd, nfc->reg_base + NFC_REG_CMD);
  
@@@ -522,10 -520,8 +522,10 @@@ static void meson_nfc_dma_buffer_releas
        struct meson_nfc *nfc = nand_get_controller_data(nand);
  
        dma_unmap_single(nfc->dev, nfc->daddr, datalen, dir);
 -      if (infolen)
 +      if (infolen) {
                dma_unmap_single(nfc->dev, nfc->iaddr, infolen, dir);
 +              nfc->info_bytes = 0;
 +      }
  }
  
  static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
        if (ret)
                goto out;
  
 -      cmd = NFC_CMD_N2M | (len & GENMASK(5, 0));
 +      cmd = NFC_CMD_N2M | (len & GENMASK(13, 0));
        writel(cmd, nfc->reg_base + NFC_REG_CMD);
  
        meson_nfc_drain_cmd(nfc);
@@@ -568,7 -564,7 +568,7 @@@ static int meson_nfc_write_buf(struct n
        if (ret)
                return ret;
  
 -      cmd = NFC_CMD_M2N | (len & GENMASK(5, 0));
 +      cmd = NFC_CMD_M2N | (len & GENMASK(13, 0));
        writel(cmd, nfc->reg_base + NFC_REG_CMD);
  
        meson_nfc_drain_cmd(nfc);
@@@ -714,8 -710,6 +714,8 @@@ static void meson_nfc_check_ecc_pages_v
                usleep_range(10, 15);
                /* info is updated by nfc dma engine*/
                smp_rmb();
 +              dma_sync_single_for_cpu(nfc->dev, nfc->iaddr, nfc->info_bytes,
 +                                      DMA_FROM_DEVICE);
                ret = *info & ECC_COMPLETE;
        } while (!ret);
  }
@@@ -997,7 -991,7 +997,7 @@@ static const struct mtd_ooblayout_ops m
  
  static int meson_nfc_clk_init(struct meson_nfc *nfc)
  {
 -      struct clk_parent_data nfc_divider_parent_data[1];
 +      struct clk_parent_data nfc_divider_parent_data[1] = {0};
        struct clk_init_data init = {0};
        int ret;
  
@@@ -1440,20 -1434,18 +1440,18 @@@ err_clk
        return ret;
  }
  
- static int meson_nfc_remove(struct platform_device *pdev)
+ static void meson_nfc_remove(struct platform_device *pdev)
  {
        struct meson_nfc *nfc = platform_get_drvdata(pdev);
  
        meson_nfc_nand_chip_cleanup(nfc);
  
        meson_nfc_disable_clk(nfc);
-       return 0;
  }
  
  static struct platform_driver meson_nfc_driver = {
        .probe  = meson_nfc_probe,
-       .remove = meson_nfc_remove,
+       .remove_new = meson_nfc_remove,
        .driver = {
                .name  = "meson-nand",
                .of_match_table = meson_nfc_id_table,
index 9e74bcd90aaa2e216a74fee29476bc0b7f209e2c,e3d09dd5963bc15fa346db812b6fb18e650b69c6..10c11cecac0803a6ce87793a9598b3c9c0d9f875
@@@ -1531,9 -1531,6 +1531,9 @@@ static int stm32_fmc2_nfc_setup_interfa
        if (IS_ERR(sdrt))
                return PTR_ERR(sdrt);
  
 +      if (conf->timings.mode > 3)
 +              return -EOPNOTSUPP;
 +
        if (chipnr == NAND_DATA_IFACE_CHECK_ONLY)
                return 0;
  
@@@ -2024,7 -2021,7 +2024,7 @@@ err_clk_disable
        return ret;
  }
  
- static int stm32_fmc2_nfc_remove(struct platform_device *pdev)
+ static void stm32_fmc2_nfc_remove(struct platform_device *pdev)
  {
        struct stm32_fmc2_nfc *nfc = platform_get_drvdata(pdev);
        struct stm32_fmc2_nand *nand = &nfc->nand;
        clk_disable_unprepare(nfc->clk);
  
        stm32_fmc2_nfc_wp_enable(nand);
-       return 0;
  }
  
  static int __maybe_unused stm32_fmc2_nfc_suspend(struct device *dev)
@@@ -2106,7 -2101,7 +2104,7 @@@ MODULE_DEVICE_TABLE(of, stm32_fmc2_nfc_
  
  static struct platform_driver stm32_fmc2_nfc_driver = {
        .probe  = stm32_fmc2_nfc_probe,
-       .remove = stm32_fmc2_nfc_remove,
+       .remove_new = stm32_fmc2_nfc_remove,
        .driver = {
                .name = "stm32_fmc2_nfc",
                .of_match_table = stm32_fmc2_nfc_match,
index 522d375aeccff6be0397ce2df10e33261358307e,143ca3c9b4772e21f5659d9750b1d78c53f401a3..0bb0ad14a2fc01c97d8e20144f8844186aea17df
@@@ -508,14 -508,16 +508,16 @@@ int spi_nor_read_cr(struct spi_nor *nor
  }
  
  /**
-  * spi_nor_set_4byte_addr_mode() - Enter/Exit 4-byte address mode.
+  * spi_nor_set_4byte_addr_mode_en4b_ex4b() - Enter/Exit 4-byte address mode
+  *                    using SPINOR_OP_EN4B/SPINOR_OP_EX4B. Typically used by
+  *                    Winbond and Macronix.
   * @nor:      pointer to 'struct spi_nor'.
   * @enable:   true to enter the 4-byte address mode, false to exit the 4-byte
   *            address mode.
   *
   * Return: 0 on success, -errno otherwise.
   */
- int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
+ int spi_nor_set_4byte_addr_mode_en4b_ex4b(struct spi_nor *nor, bool enable)
  {
        int ret;
  
  }
  
  /**
-  * spansion_set_4byte_addr_mode() - Set 4-byte address mode for Spansion
-  * flashes.
+  * spi_nor_set_4byte_addr_mode_wren_en4b_ex4b() - Set 4-byte address mode using
+  * SPINOR_OP_WREN followed by SPINOR_OP_EN4B or SPINOR_OP_EX4B. Typically used
+  * by ST and Micron flashes.
+  * @nor:      pointer to 'struct spi_nor'.
+  * @enable:   true to enter the 4-byte address mode, false to exit the 4-byte
+  *            address mode.
+  *
+  * Return: 0 on success, -errno otherwise.
+  */
+ int spi_nor_set_4byte_addr_mode_wren_en4b_ex4b(struct spi_nor *nor, bool enable)
+ {
+       int ret;
+       ret = spi_nor_write_enable(nor);
+       if (ret)
+               return ret;
+       ret = spi_nor_set_4byte_addr_mode_en4b_ex4b(nor, enable);
+       if (ret)
+               return ret;
+       return spi_nor_write_disable(nor);
+ }
+ /**
+  * spi_nor_set_4byte_addr_mode_brwr() - Set 4-byte address mode using
+  *                    SPINOR_OP_BRWR. Typically used by Spansion flashes.
   * @nor:      pointer to 'struct spi_nor'.
   * @enable:   true to enter the 4-byte address mode, false to exit the 4-byte
   *            address mode.
   *
+  * 8-bit volatile bank register used to define A[30:A24] bits. MSB (bit[7]) is
+  * used to enable/disable 4-byte address mode. When MSB is set to ‘1’, 4-byte
+  * address mode is active and A[30:24] bits are don’t care. Write instruction is
+  * SPINOR_OP_BRWR(17h) with 1 byte of data.
+  *
   * Return: 0 on success, -errno otherwise.
   */
static int spansion_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
int spi_nor_set_4byte_addr_mode_brwr(struct spi_nor *nor, bool enable)
  {
        int ret;
  
@@@ -588,6 -620,65 +620,65 @@@ int spi_nor_sr_ready(struct spi_nor *no
        return !(nor->bouncebuf[0] & SR_WIP);
  }
  
+ /**
+  * spi_nor_use_parallel_locking() - Checks if RWW locking scheme shall be used
+  * @nor:      pointer to 'struct spi_nor'.
+  *
+  * Return: true if parallel locking is enabled, false otherwise.
+  */
+ static bool spi_nor_use_parallel_locking(struct spi_nor *nor)
+ {
+       return nor->flags & SNOR_F_RWW;
+ }
+ /* Locking helpers for status read operations */
+ static int spi_nor_rww_start_rdst(struct spi_nor *nor)
+ {
+       struct spi_nor_rww *rww = &nor->rww;
+       int ret = -EAGAIN;
+       mutex_lock(&nor->lock);
+       if (rww->ongoing_io || rww->ongoing_rd)
+               goto busy;
+       rww->ongoing_io = true;
+       rww->ongoing_rd = true;
+       ret = 0;
+ busy:
+       mutex_unlock(&nor->lock);
+       return ret;
+ }
+ static void spi_nor_rww_end_rdst(struct spi_nor *nor)
+ {
+       struct spi_nor_rww *rww = &nor->rww;
+       mutex_lock(&nor->lock);
+       rww->ongoing_io = false;
+       rww->ongoing_rd = false;
+       mutex_unlock(&nor->lock);
+ }
+ static int spi_nor_lock_rdst(struct spi_nor *nor)
+ {
+       if (spi_nor_use_parallel_locking(nor))
+               return spi_nor_rww_start_rdst(nor);
+       return 0;
+ }
+ static void spi_nor_unlock_rdst(struct spi_nor *nor)
+ {
+       if (spi_nor_use_parallel_locking(nor)) {
+               spi_nor_rww_end_rdst(nor);
+               wake_up(&nor->rww.wait);
+       }
+ }
  /**
   * spi_nor_ready() - Query the flash to see if it is ready for new commands.
   * @nor:      pointer to 'struct spi_nor'.
   */
  static int spi_nor_ready(struct spi_nor *nor)
  {
+       int ret;
+       ret = spi_nor_lock_rdst(nor);
+       if (ret)
+               return 0;
        /* Flashes might override the standard routine. */
        if (nor->params->ready)
-               return nor->params->ready(nor);
+               ret = nor->params->ready(nor);
+       else
+               ret = spi_nor_sr_ready(nor);
+       spi_nor_unlock_rdst(nor);
  
-       return spi_nor_sr_ready(nor);
+       return ret;
  }
  
  /**
@@@ -1070,29 -1171,289 +1171,289 @@@ static void spi_nor_set_4byte_opcodes(s
        }
  }
  
int spi_nor_lock_and_prep(struct spi_nor *nor)
static int spi_nor_prep(struct spi_nor *nor)
  {
        int ret = 0;
  
+       if (nor->controller_ops && nor->controller_ops->prepare)
+               ret = nor->controller_ops->prepare(nor);
+       return ret;
+ }
+ static void spi_nor_unprep(struct spi_nor *nor)
+ {
+       if (nor->controller_ops && nor->controller_ops->unprepare)
+               nor->controller_ops->unprepare(nor);
+ }
+ static void spi_nor_offset_to_banks(u64 bank_size, loff_t start, size_t len,
+                                   u8 *first, u8 *last)
+ {
+       /* This is currently safe, the number of banks being very small */
+       *first = DIV_ROUND_DOWN_ULL(start, bank_size);
+       *last = DIV_ROUND_DOWN_ULL(start + len - 1, bank_size);
+ }
+ /* Generic helpers for internal locking and serialization */
+ static bool spi_nor_rww_start_io(struct spi_nor *nor)
+ {
+       struct spi_nor_rww *rww = &nor->rww;
+       bool start = false;
        mutex_lock(&nor->lock);
  
-       if (nor->controller_ops &&  nor->controller_ops->prepare) {
-               ret = nor->controller_ops->prepare(nor);
-               if (ret) {
-                       mutex_unlock(&nor->lock);
-                       return ret;
-               }
+       if (rww->ongoing_io)
+               goto busy;
+       rww->ongoing_io = true;
+       start = true;
+ busy:
+       mutex_unlock(&nor->lock);
+       return start;
+ }
+ static void spi_nor_rww_end_io(struct spi_nor *nor)
+ {
+       mutex_lock(&nor->lock);
+       nor->rww.ongoing_io = false;
+       mutex_unlock(&nor->lock);
+ }
+ static int spi_nor_lock_device(struct spi_nor *nor)
+ {
+       if (!spi_nor_use_parallel_locking(nor))
+               return 0;
+       return wait_event_killable(nor->rww.wait, spi_nor_rww_start_io(nor));
+ }
+ static void spi_nor_unlock_device(struct spi_nor *nor)
+ {
+       if (spi_nor_use_parallel_locking(nor)) {
+               spi_nor_rww_end_io(nor);
+               wake_up(&nor->rww.wait);
        }
+ }
+ /* Generic helpers for internal locking and serialization */
+ static bool spi_nor_rww_start_exclusive(struct spi_nor *nor)
+ {
+       struct spi_nor_rww *rww = &nor->rww;
+       bool start = false;
+       mutex_lock(&nor->lock);
+       if (rww->ongoing_io || rww->ongoing_rd || rww->ongoing_pe)
+               goto busy;
+       rww->ongoing_io = true;
+       rww->ongoing_rd = true;
+       rww->ongoing_pe = true;
+       start = true;
+ busy:
+       mutex_unlock(&nor->lock);
+       return start;
+ }
+ static void spi_nor_rww_end_exclusive(struct spi_nor *nor)
+ {
+       struct spi_nor_rww *rww = &nor->rww;
+       mutex_lock(&nor->lock);
+       rww->ongoing_io = false;
+       rww->ongoing_rd = false;
+       rww->ongoing_pe = false;
+       mutex_unlock(&nor->lock);
+ }
+ int spi_nor_prep_and_lock(struct spi_nor *nor)
+ {
+       int ret;
+       ret = spi_nor_prep(nor);
+       if (ret)
+               return ret;
+       if (!spi_nor_use_parallel_locking(nor))
+               mutex_lock(&nor->lock);
+       else
+               ret = wait_event_killable(nor->rww.wait,
+                                         spi_nor_rww_start_exclusive(nor));
        return ret;
  }
  
  void spi_nor_unlock_and_unprep(struct spi_nor *nor)
  {
-       if (nor->controller_ops && nor->controller_ops->unprepare)
-               nor->controller_ops->unprepare(nor);
+       if (!spi_nor_use_parallel_locking(nor)) {
+               mutex_unlock(&nor->lock);
+       } else {
+               spi_nor_rww_end_exclusive(nor);
+               wake_up(&nor->rww.wait);
+       }
+       spi_nor_unprep(nor);
+ }
+ /* Internal locking helpers for program and erase operations */
+ static bool spi_nor_rww_start_pe(struct spi_nor *nor, loff_t start, size_t len)
+ {
+       struct spi_nor_rww *rww = &nor->rww;
+       unsigned int used_banks = 0;
+       bool started = false;
+       u8 first, last;
+       int bank;
+       mutex_lock(&nor->lock);
+       if (rww->ongoing_io || rww->ongoing_rd || rww->ongoing_pe)
+               goto busy;
+       spi_nor_offset_to_banks(nor->params->bank_size, start, len, &first, &last);
+       for (bank = first; bank <= last; bank++) {
+               if (rww->used_banks & BIT(bank))
+                       goto busy;
+               used_banks |= BIT(bank);
+       }
+       rww->used_banks |= used_banks;
+       rww->ongoing_pe = true;
+       started = true;
+ busy:
+       mutex_unlock(&nor->lock);
+       return started;
+ }
+ static void spi_nor_rww_end_pe(struct spi_nor *nor, loff_t start, size_t len)
+ {
+       struct spi_nor_rww *rww = &nor->rww;
+       u8 first, last;
+       int bank;
+       mutex_lock(&nor->lock);
+       spi_nor_offset_to_banks(nor->params->bank_size, start, len, &first, &last);
+       for (bank = first; bank <= last; bank++)
+               rww->used_banks &= ~BIT(bank);
+       rww->ongoing_pe = false;
        mutex_unlock(&nor->lock);
  }
  
+ static int spi_nor_prep_and_lock_pe(struct spi_nor *nor, loff_t start, size_t len)
+ {
+       int ret;
+       ret = spi_nor_prep(nor);
+       if (ret)
+               return ret;
+       if (!spi_nor_use_parallel_locking(nor))
+               mutex_lock(&nor->lock);
+       else
+               ret = wait_event_killable(nor->rww.wait,
+                                         spi_nor_rww_start_pe(nor, start, len));
+       return ret;
+ }
+ static void spi_nor_unlock_and_unprep_pe(struct spi_nor *nor, loff_t start, size_t len)
+ {
+       if (!spi_nor_use_parallel_locking(nor)) {
+               mutex_unlock(&nor->lock);
+       } else {
+               spi_nor_rww_end_pe(nor, start, len);
+               wake_up(&nor->rww.wait);
+       }
+       spi_nor_unprep(nor);
+ }
+ /* Internal locking helpers for read operations */
+ static bool spi_nor_rww_start_rd(struct spi_nor *nor, loff_t start, size_t len)
+ {
+       struct spi_nor_rww *rww = &nor->rww;
+       unsigned int used_banks = 0;
+       bool started = false;
+       u8 first, last;
+       int bank;
+       mutex_lock(&nor->lock);
+       if (rww->ongoing_io || rww->ongoing_rd)
+               goto busy;
+       spi_nor_offset_to_banks(nor->params->bank_size, start, len, &first, &last);
+       for (bank = first; bank <= last; bank++) {
+               if (rww->used_banks & BIT(bank))
+                       goto busy;
+               used_banks |= BIT(bank);
+       }
+       rww->used_banks |= used_banks;
+       rww->ongoing_io = true;
+       rww->ongoing_rd = true;
+       started = true;
+ busy:
+       mutex_unlock(&nor->lock);
+       return started;
+ }
+ static void spi_nor_rww_end_rd(struct spi_nor *nor, loff_t start, size_t len)
+ {
+       struct spi_nor_rww *rww = &nor->rww;
+       u8 first, last;
+       int bank;
+       mutex_lock(&nor->lock);
+       spi_nor_offset_to_banks(nor->params->bank_size, start, len, &first, &last);
+       for (bank = first; bank <= last; bank++)
+               nor->rww.used_banks &= ~BIT(bank);
+       rww->ongoing_io = false;
+       rww->ongoing_rd = false;
+       mutex_unlock(&nor->lock);
+ }
+ static int spi_nor_prep_and_lock_rd(struct spi_nor *nor, loff_t start, size_t len)
+ {
+       int ret;
+       ret = spi_nor_prep(nor);
+       if (ret)
+               return ret;
+       if (!spi_nor_use_parallel_locking(nor))
+               mutex_lock(&nor->lock);
+       else
+               ret = wait_event_killable(nor->rww.wait,
+                                         spi_nor_rww_start_rd(nor, start, len));
+       return ret;
+ }
+ static void spi_nor_unlock_and_unprep_rd(struct spi_nor *nor, loff_t start, size_t len)
+ {
+       if (!spi_nor_use_parallel_locking(nor)) {
+               mutex_unlock(&nor->lock);
+       } else {
+               spi_nor_rww_end_rd(nor, start, len);
+               wake_up(&nor->rww.wait);
+       }
+       spi_nor_unprep(nor);
+ }
  static u32 spi_nor_convert_addr(struct spi_nor *nor, loff_t addr)
  {
        if (!nor->params->convert_addr)
@@@ -1397,11 -1758,18 +1758,18 @@@ static int spi_nor_erase_multi_sectors(
                        dev_vdbg(nor->dev, "erase_cmd->size = 0x%08x, erase_cmd->opcode = 0x%02x, erase_cmd->count = %u\n",
                                 cmd->size, cmd->opcode, cmd->count);
  
-                       ret = spi_nor_write_enable(nor);
+                       ret = spi_nor_lock_device(nor);
                        if (ret)
                                goto destroy_erase_cmd_list;
  
+                       ret = spi_nor_write_enable(nor);
+                       if (ret) {
+                               spi_nor_unlock_device(nor);
+                               goto destroy_erase_cmd_list;
+                       }
                        ret = spi_nor_erase_sector(nor, addr);
+                       spi_nor_unlock_device(nor);
                        if (ret)
                                goto destroy_erase_cmd_list;
  
@@@ -1446,7 -1814,7 +1814,7 @@@ static int spi_nor_erase(struct mtd_inf
        addr = instr->addr;
        len = instr->len;
  
-       ret = spi_nor_lock_and_prep(nor);
+       ret = spi_nor_prep_and_lock_pe(nor, instr->addr, instr->len);
        if (ret)
                return ret;
  
        if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
                unsigned long timeout;
  
-               ret = spi_nor_write_enable(nor);
+               ret = spi_nor_lock_device(nor);
                if (ret)
                        goto erase_err;
  
+               ret = spi_nor_write_enable(nor);
+               if (ret) {
+                       spi_nor_unlock_device(nor);
+                       goto erase_err;
+               }
                ret = spi_nor_erase_chip(nor);
+               spi_nor_unlock_device(nor);
                if (ret)
                        goto erase_err;
  
        /* "sector"-at-a-time erase */
        } else if (spi_nor_has_uniform_erase(nor)) {
                while (len) {
-                       ret = spi_nor_write_enable(nor);
+                       ret = spi_nor_lock_device(nor);
                        if (ret)
                                goto erase_err;
  
+                       ret = spi_nor_write_enable(nor);
+                       if (ret) {
+                               spi_nor_unlock_device(nor);
+                               goto erase_err;
+                       }
                        ret = spi_nor_erase_sector(nor, addr);
+                       spi_nor_unlock_device(nor);
                        if (ret)
                                goto erase_err;
  
        ret = spi_nor_write_disable(nor);
  
  erase_err:
-       spi_nor_unlock_and_unprep(nor);
+       spi_nor_unlock_and_unprep_pe(nor, instr->addr, instr->len);
  
        return ret;
  }
@@@ -1702,11 -2084,13 +2084,13 @@@ static int spi_nor_read(struct mtd_inf
                        size_t *retlen, u_char *buf)
  {
        struct spi_nor *nor = mtd_to_spi_nor(mtd);
+       loff_t from_lock = from;
+       size_t len_lock = len;
        ssize_t ret;
  
        dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
  
-       ret = spi_nor_lock_and_prep(nor);
+       ret = spi_nor_prep_and_lock_rd(nor, from_lock, len_lock);
        if (ret)
                return ret;
  
        ret = 0;
  
  read_err:
-       spi_nor_unlock_and_unprep(nor);
+       spi_nor_unlock_and_unprep_rd(nor, from_lock, len_lock);
        return ret;
  }
  
@@@ -1752,7 -2137,7 +2137,7 @@@ static int spi_nor_write(struct mtd_inf
  
        dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
  
-       ret = spi_nor_lock_and_prep(nor);
+       ret = spi_nor_prep_and_lock_pe(nor, to, len);
        if (ret)
                return ret;
  
  
                addr = spi_nor_convert_addr(nor, addr);
  
-               ret = spi_nor_write_enable(nor);
+               ret = spi_nor_lock_device(nor);
                if (ret)
                        goto write_err;
  
+               ret = spi_nor_write_enable(nor);
+               if (ret) {
+                       spi_nor_unlock_device(nor);
+                       goto write_err;
+               }
                ret = spi_nor_write_data(nor, addr, page_remain, buf + i);
+               spi_nor_unlock_device(nor);
                if (ret < 0)
                        goto write_err;
                written = ret;
        }
  
  write_err:
-       spi_nor_unlock_and_unprep(nor);
+       spi_nor_unlock_and_unprep_pe(nor, to, len);
        return ret;
  }
  
@@@ -2470,6 -2863,10 +2863,10 @@@ static void spi_nor_init_flags(struct s
  
        if (flags & NO_CHIP_ERASE)
                nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
+       if (flags & SPI_NOR_RWW && nor->info->n_banks > 1 &&
+           !nor->controller_ops)
+               nor->flags |= SNOR_F_RWW;
  }
  
  /**
@@@ -2501,6 -2898,8 +2898,8 @@@ static void spi_nor_init_fixup_flags(st
   */
  static void spi_nor_late_init_params(struct spi_nor *nor)
  {
+       struct spi_nor_flash_parameter *params = nor->params;
        if (nor->manufacturer && nor->manufacturer->fixups &&
            nor->manufacturer->fixups->late_init)
                nor->manufacturer->fixups->late_init(nor);
        if (nor->info->fixups && nor->info->fixups->late_init)
                nor->info->fixups->late_init(nor);
  
+       /* Default method kept for backward compatibility. */
+       if (!params->set_4byte_addr_mode)
+               params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_brwr;
        spi_nor_init_flags(nor);
        spi_nor_init_fixup_flags(nor);
  
         */
        if (nor->flags & SNOR_F_HAS_LOCK && !nor->params->locking_ops)
                spi_nor_init_default_locking_ops(nor);
+       nor->params->bank_size = div64_u64(nor->params->size, nor->info->n_banks);
  }
  
  /**
@@@ -2574,7 -2979,6 +2979,6 @@@ static void spi_nor_init_default_params
        struct device_node *np = spi_nor_get_flash_node(nor);
  
        params->quad_enable = spi_nor_sr2_bit1_quad_enable;
-       params->set_4byte_addr_mode = spansion_set_4byte_addr_mode;
        params->otp.org = &info->otp_org;
  
        /* Default to 16-bit Write Status (01h) Command */
@@@ -2730,6 -3134,33 +3134,33 @@@ static int spi_nor_quad_enable(struct s
        return nor->params->quad_enable(nor);
  }
  
+ /**
+  * spi_nor_set_4byte_addr_mode() - Set address mode.
+  * @nor:                pointer to a 'struct spi_nor'.
+  * @enable:             enable/disable 4 byte address mode.
+  *
+  * Return: 0 on success, -errno otherwise.
+  */
+ int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
+ {
+       struct spi_nor_flash_parameter *params = nor->params;
+       int ret;
+       ret = params->set_4byte_addr_mode(nor, enable);
+       if (ret && ret != -ENOTSUPP)
+               return ret;
+       if (enable) {
+               params->addr_nbytes = 4;
+               params->addr_mode_nbytes = 4;
+       } else {
+               params->addr_nbytes = 3;
+               params->addr_mode_nbytes = 3;
+       }
+       return 0;
+ }
  static int spi_nor_init(struct spi_nor *nor)
  {
        int err;
                 */
                WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET,
                          "enabling reset hack; may not recover from unexpected reboots\n");
-               err = nor->params->set_4byte_addr_mode(nor, true);
-               if (err && err != -ENOTSUPP)
+               err = spi_nor_set_4byte_addr_mode(nor, true);
+               if (err)
                        return err;
        }
  
@@@ -2887,14 -3318,14 +3318,14 @@@ static void spi_nor_put_device(struct m
        module_put(dev->driver->owner);
  }
  
- void spi_nor_restore(struct spi_nor *nor)
static void spi_nor_restore(struct spi_nor *nor)
  {
        int ret;
  
        /* restore the addressing mode */
        if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
            nor->flags & SNOR_F_BROKEN_RESET) {
-               ret = nor->params->set_4byte_addr_mode(nor, false);
+               ret = spi_nor_set_4byte_addr_mode(nor, false);
                if (ret)
                        /*
                         * Do not stop the execution in the hope that the flash
        if (nor->flags & SNOR_F_SOFT_RESET)
                spi_nor_soft_reset(nor);
  }
- EXPORT_SYMBOL_GPL(spi_nor_restore);
  
  static const struct flash_info *spi_nor_match_name(struct spi_nor *nor,
                                                   const char *name)
@@@ -2952,7 -3382,7 +3382,7 @@@ static const struct flash_info *spi_nor
                         * JEDEC knows better, so overwrite platform ID. We
                         * can't trust partitions any longer, but we'll let
                         * mtd apply them anyway, since some partitions may be
-                        * marked read-only, and we don't want to lose that
+                        * marked read-only, and we don't want to loose that
                         * information, even if it's not 100% accurate.
                         */
                        dev_warn(nor->dev, "found %s, expected %s\n",
@@@ -2977,6 -3407,9 +3407,9 @@@ static void spi_nor_set_mtd_info(struc
                mtd->name = dev_name(dev);
        mtd->type = MTD_NORFLASH;
        mtd->flags = MTD_CAP_NORFLASH;
+       /* Unset BIT_WRITEABLE to enable JFFS2 write buffer for ECC'd NOR */
+       if (nor->flags & SNOR_F_ECC)
+               mtd->flags &= ~MTD_BIT_WRITEABLE;
        if (nor->info->flags & SPI_NOR_NO_ERASE)
                mtd->flags |= MTD_NO_ERASE;
        else
@@@ -3064,6 -3497,9 +3497,9 @@@ int spi_nor_scan(struct spi_nor *nor, c
        if (ret)
                return ret;
  
+       if (spi_nor_use_parallel_locking(nor))
+               init_waitqueue_head(&nor->rww.wait);
        /*
         * Configure the SPI memory:
         * - select op codes for (Fast) Read, Page Program and Sector Erase.
@@@ -3343,19 -3779,7 +3779,19 @@@ static struct spi_mem_driver spi_nor_dr
        .remove = spi_nor_remove,
        .shutdown = spi_nor_shutdown,
  };
 -module_spi_mem_driver(spi_nor_driver);
 +
 +static int __init spi_nor_module_init(void)
 +{
 +      return spi_mem_driver_register(&spi_nor_driver);
 +}
 +module_init(spi_nor_module_init);
 +
 +static void __exit spi_nor_module_exit(void)
 +{
 +      spi_mem_driver_unregister(&spi_nor_driver);
 +      spi_nor_debugfs_shutdown();
 +}
 +module_exit(spi_nor_module_exit);
  
  MODULE_LICENSE("GPL v2");
  MODULE_AUTHOR("Huang Shijie <[email protected]>");
index e0cc42a4a0c8410a0148a649b58684ae6eb8940d,fd61c4793a107b82aa324738d9b05a2e19efd545..4fb5ff09c63a94d4afa4f928fb306837d7220d28
@@@ -130,6 -130,8 +130,8 @@@ enum spi_nor_option_flags 
        SNOR_F_IO_MODE_EN_VOLATILE = BIT(11),
        SNOR_F_SOFT_RESET       = BIT(12),
        SNOR_F_SWP_IS_VOLATILE  = BIT(13),
+       SNOR_F_RWW              = BIT(14),
+       SNOR_F_ECC              = BIT(15),
  };
  
  struct spi_nor_read_command {
@@@ -336,7 -338,8 +338,8 @@@ struct spi_nor_otp 
   * by the spi_nor_fixups hooks, or dynamically when parsing the JESD216
   * Serial Flash Discoverable Parameters (SFDP) tables.
   *
-  * @size:             the flash memory density in bytes.
+  * @bank_size:                the flash memory bank density in bytes.
+  * @size:             the total flash memory density in bytes.
   * @writesize         Minimal writable flash unit size. Defaults to 1. Set to
   *                    ECC unit size for ECC-ed flashes.
   * @page_size:                the page size of the SPI NOR flash memory.
   *                    in octal DTR mode.
   * @rdsr_addr_nbytes: dummy address bytes needed for Read Status Register
   *                    command in octal DTR mode.
+  * @n_dice:           number of dice in the flash memory.
+  * @vreg_offset:      volatile register offset for each die.
   * @hwcaps:           describes the read and page program hardware
   *                    capabilities.
   * @reads:            read capabilities ordered by priority: the higher index
   * @locking_ops:      SPI NOR locking methods.
   */
  struct spi_nor_flash_parameter {
+       u64                             bank_size;
        u64                             size;
        u32                             writesize;
        u32                             page_size;
        u8                              addr_mode_nbytes;
        u8                              rdsr_dummy;
        u8                              rdsr_addr_nbytes;
+       u8                              n_dice;
+       u32                             *vreg_offset;
  
        struct spi_nor_hwcaps           hwcaps;
        struct spi_nor_read_command     reads[SNOR_CMD_READ_MAX];
@@@ -422,7 -430,7 +430,7 @@@ struct spi_nor_fixups 
        int (*post_bfpt)(struct spi_nor *nor,
                         const struct sfdp_parameter_header *bfpt_header,
                         const struct sfdp_bfpt *bfpt);
-       void (*post_sfdp)(struct spi_nor *nor);
+       int (*post_sfdp)(struct spi_nor *nor);
        void (*late_init)(struct spi_nor *nor);
  };
  
   * @sector_size:    the size listed here is what works with SPINOR_OP_SE, which
   *                  isn't necessarily called a "sector" by the vendor.
   * @n_sectors:      the number of sectors.
+  * @n_banks:        the number of banks.
   * @page_size:      the flash's page size.
   * @addr_nbytes:    number of address bytes to send.
   *
   *   NO_CHIP_ERASE:           chip does not support chip erase.
   *   SPI_NOR_NO_FR:           can't do fastread.
   *   SPI_NOR_QUAD_PP:         flash supports Quad Input Page Program.
+  *   SPI_NOR_RWW:             flash supports reads while write.
   *
   * @no_sfdp_flags:  flags that indicate support that can be discovered via SFDP.
   *                  Used when SFDP tables are not defined in the flash. These
@@@ -495,6 -505,7 +505,7 @@@ struct flash_info 
        unsigned sector_size;
        u16 n_sectors;
        u16 page_size;
+       u8 n_banks;
        u8 addr_nbytes;
  
        bool parse_sfdp;
  #define NO_CHIP_ERASE                 BIT(7)
  #define SPI_NOR_NO_FR                 BIT(8)
  #define SPI_NOR_QUAD_PP                       BIT(9)
+ #define SPI_NOR_RWW                   BIT(10)
  
        u8 no_sfdp_flags;
  #define SPI_NOR_SKIP_SFDP             BIT(0)
        .id = { SPI_NOR_ID_3ITEMS(_jedec_id), SPI_NOR_ID_3ITEMS(_ext_id) }, \
        .id_len = 6
  
- #define SPI_NOR_GEOMETRY(_sector_size, _n_sectors)                    \
+ #define SPI_NOR_GEOMETRY(_sector_size, _n_sectors, _n_banks)          \
        .sector_size = (_sector_size),                                  \
        .n_sectors = (_n_sectors),                                      \
-       .page_size = 256
+       .page_size = 256,                                               \
+       .n_banks = (_n_banks)
  
  /* Used when the "_ext_id" is two bytes at most */
  #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors)            \
        SPI_NOR_ID((_jedec_id), (_ext_id)),                             \
-       SPI_NOR_GEOMETRY((_sector_size), (_n_sectors)),
+       SPI_NOR_GEOMETRY((_sector_size), (_n_sectors), 1),
+ #define INFOB(_jedec_id, _ext_id, _sector_size, _n_sectors, _n_banks) \
+       SPI_NOR_ID((_jedec_id), (_ext_id)),                             \
+       SPI_NOR_GEOMETRY((_sector_size), (_n_sectors), (_n_banks)),
  
  #define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors)           \
        SPI_NOR_ID6((_jedec_id), (_ext_id)),                            \
-       SPI_NOR_GEOMETRY((_sector_size), (_n_sectors)),
+       SPI_NOR_GEOMETRY((_sector_size), (_n_sectors), 1),
  
  #define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_nbytes)        \
                .sector_size = (_sector_size),                          \
                .n_sectors = (_n_sectors),                              \
                .page_size = (_page_size),                              \
+               .n_banks = 1,                                           \
                .addr_nbytes = (_addr_nbytes),                          \
                .flags = SPI_NOR_NO_ERASE | SPI_NOR_NO_FR,              \
  
@@@ -634,10 -652,14 +652,14 @@@ void spi_nor_spimem_setup_op(const stru
                             const enum spi_nor_protocol proto);
  int spi_nor_write_enable(struct spi_nor *nor);
  int spi_nor_write_disable(struct spi_nor *nor);
+ int spi_nor_set_4byte_addr_mode_en4b_ex4b(struct spi_nor *nor, bool enable);
+ int spi_nor_set_4byte_addr_mode_wren_en4b_ex4b(struct spi_nor *nor,
+                                              bool enable);
+ int spi_nor_set_4byte_addr_mode_brwr(struct spi_nor *nor, bool enable);
  int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable);
  int spi_nor_wait_till_ready(struct spi_nor *nor);
  int spi_nor_global_block_unlock(struct spi_nor *nor);
- int spi_nor_lock_and_prep(struct spi_nor *nor);
+ int spi_nor_prep_and_lock(struct spi_nor *nor);
  void spi_nor_unlock_and_unprep(struct spi_nor *nor);
  int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor);
  int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor);
@@@ -711,10 -733,8 +733,10 @@@ static inline struct spi_nor *mtd_to_sp
  
  #ifdef CONFIG_DEBUG_FS
  void spi_nor_debugfs_register(struct spi_nor *nor);
 +void spi_nor_debugfs_shutdown(void);
  #else
  static inline void spi_nor_debugfs_register(struct spi_nor *nor) {}
 +static inline void spi_nor_debugfs_shutdown(void) {}
  #endif
  
  #endif /* __LINUX_MTD_SPI_NOR_INTERNAL_H */
index fc7ad203df12802b0f5aa5c386a5077b3844143e,082c0c5a8626dba9cf1574833ed6b33b2d97f58b..e11536fffe0f462285e756aaf2df0e3c73fcb7ce
@@@ -25,6 -25,8 +25,8 @@@ static const char *const snor_f_names[
        SNOR_F_NAME(IO_MODE_EN_VOLATILE),
        SNOR_F_NAME(SOFT_RESET),
        SNOR_F_NAME(SWP_IS_VOLATILE),
+       SNOR_F_NAME(RWW),
+       SNOR_F_NAME(ECC),
  };
  #undef SNOR_F_NAME
  
@@@ -226,13 -228,13 +228,13 @@@ static void spi_nor_debugfs_unregister(
        nor->debugfs_root = NULL;
  }
  
 +static struct dentry *rootdir;
 +
  void spi_nor_debugfs_register(struct spi_nor *nor)
  {
 -      struct dentry *rootdir, *d;
 +      struct dentry *d;
        int ret;
  
 -      /* Create rootdir once. Will never be deleted again. */
 -      rootdir = debugfs_lookup(SPI_NOR_DEBUGFS_ROOT, NULL);
        if (!rootdir)
                rootdir = debugfs_create_dir(SPI_NOR_DEBUGFS_ROOT, NULL);
  
        debugfs_create_file("capabilities", 0444, d, nor,
                            &spi_nor_capabilities_fops);
  }
 +
 +void spi_nor_debugfs_shutdown(void)
 +{
 +      debugfs_remove(rootdir);
 +}
This page took 0.15702 seconds and 4 git commands to generate.