Merge tag 'mtd/for-5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 24 May 2022 21:31:29 +0000 (14:31 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 24 May 2022 21:31:29 +0000 (14:31 -0700)
Pull mtd updates from Miquel Raynal:
 "MTD core changes:
   - Call of_platform_populate() for MTD partitions
   - Check devicetree alias for index
   - mtdoops:
      - Add a timestamp to the mtdoops header.
      - Create a header structure for the saved mtdoops.
      - Fix the size of the header read buffer.
   - mtdblock: Warn if opened on NAND
   - Bindings:
      - reserved-memory: Support MTD/block device
      - jedec,spi-nor: remove unneeded properties
      - Extend fixed-partitions binding
      - Add Sercomm (Suzhou) Corporation vendor prefix

  MTD driver changes:
   - st_spi_fsm: add missing clk_disable_unprepare() in stfsm_remove()
   - phram:
      - Allow cached mappings
      - Allow probing via reserved-memory
   - maps: ixp4xx: Drop driver
   - bcm47xxpart: Print correct offset on read error

  CFI driver changes:
   - Rename chip_ready variables
   - Add S29GL064N ID definition
   - Use chip_ready() for write on S29GL064N
   - Move and rename chip_check/chip_ready/chip_good_for_write

  NAND core changes:
   - Print offset instead of page number for bad blocks

  Raw NAND controller drivers:
   - Cadence: Fix possible null-ptr-deref in cadence_nand_dt_probe()
   - CS553X: simplify the return expression of cs553x_write_ctrl_byte()
   - Davinci: Remove redundant unsigned comparison to zero
   - Denali: Use managed device resources
   - GPMI:
      - Add large oob bch setting support
      - Rename the variable ecc_chunk_size
      - Uninline the gpmi_check_ecc function
      - Add strict ecc strength check
      - Refactor BCH geometry settings function
   - Intel: Fix possible null-ptr-deref in ebu_nand_probe()
   - MPC5121: Check before clk_disable_unprepare() not needed
   - Mtk:
      - MTD_NAND_ECC_MEDIATEK should depend on ARCH_MEDIATEK
      - Also parse the default nand-ecc-engine property if available
      - Make mtk_ecc.c a separated module
   - OMAP ELM:
      - Convert the bindings to yaml
      - Describe the bindings for AM64 ELM
      - Add support for its compatible
   - Renesas: Use runtime PM instead of the raw clock API and update the
     bindings accordingly
   - Rockchip: Check before clk_disable_unprepare() not needed
   - TMIO: Check return value after calling platform_get_resource()

  Raw NAND chip driver:
   - Kioxia: Add support for TH58NVG3S0HBAI4 and TC58NVG0S3HTA00

  SPI-NAND chip drivers:
   - Gigadevice:
      - Add support for:
         - GD5FxGM7xExxG
         - GD5F{2,4}GQ5xExxG
         - GD5F1GQ5RExxG
         - GD5FxGQ4xExxG
      - Fix Quad IO for GD5F1GQ5UExxG
   - XTX: Add support for XT26G0xA

  SPI NOR core changes:
   - Read back written SR value to make sure the write was done
     correctly.
   - Introduce a common function for Read ID that manufacturer drivers
     can use to verify the Octal DTR switch worked correctly.
   - Add helpers for read/write any register commands so manufacturer
     drivers don't open code it every time.
   - Clarify rdsr dummy cycles documentation.
   - Add debugfs entry to expose internal flash parameters and state.

  SPI NOR manufacturer drivers changes:
   - Add support for Winbond W25Q512NW-IM, and Eon EN25QH256A.
   - Move spi_nor_write_ear() to Winbond module since only Winbond
     flashes use it.
   - Rework Micron and Cypress Octal DTR enable methods to improve
     readability.
   - Use the common Read ID function to verify switch to Octal DTR mode
     for Micron and Cypress flashes.
   - Skip polling status on volatile register writes for Micron and
     Cypress flashes since the operation is instant"

* tag 'mtd/for-5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (68 commits)
  mtd: st_spi_fsm: add missing clk_disable_unprepare() in stfsm_remove()
  dt-bindings: mtd: partitions: Extend fixed-partitions binding
  dt-bindings: Add Sercomm (Suzhou) Corporation vendor prefix
  mtd: phram: Allow cached mappings
  mtd: call of_platform_populate() for MTD partitions
  mtd: rawnand: renesas: Use runtime PM instead of the raw clock API
  dt-bindings: mtd: renesas: Fix the NAND controller description
  mtd: rawnand: mpc5121: Check before clk_disable_unprepare() not needed
  mtd: rawnand: rockchip: Check before clk_disable_unprepare() not needed
  mtd: nand: MTD_NAND_ECC_MEDIATEK should depend on ARCH_MEDIATEK
  mtd: rawnand: cs553x: simplify the return expression of cs553x_write_ctrl_byte()
  mtd: rawnand: kioxia: Add support for TH58NVG3S0HBAI4
  mtd: spi-nor: debugfs: fix format specifier
  mtd: spi-nor: support eon en25qh256a variant
  mtd: spi-nor: winbond: add support for W25Q512NW-IM
  mtd: spi-nor: expose internal parameters via debugfs
  mtd: spi-nor: export spi_nor_hwcaps_pp2cmd()
  mtd: spi-nor: move spi_nor_write_ear() to winbond module
  mtd: spi-nor: amend the rdsr dummy cycles documentation
  mtd: cfi_cmdset_0002: Rename chip_ready variables
  ...

59 files changed:
Documentation/devicetree/bindings/mtd/elm.txt [deleted file]
Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml
Documentation/devicetree/bindings/mtd/partitions/fixed-partitions.yaml
Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
Documentation/devicetree/bindings/mtd/ti,elm.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/reserved-memory/phram.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/vendor-prefixes.yaml
drivers/mtd/chips/cfi_cmdset_0002.c
drivers/mtd/devices/phram.c
drivers/mtd/devices/st_spi_fsm.c
drivers/mtd/maps/Kconfig
drivers/mtd/maps/Makefile
drivers/mtd/maps/ixp4xx.c [deleted file]
drivers/mtd/mtdblock.c
drivers/mtd/mtdcore.c
drivers/mtd/mtdoops.c
drivers/mtd/mtdpart.c
drivers/mtd/nand/Kconfig
drivers/mtd/nand/Makefile
drivers/mtd/nand/ecc-mtk.c [new file with mode: 0644]
drivers/mtd/nand/raw/Kconfig
drivers/mtd/nand/raw/Makefile
drivers/mtd/nand/raw/cadence-nand-controller.c
drivers/mtd/nand/raw/cs553x_nand.c
drivers/mtd/nand/raw/davinci_nand.c
drivers/mtd/nand/raw/denali_pci.c
drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h
drivers/mtd/nand/raw/intel-nand-controller.c
drivers/mtd/nand/raw/mpc5121_nfc.c
drivers/mtd/nand/raw/mtk_ecc.c [deleted file]
drivers/mtd/nand/raw/mtk_ecc.h [deleted file]
drivers/mtd/nand/raw/mtk_nand.c
drivers/mtd/nand/raw/nand_base.c
drivers/mtd/nand/raw/nand_ids.c
drivers/mtd/nand/raw/nand_toshiba.c
drivers/mtd/nand/raw/omap_elm.c
drivers/mtd/nand/raw/renesas-nand-controller.c
drivers/mtd/nand/raw/rockchip-nand-controller.c
drivers/mtd/nand/raw/tmio_nand.c
drivers/mtd/nand/spi/Makefile
drivers/mtd/nand/spi/core.c
drivers/mtd/nand/spi/gigadevice.c
drivers/mtd/nand/spi/xtx.c [new file with mode: 0644]
drivers/mtd/parsers/bcm47xxpart.c
drivers/mtd/spi-nor/Makefile
drivers/mtd/spi-nor/core.c
drivers/mtd/spi-nor/core.h
drivers/mtd/spi-nor/debugfs.c [new file with mode: 0644]
drivers/mtd/spi-nor/eon.c
drivers/mtd/spi-nor/micron-st.c
drivers/mtd/spi-nor/spansion.c
drivers/mtd/spi-nor/winbond.c
drivers/mtd/spi-nor/xilinx.c
drivers/of/platform.c
include/linux/mtd/cfi.h
include/linux/mtd/nand-ecc-mtk.h [new file with mode: 0644]
include/linux/mtd/spi-nor.h
include/linux/mtd/spinand.h

diff --git a/Documentation/devicetree/bindings/mtd/elm.txt b/Documentation/devicetree/bindings/mtd/elm.txt
deleted file mode 100644 (file)
index 59ddc61..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-Error location module
-
-Required properties:
-- compatible: Must be "ti,am3352-elm"
-- reg: physical base address and size of the registers map.
-- interrupts: Interrupt number for the elm.
-
-Optional properties:
-- ti,hwmods: Name of the hwmod associated to the elm
-
-Example:
-elm: elm@0 {
-       compatible = "ti,am3352-elm";
-       reg = <0x48080000 0x2000>;
-       interrupts = <4>;
-};
index 4abfb4c..7149784 100644 (file)
@@ -50,10 +50,6 @@ properties:
     minItems: 1
     maxItems: 2
 
-  spi-max-frequency: true
-  spi-rx-bus-width: true
-  spi-tx-bus-width: true
-
   m25p,fast-read:
     type: boolean
     description:
@@ -74,8 +70,6 @@ properties:
       be used on such systems, to denote the absence of a reliable reset
       mechanism.
 
-  label: true
-
   partitions:
     type: object
 
@@ -99,8 +93,6 @@ examples:
         #size-cells = <0>;
 
         flash@0 {
-            #address-cells = <1>;
-            #size-cells = <1>;
             compatible = "spansion,m25p80", "jedec,spi-nor";
             reg = <0>;
             spi-max-frequency = <40000000>;
index ea4cace..ad3ccd2 100644 (file)
@@ -19,7 +19,11 @@ maintainers:
 
 properties:
   compatible:
-    const: fixed-partitions
+    oneOf:
+      - const: fixed-partitions
+      - items:
+          - const: sercomm,sc-partitions
+          - const: fixed-partitions
 
   "#address-cells": true
 
@@ -27,7 +31,24 @@ properties:
 
 patternProperties:
   "@[0-9a-f]+$":
-    $ref: "partition.yaml#"
+    allOf:
+      - $ref: "partition.yaml#"
+      - if:
+          properties:
+            compatible:
+              contains:
+                const: sercomm,sc-partitions
+        then:
+          properties:
+            sercomm,scpart-id:
+              description: Partition id in Sercomm partition map. Mtd
+                parser uses this id to find a record in the partition map
+                containing offset and size of the current partition. The
+                values from partition map overrides partition offset and
+                size defined in reg property of the dts. Frequently these
+                values are the same, but may differ if device has bad
+                eraseblocks on a flash.
+              $ref: /schemas/types.yaml#/definitions/uint32
 
 required:
   - "#address-cells"
@@ -52,6 +73,7 @@ examples:
             reg = <0x0100000 0x200000>;
         };
     };
+
   - |
     partitions {
         compatible = "fixed-partitions";
@@ -64,6 +86,7 @@ examples:
             reg = <0x00000000 0x1 0x00000000>;
         };
     };
+
   - |
     partitions {
         compatible = "fixed-partitions";
@@ -82,6 +105,7 @@ examples:
             reg = <0x2 0x00000000 0x1 0x00000000>;
         };
     };
+
   - |
     partitions {
         compatible = "fixed-partitions";
@@ -119,3 +143,30 @@ examples:
             };
         };
     };
+
+  - |
+    partitions {
+        compatible = "sercomm,sc-partitions", "fixed-partitions";
+        #address-cells = <1>;
+        #size-cells = <1>;
+
+        partition@0 {
+            label = "u-boot";
+            reg = <0x0 0x100000>;
+            sercomm,scpart-id = <0>;
+            read-only;
+        };
+
+        partition@100000 {
+            label = "dynamic partition map";
+            reg = <0x100000 0x100000>;
+            sercomm,scpart-id = <1>;
+        };
+
+        partition@200000 {
+            label = "Factory";
+            reg = <0x200000 0x100000>;
+            sercomm,scpart-id = <2>;
+            read-only;
+        };
+    };
index 2870d36..7b18bc5 100644 (file)
@@ -36,11 +36,15 @@ properties:
       - const: hclk
       - const: eclk
 
+  power-domains:
+    maxItems: 1
+
 required:
   - compatible
   - reg
   - clocks
   - clock-names
+  - power-domains
   - interrupts
 
 unevaluatedProperties: false
@@ -56,6 +60,7 @@ examples:
         interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
         clocks = <&sysctrl R9A06G032_HCLK_NAND>, <&sysctrl R9A06G032_CLK_NAND>;
         clock-names = "hclk", "eclk";
+        power-domains = <&sysctrl>;
         #address-cells = <1>;
         #size-cells = <0>;
     };
diff --git a/Documentation/devicetree/bindings/mtd/ti,elm.yaml b/Documentation/devicetree/bindings/mtd/ti,elm.yaml
new file mode 100644 (file)
index 0000000..87128c0
--- /dev/null
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mtd/ti,elm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments Error Location Module (ELM).
+
+maintainers:
+  - Roger Quadros <rogerq@kernel.org>
+
+description:
+  ELM module is used together with GPMC and NAND Flash to detect
+  errors and the location of the error based on BCH algorithms
+  so they can be corrected if possible.
+
+properties:
+  compatible:
+    enum:
+      - ti,am3352-elm
+      - ti,am64-elm
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+    description: Functional clock.
+
+  clock-names:
+    items:
+      - const: fck
+
+  power-domains:
+    maxItems: 1
+
+  ti,hwmods:
+    description:
+      Name of the HWMOD associated with ELM. This is for legacy
+      platforms only.
+    $ref: /schemas/types.yaml#/definitions/string
+    deprecated: true
+
+required:
+  - compatible
+  - reg
+  - interrupts
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: ti,am64-elm
+    then:
+      required:
+        - clocks
+        - clock-names
+        - power-domains
+
+additionalProperties: false
+
+examples:
+  - |
+    elm: ecc@0 {
+        compatible = "ti,am3352-elm";
+        reg = <0x0 0x2000>;
+        interrupts = <4>;
+    };
diff --git a/Documentation/devicetree/bindings/reserved-memory/phram.yaml b/Documentation/devicetree/bindings/reserved-memory/phram.yaml
new file mode 100644 (file)
index 0000000..6c4db28
--- /dev/null
@@ -0,0 +1,47 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/reserved-memory/phram.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MTD/block device in RAM
+
+description: |
+  Specifies that the reserved memory region can be used as an MTD or block
+  device.
+
+  The "phram" node is named after the "MTD in PHysical RAM" driver which
+  provides an implementation of this functionality in Linux.
+
+maintainers:
+  - Vincent Whitchurch <vincent.whitchurch@axis.com>
+
+allOf:
+  - $ref: "reserved-memory.yaml"
+  - $ref: "/schemas/mtd/mtd.yaml"
+
+properties:
+  compatible:
+    const: phram
+
+  reg:
+    description: region of memory that can be used as an MTD/block device
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    reserved-memory {
+        #address-cells = <1>;
+        #size-cells = <1>;
+
+        phram: flash@12340000 {
+            compatible = "phram";
+            label = "rootfs";
+            reg = <0x12340000 0x00800000>;
+        };
+    };
index 0143097..65ff223 100644 (file)
@@ -1082,6 +1082,8 @@ patternProperties:
     description: Sensirion AG
   "^sensortek,.*":
     description: Sensortek Technology Corporation
+  "^sercomm,.*":
+    description: Sercomm (Suzhou) Corporation
   "^sff,.*":
     description: Small Form Factor Committee
   "^sgd,.*":
index a761134..67453f5 100644 (file)
@@ -48,6 +48,7 @@
 #define SST49LF040B            0x0050
 #define SST49LF008A            0x005a
 #define AT49BV6416             0x00d6
+#define S29GL064N_MN12         0x0c01
 
 /*
  * Status Register bit description. Used by flash devices that don't
 #define CFI_SR_WBASB           BIT(3)
 #define CFI_SR_SLSB            BIT(1)
 
+enum cfi_quirks {
+       CFI_QUIRK_DQ_TRUE_DATA = BIT(0),
+};
+
 static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
 static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
 #if !FORCE_WORD_WRITE
@@ -436,6 +441,15 @@ static void fixup_s29ns512p_sectors(struct mtd_info *mtd)
                mtd->name);
 }
 
+static void fixup_quirks(struct mtd_info *mtd)
+{
+       struct map_info *map = mtd->priv;
+       struct cfi_private *cfi = map->fldrv_priv;
+
+       if (cfi->mfr == CFI_MFR_AMD && cfi->id == S29GL064N_MN12)
+               cfi->quirks |= CFI_QUIRK_DQ_TRUE_DATA;
+}
+
 /* Used to fix CFI-Tables of chips without Extended Query Tables */
 static struct cfi_fixup cfi_nopri_fixup_table[] = {
        { CFI_MFR_SST, 0x234a, fixup_sst39vf }, /* SST39VF1602 */
@@ -462,7 +476,7 @@ static struct cfi_fixup cfi_fixup_table[] = {
        { CFI_MFR_AMD, 0x0056, fixup_use_secsi },
        { CFI_MFR_AMD, 0x005C, fixup_use_secsi },
        { CFI_MFR_AMD, 0x005F, fixup_use_secsi },
-       { CFI_MFR_AMD, 0x0c01, fixup_s29gl064n_sectors },
+       { CFI_MFR_AMD, S29GL064N_MN12, fixup_s29gl064n_sectors },
        { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors },
        { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors },
        { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors },
@@ -474,6 +488,7 @@ static struct cfi_fixup cfi_fixup_table[] = {
 #if !FORCE_WORD_WRITE
        { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers },
 #endif
+       { CFI_MFR_ANY, CFI_ID_ANY, fixup_quirks },
        { 0, 0, NULL }
 };
 static struct cfi_fixup jedec_fixup_table[] = {
@@ -801,47 +816,11 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
        return NULL;
 }
 
-/*
- * Return true if the chip is ready.
- *
- * Ready is one of: read mode, query mode, erase-suspend-read mode (in any
- * non-suspended sector) and is indicated by no toggle bits toggling.
- *
- * Note that anything more complicated than checking if no bits are toggling
- * (including checking DQ5 for an error status) is tricky to get working
- * correctly and is therefore not done (particularly with interleaved chips
- * as each chip must be checked independently of the others).
- */
-static int __xipram chip_ready(struct map_info *map, struct flchip *chip,
-                              unsigned long addr)
-{
-       struct cfi_private *cfi = map->fldrv_priv;
-       map_word d, t;
-
-       if (cfi_use_status_reg(cfi)) {
-               map_word ready = CMD(CFI_SR_DRB);
-               /*
-                * For chips that support status register, check device
-                * ready bit
-                */
-               cfi_send_gen_cmd(0x70, cfi->addr_unlock1, chip->start, map, cfi,
-                                cfi->device_type, NULL);
-               d = map_read(map, addr);
-
-               return map_word_andequal(map, d, ready, ready);
-       }
-
-       d = map_read(map, addr);
-       t = map_read(map, addr);
-
-       return map_word_equal(map, d, t);
-}
-
 /*
  * Return true if the chip is ready and has the correct value.
  *
  * Ready is one of: read mode, query mode, erase-suspend-read mode (in any
- * non-suspended sector) and it is indicated by no bits toggling.
+ * non-suspended sector) and is indicated by no toggle bits toggling.
  *
  * Error are indicated by toggling bits or bits held with the wrong value,
  * or with bits toggling.
@@ -850,17 +829,16 @@ static int __xipram chip_ready(struct map_info *map, struct flchip *chip,
  * (including checking DQ5 for an error status) is tricky to get working
  * correctly and is therefore not done (particularly with interleaved chips
  * as each chip must be checked independently of the others).
- *
  */
-static int __xipram chip_good(struct map_info *map, struct flchip *chip,
-                             unsigned long addr, map_word expected)
+static int __xipram chip_ready(struct map_info *map, struct flchip *chip,
+                              unsigned long addr, map_word *expected)
 {
        struct cfi_private *cfi = map->fldrv_priv;
        map_word oldd, curd;
+       int ret;
 
        if (cfi_use_status_reg(cfi)) {
                map_word ready = CMD(CFI_SR_DRB);
-
                /*
                 * For chips that support status register, check device
                 * ready bit
@@ -875,8 +853,24 @@ static int __xipram chip_good(struct map_info *map, struct flchip *chip,
        oldd = map_read(map, addr);
        curd = map_read(map, addr);
 
-       return  map_word_equal(map, oldd, curd) &&
-               map_word_equal(map, curd, expected);
+       ret = map_word_equal(map, oldd, curd);
+
+       if (!ret || !expected)
+               return ret;
+
+       return map_word_equal(map, curd, *expected);
+}
+
+static int __xipram chip_good(struct map_info *map, struct flchip *chip,
+                             unsigned long addr, map_word *expected)
+{
+       struct cfi_private *cfi = map->fldrv_priv;
+       map_word *datum = expected;
+
+       if (cfi->quirks & CFI_QUIRK_DQ_TRUE_DATA)
+               datum = NULL;
+
+       return chip_ready(map, chip, addr, datum);
 }
 
 static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
@@ -893,7 +887,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
 
        case FL_STATUS:
                for (;;) {
-                       if (chip_ready(map, chip, adr))
+                       if (chip_ready(map, chip, adr, NULL))
                                break;
 
                        if (time_after(jiffies, timeo)) {
@@ -932,7 +926,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
                chip->state = FL_ERASE_SUSPENDING;
                chip->erase_suspended = 1;
                for (;;) {
-                       if (chip_ready(map, chip, adr))
+                       if (chip_ready(map, chip, adr, NULL))
                                break;
 
                        if (time_after(jiffies, timeo)) {
@@ -1463,7 +1457,7 @@ static int do_otp_lock(struct map_info *map, struct flchip *chip, loff_t adr,
        /* wait for chip to become ready */
        timeo = jiffies + msecs_to_jiffies(2);
        for (;;) {
-               if (chip_ready(map, chip, adr))
+               if (chip_ready(map, chip, adr, NULL))
                        break;
 
                if (time_after(jiffies, timeo)) {
@@ -1699,7 +1693,7 @@ static int __xipram do_write_oneword_once(struct map_info *map,
                 * "chip_good" to avoid the failure due to scheduling.
                 */
                if (time_after(jiffies, timeo) &&
-                   !chip_good(map, chip, adr, datum)) {
+                   !chip_good(map, chip, adr, &datum)) {
                        xip_enable(map, chip, adr);
                        printk(KERN_WARNING "MTD %s(): software timeout\n", __func__);
                        xip_disable(map, chip, adr);
@@ -1707,7 +1701,7 @@ static int __xipram do_write_oneword_once(struct map_info *map,
                        break;
                }
 
-               if (chip_good(map, chip, adr, datum)) {
+               if (chip_good(map, chip, adr, &datum)) {
                        if (cfi_check_err_status(map, chip, adr))
                                ret = -EIO;
                        break;
@@ -1979,14 +1973,14 @@ static int __xipram do_write_buffer_wait(struct map_info *map,
                 * "chip_good" to avoid the failure due to scheduling.
                 */
                if (time_after(jiffies, timeo) &&
-                   !chip_good(map, chip, adr, datum)) {
+                   !chip_good(map, chip, adr, &datum)) {
                        pr_err("MTD %s(): software timeout, address:0x%.8lx.\n",
                               __func__, adr);
                        ret = -EIO;
                        break;
                }
 
-               if (chip_good(map, chip, adr, datum)) {
+               if (chip_good(map, chip, adr, &datum)) {
                        if (cfi_check_err_status(map, chip, adr))
                                ret = -EIO;
                        break;
@@ -2195,7 +2189,7 @@ static int cfi_amdstd_panic_wait(struct map_info *map, struct flchip *chip,
         * If the driver thinks the chip is idle, and no toggle bits
         * are changing, then the chip is actually idle for sure.
         */
-       if (chip->state == FL_READY && chip_ready(map, chip, adr))
+       if (chip->state == FL_READY && chip_ready(map, chip, adr, NULL))
                return 0;
 
        /*
@@ -2212,7 +2206,7 @@ static int cfi_amdstd_panic_wait(struct map_info *map, struct flchip *chip,
 
                /* wait for the chip to become ready */
                for (i = 0; i < jiffies_to_usecs(timeo); i++) {
-                       if (chip_ready(map, chip, adr))
+                       if (chip_ready(map, chip, adr, NULL))
                                return 0;
 
                        udelay(1);
@@ -2276,13 +2270,13 @@ retry:
        map_write(map, datum, adr);
 
        for (i = 0; i < jiffies_to_usecs(uWriteTimeout); i++) {
-               if (chip_ready(map, chip, adr))
+               if (chip_ready(map, chip, adr, NULL))
                        break;
 
                udelay(1);
        }
 
-       if (!chip_good(map, chip, adr, datum) ||
+       if (!chip_ready(map, chip, adr, &datum) ||
            cfi_check_err_status(map, chip, adr)) {
                /* reset on all failures. */
                map_write(map, CMD(0xF0), chip->start);
@@ -2424,6 +2418,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
        DECLARE_WAITQUEUE(wait, current);
        int ret;
        int retry_cnt = 0;
+       map_word datum = map_word_ff(map);
 
        adr = cfi->addr_unlock1;
 
@@ -2478,7 +2473,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
                        chip->erase_suspended = 0;
                }
 
-               if (chip_good(map, chip, adr, map_word_ff(map))) {
+               if (chip_ready(map, chip, adr, &datum)) {
                        if (cfi_check_err_status(map, chip, adr))
                                ret = -EIO;
                        break;
@@ -2523,6 +2518,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
        DECLARE_WAITQUEUE(wait, current);
        int ret;
        int retry_cnt = 0;
+       map_word datum = map_word_ff(map);
 
        adr += chip->start;
 
@@ -2577,7 +2573,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
                        chip->erase_suspended = 0;
                }
 
-               if (chip_good(map, chip, adr, map_word_ff(map))) {
+               if (chip_ready(map, chip, adr, &datum)) {
                        if (cfi_check_err_status(map, chip, adr))
                                ret = -EIO;
                        break;
@@ -2771,7 +2767,7 @@ static int __maybe_unused do_ppb_xxlock(struct map_info *map,
         */
        timeo = jiffies + msecs_to_jiffies(2000);       /* 2s max (un)locking */
        for (;;) {
-               if (chip_ready(map, chip, adr))
+               if (chip_ready(map, chip, adr, NULL))
                        break;
 
                if (time_after(jiffies, timeo)) {
index d503821..208bd4d 100644 (file)
 #include <linux/slab.h>
 #include <linux/mtd/mtd.h>
 #include <asm/div64.h>
+#include <linux/platform_device.h>
+#include <linux/of_address.h>
+#include <linux/of.h>
 
 struct phram_mtd_list {
        struct mtd_info mtd;
        struct list_head list;
+       bool cached;
 };
 
 static LIST_HEAD(phram_list);
@@ -77,20 +81,51 @@ static int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
        return 0;
 }
 
+static int phram_map(struct phram_mtd_list *phram, phys_addr_t start, size_t len)
+{
+       void *addr = NULL;
+
+       if (phram->cached)
+               addr = memremap(start, len, MEMREMAP_WB);
+       else
+               addr = (void __force *)ioremap(start, len);
+       if (!addr)
+               return -EIO;
+
+       phram->mtd.priv = addr;
+
+       return 0;
+}
+
+static void phram_unmap(struct phram_mtd_list *phram)
+{
+       void *addr = phram->mtd.priv;
+
+       if (phram->cached) {
+               memunmap(addr);
+               return;
+       }
+
+       iounmap((void __iomem *)addr);
+}
+
 static void unregister_devices(void)
 {
        struct phram_mtd_list *this, *safe;
 
        list_for_each_entry_safe(this, safe, &phram_list, list) {
                mtd_device_unregister(&this->mtd);
-               iounmap(this->mtd.priv);
+               phram_unmap(this);
                kfree(this->mtd.name);
                kfree(this);
        }
 }
 
-static int register_device(char *name, phys_addr_t start, size_t len, uint32_t erasesize)
+static int register_device(struct platform_device *pdev, const char *name,
+                          phys_addr_t start, size_t len, uint32_t erasesize)
 {
+       struct device_node *np = pdev ? pdev->dev.of_node : NULL;
+       bool cached = np ? !of_property_read_bool(np, "no-map") : false;
        struct phram_mtd_list *new;
        int ret = -ENOMEM;
 
@@ -98,9 +133,10 @@ static int register_device(char *name, phys_addr_t start, size_t len, uint32_t e
        if (!new)
                goto out0;
 
-       ret = -EIO;
-       new->mtd.priv = ioremap(start, len);
-       if (!new->mtd.priv) {
+       new->cached = cached;
+
+       ret = phram_map(new, start, len);
+       if (ret) {
                pr_err("ioremap failed\n");
                goto out1;
        }
@@ -119,17 +155,23 @@ static int register_device(char *name, phys_addr_t start, size_t len, uint32_t e
        new->mtd.erasesize = erasesize;
        new->mtd.writesize = 1;
 
+       mtd_set_of_node(&new->mtd, np);
+
        ret = -EAGAIN;
        if (mtd_device_register(&new->mtd, NULL, 0)) {
                pr_err("Failed to register new device\n");
                goto out2;
        }
 
-       list_add_tail(&new->list, &phram_list);
+       if (pdev)
+               platform_set_drvdata(pdev, new);
+       else
+               list_add_tail(&new->list, &phram_list);
+
        return 0;
 
 out2:
-       iounmap(new->mtd.priv);
+       phram_unmap(new);
 out1:
        kfree(new);
 out0:
@@ -278,7 +320,7 @@ static int phram_setup(const char *val)
                goto error;
        }
 
-       ret = register_device(name, start, len, (uint32_t)erasesize);
+       ret = register_device(NULL, name, start, len, (uint32_t)erasesize);
        if (ret)
                goto error;
 
@@ -325,10 +367,54 @@ static int phram_param_call(const char *val, const struct kernel_param *kp)
 module_param_call(phram, phram_param_call, NULL, NULL, 0200);
 MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>[,<erasesize>]\"");
 
+#ifdef CONFIG_OF
+static const struct of_device_id phram_of_match[] = {
+       { .compatible = "phram" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, phram_of_match);
+#endif
+
+static int phram_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENOMEM;
+
+       /* mtd_set_of_node() reads name from "label" */
+       return register_device(pdev, NULL, res->start, resource_size(res),
+                              PAGE_SIZE);
+}
+
+static int phram_remove(struct platform_device *pdev)
+{
+       struct phram_mtd_list *phram = platform_get_drvdata(pdev);
+
+       mtd_device_unregister(&phram->mtd);
+       phram_unmap(phram);
+       kfree(phram);
+
+       return 0;
+}
+
+static struct platform_driver phram_driver = {
+       .probe          = phram_probe,
+       .remove         = phram_remove,
+       .driver         = {
+               .name           = "phram",
+               .of_match_table = of_match_ptr(phram_of_match),
+       },
+};
 
 static int __init init_phram(void)
 {
-       int ret = 0;
+       int ret;
+
+       ret = platform_driver_register(&phram_driver);
+       if (ret)
+               return ret;
 
 #ifndef MODULE
        if (phram_paramline[0])
@@ -336,12 +422,16 @@ static int __init init_phram(void)
        phram_init_called = 1;
 #endif
 
+       if (ret)
+               platform_driver_unregister(&phram_driver);
+
        return ret;
 }
 
 static void __exit cleanup_phram(void)
 {
        unregister_devices();
+       platform_driver_unregister(&phram_driver);
 }
 
 module_init(init_phram);
index 983999c..d3377b1 100644 (file)
@@ -2126,6 +2126,8 @@ static int stfsm_remove(struct platform_device *pdev)
 {
        struct stfsm *fsm = platform_get_drvdata(pdev);
 
+       clk_disable_unprepare(fsm->clk);
+
        return mtd_device_unregister(&fsm->mtd);
 }
 
index 6a099bb..e098ae9 100644 (file)
@@ -300,15 +300,6 @@ config MTD_DC21285
          21285 bridge used with Intel's StrongARM processors. More info at
          <https://www.intel.com/design/bridge/docs/21285_documentation.htm>.
 
-config MTD_IXP4XX
-       tristate "CFI Flash device mapped on Intel IXP4xx based systems"
-       depends on MTD_CFI && MTD_COMPLEX_MAPPINGS && ARCH_IXP4XX && MTD_CFI_ADV_OPTIONS
-       help
-         This enables MTD access to flash devices on platforms based
-         on Intel's IXP4xx family of network processors such as the
-         IXDP425 and Coyote. If you have an IXP4xx based board and
-         would like to use the flash chips on it, say 'Y'.
-
 config MTD_IMPA7
        tristate "JEDEC Flash device mapped on impA7"
        depends on ARM && MTD_JEDECPROBE
index 2240b10..094cfb2 100644 (file)
@@ -39,7 +39,6 @@ obj-$(CONFIG_MTD_IMPA7)               += impa7.o
 obj-$(CONFIG_MTD_UCLINUX)      += uclinux.o
 obj-$(CONFIG_MTD_NETtel)       += nettel.o
 obj-$(CONFIG_MTD_SCB2_FLASH)   += scb2_flash.o
-obj-$(CONFIG_MTD_IXP4XX)       += ixp4xx.o
 obj-$(CONFIG_MTD_PLATRAM)      += plat-ram.o
 obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
 obj-$(CONFIG_MTD_VMU)          += vmu-flash.o
diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c
deleted file mode 100644 (file)
index d854320..0000000
+++ /dev/null
@@ -1,262 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * drivers/mtd/maps/ixp4xx.c
- *
- * MTD Map file for IXP4XX based systems. Please do not make per-board
- * changes in here. If your board needs special setup, do it in your
- * platform level code in arch/arm/mach-ixp4xx/board-setup.c
- *
- * Original Author: Intel Corporation
- * Maintainer: Deepak Saxena <dsaxena@mvista.com>
- *
- * Copyright (C) 2002 Intel Corporation
- * Copyright (C) 2003-2004 MontaVista Software, Inc.
- *
- */
-
-#include <linux/err.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-
-#include <asm/io.h>
-#include <asm/mach/flash.h>
-
-#include <linux/reboot.h>
-
-/*
- * Read/write a 16 bit word from flash address 'addr'.
- *
- * When the cpu is in little-endian mode it swizzles the address lines
- * ('address coherency') so we need to undo the swizzling to ensure commands
- * and the like end up on the correct flash address.
- *
- * To further complicate matters, due to the way the expansion bus controller
- * handles 32 bit reads, the byte stream ABCD is stored on the flash as:
- *     D15    D0
- *     +---+---+
- *     | A | B | 0
- *     +---+---+
- *     | C | D | 2
- *     +---+---+
- * This means that on LE systems each 16 bit word must be swapped. Note that
- * this requires CONFIG_MTD_CFI_BE_BYTE_SWAP to be enabled to 'unswap' the CFI
- * data and other flash commands which are always in D7-D0.
- */
-#ifndef __ARMEB__
-#ifndef CONFIG_MTD_CFI_BE_BYTE_SWAP
-#  error CONFIG_MTD_CFI_BE_BYTE_SWAP required
-#endif
-
-static inline u16 flash_read16(void __iomem *addr)
-{
-       return be16_to_cpu(__raw_readw((void __iomem *)((unsigned long)addr ^ 0x2)));
-}
-
-static inline void flash_write16(u16 d, void __iomem *addr)
-{
-       __raw_writew(cpu_to_be16(d), (void __iomem *)((unsigned long)addr ^ 0x2));
-}
-
-#define        BYTE0(h)        ((h) & 0xFF)
-#define        BYTE1(h)        (((h) >> 8) & 0xFF)
-
-#else
-
-static inline u16 flash_read16(const void __iomem *addr)
-{
-       return __raw_readw(addr);
-}
-
-static inline void flash_write16(u16 d, void __iomem *addr)
-{
-       __raw_writew(d, addr);
-}
-
-#define        BYTE0(h)        (((h) >> 8) & 0xFF)
-#define        BYTE1(h)        ((h) & 0xFF)
-#endif
-
-static map_word ixp4xx_read16(struct map_info *map, unsigned long ofs)
-{
-       map_word val;
-       val.x[0] = flash_read16(map->virt + ofs);
-       return val;
-}
-
-/*
- * The IXP4xx expansion bus only allows 16-bit wide acceses
- * when attached to a 16-bit wide device (such as the 28F128J3A),
- * so we can't just memcpy_fromio().
- */
-static void ixp4xx_copy_from(struct map_info *map, void *to,
-                            unsigned long from, ssize_t len)
-{
-       u8 *dest = (u8 *) to;
-       void __iomem *src = map->virt + from;
-
-       if (len <= 0)
-               return;
-
-       if (from & 1) {
-               *dest++ = BYTE1(flash_read16(src-1));
-               src++;
-               --len;
-       }
-
-       while (len >= 2) {
-               u16 data = flash_read16(src);
-               *dest++ = BYTE0(data);
-               *dest++ = BYTE1(data);
-               src += 2;
-               len -= 2;
-       }
-
-       if (len > 0)
-               *dest++ = BYTE0(flash_read16(src));
-}
-
-/*
- * Unaligned writes are ignored, causing the 8-bit
- * probe to fail and proceed to the 16-bit probe (which succeeds).
- */
-static void ixp4xx_probe_write16(struct map_info *map, map_word d, unsigned long adr)
-{
-       if (!(adr & 1))
-               flash_write16(d.x[0], map->virt + adr);
-}
-
-/*
- * Fast write16 function without the probing check above
- */
-static void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr)
-{
-       flash_write16(d.x[0], map->virt + adr);
-}
-
-struct ixp4xx_flash_info {
-       struct mtd_info *mtd;
-       struct map_info map;
-       struct resource *res;
-};
-
-static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
-
-static int ixp4xx_flash_remove(struct platform_device *dev)
-{
-       struct flash_platform_data *plat = dev_get_platdata(&dev->dev);
-       struct ixp4xx_flash_info *info = platform_get_drvdata(dev);
-
-       if(!info)
-               return 0;
-
-       if (info->mtd) {
-               mtd_device_unregister(info->mtd);
-               map_destroy(info->mtd);
-       }
-
-       if (plat->exit)
-               plat->exit();
-
-       return 0;
-}
-
-static int ixp4xx_flash_probe(struct platform_device *dev)
-{
-       struct flash_platform_data *plat = dev_get_platdata(&dev->dev);
-       struct ixp4xx_flash_info *info;
-       struct mtd_part_parser_data ppdata = {
-               .origin = dev->resource->start,
-       };
-       int err = -1;
-
-       if (!plat)
-               return -ENODEV;
-
-       if (plat->init) {
-               err = plat->init();
-               if (err)
-                       return err;
-       }
-
-       info = devm_kzalloc(&dev->dev, sizeof(struct ixp4xx_flash_info),
-                           GFP_KERNEL);
-       if(!info) {
-               err = -ENOMEM;
-               goto Error;
-       }
-
-       platform_set_drvdata(dev, info);
-
-       /*
-        * Tell the MTD layer we're not 1:1 mapped so that it does
-        * not attempt to do a direct access on us.
-        */
-       info->map.phys = NO_XIP;
-       info->map.size = resource_size(dev->resource);
-
-       /*
-        * We only support 16-bit accesses for now. If and when
-        * any board use 8-bit access, we'll fixup the driver to
-        * handle that.
-        */
-       info->map.bankwidth = 2;
-       info->map.name = dev_name(&dev->dev);
-       info->map.read = ixp4xx_read16;
-       info->map.write = ixp4xx_probe_write16;
-       info->map.copy_from = ixp4xx_copy_from;
-
-       info->map.virt = devm_ioremap_resource(&dev->dev, dev->resource);
-       if (IS_ERR(info->map.virt)) {
-               err = PTR_ERR(info->map.virt);
-               goto Error;
-       }
-
-       info->mtd = do_map_probe(plat->map_name, &info->map);
-       if (!info->mtd) {
-               printk(KERN_ERR "IXP4XXFlash: map_probe failed\n");
-               err = -ENXIO;
-               goto Error;
-       }
-       info->mtd->dev.parent = &dev->dev;
-
-       /* Use the fast version */
-       info->map.write = ixp4xx_write16;
-
-       err = mtd_device_parse_register(info->mtd, probes, &ppdata,
-                       plat->parts, plat->nr_parts);
-       if (err) {
-               printk(KERN_ERR "Could not parse partitions\n");
-               goto Error;
-       }
-
-       return 0;
-
-Error:
-       ixp4xx_flash_remove(dev);
-       return err;
-}
-
-static struct platform_driver ixp4xx_flash_driver = {
-       .probe          = ixp4xx_flash_probe,
-       .remove         = ixp4xx_flash_remove,
-       .driver         = {
-               .name   = "IXP4XX-Flash",
-       },
-};
-
-module_platform_driver(ixp4xx_flash_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("MTD map driver for Intel IXP4xx systems");
-MODULE_AUTHOR("Deepak Saxena");
-MODULE_ALIAS("platform:IXP4XX-Flash");
index 03e3de3..1e94e7d 100644 (file)
@@ -257,6 +257,10 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd)
                return 0;
        }
 
+       if (mtd_type_is_nand(mbd->mtd))
+               pr_warn("%s: MTD device '%s' is NAND, please consider using UBI block devices instead.\n",
+                       mbd->tr->name, mbd->mtd->name);
+
        /* OK, it's not open. Create cache info for it */
        mtdblk->count = 1;
        mutex_init(&mtdblk->cache_mutex);
@@ -322,10 +326,6 @@ static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
        if (!(mtd->flags & MTD_WRITEABLE))
                dev->mbd.readonly = 1;
 
-       if (mtd_type_is_nand(mtd))
-               pr_warn("%s: MTD device '%s' is NAND, please consider using UBI block devices instead.\n",
-                       tr->name, mtd->name);
-
        if (add_mtd_blktrans_dev(&dev->mbd))
                kfree(dev);
 }
index 7731796..9eb0680 100644 (file)
@@ -557,9 +557,10 @@ static int mtd_nvmem_add(struct mtd_info *mtd)
 
 int add_mtd_device(struct mtd_info *mtd)
 {
+       struct device_node *np = mtd_get_of_node(mtd);
        struct mtd_info *master = mtd_get_master(mtd);
        struct mtd_notifier *not;
-       int i, error;
+       int i, error, ofidx;
 
        /*
         * May occur, for instance, on buggy drivers which call
@@ -598,7 +599,13 @@ int add_mtd_device(struct mtd_info *mtd)
 
        mutex_lock(&mtd_table_mutex);
 
-       i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
+       ofidx = -1;
+       if (np)
+               ofidx = of_alias_get_id(np, "mtd");
+       if (ofidx >= 0)
+               i = idr_alloc(&mtd_idr, mtd, ofidx, ofidx + 1, GFP_KERNEL);
+       else
+               i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
        if (i < 0) {
                error = i;
                goto fail_locked;
index 227df24..3d4a2ff 100644 (file)
 #include <linux/wait.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/timekeeping.h>
 #include <linux/mtd/mtd.h>
 #include <linux/kmsg_dump.h>
 
 /* Maximum MTD partition size */
 #define MTDOOPS_MAX_MTD_SIZE (8 * 1024 * 1024)
 
-#define MTDOOPS_KERNMSG_MAGIC 0x5d005d00
-#define MTDOOPS_HEADER_SIZE   8
-
 static unsigned long record_size = 4096;
 module_param(record_size, ulong, 0400);
 MODULE_PARM_DESC(record_size,
@@ -40,6 +38,15 @@ module_param(dump_oops, int, 0600);
 MODULE_PARM_DESC(dump_oops,
                "set to 1 to dump oopses, 0 to only dump panics (default 1)");
 
+#define MTDOOPS_KERNMSG_MAGIC_v1 0x5d005d00  /* Original */
+#define MTDOOPS_KERNMSG_MAGIC_v2 0x5d005e00  /* Adds the timestamp */
+
+struct mtdoops_hdr {
+       u32 seq;
+       u32 magic;
+       ktime_t timestamp;
+} __packed;
+
 static struct mtdoops_context {
        struct kmsg_dumper dump;
 
@@ -178,16 +185,17 @@ static void mtdoops_write(struct mtdoops_context *cxt, int panic)
 {
        struct mtd_info *mtd = cxt->mtd;
        size_t retlen;
-       u32 *hdr;
+       struct mtdoops_hdr *hdr;
        int ret;
 
        if (test_and_set_bit(0, &cxt->oops_buf_busy))
                return;
 
        /* Add mtdoops header to the buffer */
-       hdr = cxt->oops_buf;
-       hdr[0] = cxt->nextcount;
-       hdr[1] = MTDOOPS_KERNMSG_MAGIC;
+       hdr = (struct mtdoops_hdr *)cxt->oops_buf;
+       hdr->seq = cxt->nextcount;
+       hdr->magic = MTDOOPS_KERNMSG_MAGIC_v2;
+       hdr->timestamp = ktime_get_real();
 
        if (panic) {
                ret = mtd_panic_write(mtd, cxt->nextpage * record_size,
@@ -222,8 +230,9 @@ static void mtdoops_workfunc_write(struct work_struct *work)
 static void find_next_position(struct mtdoops_context *cxt)
 {
        struct mtd_info *mtd = cxt->mtd;
+       struct mtdoops_hdr hdr;
        int ret, page, maxpos = 0;
-       u32 count[2], maxcount = 0xffffffff;
+       u32 maxcount = 0xffffffff;
        size_t retlen;
 
        for (page = 0; page < cxt->oops_pages; page++) {
@@ -231,32 +240,33 @@ static void find_next_position(struct mtdoops_context *cxt)
                        continue;
                /* Assume the page is used */
                mark_page_used(cxt, page);
-               ret = mtd_read(mtd, page * record_size, MTDOOPS_HEADER_SIZE,
-                              &retlen, (u_char *)&count[0]);
-               if (retlen != MTDOOPS_HEADER_SIZE ||
+               ret = mtd_read(mtd, page * record_size, sizeof(hdr),
+                              &retlen, (u_char *)&hdr);
+               if (retlen != sizeof(hdr) ||
                                (ret < 0 && !mtd_is_bitflip(ret))) {
-                       printk(KERN_ERR "mtdoops: read failure at %ld (%td of %d read), err %d\n",
-                              page * record_size, retlen,
-                              MTDOOPS_HEADER_SIZE, ret);
+                       printk(KERN_ERR "mtdoops: read failure at %ld (%zu of %zu read), err %d\n",
+                              page * record_size, retlen, sizeof(hdr), ret);
                        continue;
                }
 
-               if (count[0] == 0xffffffff && count[1] == 0xffffffff)
+               if (hdr.seq == 0xffffffff && hdr.magic == 0xffffffff)
                        mark_page_unused(cxt, page);
-               if (count[0] == 0xffffffff || count[1] != MTDOOPS_KERNMSG_MAGIC)
+               if (hdr.seq == 0xffffffff ||
+                   (hdr.magic != MTDOOPS_KERNMSG_MAGIC_v1 &&
+                    hdr.magic != MTDOOPS_KERNMSG_MAGIC_v2))
                        continue;
                if (maxcount == 0xffffffff) {
-                       maxcount = count[0];
+                       maxcount = hdr.seq;
                        maxpos = page;
-               } else if (count[0] < 0x40000000 && maxcount > 0xc0000000) {
-                       maxcount = count[0];
+               } else if (hdr.seq < 0x40000000 && maxcount > 0xc0000000) {
+                       maxcount = hdr.seq;
                        maxpos = page;
-               } else if (count[0] > maxcount && count[0] < 0xc0000000) {
-                       maxcount = count[0];
+               } else if (hdr.seq > maxcount && hdr.seq < 0xc0000000) {
+                       maxcount = hdr.seq;
                        maxpos = page;
-               } else if (count[0] > maxcount && count[0] > 0xc0000000
+               } else if (hdr.seq > maxcount && hdr.seq > 0xc0000000
                                        && maxcount > 0x80000000) {
-                       maxcount = count[0];
+                       maxcount = hdr.seq;
                        maxpos = page;
                }
        }
@@ -287,8 +297,9 @@ static void mtdoops_do_dump(struct kmsg_dumper *dumper,
 
        if (test_and_set_bit(0, &cxt->oops_buf_busy))
                return;
-       kmsg_dump_get_buffer(&iter, true, cxt->oops_buf + MTDOOPS_HEADER_SIZE,
-                            record_size - MTDOOPS_HEADER_SIZE, NULL);
+       kmsg_dump_get_buffer(&iter, true,
+                            cxt->oops_buf + sizeof(struct mtdoops_hdr),
+                            record_size - sizeof(struct mtdoops_hdr), NULL);
        clear_bit(0, &cxt->oops_buf_busy);
 
        if (reason != KMSG_DUMP_OOPS) {
index 357661b..d442fa9 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/mtd/partitions.h>
 #include <linux/err.h>
 #include <linux/of.h>
+#include <linux/of_platform.h>
 
 #include "mtdcore.h"
 
@@ -577,10 +578,16 @@ static int mtd_part_of_parse(struct mtd_info *master,
        struct mtd_part_parser *parser;
        struct device_node *np;
        struct property *prop;
+       struct device *dev;
        const char *compat;
        const char *fixed = "fixed-partitions";
        int ret, err = 0;
 
+       dev = &master->dev;
+       /* Use parent device (controller) if the top level MTD is not registered */
+       if (!IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) && !mtd_is_partition(master))
+               dev = master->dev.parent;
+
        np = mtd_get_of_node(master);
        if (mtd_is_partition(master))
                of_node_get(np);
@@ -593,6 +600,7 @@ static int mtd_part_of_parse(struct mtd_info *master,
                        continue;
                ret = mtd_part_do_parse(parser, master, pparts, NULL);
                if (ret > 0) {
+                       of_platform_populate(np, NULL, NULL, dev);
                        of_node_put(np);
                        return ret;
                }
@@ -600,6 +608,7 @@ static int mtd_part_of_parse(struct mtd_info *master,
                if (ret < 0 && !err)
                        err = ret;
        }
+       of_platform_populate(np, NULL, NULL, dev);
        of_node_put(np);
 
        /*
index 9b24982..5b0c2c9 100644 (file)
@@ -53,6 +53,14 @@ config MTD_NAND_ECC_MXIC
        help
          This enables support for the hardware ECC engine from Macronix.
 
+config MTD_NAND_ECC_MEDIATEK
+       tristate "Mediatek hardware ECC engine"
+       depends on HAS_IOMEM
+       depends on ARCH_MEDIATEK || COMPILE_TEST
+       select MTD_NAND_ECC
+       help
+         This enables support for the hardware ECC engine from Mediatek.
+
 endmenu
 
 endmenu
index a4e6b7a..19e1291 100644 (file)
@@ -2,6 +2,7 @@
 
 nandcore-objs := core.o bbt.o
 obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
+obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) += ecc-mtk.o
 
 obj-y  += onenand/
 obj-y  += raw/
diff --git a/drivers/mtd/nand/ecc-mtk.c b/drivers/mtd/nand/ecc-mtk.c
new file mode 100644 (file)
index 0000000..9f9b201
--- /dev/null
@@ -0,0 +1,599 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * MTK ECC controller driver.
+ * Copyright (C) 2016  MediaTek Inc.
+ * Authors:    Xiaolei Li              <xiaolei.li@mediatek.com>
+ *             Jorge Ramirez-Ortiz     <jorge.ramirez-ortiz@linaro.org>
+ */
+
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/iopoll.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/mutex.h>
+#include <linux/mtd/nand-ecc-mtk.h>
+
+#define ECC_IDLE_MASK          BIT(0)
+#define ECC_IRQ_EN             BIT(0)
+#define ECC_PG_IRQ_SEL         BIT(1)
+#define ECC_OP_ENABLE          (1)
+#define ECC_OP_DISABLE         (0)
+
+#define ECC_ENCCON             (0x00)
+#define ECC_ENCCNFG            (0x04)
+#define                ECC_MS_SHIFT            (16)
+#define ECC_ENCDIADDR          (0x08)
+#define ECC_ENCIDLE            (0x0C)
+#define ECC_DECCON             (0x100)
+#define ECC_DECCNFG            (0x104)
+#define                DEC_EMPTY_EN            BIT(31)
+#define                DEC_CNFG_CORRECT        (0x3 << 12)
+#define ECC_DECIDLE            (0x10C)
+#define ECC_DECENUM0           (0x114)
+
+#define ECC_TIMEOUT            (500000)
+
+#define ECC_IDLE_REG(op)       ((op) == ECC_ENCODE ? ECC_ENCIDLE : ECC_DECIDLE)
+#define ECC_CTL_REG(op)                ((op) == ECC_ENCODE ? ECC_ENCCON : ECC_DECCON)
+
+struct mtk_ecc_caps {
+       u32 err_mask;
+       u32 err_shift;
+       const u8 *ecc_strength;
+       const u32 *ecc_regs;
+       u8 num_ecc_strength;
+       u8 ecc_mode_shift;
+       u32 parity_bits;
+       int pg_irq_sel;
+};
+
+struct mtk_ecc {
+       struct device *dev;
+       const struct mtk_ecc_caps *caps;
+       void __iomem *regs;
+       struct clk *clk;
+
+       struct completion done;
+       struct mutex lock;
+       u32 sectors;
+
+       u8 *eccdata;
+};
+
+/* ecc strength that each IP supports */
+static const u8 ecc_strength_mt2701[] = {
+       4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36,
+       40, 44, 48, 52, 56, 60
+};
+
+static const u8 ecc_strength_mt2712[] = {
+       4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36,
+       40, 44, 48, 52, 56, 60, 68, 72, 80
+};
+
+static const u8 ecc_strength_mt7622[] = {
+       4, 6, 8, 10, 12
+};
+
+enum mtk_ecc_regs {
+       ECC_ENCPAR00,
+       ECC_ENCIRQ_EN,
+       ECC_ENCIRQ_STA,
+       ECC_DECDONE,
+       ECC_DECIRQ_EN,
+       ECC_DECIRQ_STA,
+};
+
+static int mt2701_ecc_regs[] = {
+       [ECC_ENCPAR00] =        0x10,
+       [ECC_ENCIRQ_EN] =       0x80,
+       [ECC_ENCIRQ_STA] =      0x84,
+       [ECC_DECDONE] =         0x124,
+       [ECC_DECIRQ_EN] =       0x200,
+       [ECC_DECIRQ_STA] =      0x204,
+};
+
+static int mt2712_ecc_regs[] = {
+       [ECC_ENCPAR00] =        0x300,
+       [ECC_ENCIRQ_EN] =       0x80,
+       [ECC_ENCIRQ_STA] =      0x84,
+       [ECC_DECDONE] =         0x124,
+       [ECC_DECIRQ_EN] =       0x200,
+       [ECC_DECIRQ_STA] =      0x204,
+};
+
+static int mt7622_ecc_regs[] = {
+       [ECC_ENCPAR00] =        0x10,
+       [ECC_ENCIRQ_EN] =       0x30,
+       [ECC_ENCIRQ_STA] =      0x34,
+       [ECC_DECDONE] =         0x11c,
+       [ECC_DECIRQ_EN] =       0x140,
+       [ECC_DECIRQ_STA] =      0x144,
+};
+
+static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc,
+                                    enum mtk_ecc_operation op)
+{
+       struct device *dev = ecc->dev;
+       u32 val;
+       int ret;
+
+       ret = readl_poll_timeout_atomic(ecc->regs + ECC_IDLE_REG(op), val,
+                                       val & ECC_IDLE_MASK,
+                                       10, ECC_TIMEOUT);
+       if (ret)
+               dev_warn(dev, "%s NOT idle\n",
+                        op == ECC_ENCODE ? "encoder" : "decoder");
+}
+
+static irqreturn_t mtk_ecc_irq(int irq, void *id)
+{
+       struct mtk_ecc *ecc = id;
+       u32 dec, enc;
+
+       dec = readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECIRQ_STA])
+                   & ECC_IRQ_EN;
+       if (dec) {
+               dec = readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECDONE]);
+               if (dec & ecc->sectors) {
+                       /*
+                        * Clear decode IRQ status once again to ensure that
+                        * there will be no extra IRQ.
+                        */
+                       readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECIRQ_STA]);
+                       ecc->sectors = 0;
+                       complete(&ecc->done);
+               } else {
+                       return IRQ_HANDLED;
+               }
+       } else {
+               enc = readl(ecc->regs + ecc->caps->ecc_regs[ECC_ENCIRQ_STA])
+                     & ECC_IRQ_EN;
+               if (enc)
+                       complete(&ecc->done);
+               else
+                       return IRQ_NONE;
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int mtk_ecc_config(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
+{
+       u32 ecc_bit, dec_sz, enc_sz;
+       u32 reg, i;
+
+       for (i = 0; i < ecc->caps->num_ecc_strength; i++) {
+               if (ecc->caps->ecc_strength[i] == config->strength)
+                       break;
+       }
+
+       if (i == ecc->caps->num_ecc_strength) {
+               dev_err(ecc->dev, "invalid ecc strength %d\n",
+                       config->strength);
+               return -EINVAL;
+       }
+
+       ecc_bit = i;
+
+       if (config->op == ECC_ENCODE) {
+               /* configure ECC encoder (in bits) */
+               enc_sz = config->len << 3;
+
+               reg = ecc_bit | (config->mode << ecc->caps->ecc_mode_shift);
+               reg |= (enc_sz << ECC_MS_SHIFT);
+               writel(reg, ecc->regs + ECC_ENCCNFG);
+
+               if (config->mode != ECC_NFI_MODE)
+                       writel(lower_32_bits(config->addr),
+                              ecc->regs + ECC_ENCDIADDR);
+
+       } else {
+               /* configure ECC decoder (in bits) */
+               dec_sz = (config->len << 3) +
+                        config->strength * ecc->caps->parity_bits;
+
+               reg = ecc_bit | (config->mode << ecc->caps->ecc_mode_shift);
+               reg |= (dec_sz << ECC_MS_SHIFT) | DEC_CNFG_CORRECT;
+               reg |= DEC_EMPTY_EN;
+               writel(reg, ecc->regs + ECC_DECCNFG);
+
+               if (config->sectors)
+                       ecc->sectors = 1 << (config->sectors - 1);
+       }
+
+       return 0;
+}
+
+void mtk_ecc_get_stats(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats,
+                      int sectors)
+{
+       u32 offset, i, err;
+       u32 bitflips = 0;
+
+       stats->corrected = 0;
+       stats->failed = 0;
+
+       for (i = 0; i < sectors; i++) {
+               offset = (i >> 2) << 2;
+               err = readl(ecc->regs + ECC_DECENUM0 + offset);
+               err = err >> ((i % 4) * ecc->caps->err_shift);
+               err &= ecc->caps->err_mask;
+               if (err == ecc->caps->err_mask) {
+                       /* uncorrectable errors */
+                       stats->failed++;
+                       continue;
+               }
+
+               stats->corrected += err;
+               bitflips = max_t(u32, bitflips, err);
+       }
+
+       stats->bitflips = bitflips;
+}
+EXPORT_SYMBOL(mtk_ecc_get_stats);
+
+void mtk_ecc_release(struct mtk_ecc *ecc)
+{
+       clk_disable_unprepare(ecc->clk);
+       put_device(ecc->dev);
+}
+EXPORT_SYMBOL(mtk_ecc_release);
+
+static void mtk_ecc_hw_init(struct mtk_ecc *ecc)
+{
+       mtk_ecc_wait_idle(ecc, ECC_ENCODE);
+       writew(ECC_OP_DISABLE, ecc->regs + ECC_ENCCON);
+
+       mtk_ecc_wait_idle(ecc, ECC_DECODE);
+       writel(ECC_OP_DISABLE, ecc->regs + ECC_DECCON);
+}
+
+static struct mtk_ecc *mtk_ecc_get(struct device_node *np)
+{
+       struct platform_device *pdev;
+       struct mtk_ecc *ecc;
+
+       pdev = of_find_device_by_node(np);
+       if (!pdev)
+               return ERR_PTR(-EPROBE_DEFER);
+
+       ecc = platform_get_drvdata(pdev);
+       if (!ecc) {
+               put_device(&pdev->dev);
+               return ERR_PTR(-EPROBE_DEFER);
+       }
+
+       clk_prepare_enable(ecc->clk);
+       mtk_ecc_hw_init(ecc);
+
+       return ecc;
+}
+
+struct mtk_ecc *of_mtk_ecc_get(struct device_node *of_node)
+{
+       struct mtk_ecc *ecc = NULL;
+       struct device_node *np;
+
+       np = of_parse_phandle(of_node, "nand-ecc-engine", 0);
+       /* for backward compatibility */
+       if (!np)
+               np = of_parse_phandle(of_node, "ecc-engine", 0);
+       if (np) {
+               ecc = mtk_ecc_get(np);
+               of_node_put(np);
+       }
+
+       return ecc;
+}
+EXPORT_SYMBOL(of_mtk_ecc_get);
+
+int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
+{
+       enum mtk_ecc_operation op = config->op;
+       u16 reg_val;
+       int ret;
+
+       ret = mutex_lock_interruptible(&ecc->lock);
+       if (ret) {
+               dev_err(ecc->dev, "interrupted when attempting to lock\n");
+               return ret;
+       }
+
+       mtk_ecc_wait_idle(ecc, op);
+
+       ret = mtk_ecc_config(ecc, config);
+       if (ret) {
+               mutex_unlock(&ecc->lock);
+               return ret;
+       }
+
+       if (config->mode != ECC_NFI_MODE || op != ECC_ENCODE) {
+               init_completion(&ecc->done);
+               reg_val = ECC_IRQ_EN;
+               /*
+                * For ECC_NFI_MODE, if ecc->caps->pg_irq_sel is 1, then it
+                * means this chip can only generate one ecc irq during page
+                * read / write. If is 0, generate one ecc irq each ecc step.
+                */
+               if (ecc->caps->pg_irq_sel && config->mode == ECC_NFI_MODE)
+                       reg_val |= ECC_PG_IRQ_SEL;
+               if (op == ECC_ENCODE)
+                       writew(reg_val, ecc->regs +
+                              ecc->caps->ecc_regs[ECC_ENCIRQ_EN]);
+               else
+                       writew(reg_val, ecc->regs +
+                              ecc->caps->ecc_regs[ECC_DECIRQ_EN]);
+       }
+
+       writew(ECC_OP_ENABLE, ecc->regs + ECC_CTL_REG(op));
+
+       return 0;
+}
+EXPORT_SYMBOL(mtk_ecc_enable);
+
+void mtk_ecc_disable(struct mtk_ecc *ecc)
+{
+       enum mtk_ecc_operation op = ECC_ENCODE;
+
+       /* find out the running operation */
+       if (readw(ecc->regs + ECC_CTL_REG(op)) != ECC_OP_ENABLE)
+               op = ECC_DECODE;
+
+       /* disable it */
+       mtk_ecc_wait_idle(ecc, op);
+       if (op == ECC_DECODE) {
+               /*
+                * Clear decode IRQ status in case there is a timeout to wait
+                * decode IRQ.
+                */
+               readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECDONE]);
+               writew(0, ecc->regs + ecc->caps->ecc_regs[ECC_DECIRQ_EN]);
+       } else {
+               writew(0, ecc->regs + ecc->caps->ecc_regs[ECC_ENCIRQ_EN]);
+       }
+
+       writew(ECC_OP_DISABLE, ecc->regs + ECC_CTL_REG(op));
+
+       mutex_unlock(&ecc->lock);
+}
+EXPORT_SYMBOL(mtk_ecc_disable);
+
+int mtk_ecc_wait_done(struct mtk_ecc *ecc, enum mtk_ecc_operation op)
+{
+       int ret;
+
+       ret = wait_for_completion_timeout(&ecc->done, msecs_to_jiffies(500));
+       if (!ret) {
+               dev_err(ecc->dev, "%s timeout - interrupt did not arrive)\n",
+                       (op == ECC_ENCODE) ? "encoder" : "decoder");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(mtk_ecc_wait_done);
+
+int mtk_ecc_encode(struct mtk_ecc *ecc, struct mtk_ecc_config *config,
+                  u8 *data, u32 bytes)
+{
+       dma_addr_t addr;
+       u32 len;
+       int ret;
+
+       addr = dma_map_single(ecc->dev, data, bytes, DMA_TO_DEVICE);
+       ret = dma_mapping_error(ecc->dev, addr);
+       if (ret) {
+               dev_err(ecc->dev, "dma mapping error\n");
+               return -EINVAL;
+       }
+
+       config->op = ECC_ENCODE;
+       config->addr = addr;
+       ret = mtk_ecc_enable(ecc, config);
+       if (ret) {
+               dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE);
+               return ret;
+       }
+
+       ret = mtk_ecc_wait_done(ecc, ECC_ENCODE);
+       if (ret)
+               goto timeout;
+
+       mtk_ecc_wait_idle(ecc, ECC_ENCODE);
+
+       /* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */
+       len = (config->strength * ecc->caps->parity_bits + 7) >> 3;
+
+       /* write the parity bytes generated by the ECC back to temp buffer */
+       __ioread32_copy(ecc->eccdata,
+                       ecc->regs + ecc->caps->ecc_regs[ECC_ENCPAR00],
+                       round_up(len, 4));
+
+       /* copy into possibly unaligned OOB region with actual length */
+       memcpy(data + bytes, ecc->eccdata, len);
+timeout:
+
+       dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE);
+       mtk_ecc_disable(ecc);
+
+       return ret;
+}
+EXPORT_SYMBOL(mtk_ecc_encode);
+
+void mtk_ecc_adjust_strength(struct mtk_ecc *ecc, u32 *p)
+{
+       const u8 *ecc_strength = ecc->caps->ecc_strength;
+       int i;
+
+       for (i = 0; i < ecc->caps->num_ecc_strength; i++) {
+               if (*p <= ecc_strength[i]) {
+                       if (!i)
+                               *p = ecc_strength[i];
+                       else if (*p != ecc_strength[i])
+                               *p = ecc_strength[i - 1];
+                       return;
+               }
+       }
+
+       *p = ecc_strength[ecc->caps->num_ecc_strength - 1];
+}
+EXPORT_SYMBOL(mtk_ecc_adjust_strength);
+
+unsigned int mtk_ecc_get_parity_bits(struct mtk_ecc *ecc)
+{
+       return ecc->caps->parity_bits;
+}
+EXPORT_SYMBOL(mtk_ecc_get_parity_bits);
+
+static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = {
+       .err_mask = 0x3f,
+       .err_shift = 8,
+       .ecc_strength = ecc_strength_mt2701,
+       .ecc_regs = mt2701_ecc_regs,
+       .num_ecc_strength = 20,
+       .ecc_mode_shift = 5,
+       .parity_bits = 14,
+       .pg_irq_sel = 0,
+};
+
+static const struct mtk_ecc_caps mtk_ecc_caps_mt2712 = {
+       .err_mask = 0x7f,
+       .err_shift = 8,
+       .ecc_strength = ecc_strength_mt2712,
+       .ecc_regs = mt2712_ecc_regs,
+       .num_ecc_strength = 23,
+       .ecc_mode_shift = 5,
+       .parity_bits = 14,
+       .pg_irq_sel = 1,
+};
+
+static const struct mtk_ecc_caps mtk_ecc_caps_mt7622 = {
+       .err_mask = 0x1f,
+       .err_shift = 5,
+       .ecc_strength = ecc_strength_mt7622,
+       .ecc_regs = mt7622_ecc_regs,
+       .num_ecc_strength = 5,
+       .ecc_mode_shift = 4,
+       .parity_bits = 13,
+       .pg_irq_sel = 0,
+};
+
+static const struct of_device_id mtk_ecc_dt_match[] = {
+       {
+               .compatible = "mediatek,mt2701-ecc",
+               .data = &mtk_ecc_caps_mt2701,
+       }, {
+               .compatible = "mediatek,mt2712-ecc",
+               .data = &mtk_ecc_caps_mt2712,
+       }, {
+               .compatible = "mediatek,mt7622-ecc",
+               .data = &mtk_ecc_caps_mt7622,
+       },
+       {},
+};
+
+static int mtk_ecc_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct mtk_ecc *ecc;
+       u32 max_eccdata_size;
+       int irq, ret;
+
+       ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL);
+       if (!ecc)
+               return -ENOMEM;
+
+       ecc->caps = of_device_get_match_data(dev);
+
+       max_eccdata_size = ecc->caps->num_ecc_strength - 1;
+       max_eccdata_size = ecc->caps->ecc_strength[max_eccdata_size];
+       max_eccdata_size = (max_eccdata_size * ecc->caps->parity_bits + 7) >> 3;
+       max_eccdata_size = round_up(max_eccdata_size, 4);
+       ecc->eccdata = devm_kzalloc(dev, max_eccdata_size, GFP_KERNEL);
+       if (!ecc->eccdata)
+               return -ENOMEM;
+
+       ecc->regs = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(ecc->regs))
+               return PTR_ERR(ecc->regs);
+
+       ecc->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(ecc->clk)) {
+               dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(ecc->clk));
+               return PTR_ERR(ecc->clk);
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
+
+       ret = dma_set_mask(dev, DMA_BIT_MASK(32));
+       if (ret) {
+               dev_err(dev, "failed to set DMA mask\n");
+               return ret;
+       }
+
+       ret = devm_request_irq(dev, irq, mtk_ecc_irq, 0x0, "mtk-ecc", ecc);
+       if (ret) {
+               dev_err(dev, "failed to request irq\n");
+               return -EINVAL;
+       }
+
+       ecc->dev = dev;
+       mutex_init(&ecc->lock);
+       platform_set_drvdata(pdev, ecc);
+       dev_info(dev, "probed\n");
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_ecc_suspend(struct device *dev)
+{
+       struct mtk_ecc *ecc = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(ecc->clk);
+
+       return 0;
+}
+
+static int mtk_ecc_resume(struct device *dev)
+{
+       struct mtk_ecc *ecc = dev_get_drvdata(dev);
+       int ret;
+
+       ret = clk_prepare_enable(ecc->clk);
+       if (ret) {
+               dev_err(dev, "failed to enable clk\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mtk_ecc_pm_ops, mtk_ecc_suspend, mtk_ecc_resume);
+#endif
+
+MODULE_DEVICE_TABLE(of, mtk_ecc_dt_match);
+
+static struct platform_driver mtk_ecc_driver = {
+       .probe  = mtk_ecc_probe,
+       .driver = {
+               .name  = "mtk-ecc",
+               .of_match_table = mtk_ecc_dt_match,
+#ifdef CONFIG_PM_SLEEP
+               .pm = &mtk_ecc_pm_ops,
+#endif
+       },
+};
+
+module_platform_driver(mtk_ecc_driver);
+
+MODULE_AUTHOR("Xiaolei Li <xiaolei.li@mediatek.com>");
+MODULE_DESCRIPTION("MTK Nand ECC Driver");
+MODULE_LICENSE("Dual MIT/GPL");
index 9b078e7..8b6d7a5 100644 (file)
@@ -374,6 +374,7 @@ config MTD_NAND_QCOM
 
 config MTD_NAND_MTK
        tristate "MTK NAND controller"
+       depends on MTD_NAND_ECC_MEDIATEK
        depends on ARCH_MEDIATEK || COMPILE_TEST
        depends on HAS_IOMEM
        help
index 88a5665..fa1d001 100644 (file)
@@ -48,7 +48,7 @@ obj-$(CONFIG_MTD_NAND_SUNXI)          += sunxi_nand.o
 obj-$(CONFIG_MTD_NAND_HISI504)         += hisi504_nand.o
 obj-$(CONFIG_MTD_NAND_BRCMNAND)                += brcmnand/
 obj-$(CONFIG_MTD_NAND_QCOM)            += qcom_nandc.o
-obj-$(CONFIG_MTD_NAND_MTK)             += mtk_ecc.o mtk_nand.o
+obj-$(CONFIG_MTD_NAND_MTK)             += mtk_nand.o
 obj-$(CONFIG_MTD_NAND_MXIC)            += mxic_nand.o
 obj-$(CONFIG_MTD_NAND_TEGRA)           += tegra_nand.o
 obj-$(CONFIG_MTD_NAND_STM32_FMC2)      += stm32_fmc2_nand.o
index 7eec60e..0d72672 100644 (file)
@@ -2983,11 +2983,10 @@ static int cadence_nand_dt_probe(struct platform_device *ofdev)
        if (IS_ERR(cdns_ctrl->reg))
                return PTR_ERR(cdns_ctrl->reg);
 
-       res = platform_get_resource(ofdev, IORESOURCE_MEM, 1);
-       cdns_ctrl->io.dma = res->start;
-       cdns_ctrl->io.virt = devm_ioremap_resource(&ofdev->dev, res);
+       cdns_ctrl->io.virt = devm_platform_get_and_ioremap_resource(ofdev, 1, &res);
        if (IS_ERR(cdns_ctrl->io.virt))
                return PTR_ERR(cdns_ctrl->io.virt);
+       cdns_ctrl->io.dma = res->start;
 
        dt->clk = devm_clk_get(cdns_ctrl->dev, "nf_clk");
        if (IS_ERR(dt->clk))
index 6edf78c..f0a1571 100644 (file)
@@ -104,17 +104,12 @@ static int cs553x_write_ctrl_byte(struct cs553x_nand_controller *cs553x,
                                  u32 ctl, u8 data)
 {
        u8 status;
-       int ret;
 
        writeb(ctl, cs553x->mmio + MM_NAND_CTL);
        writeb(data, cs553x->mmio + MM_NAND_IO);
-       ret = readb_poll_timeout_atomic(cs553x->mmio + MM_NAND_STS, status,
+       return readb_poll_timeout_atomic(cs553x->mmio + MM_NAND_STS, status,
                                        !(status & CS_NAND_CTLR_BUSY), 1,
                                        100000);
-       if (ret)
-               return ret;
-
-       return 0;
 }
 
 static void cs553x_data_in(struct cs553x_nand_controller *cs553x, void *buf,
index 45fec8c..3e98e3c 100644 (file)
@@ -727,7 +727,7 @@ static int nand_davinci_probe(struct platform_device *pdev)
                return -ENODEV;
 
        /* which external chipselect will we be managing? */
-       if (pdata->core_chipsel < 0 || pdata->core_chipsel > 3)
+       if (pdata->core_chipsel > 3)
                return -ENODEV;
 
        info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
index 20c085a..de7e722 100644 (file)
@@ -74,22 +74,21 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
                return ret;
        }
 
-       denali->reg = ioremap(csr_base, csr_len);
+       denali->reg = devm_ioremap(denali->dev, csr_base, csr_len);
        if (!denali->reg) {
                dev_err(&dev->dev, "Spectra: Unable to remap memory region\n");
                return -ENOMEM;
        }
 
-       denali->host = ioremap(mem_base, mem_len);
+       denali->host = devm_ioremap(denali->dev, mem_base, mem_len);
        if (!denali->host) {
                dev_err(&dev->dev, "Spectra: ioremap failed!");
-               ret = -ENOMEM;
-               goto out_unmap_reg;
+               return -ENOMEM;
        }
 
        ret = denali_init(denali);
        if (ret)
-               goto out_unmap_host;
+               return ret;
 
        nsels = denali->nbanks;
 
@@ -117,10 +116,6 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 
 out_remove_denali:
        denali_remove(denali);
-out_unmap_host:
-       iounmap(denali->host);
-out_unmap_reg:
-       iounmap(denali->reg);
        return ret;
 }
 
@@ -129,8 +124,6 @@ static void denali_pci_remove(struct pci_dev *dev)
        struct denali_controller *denali = pci_get_drvdata(dev);
 
        denali_remove(denali);
-       iounmap(denali->reg);
-       iounmap(denali->host);
 }
 
 static struct pci_driver denali_pci_driver = {
index 44b14c9..0b68d05 100644 (file)
@@ -218,7 +218,8 @@ static void gpmi_dump_info(struct gpmi_nand_data *this)
                "ECC Strength           : %u\n"
                "Page Size in Bytes     : %u\n"
                "Metadata Size in Bytes : %u\n"
-               "ECC Chunk Size in Bytes: %u\n"
+               "ECC0 Chunk Size in Bytes: %u\n"
+               "ECCn Chunk Size in Bytes: %u\n"
                "ECC Chunk Count        : %u\n"
                "Payload Size in Bytes  : %u\n"
                "Auxiliary Size in Bytes: %u\n"
@@ -229,7 +230,8 @@ static void gpmi_dump_info(struct gpmi_nand_data *this)
                geo->ecc_strength,
                geo->page_size,
                geo->metadata_size,
-               geo->ecc_chunk_size,
+               geo->ecc0_chunk_size,
+               geo->eccn_chunk_size,
                geo->ecc_chunk_count,
                geo->payload_size,
                geo->auxiliary_size,
@@ -238,9 +240,15 @@ static void gpmi_dump_info(struct gpmi_nand_data *this)
                geo->block_mark_bit_offset);
 }
 
-static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
+static bool gpmi_check_ecc(struct gpmi_nand_data *this)
 {
+       struct nand_chip *chip = &this->nand;
        struct bch_geometry *geo = &this->bch_geometry;
+       struct nand_device *nand = &chip->base;
+       struct nand_ecc_props *conf = &nand->ecc.ctx.conf;
+
+       conf->step_size = geo->eccn_chunk_size;
+       conf->strength = geo->ecc_strength;
 
        /* Do the sanity check. */
        if (GPMI_IS_MXS(this)) {
@@ -248,7 +256,47 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
                if (geo->gf_len == 14)
                        return false;
        }
-       return geo->ecc_strength <= this->devdata->bch_max_ecc_strength;
+
+       if (geo->ecc_strength > this->devdata->bch_max_ecc_strength)
+               return false;
+
+       if (!nand_ecc_is_strong_enough(nand))
+               return false;
+
+       return true;
+}
+
+/* check if bbm locates in data chunk rather than ecc chunk */
+static bool bbm_in_data_chunk(struct gpmi_nand_data *this,
+                       unsigned int *chunk_num)
+{
+       struct bch_geometry *geo = &this->bch_geometry;
+       struct nand_chip *chip = &this->nand;
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       unsigned int i, j;
+
+       if (geo->ecc0_chunk_size != geo->eccn_chunk_size) {
+               dev_err(this->dev,
+                       "The size of ecc0_chunk must equal to eccn_chunk\n");
+               return false;
+       }
+
+       i = (mtd->writesize * 8 - geo->metadata_size * 8) /
+               (geo->gf_len * geo->ecc_strength +
+                       geo->eccn_chunk_size * 8);
+
+       j = (mtd->writesize * 8 - geo->metadata_size * 8) -
+               (geo->gf_len * geo->ecc_strength +
+                       geo->eccn_chunk_size * 8) * i;
+
+       if (j < geo->eccn_chunk_size * 8) {
+               *chunk_num = i+1;
+               dev_dbg(this->dev, "Set ecc to %d and bbm in chunk %d\n",
+                               geo->ecc_strength, *chunk_num);
+               return true;
+       }
+
+       return false;
 }
 
 /*
@@ -280,13 +328,14 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this,
                        nanddev_get_ecc_requirements(&chip->base)->step_size);
                return -EINVAL;
        }
-       geo->ecc_chunk_size = ecc_step;
+       geo->ecc0_chunk_size = ecc_step;
+       geo->eccn_chunk_size = ecc_step;
        geo->ecc_strength = round_up(ecc_strength, 2);
        if (!gpmi_check_ecc(this))
                return -EINVAL;
 
        /* Keep the C >= O */
-       if (geo->ecc_chunk_size < mtd->oobsize) {
+       if (geo->eccn_chunk_size < mtd->oobsize) {
                dev_err(this->dev,
                        "unsupported nand chip. ecc size: %d, oob size : %d\n",
                        ecc_step, mtd->oobsize);
@@ -296,7 +345,7 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this,
        /* The default value, see comment in the legacy_set_geometry(). */
        geo->metadata_size = 10;
 
-       geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
+       geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size;
 
        /*
         * Now, the NAND chip with 2K page(data chunk is 512byte) shows below:
@@ -399,6 +448,134 @@ static inline int get_ecc_strength(struct gpmi_nand_data *this)
        return round_down(ecc_strength, 2);
 }
 
+static int set_geometry_for_large_oob(struct gpmi_nand_data *this)
+{
+       struct bch_geometry *geo = &this->bch_geometry;
+       struct nand_chip *chip = &this->nand;
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       const struct nand_ecc_props *requirements =
+               nanddev_get_ecc_requirements(&chip->base);
+       unsigned int block_mark_bit_offset;
+       unsigned int max_ecc;
+       unsigned int bbm_chunk;
+       unsigned int i;
+
+       /* sanity check for the minimum ecc nand required */
+       if (!(requirements->strength > 0 &&
+             requirements->step_size > 0))
+               return -EINVAL;
+       geo->ecc_strength = requirements->strength;
+
+       /* check if platform can support this nand */
+       if (!gpmi_check_ecc(this)) {
+               dev_err(this->dev,
+                       "unsupported NAND chip, minimum ecc required %d\n",
+                       geo->ecc_strength);
+               return -EINVAL;
+       }
+
+       /* calculate the maximum ecc platform can support*/
+       geo->metadata_size = 10;
+       geo->gf_len = 14;
+       geo->ecc0_chunk_size = 1024;
+       geo->eccn_chunk_size = 1024;
+       geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size;
+       max_ecc = min(get_ecc_strength(this),
+                     this->devdata->bch_max_ecc_strength);
+
+       /*
+        * search a supported ecc strength that makes bbm
+        * located in data chunk
+        */
+       geo->ecc_strength = max_ecc;
+       while (!(geo->ecc_strength < requirements->strength)) {
+               if (bbm_in_data_chunk(this, &bbm_chunk))
+                       goto geo_setting;
+               geo->ecc_strength -= 2;
+       }
+
+       /* if none of them works, keep using the minimum ecc */
+       /* nand required but changing ecc page layout  */
+       geo->ecc_strength = requirements->strength;
+       /* add extra ecc for meta data */
+       geo->ecc0_chunk_size = 0;
+       geo->ecc_chunk_count = (mtd->writesize / geo->eccn_chunk_size) + 1;
+       geo->ecc_for_meta = 1;
+       /* check if oob can afford this extra ecc chunk */
+       if (mtd->oobsize * 8 < geo->metadata_size * 8 +
+           geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) {
+               dev_err(this->dev, "unsupported NAND chip with new layout\n");
+               return -EINVAL;
+       }
+
+       /* calculate in which chunk bbm located */
+       bbm_chunk = (mtd->writesize * 8 - geo->metadata_size * 8 -
+                    geo->gf_len * geo->ecc_strength) /
+                    (geo->gf_len * geo->ecc_strength +
+                    geo->eccn_chunk_size * 8) + 1;
+
+geo_setting:
+
+       geo->page_size = mtd->writesize + geo->metadata_size +
+               (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8;
+       geo->payload_size = mtd->writesize;
+
+       /*
+        * The auxiliary buffer contains the metadata and the ECC status. The
+        * metadata is padded to the nearest 32-bit boundary. The ECC status
+        * contains one byte for every ECC chunk, and is also padded to the
+        * nearest 32-bit boundary.
+        */
+       geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4);
+       geo->auxiliary_size = ALIGN(geo->metadata_size, 4)
+                                   + ALIGN(geo->ecc_chunk_count, 4);
+
+       if (!this->swap_block_mark)
+               return 0;
+
+       /* calculate the number of ecc chunk behind the bbm */
+       i = (mtd->writesize / geo->eccn_chunk_size) - bbm_chunk + 1;
+
+       block_mark_bit_offset = mtd->writesize * 8 -
+               (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - i)
+               + geo->metadata_size * 8);
+
+       geo->block_mark_byte_offset = block_mark_bit_offset / 8;
+       geo->block_mark_bit_offset  = block_mark_bit_offset % 8;
+
+       dev_dbg(this->dev, "BCH Geometry :\n"
+               "GF length              : %u\n"
+               "ECC Strength           : %u\n"
+               "Page Size in Bytes     : %u\n"
+               "Metadata Size in Bytes : %u\n"
+               "ECC0 Chunk Size in Bytes: %u\n"
+               "ECCn Chunk Size in Bytes: %u\n"
+               "ECC Chunk Count        : %u\n"
+               "Payload Size in Bytes  : %u\n"
+               "Auxiliary Size in Bytes: %u\n"
+               "Auxiliary Status Offset: %u\n"
+               "Block Mark Byte Offset : %u\n"
+               "Block Mark Bit Offset  : %u\n"
+               "Block Mark in chunk    : %u\n"
+               "Ecc for Meta data      : %u\n",
+               geo->gf_len,
+               geo->ecc_strength,
+               geo->page_size,
+               geo->metadata_size,
+               geo->ecc0_chunk_size,
+               geo->eccn_chunk_size,
+               geo->ecc_chunk_count,
+               geo->payload_size,
+               geo->auxiliary_size,
+               geo->auxiliary_status_offset,
+               geo->block_mark_byte_offset,
+               geo->block_mark_bit_offset,
+               bbm_chunk,
+               geo->ecc_for_meta);
+
+       return 0;
+}
+
 static int legacy_set_geometry(struct gpmi_nand_data *this)
 {
        struct bch_geometry *geo = &this->bch_geometry;
@@ -418,13 +595,15 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
        geo->gf_len = 13;
 
        /* The default for chunk size. */
-       geo->ecc_chunk_size = 512;
-       while (geo->ecc_chunk_size < mtd->oobsize) {
-               geo->ecc_chunk_size *= 2; /* keep C >= O */
+       geo->ecc0_chunk_size = 512;
+       geo->eccn_chunk_size = 512;
+       while (geo->eccn_chunk_size < mtd->oobsize) {
+               geo->ecc0_chunk_size *= 2; /* keep C >= O */
+               geo->eccn_chunk_size *= 2; /* keep C >= O */
                geo->gf_len = 14;
        }
 
-       geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
+       geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size;
 
        /* We use the same ECC strength for all chunks. */
        geo->ecc_strength = get_ecc_strength(this);
@@ -514,24 +693,40 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
 static int common_nfc_set_geometry(struct gpmi_nand_data *this)
 {
        struct nand_chip *chip = &this->nand;
+       struct mtd_info *mtd = nand_to_mtd(&this->nand);
        const struct nand_ecc_props *requirements =
                nanddev_get_ecc_requirements(&chip->base);
+       bool use_minimun_ecc;
+       int err;
 
-       if (chip->ecc.strength > 0 && chip->ecc.size > 0)
-               return set_geometry_by_ecc_info(this, chip->ecc.strength,
-                                               chip->ecc.size);
+       use_minimun_ecc = of_property_read_bool(this->dev->of_node,
+                                               "fsl,use-minimum-ecc");
 
-       if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc"))
-                               || legacy_set_geometry(this)) {
-               if (!(requirements->strength > 0 && requirements->step_size > 0))
-                       return -EINVAL;
+       /* use legacy bch geometry settings by default*/
+       if ((!use_minimun_ecc && mtd->oobsize < 1024) ||
+           !(requirements->strength > 0 && requirements->step_size > 0)) {
+               dev_dbg(this->dev, "use legacy bch geometry\n");
+               err = legacy_set_geometry(this);
+               if (!err)
+                       return 0;
+       }
 
-               return set_geometry_by_ecc_info(this,
-                                               requirements->strength,
-                                               requirements->step_size);
+       /* for large oob nand */
+       if (mtd->oobsize > 1024) {
+               dev_dbg(this->dev, "use large oob bch geometry\n");
+               err = set_geometry_for_large_oob(this);
+               if (!err)
+                       return 0;
        }
 
-       return 0;
+       /* otherwise use the minimum ecc nand chip required */
+       dev_dbg(this->dev, "use minimum ecc bch geometry\n");
+       err = set_geometry_by_ecc_info(this, requirements->strength,
+                                       requirements->step_size);
+       if (err)
+               dev_err(this->dev, "none of the bch geometry setting works\n");
+
+       return err;
 }
 
 /* Configures the geometry for BCH.  */
@@ -843,7 +1038,7 @@ static int gpmi_raw_len_to_len(struct gpmi_nand_data *this, int raw_len)
         * we are passed in exec_op. Calculate the data length from it.
         */
        if (this->bch)
-               return ALIGN_DOWN(raw_len, this->bch_geometry.ecc_chunk_size);
+               return ALIGN_DOWN(raw_len, this->bch_geometry.eccn_chunk_size);
        else
                return raw_len;
 }
@@ -1235,7 +1430,7 @@ static int gpmi_count_bitflips(struct nand_chip *chip, void *buf, int first,
 
                        /* Read ECC bytes into our internal raw_buffer */
                        offset = nfc_geo->metadata_size * 8;
-                       offset += ((8 * nfc_geo->ecc_chunk_size) + eccbits) * (i + 1);
+                       offset += ((8 * nfc_geo->eccn_chunk_size) + eccbits) * (i + 1);
                        offset -= eccbits;
                        bitoffset = offset % 8;
                        eccbytes = DIV_ROUND_UP(offset + eccbits, 8);
@@ -1272,16 +1467,16 @@ static int gpmi_count_bitflips(struct nand_chip *chip, void *buf, int first,
                        if (i == 0) {
                                /* The first block includes metadata */
                                flips = nand_check_erased_ecc_chunk(
-                                               buf + i * nfc_geo->ecc_chunk_size,
-                                               nfc_geo->ecc_chunk_size,
+                                               buf + i * nfc_geo->eccn_chunk_size,
+                                               nfc_geo->eccn_chunk_size,
                                                eccbuf, eccbytes,
                                                this->auxiliary_virt,
                                                nfc_geo->metadata_size,
                                                nfc_geo->ecc_strength);
                        } else {
                                flips = nand_check_erased_ecc_chunk(
-                                               buf + i * nfc_geo->ecc_chunk_size,
-                                               nfc_geo->ecc_chunk_size,
+                                               buf + i * nfc_geo->eccn_chunk_size,
+                                               nfc_geo->eccn_chunk_size,
                                                eccbuf, eccbytes,
                                                NULL, 0,
                                                nfc_geo->ecc_strength);
@@ -1310,20 +1505,21 @@ static void gpmi_bch_layout_std(struct gpmi_nand_data *this)
        struct bch_geometry *geo = &this->bch_geometry;
        unsigned int ecc_strength = geo->ecc_strength >> 1;
        unsigned int gf_len = geo->gf_len;
-       unsigned int block_size = geo->ecc_chunk_size;
+       unsigned int block0_size = geo->ecc0_chunk_size;
+       unsigned int blockn_size = geo->eccn_chunk_size;
 
        this->bch_flashlayout0 =
                BF_BCH_FLASH0LAYOUT0_NBLOCKS(geo->ecc_chunk_count - 1) |
                BF_BCH_FLASH0LAYOUT0_META_SIZE(geo->metadata_size) |
                BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) |
                BF_BCH_FLASH0LAYOUT0_GF(gf_len, this) |
-               BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this);
+               BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block0_size, this);
 
        this->bch_flashlayout1 =
                BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(geo->page_size) |
                BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) |
                BF_BCH_FLASH0LAYOUT1_GF(gf_len, this) |
-               BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this);
+               BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(blockn_size, this);
 }
 
 static int gpmi_ecc_read_page(struct nand_chip *chip, uint8_t *buf,
@@ -1406,29 +1602,49 @@ static int gpmi_ecc_read_subpage(struct nand_chip *chip, uint32_t offs,
                }
        }
 
+       /*
+        * if there is an ECC dedicate for meta:
+        * - need to add an extra ECC size when calculating col and page_size,
+        *   if the meta size is NOT zero.
+        * - ecc0_chunk size need to set to the same size as other chunks,
+        *   if the meta size is zero.
+        */
+
        meta = geo->metadata_size;
        if (first) {
-               col = meta + (size + ecc_parity_size) * first;
+               if (geo->ecc_for_meta)
+                       col = meta + ecc_parity_size
+                               + (size + ecc_parity_size) * first;
+               else
+                       col = meta + (size + ecc_parity_size) * first;
+
                meta = 0;
                buf = buf + first * size;
        }
 
        ecc_parity_size = geo->gf_len * geo->ecc_strength / 8;
-
        n = last - first + 1;
-       page_size = meta + (size + ecc_parity_size) * n;
+
+       if (geo->ecc_for_meta && meta)
+               page_size = meta + ecc_parity_size
+                           + (size + ecc_parity_size) * n;
+       else
+               page_size = meta + (size + ecc_parity_size) * n;
+
        ecc_strength = geo->ecc_strength >> 1;
 
-       this->bch_flashlayout0 = BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1) |
+       this->bch_flashlayout0 = BF_BCH_FLASH0LAYOUT0_NBLOCKS(
+               (geo->ecc_for_meta ? n : n - 1)) |
                BF_BCH_FLASH0LAYOUT0_META_SIZE(meta) |
                BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) |
                BF_BCH_FLASH0LAYOUT0_GF(geo->gf_len, this) |
-               BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(geo->ecc_chunk_size, this);
+               BF_BCH_FLASH0LAYOUT0_DATA0_SIZE((geo->ecc_for_meta ?
+               0 : geo->ecc0_chunk_size), this);
 
        this->bch_flashlayout1 = BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) |
                BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) |
                BF_BCH_FLASH0LAYOUT1_GF(geo->gf_len, this) |
-               BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(geo->ecc_chunk_size, this);
+               BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(geo->eccn_chunk_size, this);
 
        this->bch = true;
 
@@ -1597,7 +1813,7 @@ static int gpmi_ecc_read_page_raw(struct nand_chip *chip, uint8_t *buf,
        struct mtd_info *mtd = nand_to_mtd(chip);
        struct gpmi_nand_data *this = nand_get_controller_data(chip);
        struct bch_geometry *nfc_geo = &this->bch_geometry;
-       int eccsize = nfc_geo->ecc_chunk_size;
+       int eccsize = nfc_geo->eccn_chunk_size;
        int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
        u8 *tmp_buf = this->raw_buffer;
        size_t src_bit_off;
@@ -1682,7 +1898,7 @@ static int gpmi_ecc_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
        struct mtd_info *mtd = nand_to_mtd(chip);
        struct gpmi_nand_data *this = nand_get_controller_data(chip);
        struct bch_geometry *nfc_geo = &this->bch_geometry;
-       int eccsize = nfc_geo->ecc_chunk_size;
+       int eccsize = nfc_geo->eccn_chunk_size;
        int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
        u8 *tmp_buf = this->raw_buffer;
        uint8_t *oob = chip->oob_poi;
@@ -2056,7 +2272,7 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
        ecc->read_oob_raw = gpmi_ecc_read_oob_raw;
        ecc->write_oob_raw = gpmi_ecc_write_oob_raw;
        ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
-       ecc->size       = bch_geo->ecc_chunk_size;
+       ecc->size       = bch_geo->eccn_chunk_size;
        ecc->strength   = bch_geo->ecc_strength;
        mtd_set_ooblayout(mtd, &gpmi_ooblayout_ops);
 
index 5e1c3dd..c3ff56a 100644 (file)
@@ -30,9 +30,9 @@ struct resources {
  * @page_size:                The size, in bytes, of a physical page, including
  *                            both data and OOB.
  * @metadata_size:            The size, in bytes, of the metadata.
- * @ecc_chunk_size:           The size, in bytes, of a single ECC chunk. Note
- *                            the first chunk in the page includes both data and
- *                            metadata, so it's a bit larger than this value.
+ * @ecc0_chunk_size:          The size, in bytes, of a first ECC chunk.
+ * @eccn_chunk_size:          The size, in bytes, of a single ECC chunk after
+ *                            the first chunk in the page.
  * @ecc_chunk_count:          The number of ECC chunks in the page,
  * @payload_size:             The size, in bytes, of the payload buffer.
  * @auxiliary_size:           The size, in bytes, of the auxiliary buffer.
@@ -42,19 +42,23 @@ struct resources {
  *                            which the underlying physical block mark appears.
  * @block_mark_bit_offset:    The bit offset into the ECC-based page view at
  *                            which the underlying physical block mark appears.
+ * @ecc_for_meta:             The flag to indicate if there is a dedicate ecc
+ *                            for meta.
  */
 struct bch_geometry {
        unsigned int  gf_len;
        unsigned int  ecc_strength;
        unsigned int  page_size;
        unsigned int  metadata_size;
-       unsigned int  ecc_chunk_size;
+       unsigned int  ecc0_chunk_size;
+       unsigned int  eccn_chunk_size;
        unsigned int  ecc_chunk_count;
        unsigned int  payload_size;
        unsigned int  auxiliary_size;
        unsigned int  auxiliary_status_offset;
        unsigned int  block_mark_byte_offset;
        unsigned int  block_mark_bit_offset;
+       unsigned int  ecc_for_meta; /* ECC for meta data */
 };
 
 /**
index 7c1c80d..e91b879 100644 (file)
@@ -619,9 +619,9 @@ static int ebu_nand_probe(struct platform_device *pdev)
        resname = devm_kasprintf(dev, GFP_KERNEL, "nand_cs%d", cs);
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, resname);
        ebu_host->cs[cs].chipaddr = devm_ioremap_resource(dev, res);
-       ebu_host->cs[cs].nand_pa = res->start;
        if (IS_ERR(ebu_host->cs[cs].chipaddr))
                return PTR_ERR(ebu_host->cs[cs].chipaddr);
+       ebu_host->cs[cs].nand_pa = res->start;
 
        ebu_host->clk = devm_clk_get(dev, NULL);
        if (IS_ERR(ebu_host->clk))
index 5b9271b..800d774 100644 (file)
@@ -595,8 +595,7 @@ static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd)
        struct nand_chip *chip = mtd_to_nand(mtd);
        struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
 
-       if (prv->clk)
-               clk_disable_unprepare(prv->clk);
+       clk_disable_unprepare(prv->clk);
 
        if (prv->csreg)
                iounmap(prv->csreg);
diff --git a/drivers/mtd/nand/raw/mtk_ecc.c b/drivers/mtd/nand/raw/mtk_ecc.c
deleted file mode 100644 (file)
index 49ab344..0000000
+++ /dev/null
@@ -1,597 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0 OR MIT
-/*
- * MTK ECC controller driver.
- * Copyright (C) 2016  MediaTek Inc.
- * Authors:    Xiaolei Li              <xiaolei.li@mediatek.com>
- *             Jorge Ramirez-Ortiz     <jorge.ramirez-ortiz@linaro.org>
- */
-
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/interrupt.h>
-#include <linux/clk.h>
-#include <linux/module.h>
-#include <linux/iopoll.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/mutex.h>
-
-#include "mtk_ecc.h"
-
-#define ECC_IDLE_MASK          BIT(0)
-#define ECC_IRQ_EN             BIT(0)
-#define ECC_PG_IRQ_SEL         BIT(1)
-#define ECC_OP_ENABLE          (1)
-#define ECC_OP_DISABLE         (0)
-
-#define ECC_ENCCON             (0x00)
-#define ECC_ENCCNFG            (0x04)
-#define                ECC_MS_SHIFT            (16)
-#define ECC_ENCDIADDR          (0x08)
-#define ECC_ENCIDLE            (0x0C)
-#define ECC_DECCON             (0x100)
-#define ECC_DECCNFG            (0x104)
-#define                DEC_EMPTY_EN            BIT(31)
-#define                DEC_CNFG_CORRECT        (0x3 << 12)
-#define ECC_DECIDLE            (0x10C)
-#define ECC_DECENUM0           (0x114)
-
-#define ECC_TIMEOUT            (500000)
-
-#define ECC_IDLE_REG(op)       ((op) == ECC_ENCODE ? ECC_ENCIDLE : ECC_DECIDLE)
-#define ECC_CTL_REG(op)                ((op) == ECC_ENCODE ? ECC_ENCCON : ECC_DECCON)
-
-struct mtk_ecc_caps {
-       u32 err_mask;
-       u32 err_shift;
-       const u8 *ecc_strength;
-       const u32 *ecc_regs;
-       u8 num_ecc_strength;
-       u8 ecc_mode_shift;
-       u32 parity_bits;
-       int pg_irq_sel;
-};
-
-struct mtk_ecc {
-       struct device *dev;
-       const struct mtk_ecc_caps *caps;
-       void __iomem *regs;
-       struct clk *clk;
-
-       struct completion done;
-       struct mutex lock;
-       u32 sectors;
-
-       u8 *eccdata;
-};
-
-/* ecc strength that each IP supports */
-static const u8 ecc_strength_mt2701[] = {
-       4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36,
-       40, 44, 48, 52, 56, 60
-};
-
-static const u8 ecc_strength_mt2712[] = {
-       4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36,
-       40, 44, 48, 52, 56, 60, 68, 72, 80
-};
-
-static const u8 ecc_strength_mt7622[] = {
-       4, 6, 8, 10, 12
-};
-
-enum mtk_ecc_regs {
-       ECC_ENCPAR00,
-       ECC_ENCIRQ_EN,
-       ECC_ENCIRQ_STA,
-       ECC_DECDONE,
-       ECC_DECIRQ_EN,
-       ECC_DECIRQ_STA,
-};
-
-static int mt2701_ecc_regs[] = {
-       [ECC_ENCPAR00] =        0x10,
-       [ECC_ENCIRQ_EN] =       0x80,
-       [ECC_ENCIRQ_STA] =      0x84,
-       [ECC_DECDONE] =         0x124,
-       [ECC_DECIRQ_EN] =       0x200,
-       [ECC_DECIRQ_STA] =      0x204,
-};
-
-static int mt2712_ecc_regs[] = {
-       [ECC_ENCPAR00] =        0x300,
-       [ECC_ENCIRQ_EN] =       0x80,
-       [ECC_ENCIRQ_STA] =      0x84,
-       [ECC_DECDONE] =         0x124,
-       [ECC_DECIRQ_EN] =       0x200,
-       [ECC_DECIRQ_STA] =      0x204,
-};
-
-static int mt7622_ecc_regs[] = {
-       [ECC_ENCPAR00] =        0x10,
-       [ECC_ENCIRQ_EN] =       0x30,
-       [ECC_ENCIRQ_STA] =      0x34,
-       [ECC_DECDONE] =         0x11c,
-       [ECC_DECIRQ_EN] =       0x140,
-       [ECC_DECIRQ_STA] =      0x144,
-};
-
-static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc,
-                                    enum mtk_ecc_operation op)
-{
-       struct device *dev = ecc->dev;
-       u32 val;
-       int ret;
-
-       ret = readl_poll_timeout_atomic(ecc->regs + ECC_IDLE_REG(op), val,
-                                       val & ECC_IDLE_MASK,
-                                       10, ECC_TIMEOUT);
-       if (ret)
-               dev_warn(dev, "%s NOT idle\n",
-                        op == ECC_ENCODE ? "encoder" : "decoder");
-}
-
-static irqreturn_t mtk_ecc_irq(int irq, void *id)
-{
-       struct mtk_ecc *ecc = id;
-       u32 dec, enc;
-
-       dec = readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECIRQ_STA])
-                   & ECC_IRQ_EN;
-       if (dec) {
-               dec = readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECDONE]);
-               if (dec & ecc->sectors) {
-                       /*
-                        * Clear decode IRQ status once again to ensure that
-                        * there will be no extra IRQ.
-                        */
-                       readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECIRQ_STA]);
-                       ecc->sectors = 0;
-                       complete(&ecc->done);
-               } else {
-                       return IRQ_HANDLED;
-               }
-       } else {
-               enc = readl(ecc->regs + ecc->caps->ecc_regs[ECC_ENCIRQ_STA])
-                     & ECC_IRQ_EN;
-               if (enc)
-                       complete(&ecc->done);
-               else
-                       return IRQ_NONE;
-       }
-
-       return IRQ_HANDLED;
-}
-
-static int mtk_ecc_config(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
-{
-       u32 ecc_bit, dec_sz, enc_sz;
-       u32 reg, i;
-
-       for (i = 0; i < ecc->caps->num_ecc_strength; i++) {
-               if (ecc->caps->ecc_strength[i] == config->strength)
-                       break;
-       }
-
-       if (i == ecc->caps->num_ecc_strength) {
-               dev_err(ecc->dev, "invalid ecc strength %d\n",
-                       config->strength);
-               return -EINVAL;
-       }
-
-       ecc_bit = i;
-
-       if (config->op == ECC_ENCODE) {
-               /* configure ECC encoder (in bits) */
-               enc_sz = config->len << 3;
-
-               reg = ecc_bit | (config->mode << ecc->caps->ecc_mode_shift);
-               reg |= (enc_sz << ECC_MS_SHIFT);
-               writel(reg, ecc->regs + ECC_ENCCNFG);
-
-               if (config->mode != ECC_NFI_MODE)
-                       writel(lower_32_bits(config->addr),
-                              ecc->regs + ECC_ENCDIADDR);
-
-       } else {
-               /* configure ECC decoder (in bits) */
-               dec_sz = (config->len << 3) +
-                        config->strength * ecc->caps->parity_bits;
-
-               reg = ecc_bit | (config->mode << ecc->caps->ecc_mode_shift);
-               reg |= (dec_sz << ECC_MS_SHIFT) | DEC_CNFG_CORRECT;
-               reg |= DEC_EMPTY_EN;
-               writel(reg, ecc->regs + ECC_DECCNFG);
-
-               if (config->sectors)
-                       ecc->sectors = 1 << (config->sectors - 1);
-       }
-
-       return 0;
-}
-
-void mtk_ecc_get_stats(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats,
-                      int sectors)
-{
-       u32 offset, i, err;
-       u32 bitflips = 0;
-
-       stats->corrected = 0;
-       stats->failed = 0;
-
-       for (i = 0; i < sectors; i++) {
-               offset = (i >> 2) << 2;
-               err = readl(ecc->regs + ECC_DECENUM0 + offset);
-               err = err >> ((i % 4) * ecc->caps->err_shift);
-               err &= ecc->caps->err_mask;
-               if (err == ecc->caps->err_mask) {
-                       /* uncorrectable errors */
-                       stats->failed++;
-                       continue;
-               }
-
-               stats->corrected += err;
-               bitflips = max_t(u32, bitflips, err);
-       }
-
-       stats->bitflips = bitflips;
-}
-EXPORT_SYMBOL(mtk_ecc_get_stats);
-
-void mtk_ecc_release(struct mtk_ecc *ecc)
-{
-       clk_disable_unprepare(ecc->clk);
-       put_device(ecc->dev);
-}
-EXPORT_SYMBOL(mtk_ecc_release);
-
-static void mtk_ecc_hw_init(struct mtk_ecc *ecc)
-{
-       mtk_ecc_wait_idle(ecc, ECC_ENCODE);
-       writew(ECC_OP_DISABLE, ecc->regs + ECC_ENCCON);
-
-       mtk_ecc_wait_idle(ecc, ECC_DECODE);
-       writel(ECC_OP_DISABLE, ecc->regs + ECC_DECCON);
-}
-
-static struct mtk_ecc *mtk_ecc_get(struct device_node *np)
-{
-       struct platform_device *pdev;
-       struct mtk_ecc *ecc;
-
-       pdev = of_find_device_by_node(np);
-       if (!pdev)
-               return ERR_PTR(-EPROBE_DEFER);
-
-       ecc = platform_get_drvdata(pdev);
-       if (!ecc) {
-               put_device(&pdev->dev);
-               return ERR_PTR(-EPROBE_DEFER);
-       }
-
-       clk_prepare_enable(ecc->clk);
-       mtk_ecc_hw_init(ecc);
-
-       return ecc;
-}
-
-struct mtk_ecc *of_mtk_ecc_get(struct device_node *of_node)
-{
-       struct mtk_ecc *ecc = NULL;
-       struct device_node *np;
-
-       np = of_parse_phandle(of_node, "ecc-engine", 0);
-       if (np) {
-               ecc = mtk_ecc_get(np);
-               of_node_put(np);
-       }
-
-       return ecc;
-}
-EXPORT_SYMBOL(of_mtk_ecc_get);
-
-int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
-{
-       enum mtk_ecc_operation op = config->op;
-       u16 reg_val;
-       int ret;
-
-       ret = mutex_lock_interruptible(&ecc->lock);
-       if (ret) {
-               dev_err(ecc->dev, "interrupted when attempting to lock\n");
-               return ret;
-       }
-
-       mtk_ecc_wait_idle(ecc, op);
-
-       ret = mtk_ecc_config(ecc, config);
-       if (ret) {
-               mutex_unlock(&ecc->lock);
-               return ret;
-       }
-
-       if (config->mode != ECC_NFI_MODE || op != ECC_ENCODE) {
-               init_completion(&ecc->done);
-               reg_val = ECC_IRQ_EN;
-               /*
-                * For ECC_NFI_MODE, if ecc->caps->pg_irq_sel is 1, then it
-                * means this chip can only generate one ecc irq during page
-                * read / write. If is 0, generate one ecc irq each ecc step.
-                */
-               if (ecc->caps->pg_irq_sel && config->mode == ECC_NFI_MODE)
-                       reg_val |= ECC_PG_IRQ_SEL;
-               if (op == ECC_ENCODE)
-                       writew(reg_val, ecc->regs +
-                              ecc->caps->ecc_regs[ECC_ENCIRQ_EN]);
-               else
-                       writew(reg_val, ecc->regs +
-                              ecc->caps->ecc_regs[ECC_DECIRQ_EN]);
-       }
-
-       writew(ECC_OP_ENABLE, ecc->regs + ECC_CTL_REG(op));
-
-       return 0;
-}
-EXPORT_SYMBOL(mtk_ecc_enable);
-
-void mtk_ecc_disable(struct mtk_ecc *ecc)
-{
-       enum mtk_ecc_operation op = ECC_ENCODE;
-
-       /* find out the running operation */
-       if (readw(ecc->regs + ECC_CTL_REG(op)) != ECC_OP_ENABLE)
-               op = ECC_DECODE;
-
-       /* disable it */
-       mtk_ecc_wait_idle(ecc, op);
-       if (op == ECC_DECODE) {
-               /*
-                * Clear decode IRQ status in case there is a timeout to wait
-                * decode IRQ.
-                */
-               readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECDONE]);
-               writew(0, ecc->regs + ecc->caps->ecc_regs[ECC_DECIRQ_EN]);
-       } else {
-               writew(0, ecc->regs + ecc->caps->ecc_regs[ECC_ENCIRQ_EN]);
-       }
-
-       writew(ECC_OP_DISABLE, ecc->regs + ECC_CTL_REG(op));
-
-       mutex_unlock(&ecc->lock);
-}
-EXPORT_SYMBOL(mtk_ecc_disable);
-
-int mtk_ecc_wait_done(struct mtk_ecc *ecc, enum mtk_ecc_operation op)
-{
-       int ret;
-
-       ret = wait_for_completion_timeout(&ecc->done, msecs_to_jiffies(500));
-       if (!ret) {
-               dev_err(ecc->dev, "%s timeout - interrupt did not arrive)\n",
-                       (op == ECC_ENCODE) ? "encoder" : "decoder");
-               return -ETIMEDOUT;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(mtk_ecc_wait_done);
-
-int mtk_ecc_encode(struct mtk_ecc *ecc, struct mtk_ecc_config *config,
-                  u8 *data, u32 bytes)
-{
-       dma_addr_t addr;
-       u32 len;
-       int ret;
-
-       addr = dma_map_single(ecc->dev, data, bytes, DMA_TO_DEVICE);
-       ret = dma_mapping_error(ecc->dev, addr);
-       if (ret) {
-               dev_err(ecc->dev, "dma mapping error\n");
-               return -EINVAL;
-       }
-
-       config->op = ECC_ENCODE;
-       config->addr = addr;
-       ret = mtk_ecc_enable(ecc, config);
-       if (ret) {
-               dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE);
-               return ret;
-       }
-
-       ret = mtk_ecc_wait_done(ecc, ECC_ENCODE);
-       if (ret)
-               goto timeout;
-
-       mtk_ecc_wait_idle(ecc, ECC_ENCODE);
-
-       /* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */
-       len = (config->strength * ecc->caps->parity_bits + 7) >> 3;
-
-       /* write the parity bytes generated by the ECC back to temp buffer */
-       __ioread32_copy(ecc->eccdata,
-                       ecc->regs + ecc->caps->ecc_regs[ECC_ENCPAR00],
-                       round_up(len, 4));
-
-       /* copy into possibly unaligned OOB region with actual length */
-       memcpy(data + bytes, ecc->eccdata, len);
-timeout:
-
-       dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE);
-       mtk_ecc_disable(ecc);
-
-       return ret;
-}
-EXPORT_SYMBOL(mtk_ecc_encode);
-
-void mtk_ecc_adjust_strength(struct mtk_ecc *ecc, u32 *p)
-{
-       const u8 *ecc_strength = ecc->caps->ecc_strength;
-       int i;
-
-       for (i = 0; i < ecc->caps->num_ecc_strength; i++) {
-               if (*p <= ecc_strength[i]) {
-                       if (!i)
-                               *p = ecc_strength[i];
-                       else if (*p != ecc_strength[i])
-                               *p = ecc_strength[i - 1];
-                       return;
-               }
-       }
-
-       *p = ecc_strength[ecc->caps->num_ecc_strength - 1];
-}
-EXPORT_SYMBOL(mtk_ecc_adjust_strength);
-
-unsigned int mtk_ecc_get_parity_bits(struct mtk_ecc *ecc)
-{
-       return ecc->caps->parity_bits;
-}
-EXPORT_SYMBOL(mtk_ecc_get_parity_bits);
-
-static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = {
-       .err_mask = 0x3f,
-       .err_shift = 8,
-       .ecc_strength = ecc_strength_mt2701,
-       .ecc_regs = mt2701_ecc_regs,
-       .num_ecc_strength = 20,
-       .ecc_mode_shift = 5,
-       .parity_bits = 14,
-       .pg_irq_sel = 0,
-};
-
-static const struct mtk_ecc_caps mtk_ecc_caps_mt2712 = {
-       .err_mask = 0x7f,
-       .err_shift = 8,
-       .ecc_strength = ecc_strength_mt2712,
-       .ecc_regs = mt2712_ecc_regs,
-       .num_ecc_strength = 23,
-       .ecc_mode_shift = 5,
-       .parity_bits = 14,
-       .pg_irq_sel = 1,
-};
-
-static const struct mtk_ecc_caps mtk_ecc_caps_mt7622 = {
-       .err_mask = 0x1f,
-       .err_shift = 5,
-       .ecc_strength = ecc_strength_mt7622,
-       .ecc_regs = mt7622_ecc_regs,
-       .num_ecc_strength = 5,
-       .ecc_mode_shift = 4,
-       .parity_bits = 13,
-       .pg_irq_sel = 0,
-};
-
-static const struct of_device_id mtk_ecc_dt_match[] = {
-       {
-               .compatible = "mediatek,mt2701-ecc",
-               .data = &mtk_ecc_caps_mt2701,
-       }, {
-               .compatible = "mediatek,mt2712-ecc",
-               .data = &mtk_ecc_caps_mt2712,
-       }, {
-               .compatible = "mediatek,mt7622-ecc",
-               .data = &mtk_ecc_caps_mt7622,
-       },
-       {},
-};
-
-static int mtk_ecc_probe(struct platform_device *pdev)
-{
-       struct device *dev = &pdev->dev;
-       struct mtk_ecc *ecc;
-       u32 max_eccdata_size;
-       int irq, ret;
-
-       ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL);
-       if (!ecc)
-               return -ENOMEM;
-
-       ecc->caps = of_device_get_match_data(dev);
-
-       max_eccdata_size = ecc->caps->num_ecc_strength - 1;
-       max_eccdata_size = ecc->caps->ecc_strength[max_eccdata_size];
-       max_eccdata_size = (max_eccdata_size * ecc->caps->parity_bits + 7) >> 3;
-       max_eccdata_size = round_up(max_eccdata_size, 4);
-       ecc->eccdata = devm_kzalloc(dev, max_eccdata_size, GFP_KERNEL);
-       if (!ecc->eccdata)
-               return -ENOMEM;
-
-       ecc->regs = devm_platform_ioremap_resource(pdev, 0);
-       if (IS_ERR(ecc->regs))
-               return PTR_ERR(ecc->regs);
-
-       ecc->clk = devm_clk_get(dev, NULL);
-       if (IS_ERR(ecc->clk)) {
-               dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(ecc->clk));
-               return PTR_ERR(ecc->clk);
-       }
-
-       irq = platform_get_irq(pdev, 0);
-       if (irq < 0)
-               return irq;
-
-       ret = dma_set_mask(dev, DMA_BIT_MASK(32));
-       if (ret) {
-               dev_err(dev, "failed to set DMA mask\n");
-               return ret;
-       }
-
-       ret = devm_request_irq(dev, irq, mtk_ecc_irq, 0x0, "mtk-ecc", ecc);
-       if (ret) {
-               dev_err(dev, "failed to request irq\n");
-               return -EINVAL;
-       }
-
-       ecc->dev = dev;
-       mutex_init(&ecc->lock);
-       platform_set_drvdata(pdev, ecc);
-       dev_info(dev, "probed\n");
-
-       return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int mtk_ecc_suspend(struct device *dev)
-{
-       struct mtk_ecc *ecc = dev_get_drvdata(dev);
-
-       clk_disable_unprepare(ecc->clk);
-
-       return 0;
-}
-
-static int mtk_ecc_resume(struct device *dev)
-{
-       struct mtk_ecc *ecc = dev_get_drvdata(dev);
-       int ret;
-
-       ret = clk_prepare_enable(ecc->clk);
-       if (ret) {
-               dev_err(dev, "failed to enable clk\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(mtk_ecc_pm_ops, mtk_ecc_suspend, mtk_ecc_resume);
-#endif
-
-MODULE_DEVICE_TABLE(of, mtk_ecc_dt_match);
-
-static struct platform_driver mtk_ecc_driver = {
-       .probe  = mtk_ecc_probe,
-       .driver = {
-               .name  = "mtk-ecc",
-               .of_match_table = mtk_ecc_dt_match,
-#ifdef CONFIG_PM_SLEEP
-               .pm = &mtk_ecc_pm_ops,
-#endif
-       },
-};
-
-module_platform_driver(mtk_ecc_driver);
-
-MODULE_AUTHOR("Xiaolei Li <xiaolei.li@mediatek.com>");
-MODULE_DESCRIPTION("MTK Nand ECC Driver");
-MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/mtd/nand/raw/mtk_ecc.h b/drivers/mtd/nand/raw/mtk_ecc.h
deleted file mode 100644 (file)
index 0e48c36..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 OR MIT */
-/*
- * MTK SDG1 ECC controller
- *
- * Copyright (c) 2016 Mediatek
- * Authors:    Xiaolei Li              <xiaolei.li@mediatek.com>
- *             Jorge Ramirez-Ortiz     <jorge.ramirez-ortiz@linaro.org>
- */
-
-#ifndef __DRIVERS_MTD_NAND_MTK_ECC_H__
-#define __DRIVERS_MTD_NAND_MTK_ECC_H__
-
-#include <linux/types.h>
-
-enum mtk_ecc_mode {ECC_DMA_MODE = 0, ECC_NFI_MODE = 1};
-enum mtk_ecc_operation {ECC_ENCODE, ECC_DECODE};
-
-struct device_node;
-struct mtk_ecc;
-
-struct mtk_ecc_stats {
-       u32 corrected;
-       u32 bitflips;
-       u32 failed;
-};
-
-struct mtk_ecc_config {
-       enum mtk_ecc_operation op;
-       enum mtk_ecc_mode mode;
-       dma_addr_t addr;
-       u32 strength;
-       u32 sectors;
-       u32 len;
-};
-
-int mtk_ecc_encode(struct mtk_ecc *, struct mtk_ecc_config *, u8 *, u32);
-void mtk_ecc_get_stats(struct mtk_ecc *, struct mtk_ecc_stats *, int);
-int mtk_ecc_wait_done(struct mtk_ecc *, enum mtk_ecc_operation);
-int mtk_ecc_enable(struct mtk_ecc *, struct mtk_ecc_config *);
-void mtk_ecc_disable(struct mtk_ecc *);
-void mtk_ecc_adjust_strength(struct mtk_ecc *ecc, u32 *p);
-unsigned int mtk_ecc_get_parity_bits(struct mtk_ecc *ecc);
-
-struct mtk_ecc *of_mtk_ecc_get(struct device_node *);
-void mtk_ecc_release(struct mtk_ecc *);
-
-#endif
index 66f04c6..d540454 100644 (file)
@@ -17,7 +17,7 @@
 #include <linux/iopoll.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
-#include "mtk_ecc.h"
+#include <linux/mtd/nand-ecc-mtk.h>
 
 /* NAND controller register definition */
 #define NFI_CNFG               (0x00)
index 284fff6..6b67b7d 100644 (file)
@@ -4502,11 +4502,13 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
        len = instr->len;
 
        while (len) {
+               loff_t ofs = (loff_t)page << chip->page_shift;
+
                /* Check if we have a bad block, we do not erase bad blocks! */
                if (nand_block_checkbad(chip, ((loff_t) page) <<
                                        chip->page_shift, allowbbt)) {
-                       pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
-                                   __func__, page);
+                       pr_warn("%s: attempt to erase a bad block at 0x%08llx\n",
+                                   __func__, (unsigned long long)ofs);
                        ret = -EIO;
                        goto erase_exit;
                }
@@ -4524,8 +4526,7 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
                if (ret) {
                        pr_debug("%s: failed erase, page 0x%08x\n",
                                        __func__, page);
-                       instr->fail_addr =
-                               ((loff_t)page << chip->page_shift);
+                       instr->fail_addr = ofs;
                        goto erase_exit;
                }
 
index 6e41902..88c2440 100644 (file)
@@ -29,6 +29,9 @@ struct nand_flash_dev nand_flash_ids[] = {
        {"TC58NVG0S3E 1G 3.3V 8-bit",
                { .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} },
                  SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512), },
+       {"TC58NVG0S3HTA00 1G 3.3V 8-bit",
+               { .id = {0x98, 0xf1, 0x80, 0x15} },
+                 SZ_2K, SZ_128, SZ_128K, 0, 4, 128, NAND_ECC_INFO(8, SZ_512), },
        {"TC58NVG2S0F 4G 3.3V 8-bit",
                { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
                  SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) },
@@ -58,6 +61,9 @@ struct nand_flash_dev nand_flash_ids[] = {
        {"TH58NVG2S3HBAI4 4G 3.3V 8-bit",
                { .id = {0x98, 0xdc, 0x91, 0x15, 0x76} },
                  SZ_2K, SZ_512, SZ_128K, 0, 5, 128, NAND_ECC_INFO(8, SZ_512) },
+       {"TH58NVG3S0HBAI4 8G 3.3V 8-bit",
+               { .id = {0x98, 0xd3, 0x91, 0x26, 0x76} },
+                 SZ_4K, SZ_1K, SZ_256K, 0, 5, 256, NAND_ECC_INFO(8, SZ_512)},
 
        LEGACY_ID_NAND("NAND 4MiB 5V 8-bit",   0x6B, 4, SZ_8K, SP_OPTIONS),
        LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
index cf4f379..d3d34d7 100644 (file)
@@ -287,8 +287,10 @@ static int toshiba_nand_init(struct nand_chip *chip)
        if (!strncmp("TC58NVG0S3E", chip->parameters.model,
                     sizeof("TC58NVG0S3E") - 1))
                tc58nvg0s3e_init(chip);
-       if (!strncmp("TH58NVG2S3HBAI4", chip->parameters.model,
-                    sizeof("TH58NVG2S3HBAI4") - 1))
+       if ((!strncmp("TH58NVG2S3HBAI4", chip->parameters.model,
+                    sizeof("TH58NVG2S3HBAI4") - 1)) ||
+           (!strncmp("TH58NVG3S0HBAI4", chip->parameters.model,
+                    sizeof("TH58NVG3S0HBAI4") - 1)))
                th58nvg2s3hbai4_init(chip);
 
        return 0;
index 893e997..4796a48 100644 (file)
@@ -548,6 +548,7 @@ static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume);
 #ifdef CONFIG_OF
 static const struct of_device_id elm_of_match[] = {
        { .compatible = "ti,am3352-elm" },
+       { .compatible = "ti,am64-elm" },
        {},
 };
 MODULE_DEVICE_TABLE(of, elm_of_match);
index 6db063b..1620e25 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/mtd/rawnand.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 
 #define COMMAND_REG 0x00
@@ -216,8 +217,7 @@ struct rnandc {
        struct nand_controller controller;
        struct device *dev;
        void __iomem *regs;
-       struct clk *hclk;
-       struct clk *eclk;
+       unsigned long ext_clk_rate;
        unsigned long assigned_cs;
        struct list_head chips;
        struct nand_chip *selected_chip;
@@ -891,7 +891,7 @@ static int rnandc_setup_interface(struct nand_chip *chip, int chipnr,
 {
        struct rnand_chip *rnand = to_rnand(chip);
        struct rnandc *rnandc = to_rnandc(chip->controller);
-       unsigned int period_ns = 1000000000 / clk_get_rate(rnandc->eclk);
+       unsigned int period_ns = 1000000000 / rnandc->ext_clk_rate;
        const struct nand_sdr_timings *sdr;
        unsigned int cyc, cle, ale, bef_dly, ca_to_data;
 
@@ -1319,6 +1319,7 @@ cleanup_chips:
 static int rnandc_probe(struct platform_device *pdev)
 {
        struct rnandc *rnandc;
+       struct clk *eclk;
        int irq, ret;
 
        rnandc = devm_kzalloc(&pdev->dev, sizeof(*rnandc), GFP_KERNEL);
@@ -1335,29 +1336,26 @@ static int rnandc_probe(struct platform_device *pdev)
        if (IS_ERR(rnandc->regs))
                return PTR_ERR(rnandc->regs);
 
-       /* APB clock */
-       rnandc->hclk = devm_clk_get(&pdev->dev, "hclk");
-       if (IS_ERR(rnandc->hclk))
-               return PTR_ERR(rnandc->hclk);
-
-       /* External NAND bus clock */
-       rnandc->eclk = devm_clk_get(&pdev->dev, "eclk");
-       if (IS_ERR(rnandc->eclk))
-               return PTR_ERR(rnandc->eclk);
-
-       ret = clk_prepare_enable(rnandc->hclk);
-       if (ret)
+       devm_pm_runtime_enable(&pdev->dev);
+       ret = pm_runtime_resume_and_get(&pdev->dev);
+       if (ret < 0)
                return ret;
 
-       ret = clk_prepare_enable(rnandc->eclk);
-       if (ret)
-               goto disable_hclk;
+       /* The external NAND bus clock rate is needed for computing timings */
+       eclk = clk_get(&pdev->dev, "eclk");
+       if (IS_ERR(eclk)) {
+               ret = PTR_ERR(eclk);
+               goto dis_runtime_pm;
+       }
+
+       rnandc->ext_clk_rate = clk_get_rate(eclk);
+       clk_put(eclk);
 
        rnandc_dis_interrupts(rnandc);
        irq = platform_get_irq_optional(pdev, 0);
        if (irq == -EPROBE_DEFER) {
                ret = irq;
-               goto disable_eclk;
+               goto dis_runtime_pm;
        } else if (irq < 0) {
                dev_info(&pdev->dev, "No IRQ found, fallback to polling\n");
                rnandc->use_polling = true;
@@ -1365,12 +1363,12 @@ static int rnandc_probe(struct platform_device *pdev)
                ret = devm_request_irq(&pdev->dev, irq, rnandc_irq_handler, 0,
                                       "renesas-nand-controller", rnandc);
                if (ret < 0)
-                       goto disable_eclk;
+                       goto dis_runtime_pm;
        }
 
        ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
        if (ret)
-               goto disable_eclk;
+               goto dis_runtime_pm;
 
        rnandc_clear_fifo(rnandc);
 
@@ -1378,14 +1376,12 @@ static int rnandc_probe(struct platform_device *pdev)
 
        ret = rnandc_chips_init(rnandc);
        if (ret)
-               goto disable_eclk;
+               goto dis_runtime_pm;
 
        return 0;
 
-disable_eclk:
-       clk_disable_unprepare(rnandc->eclk);
-disable_hclk:
-       clk_disable_unprepare(rnandc->hclk);
+dis_runtime_pm:
+       pm_runtime_put(&pdev->dev);
 
        return ret;
 }
@@ -1396,8 +1392,7 @@ static int rnandc_remove(struct platform_device *pdev)
 
        rnandc_chips_cleanup(rnandc);
 
-       clk_disable_unprepare(rnandc->eclk);
-       clk_disable_unprepare(rnandc->hclk);
+       pm_runtime_put(&pdev->dev);
 
        return 0;
 }
index cbaa4f1..f133985 100644 (file)
@@ -911,8 +911,7 @@ static int rk_nfc_enable_clks(struct device *dev, struct rk_nfc *nfc)
        ret = clk_prepare_enable(nfc->ahb_clk);
        if (ret) {
                dev_err(dev, "failed to enable ahb clk\n");
-               if (!IS_ERR(nfc->nfc_clk))
-                       clk_disable_unprepare(nfc->nfc_clk);
+               clk_disable_unprepare(nfc->nfc_clk);
                return ret;
        }
 
@@ -921,8 +920,7 @@ static int rk_nfc_enable_clks(struct device *dev, struct rk_nfc *nfc)
 
 static void rk_nfc_disable_clks(struct rk_nfc *nfc)
 {
-       if (!IS_ERR(nfc->nfc_clk))
-               clk_disable_unprepare(nfc->nfc_clk);
+       clk_disable_unprepare(nfc->nfc_clk);
        clk_disable_unprepare(nfc->ahb_clk);
 }
 
index de8e919..8f1a42b 100644 (file)
@@ -390,6 +390,9 @@ static int tmio_probe(struct platform_device *dev)
        if (data == NULL)
                dev_warn(&dev->dev, "NULL platform data!\n");
 
+       if (!ccr || !fcr)
+               return -EINVAL;
+
        tmio = devm_kzalloc(&dev->dev, sizeof(*tmio), GFP_KERNEL);
        if (!tmio)
                return -ENOMEM;
index 9662b9c..80dabe6 100644 (file)
@@ -1,3 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0
-spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o
+spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o
 obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
index ff83368..d5b685d 100644 (file)
@@ -933,6 +933,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = {
        &paragon_spinand_manufacturer,
        &toshiba_spinand_manufacturer,
        &winbond_spinand_manufacturer,
+       &xtx_spinand_manufacturer,
 };
 
 static int spinand_manufacturer_match(struct spinand_device *spinand,
index 1dd1c58..6b043e2 100644 (file)
@@ -39,6 +39,22 @@ static SPINAND_OP_VARIANTS(read_cache_variants_f,
                SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0),
                SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 0));
 
+static SPINAND_OP_VARIANTS(read_cache_variants_1gq5,
+               SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
+
+static SPINAND_OP_VARIANTS(read_cache_variants_2gq5,
+               SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 4, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 2, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
+
 static SPINAND_OP_VARIANTS(write_cache_variants,
                SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
                SPINAND_PROG_LOAD(true, 0, NULL, 0));
@@ -325,6 +341,36 @@ static const struct spinand_info gigadevice_spinand_table[] = {
                     SPINAND_HAS_QE_BIT,
                     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
                                     gd5fxgq4uexxg_ecc_get_status)),
+       SPINAND_INFO("GD5F1GQ4RExxG",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xc1),
+                    NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+                                    gd5fxgq4uexxg_ecc_get_status)),
+       SPINAND_INFO("GD5F2GQ4UExxG",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd2),
+                    NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+                                    gd5fxgq4uexxg_ecc_get_status)),
+       SPINAND_INFO("GD5F2GQ4RExxG",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xc2),
+                    NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+                                    gd5fxgq4uexxg_ecc_get_status)),
        SPINAND_INFO("GD5F1GQ4UFxxG",
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48),
                     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
@@ -339,12 +385,122 @@ static const struct spinand_info gigadevice_spinand_table[] = {
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51),
                     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
                     NAND_ECCREQ(4, 512),
-                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+                                    gd5fxgq5xexxg_ecc_get_status)),
+       SPINAND_INFO("GD5F1GQ5RExxG",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x41),
+                    NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
+                    NAND_ECCREQ(4, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
                                              &write_cache_variants,
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT,
                     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
                                     gd5fxgq5xexxg_ecc_get_status)),
+       SPINAND_INFO("GD5F2GQ5UExxG",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x52),
+                    NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
+                    NAND_ECCREQ(4, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+                                    gd5fxgq5xexxg_ecc_get_status)),
+       SPINAND_INFO("GD5F2GQ5RExxG",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x42),
+                    NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
+                    NAND_ECCREQ(4, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+                                    gd5fxgq5xexxg_ecc_get_status)),
+       SPINAND_INFO("GD5F4GQ6UExxG",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x55),
+                    NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 2, 1),
+                    NAND_ECCREQ(4, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+                                    gd5fxgq5xexxg_ecc_get_status)),
+       SPINAND_INFO("GD5F4GQ6RExxG",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x45),
+                    NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 2, 1),
+                    NAND_ECCREQ(4, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+                                    gd5fxgq5xexxg_ecc_get_status)),
+       SPINAND_INFO("GD5F1GM7UExxG",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x91),
+                    NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+                                    gd5fxgq4uexxg_ecc_get_status)),
+       SPINAND_INFO("GD5F1GM7RExxG",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x81),
+                    NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+                                    gd5fxgq4uexxg_ecc_get_status)),
+       SPINAND_INFO("GD5F2GM7UExxG",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92),
+                    NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+                                    gd5fxgq4uexxg_ecc_get_status)),
+       SPINAND_INFO("GD5F2GM7RExxG",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x82),
+                    NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+                                    gd5fxgq4uexxg_ecc_get_status)),
+       SPINAND_INFO("GD5F4GM8UExxG",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x95),
+                    NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+                                    gd5fxgq4uexxg_ecc_get_status)),
+       SPINAND_INFO("GD5F4GM8RExxG",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x85),
+                    NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+                                    gd5fxgq4uexxg_ecc_get_status)),
 };
 
 static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {
diff --git a/drivers/mtd/nand/spi/xtx.c b/drivers/mtd/nand/spi/xtx.c
new file mode 100644 (file)
index 0000000..3911520
--- /dev/null
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author:
+ * Felix Matouschek <felix@matouschek.org>
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mtd/spinand.h>
+
+#define SPINAND_MFR_XTX        0x0B
+
+#define XT26G0XA_STATUS_ECC_MASK       GENMASK(5, 2)
+#define XT26G0XA_STATUS_ECC_NO_DETECTED        (0 << 2)
+#define XT26G0XA_STATUS_ECC_8_CORRECTED        (3 << 4)
+#define XT26G0XA_STATUS_ECC_UNCOR_ERROR        (2 << 4)
+
+static SPINAND_OP_VARIANTS(read_cache_variants,
+               SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
+
+static SPINAND_OP_VARIANTS(write_cache_variants,
+               SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
+               SPINAND_PROG_LOAD(true, 0, NULL, 0));
+
+static SPINAND_OP_VARIANTS(update_cache_variants,
+               SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
+               SPINAND_PROG_LOAD(false, 0, NULL, 0));
+
+static int xt26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                  struct mtd_oob_region *region)
+{
+       if (section)
+               return -ERANGE;
+
+       region->offset = 48;
+       region->length = 16;
+
+       return 0;
+}
+
+static int xt26g0xa_ooblayout_free(struct mtd_info *mtd, int section,
+                                  struct mtd_oob_region *region)
+{
+       if (section)
+               return -ERANGE;
+
+       region->offset = 1;
+       region->length = 47;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops xt26g0xa_ooblayout = {
+       .ecc = xt26g0xa_ooblayout_ecc,
+       .free = xt26g0xa_ooblayout_free,
+};
+
+static int xt26g0xa_ecc_get_status(struct spinand_device *spinand,
+                                        u8 status)
+{
+       status = status & XT26G0XA_STATUS_ECC_MASK;
+
+       switch (status) {
+       case XT26G0XA_STATUS_ECC_NO_DETECTED:
+               return 0;
+       case XT26G0XA_STATUS_ECC_8_CORRECTED:
+               return 8;
+       case XT26G0XA_STATUS_ECC_UNCOR_ERROR:
+               return -EBADMSG;
+       default:
+               break;
+       }
+
+       /* At this point values greater than (2 << 4) are invalid  */
+       if (status > XT26G0XA_STATUS_ECC_UNCOR_ERROR)
+               return -EINVAL;
+
+       /* (1 << 2) through (7 << 2) are 1-7 corrected errors */
+       return status >> 2;
+}
+
+static const struct spinand_info xtx_spinand_table[] = {
+       SPINAND_INFO("XT26G01A",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE1),
+                    NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&xt26g0xa_ooblayout,
+                                    xt26g0xa_ecc_get_status)),
+       SPINAND_INFO("XT26G02A",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE2),
+                    NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&xt26g0xa_ooblayout,
+                                    xt26g0xa_ecc_get_status)),
+       SPINAND_INFO("XT26G04A",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE3),
+                    NAND_MEMORG(1, 2048, 64, 128, 2048, 40, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&xt26g0xa_ooblayout,
+                                    xt26g0xa_ecc_get_status)),
+};
+
+static const struct spinand_manufacturer_ops xtx_spinand_manuf_ops = {
+};
+
+const struct spinand_manufacturer xtx_spinand_manufacturer = {
+       .id = SPINAND_MFR_XTX,
+       .name = "XTX",
+       .chips = xtx_spinand_table,
+       .nchips = ARRAY_SIZE(xtx_spinand_table),
+       .ops = &xtx_spinand_manuf_ops,
+};
index 6012a10..50fcf4c 100644 (file)
@@ -237,7 +237,7 @@ static int bcm47xxpart_parse(struct mtd_info *master,
                               (uint8_t *)buf);
                if (err && !mtd_is_bitflip(err)) {
                        pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
-                              offset, err);
+                              offset + 0x8000, err);
                        continue;
                }
 
index 6b904e4..e347b43 100644 (file)
@@ -17,6 +17,7 @@ spi-nor-objs                  += sst.o
 spi-nor-objs                   += winbond.o
 spi-nor-objs                   += xilinx.o
 spi-nor-objs                   += xmc.o
+spi-nor-$(CONFIG_DEBUG_FS)     += debugfs.o
 obj-$(CONFIG_MTD_SPI_NOR)      += spi-nor.o
 
 obj-$(CONFIG_MTD_SPI_NOR)      += controllers/
index b4f141a..502967c 100644 (file)
@@ -307,6 +307,52 @@ ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,
        return nor->controller_ops->write(nor, to, len, buf);
 }
 
+/**
+ * spi_nor_read_any_reg() - read any register from flash memory, nonvolatile or
+ * volatile.
+ * @nor:        pointer to 'struct spi_nor'.
+ * @op:                SPI memory operation. op->data.buf must be DMA-able.
+ * @proto:     SPI protocol to use for the register operation.
+ *
+ * Return: zero on success, -errno otherwise
+ */
+int spi_nor_read_any_reg(struct spi_nor *nor, struct spi_mem_op *op,
+                        enum spi_nor_protocol proto)
+{
+       if (!nor->spimem)
+               return -EOPNOTSUPP;
+
+       spi_nor_spimem_setup_op(nor, op, proto);
+       return spi_nor_spimem_exec_op(nor, op);
+}
+
+/**
+ * spi_nor_write_any_volatile_reg() - write any volatile register to flash
+ * memory.
+ * @nor:        pointer to 'struct spi_nor'
+ * @op:                SPI memory operation. op->data.buf must be DMA-able.
+ * @proto:     SPI protocol to use for the register operation.
+ *
+ * Writing volatile registers are instant according to some manufacturers
+ * (Cypress, Micron) and do not need any status polling.
+ *
+ * Return: zero on success, -errno otherwise
+ */
+int spi_nor_write_any_volatile_reg(struct spi_nor *nor, struct spi_mem_op *op,
+                                  enum spi_nor_protocol proto)
+{
+       int ret;
+
+       if (!nor->spimem)
+               return -EOPNOTSUPP;
+
+       ret = spi_nor_write_enable(nor);
+       if (ret)
+               return ret;
+       spi_nor_spimem_setup_op(nor, op, proto);
+       return spi_nor_spimem_exec_op(nor, op);
+}
+
 /**
  * spi_nor_write_enable() - Set write enable latch with Write Enable command.
  * @nor:       pointer to 'struct spi_nor'.
@@ -318,11 +364,7 @@ int spi_nor_write_enable(struct spi_nor *nor)
        int ret;
 
        if (nor->spimem) {
-               struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 0),
-                                  SPI_MEM_OP_NO_ADDR,
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_NO_DATA);
+               struct spi_mem_op op = SPI_NOR_WREN_OP;
 
                spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
@@ -349,11 +391,7 @@ int spi_nor_write_disable(struct spi_nor *nor)
        int ret;
 
        if (nor->spimem) {
-               struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 0),
-                                  SPI_MEM_OP_NO_ADDR,
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_NO_DATA);
+               struct spi_mem_op op = SPI_NOR_WRDI_OP;
 
                spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
@@ -369,6 +407,37 @@ int spi_nor_write_disable(struct spi_nor *nor)
        return ret;
 }
 
+/**
+ * spi_nor_read_id() - Read the JEDEC ID.
+ * @nor:       pointer to 'struct spi_nor'.
+ * @naddr:     number of address bytes to send. Can be zero if the operation
+ *             does not need to send an address.
+ * @ndummy:    number of dummy bytes to send after an opcode or address. Can
+ *             be zero if the operation does not require dummy bytes.
+ * @id:                pointer to a DMA-able buffer where the value of the JEDEC ID
+ *             will be written.
+ * @proto:     the SPI protocol for register operation.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int spi_nor_read_id(struct spi_nor *nor, u8 naddr, u8 ndummy, u8 *id,
+                   enum spi_nor_protocol proto)
+{
+       int ret;
+
+       if (nor->spimem) {
+               struct spi_mem_op op =
+                       SPI_NOR_READID_OP(naddr, ndummy, id, SPI_NOR_MAX_ID_LEN);
+
+               spi_nor_spimem_setup_op(nor, &op, proto);
+               ret = spi_mem_exec_op(nor->spimem, &op);
+       } else {
+               ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDID, id,
+                                                   SPI_NOR_MAX_ID_LEN);
+       }
+       return ret;
+}
+
 /**
  * spi_nor_read_sr() - Read the Status Register.
  * @nor:       pointer to 'struct spi_nor'.
@@ -382,11 +451,7 @@ int spi_nor_read_sr(struct spi_nor *nor, u8 *sr)
        int ret;
 
        if (nor->spimem) {
-               struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 0),
-                                  SPI_MEM_OP_NO_ADDR,
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_IN(1, sr, 0));
+               struct spi_mem_op op = SPI_NOR_RDSR_OP(sr);
 
                if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) {
                        op.addr.nbytes = nor->params->rdsr_addr_nbytes;
@@ -426,11 +491,7 @@ int spi_nor_read_cr(struct spi_nor *nor, u8 *cr)
        int ret;
 
        if (nor->spimem) {
-               struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 0),
-                                  SPI_MEM_OP_NO_ADDR,
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_IN(1, cr, 0));
+               struct spi_mem_op op = SPI_NOR_RDCR_OP(cr);
 
                spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
@@ -459,14 +520,7 @@ int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
        int ret;
 
        if (nor->spimem) {
-               struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(enable ?
-                                                 SPINOR_OP_EN4B :
-                                                 SPINOR_OP_EX4B,
-                                                 0),
-                                 SPI_MEM_OP_NO_ADDR,
-                                 SPI_MEM_OP_NO_DUMMY,
-                                 SPI_MEM_OP_NO_DATA);
+               struct spi_mem_op op = SPI_NOR_EN4B_EX4B_OP(enable);
 
                spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
@@ -500,11 +554,7 @@ static int spansion_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
        nor->bouncebuf[0] = enable << 7;
 
        if (nor->spimem) {
-               struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_BRWR, 0),
-                                  SPI_MEM_OP_NO_ADDR,
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 0));
+               struct spi_mem_op op = SPI_NOR_BRWR_OP(nor->bouncebuf);
 
                spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
@@ -520,40 +570,6 @@ static int spansion_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
        return ret;
 }
 
-/**
- * spi_nor_write_ear() - Write Extended Address Register.
- * @nor:       pointer to 'struct spi_nor'.
- * @ear:       value to write to the Extended Address Register.
- *
- * Return: 0 on success, -errno otherwise.
- */
-int spi_nor_write_ear(struct spi_nor *nor, u8 ear)
-{
-       int ret;
-
-       nor->bouncebuf[0] = ear;
-
-       if (nor->spimem) {
-               struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREAR, 0),
-                                  SPI_MEM_OP_NO_ADDR,
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 0));
-
-               spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
-
-               ret = spi_mem_exec_op(nor->spimem, &op);
-       } else {
-               ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WREAR,
-                                                      nor->bouncebuf, 1);
-       }
-
-       if (ret)
-               dev_dbg(nor->dev, "error %d writing EAR\n", ret);
-
-       return ret;
-}
-
 /**
  * spi_nor_sr_ready() - Query the Status Register to see if the flash is ready
  * for new commands.
@@ -649,11 +665,7 @@ int spi_nor_global_block_unlock(struct spi_nor *nor)
                return ret;
 
        if (nor->spimem) {
-               struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_GBULK, 0),
-                                  SPI_MEM_OP_NO_ADDR,
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_NO_DATA);
+               struct spi_mem_op op = SPI_NOR_GBULK_OP;
 
                spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
@@ -688,11 +700,7 @@ int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len)
                return ret;
 
        if (nor->spimem) {
-               struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 0),
-                                  SPI_MEM_OP_NO_ADDR,
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_OUT(len, sr, 0));
+               struct spi_mem_op op = SPI_NOR_WRSR_OP(sr, len);
 
                spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
@@ -788,6 +796,15 @@ static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 sr1)
        if (ret)
                return ret;
 
+       ret = spi_nor_read_sr(nor, sr_cr);
+       if (ret)
+               return ret;
+
+       if (sr1 != sr_cr[0]) {
+               dev_dbg(nor->dev, "SR: Read back test failed\n");
+               return -EIO;
+       }
+
        if (nor->flags & SNOR_F_NO_READ_CR)
                return 0;
 
@@ -892,11 +909,7 @@ static int spi_nor_write_sr2(struct spi_nor *nor, const u8 *sr2)
                return ret;
 
        if (nor->spimem) {
-               struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 0),
-                                  SPI_MEM_OP_NO_ADDR,
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_OUT(1, sr2, 0));
+               struct spi_mem_op op = SPI_NOR_WRSR2_OP(sr2);
 
                spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
@@ -928,11 +941,7 @@ static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2)
        int ret;
 
        if (nor->spimem) {
-               struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 0),
-                                  SPI_MEM_OP_NO_ADDR,
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_IN(1, sr2, 0));
+               struct spi_mem_op op = SPI_NOR_RDSR2_OP(sr2);
 
                spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
@@ -961,11 +970,7 @@ static int spi_nor_erase_chip(struct spi_nor *nor)
        dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10));
 
        if (nor->spimem) {
-               struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 0),
-                                  SPI_MEM_OP_NO_ADDR,
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_NO_DATA);
+               struct spi_mem_op op = SPI_NOR_CHIP_ERASE_OP;
 
                spi_nor_spimem_setup_op(nor, &op, nor->write_proto);
 
@@ -1107,10 +1112,8 @@ int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
 
        if (nor->spimem) {
                struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 0),
-                                  SPI_MEM_OP_ADDR(nor->addr_width, addr, 0),
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_NO_DATA);
+                       SPI_NOR_SECTOR_ERASE_OP(nor->erase_opcode,
+                                               nor->addr_width, addr);
 
                spi_nor_spimem_setup_op(nor, &op, nor->write_proto);
 
@@ -1629,58 +1632,45 @@ static const struct spi_nor_manufacturer *manufacturers[] = {
        &spi_nor_xmc,
 };
 
-static const struct flash_info *
-spi_nor_search_part_by_id(const struct flash_info *parts, unsigned int nparts,
-                         const u8 *id)
+static const struct flash_info *spi_nor_match_id(struct spi_nor *nor,
+                                                const u8 *id)
 {
-       unsigned int i;
+       const struct flash_info *part;
+       unsigned int i, j;
 
-       for (i = 0; i < nparts; i++) {
-               if (parts[i].id_len &&
-                   !memcmp(parts[i].id, id, parts[i].id_len))
-                       return &parts[i];
+       for (i = 0; i < ARRAY_SIZE(manufacturers); i++) {
+               for (j = 0; j < manufacturers[i]->nparts; j++) {
+                       part = &manufacturers[i]->parts[j];
+                       if (part->id_len &&
+                           !memcmp(part->id, id, part->id_len)) {
+                               nor->manufacturer = manufacturers[i];
+                               return part;
+                       }
+               }
        }
 
        return NULL;
 }
 
-static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
+static const struct flash_info *spi_nor_detect(struct spi_nor *nor)
 {
        const struct flash_info *info;
        u8 *id = nor->bouncebuf;
-       unsigned int i;
        int ret;
 
-       if (nor->spimem) {
-               struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1),
-                                  SPI_MEM_OP_NO_ADDR,
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_IN(SPI_NOR_MAX_ID_LEN, id, 1));
-
-               ret = spi_mem_exec_op(nor->spimem, &op);
-       } else {
-               ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDID, id,
-                                                   SPI_NOR_MAX_ID_LEN);
-       }
+       ret = spi_nor_read_id(nor, 0, 0, id, nor->reg_proto);
        if (ret) {
                dev_dbg(nor->dev, "error %d reading JEDEC ID\n", ret);
                return ERR_PTR(ret);
        }
 
-       for (i = 0; i < ARRAY_SIZE(manufacturers); i++) {
-               info = spi_nor_search_part_by_id(manufacturers[i]->parts,
-                                                manufacturers[i]->nparts,
-                                                id);
-               if (info) {
-                       nor->manufacturer = manufacturers[i];
-                       return info;
-               }
+       info = spi_nor_match_id(nor, id);
+       if (!info) {
+               dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n",
+                       SPI_NOR_MAX_ID_LEN, id);
+               return ERR_PTR(-ENODEV);
        }
-
-       dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n",
-               SPI_NOR_MAX_ID_LEN, id);
-       return ERR_PTR(-ENODEV);
+       return info;
 }
 
 static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
@@ -1860,7 +1850,7 @@ int spi_nor_hwcaps_read2cmd(u32 hwcaps)
                                  ARRAY_SIZE(hwcaps_read2cmd));
 }
 
-static int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
+int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
 {
        static const int hwcaps_pp2cmd[][2] = {
                { SNOR_HWCAPS_PP,               SNOR_CMD_PP },
@@ -1919,10 +1909,7 @@ static int spi_nor_spimem_check_op(struct spi_nor *nor,
 static int spi_nor_spimem_check_readop(struct spi_nor *nor,
                                       const struct spi_nor_read_command *read)
 {
-       struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(read->opcode, 0),
-                                         SPI_MEM_OP_ADDR(3, 0, 0),
-                                         SPI_MEM_OP_DUMMY(1, 0),
-                                         SPI_MEM_OP_DATA_IN(2, NULL, 0));
+       struct spi_mem_op op = SPI_NOR_READ_OP(read->opcode);
 
        spi_nor_spimem_setup_op(nor, &op, read->proto);
 
@@ -1945,10 +1932,7 @@ static int spi_nor_spimem_check_readop(struct spi_nor *nor,
 static int spi_nor_spimem_check_pp(struct spi_nor *nor,
                                   const struct spi_nor_pp_command *pp)
 {
-       struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(pp->opcode, 0),
-                                         SPI_MEM_OP_ADDR(3, 0, 0),
-                                         SPI_MEM_OP_NO_DUMMY,
-                                         SPI_MEM_OP_DATA_OUT(2, NULL, 0));
+       struct spi_mem_op op = SPI_NOR_PP_OP(pp->opcode);
 
        spi_nor_spimem_setup_op(nor, &op, pp->proto);
 
@@ -2772,10 +2756,7 @@ static void spi_nor_soft_reset(struct spi_nor *nor)
        struct spi_mem_op op;
        int ret;
 
-       op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRSTEN, 0),
-                       SPI_MEM_OP_NO_DUMMY,
-                       SPI_MEM_OP_NO_ADDR,
-                       SPI_MEM_OP_NO_DATA);
+       op = (struct spi_mem_op)SPINOR_SRSTEN_OP;
 
        spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
@@ -2785,10 +2766,7 @@ static void spi_nor_soft_reset(struct spi_nor *nor)
                return;
        }
 
-       op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRST, 0),
-                       SPI_MEM_OP_NO_DUMMY,
-                       SPI_MEM_OP_NO_ADDR,
-                       SPI_MEM_OP_NO_DATA);
+       op = (struct spi_mem_op)SPINOR_SRST_OP;
 
        spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
@@ -2876,8 +2854,8 @@ void spi_nor_restore(struct spi_nor *nor)
 }
 EXPORT_SYMBOL_GPL(spi_nor_restore);
 
-static const struct flash_info *spi_nor_match_id(struct spi_nor *nor,
-                                                const char *name)
+static const struct flash_info *spi_nor_match_name(struct spi_nor *nor,
+                                                  const char *name)
 {
        unsigned int i, j;
 
@@ -2899,12 +2877,10 @@ static const struct flash_info *spi_nor_get_flash_info(struct spi_nor *nor,
        const struct flash_info *info = NULL;
 
        if (name)
-               info = spi_nor_match_id(nor, name);
+               info = spi_nor_match_name(nor, name);
        /* Try to auto-detect if chip name wasn't specified or not found */
        if (!info)
-               info = spi_nor_read_id(nor);
-       if (IS_ERR_OR_NULL(info))
-               return ERR_PTR(-ENOENT);
+               return spi_nor_detect(nor);
 
        /*
         * If caller has specified name of flash model that can normally be
@@ -2913,7 +2889,7 @@ static const struct flash_info *spi_nor_get_flash_info(struct spi_nor *nor,
        if (name && info->id_len) {
                const struct flash_info *jinfo;
 
-               jinfo = spi_nor_read_id(nor);
+               jinfo = spi_nor_detect(nor);
                if (IS_ERR(jinfo)) {
                        return jinfo;
                } else if (jinfo != info) {
@@ -3156,6 +3132,8 @@ static int spi_nor_probe(struct spi_mem *spimem)
        if (ret)
                return ret;
 
+       spi_nor_debugfs_register(nor);
+
        /*
         * None of the existing parts have > 512B pages, but let's play safe
         * and add this logic so that if anyone ever adds support for such
index b7fd760..3f841ec 100644 (file)
 
 #define SPI_NOR_MAX_ID_LEN     6
 
+/* Standard SPI NOR flash operations. */
+#define SPI_NOR_READID_OP(naddr, ndummy, buf, len)                     \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 0),                   \
+                  SPI_MEM_OP_ADDR(naddr, 0, 0),                        \
+                  SPI_MEM_OP_DUMMY(ndummy, 0),                         \
+                  SPI_MEM_OP_DATA_IN(len, buf, 0))
+
+#define SPI_NOR_WREN_OP                                                        \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 0),                   \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_NO_DATA)
+
+#define SPI_NOR_WRDI_OP                                                        \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 0),                   \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_NO_DATA)
+
+#define SPI_NOR_RDSR_OP(buf)                                           \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 0),                   \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_DATA_IN(1, buf, 0))
+
+#define SPI_NOR_WRSR_OP(buf, len)                                      \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 0),                   \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_DATA_OUT(len, buf, 0))
+
+#define SPI_NOR_RDSR2_OP(buf)                                          \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 0),                  \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_DATA_OUT(1, buf, 0))
+
+#define SPI_NOR_WRSR2_OP(buf)                                          \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 0),                  \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_DATA_OUT(1, buf, 0))
+
+#define SPI_NOR_RDCR_OP(buf)                                           \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 0),                   \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_DATA_IN(1, buf, 0))
+
+#define SPI_NOR_EN4B_EX4B_OP(enable)                                   \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B, 0), \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_NO_DATA)
+
+#define SPI_NOR_BRWR_OP(buf)                                           \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_BRWR, 0),                   \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_DATA_OUT(1, buf, 0))
+
+#define SPI_NOR_GBULK_OP                                               \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_GBULK, 0),                  \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_NO_DATA)
+
+#define SPI_NOR_CHIP_ERASE_OP                                          \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 0),             \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_NO_DATA)
+
+#define SPI_NOR_SECTOR_ERASE_OP(opcode, addr_width, addr)              \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0),                           \
+                  SPI_MEM_OP_ADDR(addr_width, addr, 0),                \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_NO_DATA)
+
+#define SPI_NOR_READ_OP(opcode)                                                \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0),                           \
+                  SPI_MEM_OP_ADDR(3, 0, 0),                            \
+                  SPI_MEM_OP_DUMMY(1, 0),                              \
+                  SPI_MEM_OP_DATA_IN(2, NULL, 0))
+
+#define SPI_NOR_PP_OP(opcode)                                          \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0),                           \
+                  SPI_MEM_OP_ADDR(3, 0, 0),                            \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_DATA_OUT(2, NULL, 0))
+
+#define SPINOR_SRSTEN_OP                                               \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRSTEN, 0),                 \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DATA)
+
+#define SPINOR_SRST_OP                                                 \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRST, 0),                   \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DATA)
+
+/* Keep these in sync with the list in debugfs.c */
 enum spi_nor_option_flags {
        SNOR_F_HAS_SR_TB        = BIT(0),
        SNOR_F_NO_OP_CHIP_ERASE = BIT(1),
@@ -236,9 +340,10 @@ struct spi_nor_otp {
  * @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.
- * @rdsr_dummy:                dummy cycles needed for Read Status Register command.
+ * @rdsr_dummy:                dummy cycles needed for Read Status Register command
+ *                     in octal DTR mode.
  * @rdsr_addr_nbytes:  dummy address bytes needed for Read Status Register
- *                     command.
+ *                     command in octal DTR mode.
  * @hwcaps:            describes the read and page program hardware
  *                     capabilities.
  * @reads:             read capabilities ordered by priority: the higher index
@@ -526,7 +631,6 @@ void spi_nor_spimem_setup_op(const struct spi_nor *nor,
 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(struct spi_nor *nor, bool enable);
-int spi_nor_write_ear(struct spi_nor *nor, u8 ear);
 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);
@@ -534,6 +638,8 @@ 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);
 int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor);
+int spi_nor_read_id(struct spi_nor *nor, u8 naddr, u8 ndummy, u8 *id,
+                   enum spi_nor_protocol reg_proto);
 int spi_nor_read_sr(struct spi_nor *nor, u8 *sr);
 int spi_nor_sr_ready(struct spi_nor *nor);
 int spi_nor_read_cr(struct spi_nor *nor, u8 *cr);
@@ -545,6 +651,10 @@ ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
                          u8 *buf);
 ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,
                           const u8 *buf);
+int spi_nor_read_any_reg(struct spi_nor *nor, struct spi_mem_op *op,
+                        enum spi_nor_protocol proto);
+int spi_nor_write_any_volatile_reg(struct spi_nor *nor, struct spi_mem_op *op,
+                                  enum spi_nor_protocol proto);
 int spi_nor_erase_sector(struct spi_nor *nor, u32 addr);
 
 int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf);
@@ -555,6 +665,7 @@ int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region);
 int spi_nor_otp_is_locked_sr2(struct spi_nor *nor, unsigned int region);
 
 int spi_nor_hwcaps_read2cmd(u32 hwcaps);
+int spi_nor_hwcaps_pp2cmd(u32 hwcaps);
 u8 spi_nor_convert_3to4_read(u8 opcode);
 void spi_nor_set_read_settings(struct spi_nor_read_command *read,
                               u8 num_mode_clocks,
@@ -590,4 +701,10 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
        return container_of(mtd, struct spi_nor, mtd);
 }
 
+#ifdef CONFIG_DEBUG_FS
+void spi_nor_debugfs_register(struct spi_nor *nor);
+#else
+static inline void spi_nor_debugfs_register(struct spi_nor *nor) {}
+#endif
+
 #endif /* __LINUX_MTD_SPI_NOR_INTERNAL_H */
diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c
new file mode 100644 (file)
index 0000000..eaf84f7
--- /dev/null
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/mtd/spi-nor.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi-mem.h>
+#include <linux/debugfs.h>
+
+#include "core.h"
+
+#define SPI_NOR_DEBUGFS_ROOT "spi-nor"
+
+#define SNOR_F_NAME(name) [ilog2(SNOR_F_##name)] = #name
+static const char *const snor_f_names[] = {
+       SNOR_F_NAME(HAS_SR_TB),
+       SNOR_F_NAME(NO_OP_CHIP_ERASE),
+       SNOR_F_NAME(BROKEN_RESET),
+       SNOR_F_NAME(4B_OPCODES),
+       SNOR_F_NAME(HAS_4BAIT),
+       SNOR_F_NAME(HAS_LOCK),
+       SNOR_F_NAME(HAS_16BIT_SR),
+       SNOR_F_NAME(NO_READ_CR),
+       SNOR_F_NAME(HAS_SR_TB_BIT6),
+       SNOR_F_NAME(HAS_4BIT_BP),
+       SNOR_F_NAME(HAS_SR_BP3_BIT6),
+       SNOR_F_NAME(IO_MODE_EN_VOLATILE),
+       SNOR_F_NAME(SOFT_RESET),
+       SNOR_F_NAME(SWP_IS_VOLATILE),
+};
+#undef SNOR_F_NAME
+
+static const char *spi_nor_protocol_name(enum spi_nor_protocol proto)
+{
+       switch (proto) {
+       case SNOR_PROTO_1_1_1:     return "1S-1S-1S";
+       case SNOR_PROTO_1_1_2:     return "1S-1S-2S";
+       case SNOR_PROTO_1_1_4:     return "1S-1S-4S";
+       case SNOR_PROTO_1_1_8:     return "1S-1S-8S";
+       case SNOR_PROTO_1_2_2:     return "1S-2S-2S";
+       case SNOR_PROTO_1_4_4:     return "1S-4S-4S";
+       case SNOR_PROTO_1_8_8:     return "1S-8S-8S";
+       case SNOR_PROTO_2_2_2:     return "2S-2S-2S";
+       case SNOR_PROTO_4_4_4:     return "4S-4S-4S";
+       case SNOR_PROTO_8_8_8:     return "8S-8S-8S";
+       case SNOR_PROTO_1_1_1_DTR: return "1D-1D-1D";
+       case SNOR_PROTO_1_2_2_DTR: return "1D-2D-2D";
+       case SNOR_PROTO_1_4_4_DTR: return "1D-4D-4D";
+       case SNOR_PROTO_1_8_8_DTR: return "1D-8D-8D";
+       case SNOR_PROTO_8_8_8_DTR: return "8D-8D-8D";
+       }
+
+       return "<unknown>";
+}
+
+static void spi_nor_print_flags(struct seq_file *s, unsigned long flags,
+                               const char *const *names, int names_len)
+{
+       bool sep = false;
+       int i;
+
+       for (i = 0; i < sizeof(flags) * BITS_PER_BYTE; i++) {
+               if (!(flags & BIT(i)))
+                       continue;
+               if (sep)
+                       seq_puts(s, " | ");
+               sep = true;
+               if (i < names_len && names[i])
+                       seq_puts(s, names[i]);
+               else
+                       seq_printf(s, "1<<%d", i);
+       }
+}
+
+static int spi_nor_params_show(struct seq_file *s, void *data)
+{
+       struct spi_nor *nor = s->private;
+       struct spi_nor_flash_parameter *params = nor->params;
+       struct spi_nor_erase_map *erase_map = &params->erase_map;
+       struct spi_nor_erase_region *region;
+       const struct flash_info *info = nor->info;
+       char buf[16], *str;
+       int i;
+
+       seq_printf(s, "name\t\t%s\n", info->name);
+       seq_printf(s, "id\t\t%*ph\n", info->id_len, info->id);
+       string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf));
+       seq_printf(s, "size\t\t%s\n", buf);
+       seq_printf(s, "write size\t%u\n", params->writesize);
+       seq_printf(s, "page size\t%u\n", params->page_size);
+       seq_printf(s, "address width\t%u\n", nor->addr_width);
+
+       seq_puts(s, "flags\t\t");
+       spi_nor_print_flags(s, nor->flags, snor_f_names, sizeof(snor_f_names));
+       seq_puts(s, "\n");
+
+       seq_puts(s, "\nopcodes\n");
+       seq_printf(s, " read\t\t0x%02x\n", nor->read_opcode);
+       seq_printf(s, "  dummy cycles\t%u\n", nor->read_dummy);
+       seq_printf(s, " erase\t\t0x%02x\n", nor->erase_opcode);
+       seq_printf(s, " program\t0x%02x\n", nor->program_opcode);
+
+       switch (nor->cmd_ext_type) {
+       case SPI_NOR_EXT_NONE:
+               str = "none";
+               break;
+       case SPI_NOR_EXT_REPEAT:
+               str = "repeat";
+               break;
+       case SPI_NOR_EXT_INVERT:
+               str = "invert";
+               break;
+       default:
+               str = "<unknown>";
+               break;
+       }
+       seq_printf(s, " 8D extension\t%s\n", str);
+
+       seq_puts(s, "\nprotocols\n");
+       seq_printf(s, " read\t\t%s\n",
+                  spi_nor_protocol_name(nor->read_proto));
+       seq_printf(s, " write\t\t%s\n",
+                  spi_nor_protocol_name(nor->write_proto));
+       seq_printf(s, " register\t%s\n",
+                  spi_nor_protocol_name(nor->reg_proto));
+
+       seq_puts(s, "\nerase commands\n");
+       for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) {
+               struct spi_nor_erase_type *et = &erase_map->erase_type[i];
+
+               if (et->size) {
+                       string_get_size(et->size, 1, STRING_UNITS_2, buf,
+                                       sizeof(buf));
+                       seq_printf(s, " %02x (%s) [%d]\n", et->opcode, buf, i);
+               }
+       }
+
+       if (!(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
+               string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf));
+               seq_printf(s, " %02x (%s)\n", SPINOR_OP_CHIP_ERASE, buf);
+       }
+
+       seq_puts(s, "\nsector map\n");
+       seq_puts(s, " region (in hex)   | erase mask | flags\n");
+       seq_puts(s, " ------------------+------------+----------\n");
+       for (region = erase_map->regions;
+            region;
+            region = spi_nor_region_next(region)) {
+               u64 start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
+               u64 flags = region->offset & SNOR_ERASE_FLAGS_MASK;
+               u64 end = start + region->size - 1;
+
+               seq_printf(s, " %08llx-%08llx |     [%c%c%c%c] | %s\n",
+                          start, end,
+                          flags & BIT(0) ? '0' : ' ',
+                          flags & BIT(1) ? '1' : ' ',
+                          flags & BIT(2) ? '2' : ' ',
+                          flags & BIT(3) ? '3' : ' ',
+                          flags & SNOR_OVERLAID_REGION ? "overlaid" : "");
+       }
+
+       return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(spi_nor_params);
+
+static void spi_nor_print_read_cmd(struct seq_file *s, u32 cap,
+                                  struct spi_nor_read_command *cmd)
+{
+       seq_printf(s, " %s%s\n", spi_nor_protocol_name(cmd->proto),
+                  cap == SNOR_HWCAPS_READ_FAST ? " (fast read)" : "");
+       seq_printf(s, "  opcode\t0x%02x\n", cmd->opcode);
+       seq_printf(s, "  mode cycles\t%u\n", cmd->num_mode_clocks);
+       seq_printf(s, "  dummy cycles\t%u\n", cmd->num_wait_states);
+}
+
+static void spi_nor_print_pp_cmd(struct seq_file *s,
+                                struct spi_nor_pp_command *cmd)
+{
+       seq_printf(s, " %s\n", spi_nor_protocol_name(cmd->proto));
+       seq_printf(s, "  opcode\t0x%02x\n", cmd->opcode);
+}
+
+static int spi_nor_capabilities_show(struct seq_file *s, void *data)
+{
+       struct spi_nor *nor = s->private;
+       struct spi_nor_flash_parameter *params = nor->params;
+       u32 hwcaps = params->hwcaps.mask;
+       int i, cmd;
+
+       seq_puts(s, "Supported read modes by the flash\n");
+       for (i = 0; i < sizeof(hwcaps) * BITS_PER_BYTE; i++) {
+               if (!(hwcaps & BIT(i)))
+                       continue;
+
+               cmd = spi_nor_hwcaps_read2cmd(BIT(i));
+               if (cmd < 0)
+                       continue;
+
+               spi_nor_print_read_cmd(s, BIT(i), &params->reads[cmd]);
+               hwcaps &= ~BIT(i);
+       }
+
+       seq_puts(s, "\nSupported page program modes by the flash\n");
+       for (i = 0; i < sizeof(hwcaps) * BITS_PER_BYTE; i++) {
+               if (!(hwcaps & BIT(i)))
+                       continue;
+
+               cmd = spi_nor_hwcaps_pp2cmd(BIT(i));
+               if (cmd < 0)
+                       continue;
+
+               spi_nor_print_pp_cmd(s, &params->page_programs[cmd]);
+               hwcaps &= ~BIT(i);
+       }
+
+       if (hwcaps)
+               seq_printf(s, "\nunknown hwcaps 0x%x\n", hwcaps);
+
+       return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(spi_nor_capabilities);
+
+static void spi_nor_debugfs_unregister(void *data)
+{
+       struct spi_nor *nor = data;
+
+       debugfs_remove(nor->debugfs_root);
+       nor->debugfs_root = NULL;
+}
+
+void spi_nor_debugfs_register(struct spi_nor *nor)
+{
+       struct dentry *rootdir, *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);
+
+       ret = devm_add_action(nor->dev, spi_nor_debugfs_unregister, nor);
+       if (ret)
+               return;
+
+       d = debugfs_create_dir(dev_name(nor->dev), rootdir);
+       nor->debugfs_root = d;
+
+       debugfs_create_file("params", 0444, d, nor, &spi_nor_params_fops);
+       debugfs_create_file("capabilities", 0444, d, nor,
+                           &spi_nor_capabilities_fops);
+}
index 8c1c575..50a1105 100644 (file)
@@ -25,7 +25,8 @@ static const struct flash_info eon_nor_parts[] = {
        { "en25qh64",   INFO(0x1c7017, 0, 64 * 1024,  128)
                NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) },
        { "en25qh128",  INFO(0x1c7018, 0, 64 * 1024,  256) },
-       { "en25qh256",  INFO(0x1c7019, 0, 64 * 1024,  512) },
+       { "en25qh256",  INFO(0x1c7019, 0, 64 * 1024,  512)
+               PARSE_SFDP },
        { "en25s64",    INFO(0x1c3817, 0, 64 * 1024,  128)
                NO_SFDP_FLAGS(SECT_4K) },
 };
index 8a20475..a96f74e 100644 (file)
 #define FSR_P_ERR              BIT(4)  /* Program operation status */
 #define FSR_PT_ERR             BIT(1)  /* Protection error bit */
 
-static int micron_st_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
+/* Micron ST SPI NOR flash operations. */
+#define MICRON_ST_NOR_WR_ANY_REG_OP(naddr, addr, ndata, buf)           \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 0),          \
+                  SPI_MEM_OP_ADDR(naddr, addr, 0),                     \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_DATA_OUT(ndata, buf, 0))
+
+#define MICRON_ST_RDFSR_OP(buf)                                                \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 0),                  \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_DATA_IN(1, buf, 0))
+
+#define MICRON_ST_CLFSR_OP                                             \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 0),                  \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_NO_DATA)
+
+static int micron_st_nor_octal_dtr_en(struct spi_nor *nor)
 {
        struct spi_mem_op op;
        u8 *buf = nor->bouncebuf;
        int ret;
 
-       if (enable) {
-               /* Use 20 dummy cycles for memory array reads. */
-               ret = spi_nor_write_enable(nor);
-               if (ret)
-                       return ret;
-
-               *buf = 20;
-               op = (struct spi_mem_op)
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1),
-                                  SPI_MEM_OP_ADDR(3, SPINOR_REG_MT_CFR1V, 1),
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_OUT(1, buf, 1));
-
-               ret = spi_mem_exec_op(nor->spimem, &op);
-               if (ret)
-                       return ret;
-
-               ret = spi_nor_wait_till_ready(nor);
-               if (ret)
-                       return ret;
-       }
+       /* Use 20 dummy cycles for memory array reads. */
+       *buf = 20;
+       op = (struct spi_mem_op)
+               MICRON_ST_NOR_WR_ANY_REG_OP(3, SPINOR_REG_MT_CFR1V, 1, buf);
+       ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
+       if (ret)
+               return ret;
 
-       ret = spi_nor_write_enable(nor);
+       buf[0] = SPINOR_MT_OCT_DTR;
+       op = (struct spi_mem_op)
+               MICRON_ST_NOR_WR_ANY_REG_OP(3, SPINOR_REG_MT_CFR0V, 1, buf);
+       ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
        if (ret)
                return ret;
 
-       if (enable) {
-               buf[0] = SPINOR_MT_OCT_DTR;
-       } else {
-               /*
-                * The register is 1-byte wide, but 1-byte transactions are not
-                * allowed in 8D-8D-8D mode. The next register is the dummy
-                * cycle configuration register. Since the transaction needs to
-                * be at least 2 bytes wide, set the next register to its
-                * default value. This also makes sense because the value was
-                * changed when enabling 8D-8D-8D mode, it should be reset when
-                * disabling.
-                */
-               buf[0] = SPINOR_MT_EXSPI;
-               buf[1] = SPINOR_REG_MT_CFR1V_DEF;
+       /* Read flash ID to make sure the switch was successful. */
+       ret = spi_nor_read_id(nor, 0, 8, buf, SNOR_PROTO_8_8_8_DTR);
+       if (ret) {
+               dev_dbg(nor->dev, "error %d reading JEDEC ID after enabling 8D-8D-8D mode\n", ret);
+               return ret;
        }
 
-       op = (struct spi_mem_op)
-               SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1),
-                          SPI_MEM_OP_ADDR(enable ? 3 : 4,
-                                          SPINOR_REG_MT_CFR0V, 1),
-                          SPI_MEM_OP_NO_DUMMY,
-                          SPI_MEM_OP_DATA_OUT(enable ? 1 : 2, buf, 1));
+       if (memcmp(buf, nor->info->id, nor->info->id_len))
+               return -EINVAL;
 
-       if (!enable)
-               spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR);
+       return 0;
+}
 
-       ret = spi_mem_exec_op(nor->spimem, &op);
+static int micron_st_nor_octal_dtr_dis(struct spi_nor *nor)
+{
+       struct spi_mem_op op;
+       u8 *buf = nor->bouncebuf;
+       int ret;
+
+       /*
+        * The register is 1-byte wide, but 1-byte transactions are not allowed
+        * in 8D-8D-8D mode. The next register is the dummy cycle configuration
+        * register. Since the transaction needs to be at least 2 bytes wide,
+        * set the next register to its default value. This also makes sense
+        * because the value was changed when enabling 8D-8D-8D mode, it should
+        * be reset when disabling.
+        */
+       buf[0] = SPINOR_MT_EXSPI;
+       buf[1] = SPINOR_REG_MT_CFR1V_DEF;
+       op = (struct spi_mem_op)
+               MICRON_ST_NOR_WR_ANY_REG_OP(4, SPINOR_REG_MT_CFR0V, 2, buf);
+       ret = spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR);
        if (ret)
                return ret;
 
        /* Read flash ID to make sure the switch was successful. */
-       op = (struct spi_mem_op)
-               SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1),
-                          SPI_MEM_OP_NO_ADDR,
-                          SPI_MEM_OP_DUMMY(enable ? 8 : 0, 1),
-                          SPI_MEM_OP_DATA_IN(round_up(nor->info->id_len, 2),
-                                             buf, 1));
-
-       if (enable)
-               spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR);
-
-       ret = spi_mem_exec_op(nor->spimem, &op);
-       if (ret)
+       ret = spi_nor_read_id(nor, 0, 0, buf, SNOR_PROTO_1_1_1);
+       if (ret) {
+               dev_dbg(nor->dev, "error %d reading JEDEC ID after disabling 8D-8D-8D mode\n", ret);
                return ret;
+       }
 
        if (memcmp(buf, nor->info->id, nor->info->id_len))
                return -EINVAL;
@@ -111,6 +116,12 @@ static int micron_st_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
        return 0;
 }
 
+static int micron_st_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
+{
+       return enable ? micron_st_nor_octal_dtr_en(nor) :
+                       micron_st_nor_octal_dtr_dis(nor);
+}
+
 static void mt35xu512aba_default_init(struct spi_nor *nor)
 {
        nor->params->octal_dtr_enable = micron_st_nor_octal_dtr_enable;
@@ -322,11 +333,7 @@ static int micron_st_nor_read_fsr(struct spi_nor *nor, u8 *fsr)
        int ret;
 
        if (nor->spimem) {
-               struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 0),
-                                  SPI_MEM_OP_NO_ADDR,
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_IN(1, fsr, 0));
+               struct spi_mem_op op = MICRON_ST_RDFSR_OP(fsr);
 
                if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) {
                        op.addr.nbytes = nor->params->rdsr_addr_nbytes;
@@ -361,11 +368,7 @@ static void micron_st_nor_clear_fsr(struct spi_nor *nor)
        int ret;
 
        if (nor->spimem) {
-               struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 0),
-                                  SPI_MEM_OP_NO_ADDR,
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_NO_DATA);
+               struct spi_mem_op op = MICRON_ST_CLFSR_OP;
 
                spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
index f24e546..43cd6cd 100644 (file)
 #define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS    0
 #define SPINOR_OP_CYPRESS_RD_FAST              0xee
 
-/**
- * cypress_nor_octal_dtr_enable() - Enable octal DTR on Cypress flashes.
- * @nor:               pointer to a 'struct spi_nor'
- * @enable:              whether to enable or disable Octal DTR
- *
- * This also sets the memory access latency cycles to 24 to allow the flash to
- * run at up to 200MHz.
- *
- * Return: 0 on success, -errno otherwise.
- */
-static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
+/* Cypress SPI NOR flash operations. */
+#define CYPRESS_NOR_WR_ANY_REG_OP(naddr, addr, ndata, buf)             \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 0),             \
+                  SPI_MEM_OP_ADDR(naddr, addr, 0),                     \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_DATA_OUT(ndata, buf, 0))
+
+#define CYPRESS_NOR_RD_ANY_REG_OP(naddr, addr, buf)                    \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 0),             \
+                  SPI_MEM_OP_ADDR(naddr, addr, 0),                     \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_DATA_IN(1, buf, 0))
+
+#define SPANSION_CLSR_OP                                               \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 0),                   \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_NO_DATA)
+
+static int cypress_nor_octal_dtr_en(struct spi_nor *nor)
 {
        struct spi_mem_op op;
        u8 *buf = nor->bouncebuf;
        int ret;
 
-       if (enable) {
-               /* Use 24 dummy cycles for memory array reads. */
-               ret = spi_nor_write_enable(nor);
-               if (ret)
-                       return ret;
-
-               *buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24;
-               op = (struct spi_mem_op)
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1),
-                                  SPI_MEM_OP_ADDR(3, SPINOR_REG_CYPRESS_CFR2V,
-                                                  1),
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_OUT(1, buf, 1));
+       /* Use 24 dummy cycles for memory array reads. */
+       *buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24;
+       op = (struct spi_mem_op)
+               CYPRESS_NOR_WR_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR2V, 1, buf);
 
-               ret = spi_mem_exec_op(nor->spimem, &op);
-               if (ret)
-                       return ret;
+       ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
+       if (ret)
+               return ret;
 
-               ret = spi_nor_wait_till_ready(nor);
-               if (ret)
-                       return ret;
+       nor->read_dummy = 24;
 
-               nor->read_dummy = 24;
-       }
+       /* Set the octal and DTR enable bits. */
+       buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN;
+       op = (struct spi_mem_op)
+               CYPRESS_NOR_WR_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR5V, 1, buf);
 
-       /* Set/unset the octal and DTR enable bits. */
-       ret = spi_nor_write_enable(nor);
+       ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
        if (ret)
                return ret;
 
-       if (enable) {
-               buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN;
-       } else {
-               /*
-                * The register is 1-byte wide, but 1-byte transactions are not
-                * allowed in 8D-8D-8D mode. Since there is no register at the
-                * next location, just initialize the value to 0 and let the
-                * transaction go on.
-                */
-               buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS;
-               buf[1] = 0;
+       /* Read flash ID to make sure the switch was successful. */
+       ret = spi_nor_read_id(nor, 4, 3, buf, SNOR_PROTO_8_8_8_DTR);
+       if (ret) {
+               dev_dbg(nor->dev, "error %d reading JEDEC ID after enabling 8D-8D-8D mode\n", ret);
+               return ret;
        }
 
-       op = (struct spi_mem_op)
-               SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1),
-                          SPI_MEM_OP_ADDR(enable ? 3 : 4,
-                                          SPINOR_REG_CYPRESS_CFR5V,
-                                          1),
-                          SPI_MEM_OP_NO_DUMMY,
-                          SPI_MEM_OP_DATA_OUT(enable ? 1 : 2, buf, 1));
+       if (memcmp(buf, nor->info->id, nor->info->id_len))
+               return -EINVAL;
 
-       if (!enable)
-               spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR);
+       return 0;
+}
 
-       ret = spi_mem_exec_op(nor->spimem, &op);
+static int cypress_nor_octal_dtr_dis(struct spi_nor *nor)
+{
+       struct spi_mem_op op;
+       u8 *buf = nor->bouncebuf;
+       int ret;
+
+       /*
+        * The register is 1-byte wide, but 1-byte transactions are not allowed
+        * in 8D-8D-8D mode. Since there is no register at the next location,
+        * just initialize the value to 0 and let the transaction go on.
+        */
+       buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS;
+       buf[1] = 0;
+       op = (struct spi_mem_op)
+               CYPRESS_NOR_WR_ANY_REG_OP(4, SPINOR_REG_CYPRESS_CFR5V, 2, buf);
+       ret = spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR);
        if (ret)
                return ret;
 
        /* Read flash ID to make sure the switch was successful. */
-       op = (struct spi_mem_op)
-               SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1),
-                          SPI_MEM_OP_ADDR(enable ? 4 : 0, 0, 1),
-                          SPI_MEM_OP_DUMMY(enable ? 3 : 0, 1),
-                          SPI_MEM_OP_DATA_IN(round_up(nor->info->id_len, 2),
-                                             buf, 1));
-
-       if (enable)
-               spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR);
-
-       ret = spi_mem_exec_op(nor->spimem, &op);
-       if (ret)
+       ret = spi_nor_read_id(nor, 0, 0, buf, SNOR_PROTO_1_1_1);
+       if (ret) {
+               dev_dbg(nor->dev, "error %d reading JEDEC ID after disabling 8D-8D-8D mode\n", ret);
                return ret;
+       }
 
        if (memcmp(buf, nor->info->id, nor->info->id_len))
                return -EINVAL;
@@ -118,6 +113,22 @@ static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
        return 0;
 }
 
+/**
+ * cypress_nor_octal_dtr_enable() - Enable octal DTR on Cypress flashes.
+ * @nor:               pointer to a 'struct spi_nor'
+ * @enable:              whether to enable or disable Octal DTR
+ *
+ * This also sets the memory access latency cycles to 24 to allow the flash to
+ * run at up to 200MHz.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
+{
+       return enable ? cypress_nor_octal_dtr_en(nor) :
+                       cypress_nor_octal_dtr_dis(nor);
+}
+
 static void s28hs512t_default_init(struct spi_nor *nor)
 {
        nor->params->octal_dtr_enable = cypress_nor_octal_dtr_enable;
@@ -162,12 +173,12 @@ static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor,
         * CFR3V[4] and set the correct size.
         */
        struct spi_mem_op op =
-               SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 1),
-                          SPI_MEM_OP_ADDR(3, SPINOR_REG_CYPRESS_CFR3V, 1),
-                          SPI_MEM_OP_NO_DUMMY,
-                          SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1));
+               CYPRESS_NOR_RD_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR3V,
+                                         nor->bouncebuf);
        int ret;
 
+       spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
        ret = spi_mem_exec_op(nor->spimem, &op);
        if (ret)
                return ret;
@@ -317,11 +328,7 @@ static void spansion_nor_clear_sr(struct spi_nor *nor)
        int ret;
 
        if (nor->spimem) {
-               struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 0),
-                                  SPI_MEM_OP_NO_ADDR,
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_NO_DATA);
+               struct spi_mem_op op = SPANSION_CLSR_OP;
 
                spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
index fe80dff..ffaa240 100644 (file)
@@ -8,6 +8,15 @@
 
 #include "core.h"
 
+#define WINBOND_NOR_OP_RDEAR   0xc8    /* Read Extended Address Register */
+#define WINBOND_NOR_OP_WREAR   0xc5    /* Write Extended Address Register */
+
+#define WINBOND_NOR_WREAR_OP(buf)                                      \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(WINBOND_NOR_OP_WREAR, 0),             \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_DATA_OUT(1, buf, 0))
+
 static int
 w25q256_post_bfpt_fixups(struct spi_nor *nor,
                         const struct sfdp_parameter_header *bfpt_header,
@@ -124,11 +133,45 @@ static const struct flash_info winbond_nor_parts[] = {
        { "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024)
                NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ |
                              SPI_NOR_DUAL_READ) },
+       { "w25q512nwm", INFO(0xef8020, 0, 64 * 1024, 1024)
+               PARSE_SFDP
+               OTP_INFO(256, 3, 0x1000, 0x1000) },
        { "w25q512jvq", INFO(0xef4020, 0, 64 * 1024, 1024)
                NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
                              SPI_NOR_QUAD_READ) },
 };
 
+/**
+ * winbond_nor_write_ear() - Write Extended Address Register.
+ * @nor:       pointer to 'struct spi_nor'.
+ * @ear:       value to write to the Extended Address Register.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int winbond_nor_write_ear(struct spi_nor *nor, u8 ear)
+{
+       int ret;
+
+       nor->bouncebuf[0] = ear;
+
+       if (nor->spimem) {
+               struct spi_mem_op op = WINBOND_NOR_WREAR_OP(nor->bouncebuf);
+
+               spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
+               ret = spi_mem_exec_op(nor->spimem, &op);
+       } else {
+               ret = spi_nor_controller_ops_write_reg(nor,
+                                                      WINBOND_NOR_OP_WREAR,
+                                                      nor->bouncebuf, 1);
+       }
+
+       if (ret)
+               dev_dbg(nor->dev, "error %d writing EAR\n", ret);
+
+       return ret;
+}
+
 /**
  * winbond_nor_set_4byte_addr_mode() - Set 4-byte address mode for Winbond
  * flashes.
@@ -155,7 +198,7 @@ static int winbond_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
        if (ret)
                return ret;
 
-       ret = spi_nor_write_ear(nor, 0);
+       ret = winbond_nor_write_ear(nor, 0);
        if (ret)
                return ret;
 
index 9459ac2..1d2f5db 100644 (file)
 #define XSR_PAGESIZE           BIT(0)  /* Page size in Po2 or Linear */
 #define XSR_RDY                        BIT(7)  /* Ready */
 
+#define XILINX_RDSR_OP(buf)                                            \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(XILINX_OP_RDSR, 0),                   \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_DATA_IN(1, buf, 0))
+
 #define S3AN_INFO(_jedec_id, _n_sectors, _page_size)                   \
                .id = {                                                 \
                        ((_jedec_id) >> 16) & 0xff,                     \
@@ -72,11 +78,7 @@ static int xilinx_nor_read_sr(struct spi_nor *nor, u8 *sr)
        int ret;
 
        if (nor->spimem) {
-               struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(XILINX_OP_RDSR, 0),
-                                  SPI_MEM_OP_NO_ADDR,
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_IN(1, sr, 0));
+               struct spi_mem_op op = XILINX_RDSR_OP(sr);
 
                spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
 
index a16b74f..55d62b8 100644 (file)
@@ -509,6 +509,7 @@ EXPORT_SYMBOL_GPL(of_platform_default_populate);
 
 #ifndef CONFIG_PPC
 static const struct of_device_id reserved_mem_matches[] = {
+       { .compatible = "phram" },
        { .compatible = "qcom,rmtfs-mem" },
        { .compatible = "qcom,cmd-db" },
        { .compatible = "qcom,smem" },
index fd1ecb8..d88bb56 100644 (file)
@@ -286,6 +286,7 @@ struct cfi_private {
        map_word sector_erase_cmd;
        unsigned long chipshift; /* Because they're of the same type */
        const char *im_name;     /* inter_module name for cmdset_setup */
+       unsigned long quirks;
        struct flchip chips[];  /* per-chip data structure for each chip */
 };
 
diff --git a/include/linux/mtd/nand-ecc-mtk.h b/include/linux/mtd/nand-ecc-mtk.h
new file mode 100644 (file)
index 0000000..0e48c36
--- /dev/null
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * MTK SDG1 ECC controller
+ *
+ * Copyright (c) 2016 Mediatek
+ * Authors:    Xiaolei Li              <xiaolei.li@mediatek.com>
+ *             Jorge Ramirez-Ortiz     <jorge.ramirez-ortiz@linaro.org>
+ */
+
+#ifndef __DRIVERS_MTD_NAND_MTK_ECC_H__
+#define __DRIVERS_MTD_NAND_MTK_ECC_H__
+
+#include <linux/types.h>
+
+enum mtk_ecc_mode {ECC_DMA_MODE = 0, ECC_NFI_MODE = 1};
+enum mtk_ecc_operation {ECC_ENCODE, ECC_DECODE};
+
+struct device_node;
+struct mtk_ecc;
+
+struct mtk_ecc_stats {
+       u32 corrected;
+       u32 bitflips;
+       u32 failed;
+};
+
+struct mtk_ecc_config {
+       enum mtk_ecc_operation op;
+       enum mtk_ecc_mode mode;
+       dma_addr_t addr;
+       u32 strength;
+       u32 sectors;
+       u32 len;
+};
+
+int mtk_ecc_encode(struct mtk_ecc *, struct mtk_ecc_config *, u8 *, u32);
+void mtk_ecc_get_stats(struct mtk_ecc *, struct mtk_ecc_stats *, int);
+int mtk_ecc_wait_done(struct mtk_ecc *, enum mtk_ecc_operation);
+int mtk_ecc_enable(struct mtk_ecc *, struct mtk_ecc_config *);
+void mtk_ecc_disable(struct mtk_ecc *);
+void mtk_ecc_adjust_strength(struct mtk_ecc *ecc, u32 *p);
+unsigned int mtk_ecc_get_parity_bits(struct mtk_ecc *ecc);
+
+struct mtk_ecc *of_mtk_ecc_get(struct device_node *);
+void mtk_ecc_release(struct mtk_ecc *);
+
+#endif
index 5e25a7b..1ede4c8 100644 (file)
@@ -47,8 +47,6 @@
 #define SPINOR_OP_RDID         0x9f    /* Read JEDEC ID */
 #define SPINOR_OP_RDSFDP       0x5a    /* Read SFDP */
 #define SPINOR_OP_RDCR         0x35    /* Read configuration register */
-#define SPINOR_OP_RDEAR                0xc8    /* Read Extended Address Register */
-#define SPINOR_OP_WREAR                0xc5    /* Write Extended Address Register */
 #define SPINOR_OP_SRSTEN       0x66    /* Software Reset Enable */
 #define SPINOR_OP_SRST         0x99    /* Software Reset */
 #define SPINOR_OP_GBULK                0x98    /* Global Block Unlock */
@@ -365,6 +363,7 @@ struct spi_nor_flash_parameter;
  * @write_proto:       the SPI protocol for write operations
  * @reg_proto:         the SPI protocol for read_reg/write_reg/erase operations
  * @sfdp:              the SFDP data of the flash
+ * @debugfs_root:      pointer to the debugfs directory
  * @controller_ops:    SPI NOR controller driver specific operations.
  * @params:            [FLASH-SPECIFIC] SPI NOR flash parameters and settings.
  *                      The structure includes legacy flash parameters and
@@ -394,6 +393,7 @@ struct spi_nor {
        u32                     flags;
        enum spi_nor_cmd_ext    cmd_ext_type;
        struct sfdp             *sfdp;
+       struct dentry           *debugfs_root;
 
        const struct spi_nor_controller_ops *controller_ops;
 
index 3aa2824..5584d3b 100644 (file)
@@ -266,6 +266,7 @@ extern const struct spinand_manufacturer micron_spinand_manufacturer;
 extern const struct spinand_manufacturer paragon_spinand_manufacturer;
 extern const struct spinand_manufacturer toshiba_spinand_manufacturer;
 extern const struct spinand_manufacturer winbond_spinand_manufacturer;
+extern const struct spinand_manufacturer xtx_spinand_manufacturer;
 
 /**
  * struct spinand_op_variants - SPI NAND operation variants