MTD core changes:

* Fix refcount error in del_mtd_device()
 * Fix possible resource leak in init_mtd()
 * Set ROOT_DEV for partitions marked as rootfs in DT
 * Describe marking rootfs partitions in the bindings
 * Fix device name leak when register device failed in add_mtd_device()
 * Try to find OF node for every MTD partition
 * simplify (a bit) code find partition-matching dynamic OF node
 
 MTD driver changes:
 * pxa2xx-flash maps: fix memory leak in probe
 * BCM parser: refer to ARCH_BCMBCA instead of ARCH_BCM4908
 * lpddr2_nvm: Fix possible null-ptr-deref
 * inftlcore: fix repeated words in comments
 * lart: remove driver
 * tplink:
   - Add TP-Link SafeLoader partitions table parser and bindings
   - Describe TP-Link SafeLoader parser
   - Describe TP-Link SafeLoader dynamic subpartitions
 * mtdoops:
   - Panic caused mtdoops to call mtdoops_erase function immediately
   - Add mtdoops_erase function and move mtdoops_inc_counter to after it
   - Change printk() to counterpart pr_ functions
 
 MTD binding cleanup:
 * Fixed-partitions: Fix 'sercomm,scpart-id' schema
 * Standardize the style in the examples
 * Drop object types when referencing other files
 * Argue in favor of keeping additionalProperties set to true
 * NVMEM-cells:
   - Inherit from MTD partitions
   - Drop range property from example
 * Partitions:
   - Change qcom,smem-part partition type
   - Constrain the list of parsers
 * Physmap: Reuse the generic definitions
 * SPI-NOR: Drop common properties
 * Sunxi-nand: Add an example to validate the bindings
 * Onenand: Mention the expected node name
 * Ingenic: Mark partitions in the controller node as deprecated
 * NAND:
   - Standardize the child node name
   - Drop common properties already defined in generic files
   - nand-chip.yaml should reference mtd.yaml
 * Remove useless file about partitions
 * Clarify all partition subnodes
 
 SPI NOR core changes:
 * Add support for flash reset using the dt reset-gpios property.
 * Update hwcaps.mask to include 8D-8D-8D read and page program ops
   when xSPI profile 1.0 table is defined.
 * Bypass zero erase size in spi_nor_find_best_erase_type().
 * Fix select_uniform_erase to skip 0 erase size
 * Add generic flash driver. If a flash is not found in the flash_info
   array, fall back to the generic flash driver which is described solely
   by the flash's SFDP tables.
 * Fix the number of bytes for the dummy cycles in
   spi_nor_spimem_check_readop().
 * Introduce SPI_NOR_QUAD_PP flag, as PP_1_1_4 is not SFDP discoverable.
 
 SPI NOR manufacturer drivers changes:
 * Spansion:
   - use PARSE_SFDP for s28hs512t,
   - add support for s28hl512t, s28hl01gt, and s28hs01gt.
 * Gigadevice: Replace default_init() with post_bfpt() for gd25q256.
 * Micron - ST: Enable locking for mt25qu256a.
 * Winbond: Add support for W25Q512NW-IQ.
 * ISSI: Use PARSE_SFDP and SPI_NOR_QUAD_PP.
 
 Raw NAND core changes:
 * Drop obsolete dependencies on COMPILE_TEST
 * MAINTAINERS: rectify entry for MESON NAND controller bindings
 * Drop EXPORT_SYMBOL_GPL for nanddev_erase()
 
 Raw NAND driver changes:
 * marvell: Enable NFC/DEVBUS arbiter
 * gpmi: Use pm_runtime_resume_and_get instead of pm_runtime_get_sync
 * mpc5121: Replace NO_IRQ by 0
 * lpc32xx_{slc,mlc}:
   - Switch to using pm_ptr()
   - Switch to using gpiod API
 * lpc32xx_mlc: Switch to using pm_ptr()
 * cadence: Support 64-bit slave dma interface
 * rockchip: Describe rk3128-nfc in the bindings
 * brcmnand: Update interrupts description in the bindings
 
 SPI-NAND driver changes:
 * winbond:
   - Add Winbond W25N02KV flash support
   - Fix flash identification
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAmOOBbQACgkQJWrqGEe9
 VoRB7wf9EY8IeRhumaeN9WT+VrmPxGOu61m/eilUK4xvgj6JX/yhYo/kKW9eEp+o
 5I8JLimG6uo7rZNlbixGMbHd41aTu9MplLrTA42nzGadl3w4D8p9c1ic6sEiv7SI
 qgnAbzq3FLUPDLLDV0IfrMQvsQgJkv3b+r8ShPt9uJy3cmnKQzOvIhs43wwLnOXp
 NPiMbVjpS4FAmNvG7nI0xfcBLO/eFaTuQc+UyfP2SwkVIAN5xfUAwOPOHZWBstmR
 zhJlHSFJWrg+J9T9bsRW7D5q/MVxqNsMH589c9aXHNqzgzZs2FTopDUf6cWwHr0f
 2Ssot2naIdvU+eLGA12hDuUDDoRwKA==
 =Igti
 -----END PGP SIGNATURE-----

Merge tag 'mtd/for-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux

Pull mtd updates from Miquel Raynal:
 "MTD core changes:
   - Fix refcount error in del_mtd_device()
   - Fix possible resource leak in init_mtd()
   - Set ROOT_DEV for partitions marked as rootfs in DT
   - Describe marking rootfs partitions in the bindings
   - Fix device name leak when register device fails in add_mtd_device()
   - Try to find OF node for every MTD partition
   - simplify (a bit) code find partition-matching dynamic OF node

  MTD driver changes:
   - pxa2xx-flash maps: fix memory leak in probe
   - BCM parser: refer to ARCH_BCMBCA instead of ARCH_BCM4908
   - lpddr2_nvm: Fix possible null-ptr-deref
   - inftlcore: fix repeated words in comments
   - lart: remove driver
   - tplink:
      - Add TP-Link SafeLoader partitions table parser and bindings
      - Describe TP-Link SafeLoader parser
      - Describe TP-Link SafeLoader dynamic subpartitions
   - mtdoops:
      - Panic caused mtdoops to call mtdoops_erase function immediately
      - Add mtdoops_erase function and move mtdoops_inc_counter after it
      - Change printk() to counterpart pr_ functions

  MTD binding cleanup:
   - Fixed-partitions: Fix 'sercomm,scpart-id' schema
   - Standardize the style in the examples
   - Drop object types when referencing other files
   - Argue in favor of keeping additionalProperties set to true
   - NVMEM-cells:
      - Inherit from MTD partitions
      - Drop range property from example
   - Partitions:
      - Change qcom,smem-part partition type
      - Constrain the list of parsers
   - Physmap: Reuse the generic definitions
   - SPI-NOR: Drop common properties
   - Sunxi-nand: Add an example to validate the bindings
   - Onenand: Mention the expected node name
   - Ingenic: Mark partitions in the controller node as deprecated
   - NAND:
      - Standardize the child node name
      - Drop common properties already defined in generic files
      - nand-chip.yaml should reference mtd.yaml
   - Remove useless file about partitions
   - Clarify all partition subnodes

  SPI NOR core changes:
   - Add support for flash reset using the dt reset-gpios property.
   - Update hwcaps.mask to include 8D-8D-8D read and page program ops
     when xSPI profile 1.0 table is defined.
   - Bypass zero erase size in spi_nor_find_best_erase_type().
   - Fix select_uniform_erase to skip 0 erase size
   - Add generic flash driver. If a flash is not found in the flash_info
     array, fall back to the generic flash driver which is described
     solely by the flash's SFDP tables.
   - Fix the number of bytes for the dummy cycles in
     spi_nor_spimem_check_readop().
   - Introduce SPI_NOR_QUAD_PP flag, as PP_1_1_4 is not SFDP
     discoverable.

  SPI NOR manufacturer drivers changes:
   - Spansion:
      - use PARSE_SFDP for s28hs512t,
      - add support for s28hl512t, s28hl01gt, and s28hs01gt.
   - Gigadevice: Replace default_init() with post_bfpt() for gd25q256.
   - Micron - ST: Enable locking for mt25qu256a.
   - Winbond: Add support for W25Q512NW-IQ.
   - ISSI: Use PARSE_SFDP and SPI_NOR_QUAD_PP.

  Raw NAND core changes:
   - Drop obsolete dependencies on COMPILE_TEST
   - MAINTAINERS: rectify entry for MESON NAND controller bindings
   - Drop EXPORT_SYMBOL_GPL for nanddev_erase()

  Raw NAND driver changes:
   - marvell: Enable NFC/DEVBUS arbiter
   - gpmi: Use pm_runtime_resume_and_get instead of pm_runtime_get_sync
   - mpc5121: Replace NO_IRQ by 0
   - lpc32xx_{slc,mlc}:
      - Switch to using pm_ptr()
      - Switch to using gpiod API
   - lpc32xx_mlc: Switch to using pm_ptr()
   - cadence: Support 64-bit slave dma interface
   - rockchip: Describe rk3128-nfc in the bindings
   - brcmnand: Update interrupts description in the bindings

  SPI-NAND driver changes:
   - winbond:
      - Add Winbond W25N02KV flash support
      - Fix flash identification"

* tag 'mtd/for-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (76 commits)
  mtd: rawnand: Drop obsolete dependencies on COMPILE_TEST
  mtd: maps: pxa2xx-flash: fix memory leak in probe
  mtd: core: Fix refcount error in del_mtd_device()
  mtd: spi-nor: add SFDP fixups for Quad Page Program
  mtd: spi-nor: issi: is25wp256: Init flash based on SFDP
  mtd: spi-nor: winbond: add support for W25Q512NW-IQ
  mtd: spi-nor: micron-st: Enable locking for mt25qu256a
  mtd: spi-nor: Fix the number of bytes for the dummy cycles
  mtd: spi-nor: gigadevice: gd25q256: replace gd25q256_default_init with gd25q256_post_bfpt
  mtd: spi-nor: Fix formatting in spi_nor_read_raw() kerneldoc comment
  mtd: spi-nor: sysfs: print JEDEC ID for generic flash driver
  mtd: spi-nor: add generic flash driver
  mtd: spi-nor: fix select_uniform_erase to skip 0 erase size
  mtd: spi-nor: move function declaration out of sfdp.h
  mtd: spi-nor: remember full JEDEC flash ID
  mtd: spi-nor: sysfs: hide manufacturer if it is not set
  mtd: spi-nor: hide jedec_id sysfs attribute if not present
  mtd: spi-nor: Check for zero erase size in spi_nor_find_best_erase_type()
  mtd: rawnand: marvell: Enable NFC/DEVBUS arbiter
  mtd: parsers: refer to ARCH_BCMBCA instead of ARCH_BCM4908
  ...
This commit is contained in:
Linus Torvalds 2022-12-13 12:32:07 -08:00
commit 1e4fa020d5
69 changed files with 1153 additions and 1256 deletions

View File

@ -5,6 +5,9 @@ Contact: linux-mtd@lists.infradead.org
Description: (RO) The JEDEC ID of the SPI NOR flash as reported by the Description: (RO) The JEDEC ID of the SPI NOR flash as reported by the
flash device. flash device.
The attribute is not present if the flash doesn't support
the "Read JEDEC ID" command (9Fh). This is the case for
non-JEDEC compliant flashes.
What: /sys/bus/spi/devices/.../spi-nor/manufacturer What: /sys/bus/spi/devices/.../spi-nor/manufacturer
Date: April 2021 Date: April 2021
@ -12,6 +15,9 @@ KernelVersion: 5.14
Contact: linux-mtd@lists.infradead.org Contact: linux-mtd@lists.infradead.org
Description: (RO) Manufacturer of the SPI NOR flash. Description: (RO) Manufacturer of the SPI NOR flash.
The attribute is not present if the flash device isn't
known to the kernel and is only probed by its SFDP
tables.
What: /sys/bus/spi/devices/.../spi-nor/partname What: /sys/bus/spi/devices/.../spi-nor/partname
Date: April 2021 Date: April 2021

View File

@ -14,9 +14,6 @@ maintainers:
- Maxime Ripard <mripard@kernel.org> - Maxime Ripard <mripard@kernel.org>
properties: properties:
"#address-cells": true
"#size-cells": true
compatible: compatible:
enum: enum:
- allwinner,sun4i-a10-nand - allwinner,sun4i-a10-nand
@ -49,12 +46,8 @@ properties:
dma-names: dma-names:
const: rxtx const: rxtx
pinctrl-names: true
patternProperties: patternProperties:
"^pinctrl-[0-9]+$": true "^nand@[a-f0-9]$":
"^nand@[a-f0-9]+$":
type: object type: object
properties: properties:
reg: reg:
@ -91,6 +84,29 @@ required:
- clocks - clocks
- clock-names - clock-names
additionalProperties: false unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/sun6i-rtc.h>
#include <dt-bindings/clock/sun8i-a23-a33-ccu.h>
#include <dt-bindings/reset/sun8i-a23-a33-ccu.h>
nand-controller@1c03000 {
compatible = "allwinner,sun8i-a23-nand-controller";
reg = <0x01c03000 0x1000>;
interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ccu CLK_BUS_NAND>, <&ccu CLK_NAND>;
clock-names = "ahb", "mod";
resets = <&ccu RST_BUS_NAND>;
reset-names = "ahb";
dmas = <&dma 5>;
dma-names = "rxtx";
pinctrl-names = "default";
pinctrl-0 = <&nand_pins &nand_cs0_pin &nand_rb0_pin>;
#address-cells = <1>;
#size-cells = <0>;
};
... ...

View File

@ -35,9 +35,6 @@ properties:
interrupts: interrupts:
maxItems: 1 maxItems: 1
"#address-cells": true
"#size-cells": true
required: required:
- compatible - compatible
- reg - reg
@ -45,7 +42,7 @@ required:
- clock-names - clock-names
- interrupts - interrupts
additionalProperties: true unevaluatedProperties: true
examples: examples:
- | - |

View File

@ -45,10 +45,8 @@ Optional properties:
- atmel,rb: an integer identifying the native Ready/Busy pin. Only meaningful - atmel,rb: an integer identifying the native Ready/Busy pin. Only meaningful
on sama5 SoCs. on sama5 SoCs.
All generic properties described in All generic properties are described in the generic yaml files under
Documentation/devicetree/bindings/mtd/{common,nand}.txt also apply to the NAND Documentation/devicetree/bindings/mtd/.
device node, and NAND partitions should be defined under the NAND node as
described in Documentation/devicetree/bindings/mtd/partition.txt.
* ECC engine (PMECC) bindings: * ECC engine (PMECC) bindings:

View File

@ -86,15 +86,15 @@ properties:
minItems: 1 minItems: 1
items: items:
- description: NAND CTLRDY interrupt - description: NAND CTLRDY interrupt
- description: FLASH_DMA_DONE if flash DMA is available - description: FLASH_DMA_DONE (if flash DMA is available) or FLASH_EDU_DONE (if EDU is available)
- description: FLASH_EDU_DONE if EDU is available
interrupt-names: interrupt-names:
minItems: 1 minItems: 1
items: items:
- const: nand_ctlrdy - const: nand_ctlrdy
- const: flash_dma_done - enum:
- const: flash_edu_done - flash_dma_done
- flash_edu_done
clocks: clocks:
maxItems: 1 maxItems: 1
@ -173,6 +173,13 @@ allOf:
- const: nand - const: nand
- const: iproc-idm - const: iproc-idm
- const: iproc-ext - const: iproc-ext
- if:
properties:
interrupts:
minItems: 2
then:
required:
- interrupt-names
unevaluatedProperties: false unevaluatedProperties: false
@ -190,6 +197,7 @@ examples:
reg-names = "nand", "flash-dma"; reg-names = "nand", "flash-dma";
interrupt-parent = <&hif_intr2_intc>; interrupt-parent = <&hif_intr2_intc>;
interrupts = <24>, <4>; interrupts = <24>, <4>;
interrupt-names = "nand_ctlrdy", "flash_dma_done";
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;

View File

@ -32,9 +32,9 @@ properties:
partitions: partitions:
type: object type: object
deprecated: true
description: description:
Node containing description of fixed partitions. Node containing description of fixed partitions.
See Documentation/devicetree/bindings/mtd/partition.txt
patternProperties: patternProperties:
"^nand@[a-f0-9]$": "^nand@[a-f0-9]$":

View File

@ -39,14 +39,8 @@ properties:
- const: tx - const: tx
- const: rx - const: rx
"#address-cells":
const: 1
"#size-cells":
const: 0
patternProperties: patternProperties:
"^nand@[a-f0-9]+$": "^nand@[a-f0-9]$":
type: object type: object
properties: properties:
reg: reg:
@ -67,10 +61,8 @@ required:
- clocks - clocks
- dmas - dmas
- dma-names - dma-names
- "#address-cells"
- "#size-cells"
additionalProperties: false unevaluatedProperties: false
examples: examples:
- | - |

View File

@ -70,24 +70,17 @@ properties:
be used on such systems, to denote the absence of a reliable reset be used on such systems, to denote the absence of a reliable reset
mechanism. mechanism.
partitions: reset-gpios:
type: object description:
A GPIO line connected to the RESET (active low) signal of the device.
'#address-cells': true If "broken-flash-reset" is present then having this property does not
'#size-cells': true make any difference.
patternProperties:
# Note: use 'partitions' node for new users
'^partition@':
type: object
"^otp(-[0-9]+)?$":
type: object
unevaluatedProperties: false unevaluatedProperties: false
examples: examples:
- | - |
#include <dt-bindings/gpio/gpio.h>
spi { spi {
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
@ -97,6 +90,7 @@ examples:
reg = <0>; reg = <0>;
spi-max-frequency = <40000000>; spi-max-frequency = <40000000>;
m25p,fast-read; m25p,fast-read;
reset-gpios = <&gpio 12 GPIO_ACTIVE_LOW>;
}; };
}; };
... ...

View File

@ -19,7 +19,7 @@ accuracy:)
- nxp,wr_low: WR_LOW - nxp,wr_low: WR_LOW
Optional subnodes: Optional subnodes:
- Partitions, see Documentation/devicetree/bindings/mtd/partition.txt - Partitions, see Documentation/devicetree/bindings/mtd/mtd.yaml
Example: Example:

View File

@ -20,7 +20,7 @@ clock speed:)
- nxp,rsetup: Read setup time (R_SETUP) - nxp,rsetup: Read setup time (R_SETUP)
Optional subnodes: Optional subnodes:
- Partitions, see Documentation/devicetree/bindings/mtd/partition.txt - Partitions, see Documentation/devicetree/bindings/mtd/mtd.yaml
Example: Example:

View File

@ -13,6 +13,9 @@ description: |
Flash chips (Memory Technology Devices) are often used for solid state Flash chips (Memory Technology Devices) are often used for solid state
file systems on embedded devices. file systems on embedded devices.
allOf:
- $ref: "mtd.yaml#"
properties: properties:
compatible: compatible:
oneOf: oneOf:
@ -121,10 +124,6 @@ properties:
big-endian: true big-endian: true
little-endian: true little-endian: true
patternProperties:
'@[0-9a-f]+$':
$ref: partitions/partition.yaml
required: required:
- compatible - compatible
- reg - reg

View File

@ -12,7 +12,7 @@ maintainers:
properties: properties:
$nodename: $nodename:
pattern: "^flash(@.*)?$" pattern: "^(flash|.*sram)(@.*)?$"
label: label:
description: description:
@ -21,9 +21,28 @@ properties:
based name) in order to ease flash device identification and/or based name) in order to ease flash device identification and/or
describe what they are used for. describe what they are used for.
'#address-cells':
deprecated: true
'#size-cells':
deprecated: true
partitions:
$ref: /schemas/mtd/partitions/partitions.yaml
required:
- compatible
patternProperties: patternProperties:
"@[0-9a-f]+$":
$ref: partitions/partition.yaml
deprecated: true
"^partition@[0-9a-f]+":
$ref: partitions/partition.yaml
deprecated: true
"^otp(-[0-9]+)?$": "^otp(-[0-9]+)?$":
type: object
$ref: ../nvmem/nvmem.yaml# $ref: ../nvmem/nvmem.yaml#
description: | description: |
@ -40,6 +59,7 @@ patternProperties:
required: required:
- compatible - compatible
# This is a generic file other binding inherit from
additionalProperties: true additionalProperties: true
examples: examples:

View File

@ -131,7 +131,7 @@ Example:
}; };
NAND chip optional subnodes: NAND chip optional subnodes:
- Partitions, see Documentation/devicetree/bindings/mtd/partition.txt - Partitions, see Documentation/devicetree/bindings/mtd/mtd.yaml
Example: Example:
nand@0 { nand@0 {

View File

@ -9,6 +9,9 @@ title: NAND Chip and NAND Controller Generic Binding
maintainers: maintainers:
- Miquel Raynal <miquel.raynal@bootlin.com> - Miquel Raynal <miquel.raynal@bootlin.com>
allOf:
- $ref: "mtd.yaml#"
description: | description: |
This file covers the generic description of a NAND chip. It implies that the This file covers the generic description of a NAND chip. It implies that the
bus interface should not be taken into account: both raw NAND devices and bus interface should not be taken into account: both raw NAND devices and
@ -67,4 +70,5 @@ properties:
required: required:
- reg - reg
# This file can be referenced by more specific devices (like spi-nands)
additionalProperties: true additionalProperties: true

View File

@ -51,7 +51,6 @@ properties:
patternProperties: patternProperties:
"^nand@[a-f0-9]$": "^nand@[a-f0-9]$":
type: object
$ref: "nand-chip.yaml#" $ref: "nand-chip.yaml#"
properties: properties:
@ -130,6 +129,7 @@ required:
- "#address-cells" - "#address-cells"
- "#size-cells" - "#size-cells"
# This is a generic file other binding inherit from and extend
additionalProperties: true additionalProperties: true
examples: examples:

View File

@ -1,33 +0,0 @@
Flash partitions in device tree
===============================
Flash devices can be partitioned into one or more functional ranges (e.g. "boot
code", "nvram", "kernel").
Different devices may be partitioned in a different ways. Some may use a fixed
flash layout set at production time. Some may use on-flash table that describes
the geometry and naming/purpose of each functional region. It is also possible
to see these methods mixed.
To assist system software in locating partitions, we allow describing which
method is used for a given flash device. To describe the method there should be
a subnode of the flash device that is named 'partitions'. It must have a
'compatible' property, which is used to identify the method to use.
When a single partition is represented with a DT node (it depends on a used
format) it may also be described using above rules ('compatible' and optionally
some extra properties / subnodes). It allows describing more complex,
hierarchical (multi-level) layouts and should be used if there is some
significant relation between partitions or some partition internally uses
another partitioning method.
Available bindings are listed in the "partitions" subdirectory.
Deprecated: partitions defined in flash node
============================================
For backwards compatibility partitions as direct subnodes of the flash device are
supported. This use is discouraged.
NOTE: also for backwards compatibility, direct subnodes that have a compatible
string are not considered partitions, as they may be used for other bindings.

View File

@ -9,6 +9,8 @@ title: ARM Firmware Suite (AFS) Partitions
maintainers: maintainers:
- Linus Walleij <linus.walleij@linaro.org> - Linus Walleij <linus.walleij@linaro.org>
select: false
description: | description: |
The ARM Firmware Suite is a flash partitioning system found on the The ARM Firmware Suite is a flash partitioning system found on the
ARM reference designs: Integrator AP, Integrator CP, Versatile AB, ARM reference designs: Integrator AP, Integrator CP, Versatile AB,

View File

@ -17,6 +17,8 @@ description: |
maintainers: maintainers:
- Rafał Miłecki <rafal@milecki.pl> - Rafał Miłecki <rafal@milecki.pl>
select: false
properties: properties:
compatible: compatible:
const: brcm,bcm4908-partitions const: brcm,bcm4908-partitions

View File

@ -35,6 +35,8 @@ description: |
maintainers: maintainers:
- Rafał Miłecki <rafal@milecki.pl> - Rafał Miłecki <rafal@milecki.pl>
select: false
properties: properties:
compatible: compatible:
const: brcm,bcm947xx-cfe-partitions const: brcm,bcm947xx-cfe-partitions

View File

@ -31,23 +31,16 @@ properties:
patternProperties: patternProperties:
"@[0-9a-f]+$": "@[0-9a-f]+$":
allOf: $ref: partition.yaml#
- $ref: "partition.yaml#"
- if:
properties:
compatible:
contains:
const: sercomm,sc-partitions
then:
properties: properties:
sercomm,scpart-id: sercomm,scpart-id:
description: Partition id in Sercomm partition map. Mtd description: Partition id in Sercomm partition map. Mtd parser
parser uses this id to find a record in the partition map uses this id to find a record in the partition map containing
containing offset and size of the current partition. The offset and size of the current partition. The values from
values from partition map overrides partition offset and partition map overrides partition offset and size defined in
size defined in reg property of the dts. Frequently these reg property of the dts. Frequently these values are the same,
values are the same, but may differ if device has bad but may differ if device has bad eraseblocks on a flash.
eraseblocks on a flash.
$ref: /schemas/types.yaml#/definitions/uint32 $ref: /schemas/types.yaml#/definitions/uint32
required: required:
@ -84,6 +77,7 @@ examples:
partition@0 { partition@0 {
label = "filesystem"; label = "filesystem";
reg = <0x00000000 0x1 0x00000000>; reg = <0x00000000 0x1 0x00000000>;
linux,rootfs;
}; };
}; };

View File

@ -18,6 +18,8 @@ description: |
maintainers: maintainers:
- Rafał Miłecki <rafal@milecki.pl> - Rafał Miłecki <rafal@milecki.pl>
select: false
properties: properties:
compatible: compatible:
const: linksys,ns-partitions const: linksys,ns-partitions

View File

@ -17,6 +17,7 @@ maintainers:
- Ansuel Smith <ansuelsmth@gmail.com> - Ansuel Smith <ansuelsmth@gmail.com>
allOf: allOf:
- $ref: /schemas/mtd/partitions/partition.yaml#
- $ref: /schemas/nvmem/nvmem.yaml# - $ref: /schemas/nvmem/nvmem.yaml#
properties: properties:
@ -26,7 +27,7 @@ properties:
required: required:
- compatible - compatible
additionalProperties: true unevaluatedProperties: false
examples: examples:
- | - |
@ -84,7 +85,6 @@ examples:
compatible = "nvmem-cells"; compatible = "nvmem-cells";
label = "calibration"; label = "calibration";
reg = <0xf00000 0x100000>; reg = <0xf00000 0x100000>;
ranges = <0 0xf00000 0x100000>;
#address-cells = <1>; #address-cells = <1>;
#size-cells = <1>; #size-cells = <1>;

View File

@ -52,6 +52,10 @@ properties:
immune to paired-pages corruptions immune to paired-pages corruptions
type: boolean type: boolean
linux,rootfs:
description: Marks partition that contains root filesystem to mount and boot
user space from
if: if:
not: not:
required: [ reg ] required: [ reg ]
@ -60,4 +64,5 @@ then:
$nodename: $nodename:
pattern: '^partition-.*$' pattern: '^partition-.*$'
# This is a generic file other binding inherit from and extend
additionalProperties: true additionalProperties: true

View File

@ -0,0 +1,41 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/partitions/partitions.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Partitions
description: |
This binding is generic and describes the content of the partitions container
node. All partition parsers must be referenced here.
maintainers:
- Miquel Raynal <miquel.raynal@bootlin.com>
oneOf:
- $ref: arm,arm-firmware-suite.yaml
- $ref: brcm,bcm4908-partitions.yaml
- $ref: brcm,bcm947xx-cfe-partitions.yaml
- $ref: fixed-partitions.yaml
- $ref: linksys,ns-partitions.yaml
- $ref: qcom,smem-part.yaml
- $ref: redboot-fis.yaml
properties:
compatible: true
'#address-cells':
enum: [1, 2]
'#size-cells':
enum: [1, 2]
patternProperties:
"partition(-.+|@[0-9a-f]+)":
$ref: partition.yaml
required:
- compatible
unevaluatedProperties: false

View File

@ -15,13 +15,15 @@ description: |
varies between partition table revisions. V3 supports maximum 16 partitions varies between partition table revisions. V3 supports maximum 16 partitions
and V4 supports 48 partitions. and V4 supports 48 partitions.
select: false
properties: properties:
compatible: compatible:
const: qcom,smem-part const: qcom,smem-part
patternProperties: patternProperties:
"^partition-[0-9a-z]+$": "^partition-[0-9a-z]+$":
$ref: partition.yaml# $ref: nvmem-cells.yaml
required: required:
- compatible - compatible

View File

@ -16,6 +16,8 @@ description: The FLASH Image System (FIS) directory is a flash description
maintainers: maintainers:
- Linus Walleij <linus.walleij@linaro.org> - Linus Walleij <linus.walleij@linaro.org>
select: false
properties: properties:
compatible: compatible:
const: redboot-fis const: redboot-fis
@ -26,6 +28,10 @@ properties:
device. On a flash memory with 32KB eraseblocks, 0 means the first device. On a flash memory with 32KB eraseblocks, 0 means the first
eraseblock at 0x00000000, 1 means the second eraseblock at 0x00008000 and so on. eraseblock at 0x00000000, 1 means the second eraseblock at 0x00008000 and so on.
'#address-cells': false
'#size-cells': false
required: required:
- compatible - compatible
- fis-index-block - fis-index-block

View File

@ -0,0 +1,57 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/partitions/tplink,safeloader-partitions.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TP-Link SafeLoader partitions
description: |
TP-Link home routers store various data on flash (e.g. bootloader,
flash layout, firmware, product info, configuration, calibration
data). That requires flash partitioning.
Flash space layout of TP-Link devices is stored on flash itself using
a custom ASCII-based format. That format was first found in TP-Link
devices with a custom SafeLoader bootloader. Later it was adapted to
CFE and U-Boot bootloaders.
Partitions specified in partitions table cover whole flash space. Some
contain static data that shouldn't get modified (device's MAC or WiFi
calibration data). Others are semi-static (like kernel). Finally some
partitions contain fully changeable content (like rootfs).
This binding describes partitioning method and defines offset of ASCII
based partitions table. That offset is picked at manufacturing process
and doesn't change.
maintainers:
- Rafał Miłecki <rafal@milecki.pl>
properties:
compatible:
const: tplink,safeloader-partitions
partitions-table-offset:
description: Flash offset of partitions table
$ref: /schemas/types.yaml#/definitions/uint32
patternProperties:
"^partition-.*$":
$ref: partition.yaml#
required:
- partitions-table-offset
additionalProperties: false
examples:
- |
partitions {
compatible = "tplink,safeloader-partitions";
partitions-table-offset = <0x100000>;
partition-file-system {
linux,rootfs;
};
};

View File

@ -31,9 +31,6 @@ properties:
- const: core - const: core
- const: aon - const: aon
"#address-cells": true
"#size-cells": true
patternProperties: patternProperties:
"^nand@[a-f0-9]$": "^nand@[a-f0-9]$":
type: object type: object

View File

@ -19,7 +19,9 @@ properties:
- const: rockchip,rk2928-nfc - const: rockchip,rk2928-nfc
- const: rockchip,rv1108-nfc - const: rockchip,rv1108-nfc
- items: - items:
- const: rockchip,rk3036-nfc - enum:
- rockchip,rk3036-nfc
- rockchip,rk3128-nfc
- const: rockchip,rk2928-nfc - const: rockchip,rk2928-nfc
- items: - items:
- const: rockchip,rk3308-nfc - const: rockchip,rk3308-nfc

View File

@ -101,6 +101,7 @@ examples:
#include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/stm32mp1-clks.h> #include <dt-bindings/clock/stm32mp1-clks.h>
#include <dt-bindings/reset/stm32mp1-resets.h> #include <dt-bindings/reset/stm32mp1-resets.h>
nand-controller@58002000 { nand-controller@58002000 {
compatible = "st,stm32mp15-fmc2"; compatible = "st,stm32mp15-fmc2";
reg = <0x58002000 0x1000>, reg = <0x58002000 0x1000>,

View File

@ -15,6 +15,9 @@ description:
as child nodes of the GPMC controller. as child nodes of the GPMC controller.
properties: properties:
$nodename:
pattern: "^onenand@[0-9],[0,9]$"
compatible: compatible:
const: ti,omap2-onenand const: ti,omap2-onenand

View File

@ -13464,7 +13464,7 @@ MESON NAND CONTROLLER DRIVER FOR AMLOGIC SOCS
M: Liang Yang <liang.yang@amlogic.com> M: Liang Yang <liang.yang@amlogic.com>
L: linux-mtd@lists.infradead.org L: linux-mtd@lists.infradead.org
S: Maintained S: Maintained
F: Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt F: Documentation/devicetree/bindings/mtd/amlogic,meson-nand.yaml
F: drivers/mtd/nand/raw/meson_* F: drivers/mtd/nand/raw/meson_*
MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS

View File

@ -136,14 +136,6 @@ config MTD_PHRAM
doesn't have access to, memory beyond the mem=xxx limit, nvram, doesn't have access to, memory beyond the mem=xxx limit, nvram,
memory on the video card, etc... memory on the video card, etc...
config MTD_LART
tristate "28F160xx flash driver for LART"
depends on SA1100_LART
help
This enables the flash driver for LART. Please note that you do
not need any mapping/chip driver for LART. This one does it all
for you, so go disable all of those if you enabled some of them (:
config MTD_MTDRAM config MTD_MTDRAM
tristate "Test driver using RAM" tristate "Test driver using RAM"
help help

View File

@ -9,7 +9,6 @@ obj-$(CONFIG_MTD_PHRAM) += phram.o
obj-$(CONFIG_MTD_PMC551) += pmc551.o obj-$(CONFIG_MTD_PMC551) += pmc551.o
obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o
obj-$(CONFIG_MTD_MTDRAM) += mtdram.o obj-$(CONFIG_MTD_MTDRAM) += mtdram.o
obj-$(CONFIG_MTD_LART) += lart.o
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
obj-$(CONFIG_MTD_MCHP23K256) += mchp23k256.o obj-$(CONFIG_MTD_MCHP23K256) += mchp23k256.o

View File

@ -1,682 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* MTD driver for the 28F160F3 Flash Memory (non-CFI) on LART.
*
* Author: Abraham vd Merwe <abraham@2d3d.co.za>
*
* Copyright (c) 2001, 2d3D, Inc.
*
* References:
*
* [1] 3 Volt Fast Boot Block Flash Memory" Intel Datasheet
* - Order Number: 290644-005
* - January 2000
*
* [2] MTD internal API documentation
* - http://www.linux-mtd.infradead.org/
*
* Limitations:
*
* Even though this driver is written for 3 Volt Fast Boot
* Block Flash Memory, it is rather specific to LART. With
* Minor modifications, notably the without data/address line
* mangling and different bus settings, etc. it should be
* trivial to adapt to other platforms.
*
* If somebody would sponsor me a different board, I'll
* adapt the driver (:
*/
/* debugging */
//#define LART_DEBUG
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#ifndef CONFIG_SA1100_LART
#error This is for LART architecture only
#endif
static char module_name[] = "lart";
/*
* These values is specific to 28Fxxxx3 flash memory.
* See section 2.3.1 in "3 Volt Fast Boot Block Flash Memory" Intel Datasheet
*/
#define FLASH_BLOCKSIZE_PARAM (4096 * BUSWIDTH)
#define FLASH_NUMBLOCKS_16m_PARAM 8
#define FLASH_NUMBLOCKS_8m_PARAM 8
/*
* These values is specific to 28Fxxxx3 flash memory.
* See section 2.3.2 in "3 Volt Fast Boot Block Flash Memory" Intel Datasheet
*/
#define FLASH_BLOCKSIZE_MAIN (32768 * BUSWIDTH)
#define FLASH_NUMBLOCKS_16m_MAIN 31
#define FLASH_NUMBLOCKS_8m_MAIN 15
/*
* These values are specific to LART
*/
/* general */
#define BUSWIDTH 4 /* don't change this - a lot of the code _will_ break if you change this */
#define FLASH_OFFSET 0xe8000000 /* see linux/arch/arm/mach-sa1100/lart.c */
/* blob */
#define NUM_BLOB_BLOCKS FLASH_NUMBLOCKS_16m_PARAM
#define PART_BLOB_START 0x00000000
#define PART_BLOB_LEN (NUM_BLOB_BLOCKS * FLASH_BLOCKSIZE_PARAM)
/* kernel */
#define NUM_KERNEL_BLOCKS 7
#define PART_KERNEL_START (PART_BLOB_START + PART_BLOB_LEN)
#define PART_KERNEL_LEN (NUM_KERNEL_BLOCKS * FLASH_BLOCKSIZE_MAIN)
/* initial ramdisk */
#define NUM_INITRD_BLOCKS 24
#define PART_INITRD_START (PART_KERNEL_START + PART_KERNEL_LEN)
#define PART_INITRD_LEN (NUM_INITRD_BLOCKS * FLASH_BLOCKSIZE_MAIN)
/*
* See section 4.0 in "3 Volt Fast Boot Block Flash Memory" Intel Datasheet
*/
#define READ_ARRAY 0x00FF00FF /* Read Array/Reset */
#define READ_ID_CODES 0x00900090 /* Read Identifier Codes */
#define ERASE_SETUP 0x00200020 /* Block Erase */
#define ERASE_CONFIRM 0x00D000D0 /* Block Erase and Program Resume */
#define PGM_SETUP 0x00400040 /* Program */
#define STATUS_READ 0x00700070 /* Read Status Register */
#define STATUS_CLEAR 0x00500050 /* Clear Status Register */
#define STATUS_BUSY 0x00800080 /* Write State Machine Status (WSMS) */
#define STATUS_ERASE_ERR 0x00200020 /* Erase Status (ES) */
#define STATUS_PGM_ERR 0x00100010 /* Program Status (PS) */
/*
* See section 4.2 in "3 Volt Fast Boot Block Flash Memory" Intel Datasheet
*/
#define FLASH_MANUFACTURER 0x00890089
#define FLASH_DEVICE_8mbit_TOP 0x88f188f1
#define FLASH_DEVICE_8mbit_BOTTOM 0x88f288f2
#define FLASH_DEVICE_16mbit_TOP 0x88f388f3
#define FLASH_DEVICE_16mbit_BOTTOM 0x88f488f4
/***************************************************************************************************/
/*
* The data line mapping on LART is as follows:
*
* U2 CPU | U3 CPU
* -------------------
* 0 20 | 0 12
* 1 22 | 1 14
* 2 19 | 2 11
* 3 17 | 3 9
* 4 24 | 4 0
* 5 26 | 5 2
* 6 31 | 6 7
* 7 29 | 7 5
* 8 21 | 8 13
* 9 23 | 9 15
* 10 18 | 10 10
* 11 16 | 11 8
* 12 25 | 12 1
* 13 27 | 13 3
* 14 30 | 14 6
* 15 28 | 15 4
*/
/* Mangle data (x) */
#define DATA_TO_FLASH(x) \
( \
(((x) & 0x08009000) >> 11) + \
(((x) & 0x00002000) >> 10) + \
(((x) & 0x04004000) >> 8) + \
(((x) & 0x00000010) >> 4) + \
(((x) & 0x91000820) >> 3) + \
(((x) & 0x22080080) >> 2) + \
((x) & 0x40000400) + \
(((x) & 0x00040040) << 1) + \
(((x) & 0x00110000) << 4) + \
(((x) & 0x00220100) << 5) + \
(((x) & 0x00800208) << 6) + \
(((x) & 0x00400004) << 9) + \
(((x) & 0x00000001) << 12) + \
(((x) & 0x00000002) << 13) \
)
/* Unmangle data (x) */
#define FLASH_TO_DATA(x) \
( \
(((x) & 0x00010012) << 11) + \
(((x) & 0x00000008) << 10) + \
(((x) & 0x00040040) << 8) + \
(((x) & 0x00000001) << 4) + \
(((x) & 0x12200104) << 3) + \
(((x) & 0x08820020) << 2) + \
((x) & 0x40000400) + \
(((x) & 0x00080080) >> 1) + \
(((x) & 0x01100000) >> 4) + \
(((x) & 0x04402000) >> 5) + \
(((x) & 0x20008200) >> 6) + \
(((x) & 0x80000800) >> 9) + \
(((x) & 0x00001000) >> 12) + \
(((x) & 0x00004000) >> 13) \
)
/*
* The address line mapping on LART is as follows:
*
* U3 CPU | U2 CPU
* -------------------
* 0 2 | 0 2
* 1 3 | 1 3
* 2 9 | 2 9
* 3 13 | 3 8
* 4 8 | 4 7
* 5 12 | 5 6
* 6 11 | 6 5
* 7 10 | 7 4
* 8 4 | 8 10
* 9 5 | 9 11
* 10 6 | 10 12
* 11 7 | 11 13
*
* BOOT BLOCK BOUNDARY
*
* 12 15 | 12 15
* 13 14 | 13 14
* 14 16 | 14 16
*
* MAIN BLOCK BOUNDARY
*
* 15 17 | 15 18
* 16 18 | 16 17
* 17 20 | 17 20
* 18 19 | 18 19
* 19 21 | 19 21
*
* As we can see from above, the addresses aren't mangled across
* block boundaries, so we don't need to worry about address
* translations except for sending/reading commands during
* initialization
*/
/* Mangle address (x) on chip U2 */
#define ADDR_TO_FLASH_U2(x) \
( \
(((x) & 0x00000f00) >> 4) + \
(((x) & 0x00042000) << 1) + \
(((x) & 0x0009c003) << 2) + \
(((x) & 0x00021080) << 3) + \
(((x) & 0x00000010) << 4) + \
(((x) & 0x00000040) << 5) + \
(((x) & 0x00000024) << 7) + \
(((x) & 0x00000008) << 10) \
)
/* Unmangle address (x) on chip U2 */
#define FLASH_U2_TO_ADDR(x) \
( \
(((x) << 4) & 0x00000f00) + \
(((x) >> 1) & 0x00042000) + \
(((x) >> 2) & 0x0009c003) + \
(((x) >> 3) & 0x00021080) + \
(((x) >> 4) & 0x00000010) + \
(((x) >> 5) & 0x00000040) + \
(((x) >> 7) & 0x00000024) + \
(((x) >> 10) & 0x00000008) \
)
/* Mangle address (x) on chip U3 */
#define ADDR_TO_FLASH_U3(x) \
( \
(((x) & 0x00000080) >> 3) + \
(((x) & 0x00000040) >> 1) + \
(((x) & 0x00052020) << 1) + \
(((x) & 0x00084f03) << 2) + \
(((x) & 0x00029010) << 3) + \
(((x) & 0x00000008) << 5) + \
(((x) & 0x00000004) << 7) \
)
/* Unmangle address (x) on chip U3 */
#define FLASH_U3_TO_ADDR(x) \
( \
(((x) << 3) & 0x00000080) + \
(((x) << 1) & 0x00000040) + \
(((x) >> 1) & 0x00052020) + \
(((x) >> 2) & 0x00084f03) + \
(((x) >> 3) & 0x00029010) + \
(((x) >> 5) & 0x00000008) + \
(((x) >> 7) & 0x00000004) \
)
/***************************************************************************************************/
static __u8 read8 (__u32 offset)
{
volatile __u8 *data = (__u8 *) (FLASH_OFFSET + offset);
#ifdef LART_DEBUG
printk (KERN_DEBUG "%s(): 0x%.8x -> 0x%.2x\n", __func__, offset, *data);
#endif
return (*data);
}
static __u32 read32 (__u32 offset)
{
volatile __u32 *data = (__u32 *) (FLASH_OFFSET + offset);
#ifdef LART_DEBUG
printk (KERN_DEBUG "%s(): 0x%.8x -> 0x%.8x\n", __func__, offset, *data);
#endif
return (*data);
}
static void write32 (__u32 x,__u32 offset)
{
volatile __u32 *data = (__u32 *) (FLASH_OFFSET + offset);
*data = x;
#ifdef LART_DEBUG
printk (KERN_DEBUG "%s(): 0x%.8x <- 0x%.8x\n", __func__, offset, *data);
#endif
}
/***************************************************************************************************/
/*
* Probe for 16mbit flash memory on a LART board without doing
* too much damage. Since we need to write 1 dword to memory,
* we're f**cked if this happens to be DRAM since we can't
* restore the memory (otherwise we might exit Read Array mode).
*
* Returns 1 if we found 16mbit flash memory on LART, 0 otherwise.
*/
static int flash_probe (void)
{
__u32 manufacturer,devtype;
/* setup "Read Identifier Codes" mode */
write32 (DATA_TO_FLASH (READ_ID_CODES),0x00000000);
/* probe U2. U2/U3 returns the same data since the first 3
* address lines is mangled in the same way */
manufacturer = FLASH_TO_DATA (read32 (ADDR_TO_FLASH_U2 (0x00000000)));
devtype = FLASH_TO_DATA (read32 (ADDR_TO_FLASH_U2 (0x00000001)));
/* put the flash back into command mode */
write32 (DATA_TO_FLASH (READ_ARRAY),0x00000000);
return (manufacturer == FLASH_MANUFACTURER && (devtype == FLASH_DEVICE_16mbit_TOP || devtype == FLASH_DEVICE_16mbit_BOTTOM));
}
/*
* Erase one block of flash memory at offset ``offset'' which is any
* address within the block which should be erased.
*
* Returns 1 if successful, 0 otherwise.
*/
static inline int erase_block (__u32 offset)
{
__u32 status;
#ifdef LART_DEBUG
printk (KERN_DEBUG "%s(): 0x%.8x\n", __func__, offset);
#endif
/* erase and confirm */
write32 (DATA_TO_FLASH (ERASE_SETUP),offset);
write32 (DATA_TO_FLASH (ERASE_CONFIRM),offset);
/* wait for block erase to finish */
do
{
write32 (DATA_TO_FLASH (STATUS_READ),offset);
status = FLASH_TO_DATA (read32 (offset));
}
while ((~status & STATUS_BUSY) != 0);
/* put the flash back into command mode */
write32 (DATA_TO_FLASH (READ_ARRAY),offset);
/* was the erase successful? */
if ((status & STATUS_ERASE_ERR))
{
printk (KERN_WARNING "%s: erase error at address 0x%.8x.\n",module_name,offset);
return (0);
}
return (1);
}
static int flash_erase (struct mtd_info *mtd,struct erase_info *instr)
{
__u32 addr,len;
int i,first;
#ifdef LART_DEBUG
printk (KERN_DEBUG "%s(addr = 0x%.8x, len = %d)\n", __func__, instr->addr, instr->len);
#endif
/*
* check that both start and end of the requested erase are
* aligned with the erasesize at the appropriate addresses.
*
* skip all erase regions which are ended before the start of
* the requested erase. Actually, to save on the calculations,
* we skip to the first erase region which starts after the
* start of the requested erase, and then go back one.
*/
for (i = 0; i < mtd->numeraseregions && instr->addr >= mtd->eraseregions[i].offset; i++) ;
i--;
/*
* ok, now i is pointing at the erase region in which this
* erase request starts. Check the start of the requested
* erase range is aligned with the erase size which is in
* effect here.
*/
if (i < 0 || (instr->addr & (mtd->eraseregions[i].erasesize - 1)))
return -EINVAL;
/* Remember the erase region we start on */
first = i;
/*
* next, check that the end of the requested erase is aligned
* with the erase region at that address.
*
* as before, drop back one to point at the region in which
* the address actually falls
*/
for (; i < mtd->numeraseregions && instr->addr + instr->len >= mtd->eraseregions[i].offset; i++) ;
i--;
/* is the end aligned on a block boundary? */
if (i < 0 || ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1)))
return -EINVAL;
addr = instr->addr;
len = instr->len;
i = first;
/* now erase those blocks */
while (len)
{
if (!erase_block (addr))
return (-EIO);
addr += mtd->eraseregions[i].erasesize;
len -= mtd->eraseregions[i].erasesize;
if (addr == mtd->eraseregions[i].offset + (mtd->eraseregions[i].erasesize * mtd->eraseregions[i].numblocks)) i++;
}
return (0);
}
static int flash_read (struct mtd_info *mtd,loff_t from,size_t len,size_t *retlen,u_char *buf)
{
#ifdef LART_DEBUG
printk (KERN_DEBUG "%s(from = 0x%.8x, len = %d)\n", __func__, (__u32)from, len);
#endif
/* we always read len bytes */
*retlen = len;
/* first, we read bytes until we reach a dword boundary */
if (from & (BUSWIDTH - 1))
{
int gap = BUSWIDTH - (from & (BUSWIDTH - 1));
while (len && gap--) {
*buf++ = read8 (from++);
len--;
}
}
/* now we read dwords until we reach a non-dword boundary */
while (len >= BUSWIDTH)
{
*((__u32 *) buf) = read32 (from);
buf += BUSWIDTH;
from += BUSWIDTH;
len -= BUSWIDTH;
}
/* top up the last unaligned bytes */
if (len & (BUSWIDTH - 1))
while (len--) *buf++ = read8 (from++);
return (0);
}
/*
* Write one dword ``x'' to flash memory at offset ``offset''. ``offset''
* must be 32 bits, i.e. it must be on a dword boundary.
*
* Returns 1 if successful, 0 otherwise.
*/
static inline int write_dword (__u32 offset,__u32 x)
{
__u32 status;
#ifdef LART_DEBUG
printk (KERN_DEBUG "%s(): 0x%.8x <- 0x%.8x\n", __func__, offset, x);
#endif
/* setup writing */
write32 (DATA_TO_FLASH (PGM_SETUP),offset);
/* write the data */
write32 (x,offset);
/* wait for the write to finish */
do
{
write32 (DATA_TO_FLASH (STATUS_READ),offset);
status = FLASH_TO_DATA (read32 (offset));
}
while ((~status & STATUS_BUSY) != 0);
/* put the flash back into command mode */
write32 (DATA_TO_FLASH (READ_ARRAY),offset);
/* was the write successful? */
if ((status & STATUS_PGM_ERR) || read32 (offset) != x)
{
printk (KERN_WARNING "%s: write error at address 0x%.8x.\n",module_name,offset);
return (0);
}
return (1);
}
static int flash_write (struct mtd_info *mtd,loff_t to,size_t len,size_t *retlen,const u_char *buf)
{
__u8 tmp[4];
int i,n;
#ifdef LART_DEBUG
printk (KERN_DEBUG "%s(to = 0x%.8x, len = %d)\n", __func__, (__u32)to, len);
#endif
/* sanity checks */
if (!len) return (0);
/* first, we write a 0xFF.... padded byte until we reach a dword boundary */
if (to & (BUSWIDTH - 1))
{
__u32 aligned = to & ~(BUSWIDTH - 1);
int gap = to - aligned;
i = n = 0;
while (gap--) tmp[i++] = 0xFF;
while (len && i < BUSWIDTH) {
tmp[i++] = buf[n++];
len--;
}
while (i < BUSWIDTH) tmp[i++] = 0xFF;
if (!write_dword (aligned,*((__u32 *) tmp))) return (-EIO);
to += n;
buf += n;
*retlen += n;
}
/* now we write dwords until we reach a non-dword boundary */
while (len >= BUSWIDTH)
{
if (!write_dword (to,*((__u32 *) buf))) return (-EIO);
to += BUSWIDTH;
buf += BUSWIDTH;
*retlen += BUSWIDTH;
len -= BUSWIDTH;
}
/* top up the last unaligned bytes, padded with 0xFF.... */
if (len & (BUSWIDTH - 1))
{
i = n = 0;
while (len--) tmp[i++] = buf[n++];
while (i < BUSWIDTH) tmp[i++] = 0xFF;
if (!write_dword (to,*((__u32 *) tmp))) return (-EIO);
*retlen += n;
}
return (0);
}
/***************************************************************************************************/
static struct mtd_info mtd;
static struct mtd_erase_region_info erase_regions[] = {
/* parameter blocks */
{
.offset = 0x00000000,
.erasesize = FLASH_BLOCKSIZE_PARAM,
.numblocks = FLASH_NUMBLOCKS_16m_PARAM,
},
/* main blocks */
{
.offset = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM,
.erasesize = FLASH_BLOCKSIZE_MAIN,
.numblocks = FLASH_NUMBLOCKS_16m_MAIN,
}
};
static const struct mtd_partition lart_partitions[] = {
/* blob */
{
.name = "blob",
.offset = PART_BLOB_START,
.size = PART_BLOB_LEN,
},
/* kernel */
{
.name = "kernel",
.offset = PART_KERNEL_START, /* MTDPART_OFS_APPEND */
.size = PART_KERNEL_LEN,
},
/* initial ramdisk / file system */
{
.name = "file system",
.offset = PART_INITRD_START, /* MTDPART_OFS_APPEND */
.size = PART_INITRD_LEN, /* MTDPART_SIZ_FULL */
}
};
#define NUM_PARTITIONS ARRAY_SIZE(lart_partitions)
static int __init lart_flash_init (void)
{
int result;
memset (&mtd,0,sizeof (mtd));
printk ("MTD driver for LART. Written by Abraham vd Merwe <abraham@2d3d.co.za>\n");
printk ("%s: Probing for 28F160x3 flash on LART...\n",module_name);
if (!flash_probe ())
{
printk (KERN_WARNING "%s: Found no LART compatible flash device\n",module_name);
return (-ENXIO);
}
printk ("%s: This looks like a LART board to me.\n",module_name);
mtd.name = module_name;
mtd.type = MTD_NORFLASH;
mtd.writesize = 1;
mtd.writebufsize = 4;
mtd.flags = MTD_CAP_NORFLASH;
mtd.size = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM + FLASH_BLOCKSIZE_MAIN * FLASH_NUMBLOCKS_16m_MAIN;
mtd.erasesize = FLASH_BLOCKSIZE_MAIN;
mtd.numeraseregions = ARRAY_SIZE(erase_regions);
mtd.eraseregions = erase_regions;
mtd._erase = flash_erase;
mtd._read = flash_read;
mtd._write = flash_write;
mtd.owner = THIS_MODULE;
#ifdef LART_DEBUG
printk (KERN_DEBUG
"mtd.name = %s\n"
"mtd.size = 0x%.8x (%uM)\n"
"mtd.erasesize = 0x%.8x (%uK)\n"
"mtd.numeraseregions = %d\n",
mtd.name,
mtd.size,mtd.size / (1024*1024),
mtd.erasesize,mtd.erasesize / 1024,
mtd.numeraseregions);
if (mtd.numeraseregions)
for (result = 0; result < mtd.numeraseregions; result++)
printk (KERN_DEBUG
"\n\n"
"mtd.eraseregions[%d].offset = 0x%.8x\n"
"mtd.eraseregions[%d].erasesize = 0x%.8x (%uK)\n"
"mtd.eraseregions[%d].numblocks = %d\n",
result,mtd.eraseregions[result].offset,
result,mtd.eraseregions[result].erasesize,mtd.eraseregions[result].erasesize / 1024,
result,mtd.eraseregions[result].numblocks);
printk ("\npartitions = %d\n", ARRAY_SIZE(lart_partitions));
for (result = 0; result < ARRAY_SIZE(lart_partitions); result++)
printk (KERN_DEBUG
"\n\n"
"lart_partitions[%d].name = %s\n"
"lart_partitions[%d].offset = 0x%.8x\n"
"lart_partitions[%d].size = 0x%.8x (%uK)\n",
result,lart_partitions[result].name,
result,lart_partitions[result].offset,
result,lart_partitions[result].size,lart_partitions[result].size / 1024);
#endif
result = mtd_device_register(&mtd, lart_partitions,
ARRAY_SIZE(lart_partitions));
return (result);
}
static void __exit lart_flash_exit (void)
{
mtd_device_unregister(&mtd);
}
module_init (lart_flash_init);
module_exit (lart_flash_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Abraham vd Merwe <abraham@2d3d.co.za>");
MODULE_DESCRIPTION("MTD driver for Intel 28F160F3 on LART board");

View File

@ -356,7 +356,7 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
* Newest unit in chain now contains data from _all_ older units. * Newest unit in chain now contains data from _all_ older units.
* So go through and erase each unit in chain, oldest first. (This * So go through and erase each unit in chain, oldest first. (This
* is important, by doing oldest first if we crash/reboot then it * is important, by doing oldest first if we crash/reboot then it
* it is relatively simple to clean up the mess). * is relatively simple to clean up the mess).
*/ */
pr_debug("INFTL: want to erase virtual chain %d\n", thisVUC); pr_debug("INFTL: want to erase virtual chain %d\n", thisVUC);

View File

@ -433,6 +433,8 @@ static int lpddr2_nvm_probe(struct platform_device *pdev)
/* lpddr2_nvm address range */ /* lpddr2_nvm address range */
add_range = platform_get_resource(pdev, IORESOURCE_MEM, 0); add_range = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!add_range)
return -ENODEV;
/* Populate map_info data structure */ /* Populate map_info data structure */
*map = (struct map_info) { *map = (struct map_info) {

View File

@ -64,6 +64,7 @@ static int pxa2xx_flash_probe(struct platform_device *pdev)
if (!info->map.virt) { if (!info->map.virt) {
printk(KERN_WARNING "Failed to ioremap %s\n", printk(KERN_WARNING "Failed to ioremap %s\n",
info->map.name); info->map.name);
kfree(info);
return -ENOMEM; return -ENOMEM;
} }
info->map.cached = ioremap_cache(info->map.phys, info->map.size); info->map.cached = ioremap_cache(info->map.phys, info->map.size);
@ -85,6 +86,7 @@ static int pxa2xx_flash_probe(struct platform_device *pdev)
iounmap((void *)info->map.virt); iounmap((void *)info->map.virt);
if (info->map.cached) if (info->map.cached)
iounmap(info->map.cached); iounmap(info->map.cached);
kfree(info);
return -EIO; return -EIO;
} }
info->mtd->dev.parent = &pdev->dev; info->mtd->dev.parent = &pdev->dev;

View File

@ -28,6 +28,7 @@
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/nvmem-provider.h> #include <linux/nvmem-provider.h>
#include <linux/root_dev.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
@ -551,21 +552,21 @@ static void mtd_check_of_node(struct mtd_info *mtd)
struct device_node *partitions, *parent_dn, *mtd_dn = NULL; struct device_node *partitions, *parent_dn, *mtd_dn = NULL;
const char *pname, *prefix = "partition-"; const char *pname, *prefix = "partition-";
int plen, mtd_name_len, offset, prefix_len; int plen, mtd_name_len, offset, prefix_len;
struct mtd_info *parent;
bool found = false;
/* Check if MTD already has a device node */ /* Check if MTD already has a device node */
if (dev_of_node(&mtd->dev)) if (mtd_get_of_node(mtd))
return; return;
/* Check if a partitions node exist */
if (!mtd_is_partition(mtd)) if (!mtd_is_partition(mtd))
return; return;
parent = mtd->parent;
parent_dn = of_node_get(dev_of_node(&parent->dev)); parent_dn = of_node_get(mtd_get_of_node(mtd->parent));
if (!parent_dn) if (!parent_dn)
return; return;
if (mtd_is_partition(mtd->parent))
partitions = of_node_get(parent_dn);
else
partitions = of_get_child_by_name(parent_dn, "partitions"); partitions = of_get_child_by_name(parent_dn, "partitions");
if (!partitions) if (!partitions)
goto exit_parent; goto exit_parent;
@ -575,34 +576,26 @@ static void mtd_check_of_node(struct mtd_info *mtd)
/* Search if a partition is defined with the same name */ /* Search if a partition is defined with the same name */
for_each_child_of_node(partitions, mtd_dn) { for_each_child_of_node(partitions, mtd_dn) {
offset = 0;
/* Skip partition with no/wrong prefix */ /* Skip partition with no/wrong prefix */
if (!of_node_name_prefix(mtd_dn, "partition-")) if (!of_node_name_prefix(mtd_dn, prefix))
continue; continue;
/* Label have priority. Check that first */ /* Label have priority. Check that first */
if (of_property_read_string(mtd_dn, "label", &pname)) { if (!of_property_read_string(mtd_dn, "label", &pname)) {
of_property_read_string(mtd_dn, "name", &pname); offset = 0;
} else {
pname = mtd_dn->name;
offset = prefix_len; offset = prefix_len;
} }
plen = strlen(pname) - offset; plen = strlen(pname) - offset;
if (plen == mtd_name_len && if (plen == mtd_name_len &&
!strncmp(mtd->name, pname + offset, plen)) { !strncmp(mtd->name, pname + offset, plen)) {
found = true; mtd_set_of_node(mtd, mtd_dn);
break; break;
} }
} }
if (!found)
goto exit_partitions;
/* Set of_node only for nvmem */
if (of_device_is_compatible(mtd_dn, "nvmem-cells"))
mtd_set_of_node(mtd, mtd_dn);
exit_partitions:
of_node_put(partitions); of_node_put(partitions);
exit_parent: exit_parent:
of_node_put(parent_dn); of_node_put(parent_dn);
@ -723,8 +716,10 @@ int add_mtd_device(struct mtd_info *mtd)
mtd_check_of_node(mtd); mtd_check_of_node(mtd);
of_node_get(mtd_get_of_node(mtd)); of_node_get(mtd_get_of_node(mtd));
error = device_register(&mtd->dev); error = device_register(&mtd->dev);
if (error) if (error) {
put_device(&mtd->dev);
goto fail_added; goto fail_added;
}
/* Add the nvmem provider */ /* Add the nvmem provider */
error = mtd_nvmem_add(mtd); error = mtd_nvmem_add(mtd);
@ -743,6 +738,17 @@ int add_mtd_device(struct mtd_info *mtd)
not->add(mtd); not->add(mtd);
mutex_unlock(&mtd_table_mutex); mutex_unlock(&mtd_table_mutex);
if (of_find_property(mtd_get_of_node(mtd), "linux,rootfs", NULL)) {
if (IS_BUILTIN(CONFIG_MTD)) {
pr_info("mtd: setting mtd%d (%s) as root device\n", mtd->index, mtd->name);
ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, mtd->index);
} else {
pr_warn("mtd: can't set mtd%d (%s) as root device - mtd must be builtin\n",
mtd->index, mtd->name);
}
}
/* We _know_ we aren't being removed, because /* We _know_ we aren't being removed, because
our caller is still holding us here. So none our caller is still holding us here. So none
of this try_ nonsense, and no bitching about it of this try_ nonsense, and no bitching about it
@ -774,6 +780,7 @@ int del_mtd_device(struct mtd_info *mtd)
{ {
int ret; int ret;
struct mtd_notifier *not; struct mtd_notifier *not;
struct device_node *mtd_of_node;
mutex_lock(&mtd_table_mutex); mutex_lock(&mtd_table_mutex);
@ -792,6 +799,7 @@ int del_mtd_device(struct mtd_info *mtd)
mtd->index, mtd->name, mtd->usecount); mtd->index, mtd->name, mtd->usecount);
ret = -EBUSY; ret = -EBUSY;
} else { } else {
mtd_of_node = mtd_get_of_node(mtd);
debugfs_remove_recursive(mtd->dbg.dfs_dir); debugfs_remove_recursive(mtd->dbg.dfs_dir);
/* Try to remove the NVMEM provider */ /* Try to remove the NVMEM provider */
@ -803,7 +811,7 @@ int del_mtd_device(struct mtd_info *mtd)
memset(&mtd->dev, 0, sizeof(mtd->dev)); memset(&mtd->dev, 0, sizeof(mtd->dev));
idr_remove(&mtd_idr, mtd->index); idr_remove(&mtd_idr, mtd->index);
of_node_put(mtd_get_of_node(mtd)); of_node_put(mtd_of_node);
module_put(THIS_MODULE); module_put(THIS_MODULE);
ret = 0; ret = 0;
@ -2483,6 +2491,7 @@ static int __init init_mtd(void)
out_procfs: out_procfs:
if (proc_mtd) if (proc_mtd)
remove_proc_entry("mtd", NULL); remove_proc_entry("mtd", NULL);
bdi_unregister(mtd_bdi);
bdi_put(mtd_bdi); bdi_put(mtd_bdi);
err_bdi: err_bdi:
class_unregister(&mtd_class); class_unregister(&mtd_class);

View File

@ -7,6 +7,8 @@
* Author: Richard Purdie <rpurdie@openedhand.com> * Author: Richard Purdie <rpurdie@openedhand.com>
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/console.h> #include <linux/console.h>
@ -93,7 +95,7 @@ static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset)
ret = mtd_erase(mtd, &erase); ret = mtd_erase(mtd, &erase);
if (ret) { if (ret) {
printk(KERN_WARNING "mtdoops: erase of region [0x%llx, 0x%llx] on \"%s\" failed\n", pr_warn("erase of region [0x%llx, 0x%llx] on \"%s\" failed\n",
(unsigned long long)erase.addr, (unsigned long long)erase.addr,
(unsigned long long)erase.len, mtddev); (unsigned long long)erase.len, mtddev);
return ret; return ret;
@ -106,29 +108,8 @@ static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset)
return 0; return 0;
} }
static void mtdoops_inc_counter(struct mtdoops_context *cxt) static void mtdoops_erase(struct mtdoops_context *cxt)
{ {
cxt->nextpage++;
if (cxt->nextpage >= cxt->oops_pages)
cxt->nextpage = 0;
cxt->nextcount++;
if (cxt->nextcount == 0xffffffff)
cxt->nextcount = 0;
if (page_is_used(cxt, cxt->nextpage)) {
schedule_work(&cxt->work_erase);
return;
}
printk(KERN_DEBUG "mtdoops: ready %d, %d (no erase)\n",
cxt->nextpage, cxt->nextcount);
}
/* Scheduled work - when we can't proceed without erasing a block */
static void mtdoops_workfunc_erase(struct work_struct *work)
{
struct mtdoops_context *cxt =
container_of(work, struct mtdoops_context, work_erase);
struct mtd_info *mtd = cxt->mtd; struct mtd_info *mtd = cxt->mtd;
int i = 0, j, ret, mod; int i = 0, j, ret, mod;
@ -145,20 +126,20 @@ static void mtdoops_workfunc_erase(struct work_struct *work)
while ((ret = mtd_block_isbad(mtd, cxt->nextpage * record_size)) > 0) { while ((ret = mtd_block_isbad(mtd, cxt->nextpage * record_size)) > 0) {
badblock: badblock:
printk(KERN_WARNING "mtdoops: bad block at %08lx\n", pr_warn("bad block at %08lx\n",
cxt->nextpage * record_size); cxt->nextpage * record_size);
i++; i++;
cxt->nextpage = cxt->nextpage + (mtd->erasesize / record_size); cxt->nextpage = cxt->nextpage + (mtd->erasesize / record_size);
if (cxt->nextpage >= cxt->oops_pages) if (cxt->nextpage >= cxt->oops_pages)
cxt->nextpage = 0; cxt->nextpage = 0;
if (i == cxt->oops_pages / (mtd->erasesize / record_size)) { if (i == cxt->oops_pages / (mtd->erasesize / record_size)) {
printk(KERN_ERR "mtdoops: all blocks bad!\n"); pr_err("all blocks bad!\n");
return; return;
} }
} }
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "mtdoops: mtd_block_isbad failed, aborting\n"); pr_err("mtd_block_isbad failed, aborting\n");
return; return;
} }
@ -166,7 +147,7 @@ badblock:
ret = mtdoops_erase_block(cxt, cxt->nextpage * record_size); ret = mtdoops_erase_block(cxt, cxt->nextpage * record_size);
if (ret >= 0) { if (ret >= 0) {
printk(KERN_DEBUG "mtdoops: ready %d, %d\n", pr_debug("ready %d, %d\n",
cxt->nextpage, cxt->nextcount); cxt->nextpage, cxt->nextcount);
return; return;
} }
@ -174,13 +155,47 @@ badblock:
if (ret == -EIO) { if (ret == -EIO) {
ret = mtd_block_markbad(mtd, cxt->nextpage * record_size); ret = mtd_block_markbad(mtd, cxt->nextpage * record_size);
if (ret < 0 && ret != -EOPNOTSUPP) { if (ret < 0 && ret != -EOPNOTSUPP) {
printk(KERN_ERR "mtdoops: block_markbad failed, aborting\n"); pr_err("block_markbad failed, aborting\n");
return; return;
} }
} }
goto badblock; goto badblock;
} }
/* Scheduled work - when we can't proceed without erasing a block */
static void mtdoops_workfunc_erase(struct work_struct *work)
{
struct mtdoops_context *cxt =
container_of(work, struct mtdoops_context, work_erase);
mtdoops_erase(cxt);
}
static void mtdoops_inc_counter(struct mtdoops_context *cxt, int panic)
{
cxt->nextpage++;
if (cxt->nextpage >= cxt->oops_pages)
cxt->nextpage = 0;
cxt->nextcount++;
if (cxt->nextcount == 0xffffffff)
cxt->nextcount = 0;
if (page_is_used(cxt, cxt->nextpage)) {
pr_debug("not ready %d, %d (erase %s)\n",
cxt->nextpage, cxt->nextcount,
panic ? "immediately" : "scheduled");
if (panic) {
/* In case of panic, erase immediately */
mtdoops_erase(cxt);
} else {
/* Otherwise, schedule work to erase it "nicely" */
schedule_work(&cxt->work_erase);
}
} else {
pr_debug("ready %d, %d (no erase)\n",
cxt->nextpage, cxt->nextcount);
}
}
static void mtdoops_write(struct mtdoops_context *cxt, int panic) static void mtdoops_write(struct mtdoops_context *cxt, int panic)
{ {
struct mtd_info *mtd = cxt->mtd; struct mtd_info *mtd = cxt->mtd;
@ -201,7 +216,7 @@ static void mtdoops_write(struct mtdoops_context *cxt, int panic)
ret = mtd_panic_write(mtd, cxt->nextpage * record_size, ret = mtd_panic_write(mtd, cxt->nextpage * record_size,
record_size, &retlen, cxt->oops_buf); record_size, &retlen, cxt->oops_buf);
if (ret == -EOPNOTSUPP) { if (ret == -EOPNOTSUPP) {
printk(KERN_ERR "mtdoops: Cannot write from panic without panic_write\n"); pr_err("Cannot write from panic without panic_write\n");
goto out; goto out;
} }
} else } else
@ -209,12 +224,12 @@ static void mtdoops_write(struct mtdoops_context *cxt, int panic)
record_size, &retlen, cxt->oops_buf); record_size, &retlen, cxt->oops_buf);
if (retlen != record_size || ret < 0) if (retlen != record_size || ret < 0)
printk(KERN_ERR "mtdoops: write failure at %ld (%td of %ld written), error %d\n", pr_err("write failure at %ld (%td of %ld written), error %d\n",
cxt->nextpage * record_size, retlen, record_size, ret); cxt->nextpage * record_size, retlen, record_size, ret);
mark_page_used(cxt, cxt->nextpage); mark_page_used(cxt, cxt->nextpage);
memset(cxt->oops_buf, 0xff, record_size); memset(cxt->oops_buf, 0xff, record_size);
mtdoops_inc_counter(cxt); mtdoops_inc_counter(cxt, panic);
out: out:
clear_bit(0, &cxt->oops_buf_busy); clear_bit(0, &cxt->oops_buf_busy);
} }
@ -244,7 +259,7 @@ static void find_next_position(struct mtdoops_context *cxt)
&retlen, (u_char *)&hdr); &retlen, (u_char *)&hdr);
if (retlen != sizeof(hdr) || if (retlen != sizeof(hdr) ||
(ret < 0 && !mtd_is_bitflip(ret))) { (ret < 0 && !mtd_is_bitflip(ret))) {
printk(KERN_ERR "mtdoops: read failure at %ld (%zu of %zu read), err %d\n", pr_err("read failure at %ld (%zu of %zu read), err %d\n",
page * record_size, retlen, sizeof(hdr), ret); page * record_size, retlen, sizeof(hdr), ret);
continue; continue;
} }
@ -279,7 +294,7 @@ static void find_next_position(struct mtdoops_context *cxt)
cxt->nextcount = maxcount; cxt->nextcount = maxcount;
} }
mtdoops_inc_counter(cxt); mtdoops_inc_counter(cxt, 0);
} }
static void mtdoops_do_dump(struct kmsg_dumper *dumper, static void mtdoops_do_dump(struct kmsg_dumper *dumper,
@ -324,17 +339,17 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
return; return;
if (mtd->size < mtd->erasesize * 2) { if (mtd->size < mtd->erasesize * 2) {
printk(KERN_ERR "mtdoops: MTD partition %d not big enough for mtdoops\n", pr_err("MTD partition %d not big enough for mtdoops\n",
mtd->index); mtd->index);
return; return;
} }
if (mtd->erasesize < record_size) { if (mtd->erasesize < record_size) {
printk(KERN_ERR "mtdoops: eraseblock size of MTD partition %d too small\n", pr_err("eraseblock size of MTD partition %d too small\n",
mtd->index); mtd->index);
return; return;
} }
if (mtd->size > MTDOOPS_MAX_MTD_SIZE) { if (mtd->size > MTDOOPS_MAX_MTD_SIZE) {
printk(KERN_ERR "mtdoops: mtd%d is too large (limit is %d MiB)\n", pr_err("mtd%d is too large (limit is %d MiB)\n",
mtd->index, MTDOOPS_MAX_MTD_SIZE / 1024 / 1024); mtd->index, MTDOOPS_MAX_MTD_SIZE / 1024 / 1024);
return; return;
} }
@ -345,7 +360,7 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
DIV_ROUND_UP(mtdoops_pages, DIV_ROUND_UP(mtdoops_pages,
BITS_PER_LONG))); BITS_PER_LONG)));
if (!cxt->oops_page_used) { if (!cxt->oops_page_used) {
printk(KERN_ERR "mtdoops: could not allocate page array\n"); pr_err("could not allocate page array\n");
return; return;
} }
@ -353,7 +368,7 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
cxt->dump.dump = mtdoops_do_dump; cxt->dump.dump = mtdoops_do_dump;
err = kmsg_dump_register(&cxt->dump); err = kmsg_dump_register(&cxt->dump);
if (err) { if (err) {
printk(KERN_ERR "mtdoops: registering kmsg dumper failed, error %d\n", err); pr_err("registering kmsg dumper failed, error %d\n", err);
vfree(cxt->oops_page_used); vfree(cxt->oops_page_used);
cxt->oops_page_used = NULL; cxt->oops_page_used = NULL;
return; return;
@ -362,7 +377,7 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
cxt->mtd = mtd; cxt->mtd = mtd;
cxt->oops_pages = (int)mtd->size / record_size; cxt->oops_pages = (int)mtd->size / record_size;
find_next_position(cxt); find_next_position(cxt);
printk(KERN_INFO "mtdoops: Attached to MTD device %d\n", mtd->index); pr_info("Attached to MTD device %d\n", mtd->index);
} }
static void mtdoops_notify_remove(struct mtd_info *mtd) static void mtdoops_notify_remove(struct mtd_info *mtd)
@ -373,7 +388,7 @@ static void mtdoops_notify_remove(struct mtd_info *mtd)
return; return;
if (kmsg_dump_unregister(&cxt->dump) < 0) if (kmsg_dump_unregister(&cxt->dump) < 0)
printk(KERN_WARNING "mtdoops: could not unregister kmsg_dumper\n"); pr_warn("could not unregister kmsg_dumper\n");
cxt->mtd = NULL; cxt->mtd = NULL;
flush_work(&cxt->work_erase); flush_work(&cxt->work_erase);
@ -393,15 +408,15 @@ static int __init mtdoops_init(void)
char *endp; char *endp;
if (strlen(mtddev) == 0) { if (strlen(mtddev) == 0) {
printk(KERN_ERR "mtdoops: mtd device (mtddev=name/number) must be supplied\n"); pr_err("mtd device (mtddev=name/number) must be supplied\n");
return -EINVAL; return -EINVAL;
} }
if ((record_size & 4095) != 0) { if ((record_size & 4095) != 0) {
printk(KERN_ERR "mtdoops: record_size must be a multiple of 4096\n"); pr_err("record_size must be a multiple of 4096\n");
return -EINVAL; return -EINVAL;
} }
if (record_size < 4096) { if (record_size < 4096) {
printk(KERN_ERR "mtdoops: record_size must be over 4096 bytes\n"); pr_err("record_size must be over 4096 bytes\n");
return -EINVAL; return -EINVAL;
} }

View File

@ -126,7 +126,7 @@ EXPORT_SYMBOL_GPL(nanddev_isreserved);
* *
* Return: 0 in case of success, a negative error code otherwise. * Return: 0 in case of success, a negative error code otherwise.
*/ */
int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos) static int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos)
{ {
if (nanddev_isbad(nand, pos) || nanddev_isreserved(nand, pos)) { if (nanddev_isbad(nand, pos) || nanddev_isreserved(nand, pos)) {
pr_warn("attempt to erase a bad/reserved block @%llx\n", pr_warn("attempt to erase a bad/reserved block @%llx\n",
@ -136,7 +136,6 @@ int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos)
return nand->ops->erase(nand, pos); return nand->ops->erase(nand, pos);
} }
EXPORT_SYMBOL_GPL(nanddev_erase);
/** /**
* nanddev_mtd_erase() - Generic mtd->_erase() implementation for NAND devices * nanddev_mtd_erase() - Generic mtd->_erase() implementation for NAND devices

View File

@ -415,7 +415,7 @@ config MTD_NAND_PLATFORM
config MTD_NAND_CADENCE config MTD_NAND_CADENCE
tristate "Support Cadence NAND (HPNFC) controller" tristate "Support Cadence NAND (HPNFC) controller"
depends on (OF || COMPILE_TEST) && HAS_IOMEM depends on OF && HAS_IOMEM
help help
Enable the driver for NAND flash on platforms using a Cadence NAND Enable the driver for NAND flash on platforms using a Cadence NAND
controller. controller.
@ -430,7 +430,7 @@ config MTD_NAND_ARASAN
config MTD_NAND_INTEL_LGM config MTD_NAND_INTEL_LGM
tristate "Support for NAND controller on Intel LGM SoC" tristate "Support for NAND controller on Intel LGM SoC"
depends on OF || COMPILE_TEST depends on OF
depends on HAS_IOMEM depends on HAS_IOMEM
help help
Enables support for NAND Flash chips on Intel's LGM SoC. Enables support for NAND Flash chips on Intel's LGM SoC.
@ -450,7 +450,7 @@ config MTD_NAND_ROCKCHIP
config MTD_NAND_PL35X config MTD_NAND_PL35X
tristate "ARM PL35X NAND controller" tristate "ARM PL35X NAND controller"
depends on OF || COMPILE_TEST depends on OF
depends on PL353_SMC depends on PL353_SMC
help help
Enables support for PrimeCell SMC PL351 and PL353 NAND Enables support for PrimeCell SMC PL351 and PL353 NAND

View File

@ -1184,6 +1184,14 @@ static int cadence_nand_hw_init(struct cdns_nand_ctrl *cdns_ctrl)
if (cadence_nand_read_bch_caps(cdns_ctrl)) if (cadence_nand_read_bch_caps(cdns_ctrl))
return -EIO; return -EIO;
#ifndef CONFIG_64BIT
if (cdns_ctrl->caps2.data_dma_width == 8) {
dev_err(cdns_ctrl->dev,
"cannot access 64-bit dma on !64-bit architectures");
return -EIO;
}
#endif
/* /*
* Set IO width access to 8. * Set IO width access to 8.
* It is because during SW device discovering width access * It is because during SW device discovering width access
@ -1882,17 +1890,36 @@ static int cadence_nand_read_buf(struct cdns_nand_ctrl *cdns_ctrl,
return status; return status;
if (!cdns_ctrl->caps1->has_dma) { if (!cdns_ctrl->caps1->has_dma) {
int len_in_words = len >> 2; u8 data_dma_width = cdns_ctrl->caps2.data_dma_width;
int len_in_words = (data_dma_width == 4) ? len >> 2 : len >> 3;
/* read alingment data */ /* read alingment data */
if (data_dma_width == 4)
ioread32_rep(cdns_ctrl->io.virt, buf, len_in_words); ioread32_rep(cdns_ctrl->io.virt, buf, len_in_words);
#ifdef CONFIG_64BIT
else
readsq(cdns_ctrl->io.virt, buf, len_in_words);
#endif
if (sdma_size > len) { if (sdma_size > len) {
int read_bytes = (data_dma_width == 4) ?
len_in_words << 2 : len_in_words << 3;
/* read rest data from slave DMA interface if any */ /* read rest data from slave DMA interface if any */
ioread32_rep(cdns_ctrl->io.virt, cdns_ctrl->buf, if (data_dma_width == 4)
ioread32_rep(cdns_ctrl->io.virt,
cdns_ctrl->buf,
sdma_size / 4 - len_in_words); sdma_size / 4 - len_in_words);
#ifdef CONFIG_64BIT
else
readsq(cdns_ctrl->io.virt, cdns_ctrl->buf,
sdma_size / 8 - len_in_words);
#endif
/* copy rest of data */ /* copy rest of data */
memcpy(buf + (len_in_words << 2), cdns_ctrl->buf, memcpy(buf + read_bytes, cdns_ctrl->buf,
len - (len_in_words << 2)); len - read_bytes);
} }
return 0; return 0;
} }
@ -1936,16 +1963,35 @@ static int cadence_nand_write_buf(struct cdns_nand_ctrl *cdns_ctrl,
return status; return status;
if (!cdns_ctrl->caps1->has_dma) { if (!cdns_ctrl->caps1->has_dma) {
int len_in_words = len >> 2; u8 data_dma_width = cdns_ctrl->caps2.data_dma_width;
int len_in_words = (data_dma_width == 4) ? len >> 2 : len >> 3;
if (data_dma_width == 4)
iowrite32_rep(cdns_ctrl->io.virt, buf, len_in_words); iowrite32_rep(cdns_ctrl->io.virt, buf, len_in_words);
#ifdef CONFIG_64BIT
else
writesq(cdns_ctrl->io.virt, buf, len_in_words);
#endif
if (sdma_size > len) { if (sdma_size > len) {
int written_bytes = (data_dma_width == 4) ?
len_in_words << 2 : len_in_words << 3;
/* copy rest of data */ /* copy rest of data */
memcpy(cdns_ctrl->buf, buf + (len_in_words << 2), memcpy(cdns_ctrl->buf, buf + written_bytes,
len - (len_in_words << 2)); len - written_bytes);
/* write all expected by nand controller data */ /* write all expected by nand controller data */
iowrite32_rep(cdns_ctrl->io.virt, cdns_ctrl->buf, if (data_dma_width == 4)
iowrite32_rep(cdns_ctrl->io.virt,
cdns_ctrl->buf,
sdma_size / 4 - len_in_words); sdma_size / 4 - len_in_words);
#ifdef CONFIG_64BIT
else
writesq(cdns_ctrl->io.virt, cdns_ctrl->buf,
sdma_size / 8 - len_in_words);
#endif
} }
return 0; return 0;

View File

@ -148,11 +148,9 @@ static int gpmi_init(struct gpmi_nand_data *this)
struct resources *r = &this->resources; struct resources *r = &this->resources;
int ret; int ret;
ret = pm_runtime_get_sync(this->dev); ret = pm_runtime_resume_and_get(this->dev);
if (ret < 0) { if (ret < 0)
pm_runtime_put_noidle(this->dev);
return ret; return ret;
}
ret = gpmi_reset_block(r->gpmi_regs, false); ret = gpmi_reset_block(r->gpmi_regs, false);
if (ret) if (ret)
@ -2504,11 +2502,9 @@ static int gpmi_nfc_exec_op(struct nand_chip *chip,
for (i = 0; i < GPMI_MAX_TRANSFERS; i++) for (i = 0; i < GPMI_MAX_TRANSFERS; i++)
this->transfers[i].direction = DMA_NONE; this->transfers[i].direction = DMA_NONE;
ret = pm_runtime_get_sync(this->dev); ret = pm_runtime_resume_and_get(this->dev);
if (ret < 0) { if (ret < 0)
pm_runtime_put_noidle(this->dev);
return ret; return ret;
}
/* /*
* This driver currently supports only one NAND chip. Plus, dies share * This driver currently supports only one NAND chip. Plus, dies share

View File

@ -25,7 +25,7 @@
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_gpio.h> #include <linux/gpio/consumer.h>
#include <linux/mtd/lpc32xx_mlc.h> #include <linux/mtd/lpc32xx_mlc.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/mm.h> #include <linux/mm.h>
@ -122,7 +122,6 @@ struct lpc32xx_nand_cfg_mlc {
uint32_t rd_low; uint32_t rd_low;
uint32_t wr_high; uint32_t wr_high;
uint32_t wr_low; uint32_t wr_low;
int wp_gpio;
struct mtd_partition *parts; struct mtd_partition *parts;
unsigned num_parts; unsigned num_parts;
}; };
@ -177,6 +176,7 @@ struct lpc32xx_nand_host {
struct nand_chip nand_chip; struct nand_chip nand_chip;
struct lpc32xx_mlc_platform_data *pdata; struct lpc32xx_mlc_platform_data *pdata;
struct clk *clk; struct clk *clk;
struct gpio_desc *wp_gpio;
void __iomem *io_base; void __iomem *io_base;
int irq; int irq;
struct lpc32xx_nand_cfg_mlc *ncfg; struct lpc32xx_nand_cfg_mlc *ncfg;
@ -370,8 +370,8 @@ static int lpc32xx_waitfunc(struct nand_chip *chip)
*/ */
static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host) static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host)
{ {
if (gpio_is_valid(host->ncfg->wp_gpio)) if (host->wp_gpio)
gpio_set_value(host->ncfg->wp_gpio, 0); gpiod_set_value_cansleep(host->wp_gpio, 1);
} }
/* /*
@ -379,8 +379,8 @@ static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host)
*/ */
static void lpc32xx_wp_disable(struct lpc32xx_nand_host *host) static void lpc32xx_wp_disable(struct lpc32xx_nand_host *host)
{ {
if (gpio_is_valid(host->ncfg->wp_gpio)) if (host->wp_gpio)
gpio_set_value(host->ncfg->wp_gpio, 1); gpiod_set_value_cansleep(host->wp_gpio, 0);
} }
static void lpc32xx_dma_complete_func(void *completion) static void lpc32xx_dma_complete_func(void *completion)
@ -636,8 +636,6 @@ static struct lpc32xx_nand_cfg_mlc *lpc32xx_parse_dt(struct device *dev)
return NULL; return NULL;
} }
ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0);
return ncfg; return ncfg;
} }
@ -713,14 +711,18 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
"Missing or bad NAND config from device tree\n"); "Missing or bad NAND config from device tree\n");
return -ENOENT; return -ENOENT;
} }
if (host->ncfg->wp_gpio == -EPROBE_DEFER)
return -EPROBE_DEFER; /* Start with WP disabled, if available */
if (gpio_is_valid(host->ncfg->wp_gpio) && host->wp_gpio = gpiod_get_optional(&pdev->dev, NULL, GPIOD_OUT_LOW);
gpio_request(host->ncfg->wp_gpio, "NAND WP")) { res = PTR_ERR_OR_ZERO(host->wp_gpio);
dev_err(&pdev->dev, "GPIO not available\n"); if (res) {
return -EBUSY; if (res != -EPROBE_DEFER)
dev_err(&pdev->dev, "WP GPIO is not available: %d\n",
res);
return res;
} }
lpc32xx_wp_disable(host);
gpiod_set_consumer_name(host->wp_gpio, "NAND WP");
host->pdata = dev_get_platdata(&pdev->dev); host->pdata = dev_get_platdata(&pdev->dev);
@ -817,7 +819,7 @@ put_clk:
clk_put(host->clk); clk_put(host->clk);
free_gpio: free_gpio:
lpc32xx_wp_enable(host); lpc32xx_wp_enable(host);
gpio_free(host->ncfg->wp_gpio); gpiod_put(host->wp_gpio);
return res; return res;
} }
@ -843,12 +845,11 @@ static int lpc32xx_nand_remove(struct platform_device *pdev)
clk_put(host->clk); clk_put(host->clk);
lpc32xx_wp_enable(host); lpc32xx_wp_enable(host);
gpio_free(host->ncfg->wp_gpio); gpiod_put(host->wp_gpio);
return 0; return 0;
} }
#ifdef CONFIG_PM
static int lpc32xx_nand_resume(struct platform_device *pdev) static int lpc32xx_nand_resume(struct platform_device *pdev)
{ {
struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
@ -880,11 +881,6 @@ static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm)
return 0; return 0;
} }
#else
#define lpc32xx_nand_resume NULL
#define lpc32xx_nand_suspend NULL
#endif
static const struct of_device_id lpc32xx_nand_match[] = { static const struct of_device_id lpc32xx_nand_match[] = {
{ .compatible = "nxp,lpc3220-mlc" }, { .compatible = "nxp,lpc3220-mlc" },
{ /* sentinel */ }, { /* sentinel */ },
@ -894,8 +890,8 @@ MODULE_DEVICE_TABLE(of, lpc32xx_nand_match);
static struct platform_driver lpc32xx_nand_driver = { static struct platform_driver lpc32xx_nand_driver = {
.probe = lpc32xx_nand_probe, .probe = lpc32xx_nand_probe,
.remove = lpc32xx_nand_remove, .remove = lpc32xx_nand_remove,
.resume = lpc32xx_nand_resume, .resume = pm_ptr(lpc32xx_nand_resume),
.suspend = lpc32xx_nand_suspend, .suspend = pm_ptr(lpc32xx_nand_suspend),
.driver = { .driver = {
.name = DRV_NAME, .name = DRV_NAME,
.of_match_table = lpc32xx_nand_match, .of_match_table = lpc32xx_nand_match,

View File

@ -23,9 +23,8 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/dmaengine.h> #include <linux/dmaengine.h>
#include <linux/gpio.h> #include <linux/gpio/consumer.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/mtd/lpc32xx_slc.h> #include <linux/mtd/lpc32xx_slc.h>
#define LPC32XX_MODNAME "lpc32xx-nand" #define LPC32XX_MODNAME "lpc32xx-nand"
@ -208,7 +207,6 @@ struct lpc32xx_nand_cfg_slc {
uint32_t rwidth; uint32_t rwidth;
uint32_t rhold; uint32_t rhold;
uint32_t rsetup; uint32_t rsetup;
int wp_gpio;
struct mtd_partition *parts; struct mtd_partition *parts;
unsigned num_parts; unsigned num_parts;
}; };
@ -217,6 +215,7 @@ struct lpc32xx_nand_host {
struct nand_chip nand_chip; struct nand_chip nand_chip;
struct lpc32xx_slc_platform_data *pdata; struct lpc32xx_slc_platform_data *pdata;
struct clk *clk; struct clk *clk;
struct gpio_desc *wp_gpio;
void __iomem *io_base; void __iomem *io_base;
struct lpc32xx_nand_cfg_slc *ncfg; struct lpc32xx_nand_cfg_slc *ncfg;
@ -309,8 +308,8 @@ static int lpc32xx_nand_device_ready(struct nand_chip *chip)
*/ */
static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host) static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host)
{ {
if (gpio_is_valid(host->ncfg->wp_gpio)) if (host->wp_gpio)
gpio_set_value(host->ncfg->wp_gpio, 0); gpiod_set_value_cansleep(host->wp_gpio, 1);
} }
/* /*
@ -318,8 +317,8 @@ static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host)
*/ */
static void lpc32xx_wp_disable(struct lpc32xx_nand_host *host) static void lpc32xx_wp_disable(struct lpc32xx_nand_host *host)
{ {
if (gpio_is_valid(host->ncfg->wp_gpio)) if (host->wp_gpio)
gpio_set_value(host->ncfg->wp_gpio, 1); gpiod_set_value_cansleep(host->wp_gpio, 0);
} }
/* /*
@ -764,8 +763,6 @@ static struct lpc32xx_nand_cfg_slc *lpc32xx_parse_dt(struct device *dev)
return NULL; return NULL;
} }
ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0);
return ncfg; return ncfg;
} }
@ -852,14 +849,18 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
"Missing or bad NAND config from device tree\n"); "Missing or bad NAND config from device tree\n");
return -ENOENT; return -ENOENT;
} }
if (host->ncfg->wp_gpio == -EPROBE_DEFER)
return -EPROBE_DEFER; /* Start with WP disabled, if available */
if (gpio_is_valid(host->ncfg->wp_gpio) && devm_gpio_request(&pdev->dev, host->wp_gpio = gpiod_get_optional(&pdev->dev, NULL, GPIOD_OUT_LOW);
host->ncfg->wp_gpio, "NAND WP")) { res = PTR_ERR_OR_ZERO(host->wp_gpio);
dev_err(&pdev->dev, "GPIO not available\n"); if (res) {
return -EBUSY; if (res != -EPROBE_DEFER)
dev_err(&pdev->dev, "WP GPIO is not available: %d\n",
res);
return res;
} }
lpc32xx_wp_disable(host);
gpiod_set_consumer_name(host->wp_gpio, "NAND WP");
host->pdata = dev_get_platdata(&pdev->dev); host->pdata = dev_get_platdata(&pdev->dev);
@ -968,7 +969,6 @@ static int lpc32xx_nand_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int lpc32xx_nand_resume(struct platform_device *pdev) static int lpc32xx_nand_resume(struct platform_device *pdev)
{ {
struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
@ -1007,11 +1007,6 @@ static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm)
return 0; return 0;
} }
#else
#define lpc32xx_nand_resume NULL
#define lpc32xx_nand_suspend NULL
#endif
static const struct of_device_id lpc32xx_nand_match[] = { static const struct of_device_id lpc32xx_nand_match[] = {
{ .compatible = "nxp,lpc3220-slc" }, { .compatible = "nxp,lpc3220-slc" },
{ /* sentinel */ }, { /* sentinel */ },
@ -1021,8 +1016,8 @@ MODULE_DEVICE_TABLE(of, lpc32xx_nand_match);
static struct platform_driver lpc32xx_nand_driver = { static struct platform_driver lpc32xx_nand_driver = {
.probe = lpc32xx_nand_probe, .probe = lpc32xx_nand_probe,
.remove = lpc32xx_nand_remove, .remove = lpc32xx_nand_remove,
.resume = lpc32xx_nand_resume, .resume = pm_ptr(lpc32xx_nand_resume),
.suspend = lpc32xx_nand_suspend, .suspend = pm_ptr(lpc32xx_nand_suspend),
.driver = { .driver = {
.name = LPC32XX_MODNAME, .name = LPC32XX_MODNAME,
.of_match_table = lpc32xx_nand_match, .of_match_table = lpc32xx_nand_match,

View File

@ -114,6 +114,7 @@
#define GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST BIT(20) #define GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST BIT(20)
#define GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST BIT(21) #define GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST BIT(21)
#define GENCONF_SOC_DEVICE_MUX_NFC_INT_EN BIT(25) #define GENCONF_SOC_DEVICE_MUX_NFC_INT_EN BIT(25)
#define GENCONF_SOC_DEVICE_MUX_NFC_DEVBUS_ARB_EN BIT(27)
#define GENCONF_CLK_GATING_CTRL 0x220 #define GENCONF_CLK_GATING_CTRL 0x220
#define GENCONF_CLK_GATING_CTRL_ND_GATE BIT(2) #define GENCONF_CLK_GATING_CTRL_ND_GATE BIT(2)
#define GENCONF_ND_CLK_CTRL 0x700 #define GENCONF_ND_CLK_CTRL 0x700
@ -2880,7 +2881,8 @@ static int marvell_nfc_init(struct marvell_nfc *nfc)
GENCONF_SOC_DEVICE_MUX_NFC_EN | GENCONF_SOC_DEVICE_MUX_NFC_EN |
GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST | GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST |
GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST | GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST |
GENCONF_SOC_DEVICE_MUX_NFC_INT_EN); GENCONF_SOC_DEVICE_MUX_NFC_INT_EN |
GENCONF_SOC_DEVICE_MUX_NFC_DEVBUS_ARB_EN);
regmap_update_bits(sysctrl_base, GENCONF_CLK_GATING_CTRL, regmap_update_bits(sysctrl_base, GENCONF_CLK_GATING_CTRL,
GENCONF_CLK_GATING_CTRL_ND_GATE, GENCONF_CLK_GATING_CTRL_ND_GATE,

View File

@ -663,7 +663,7 @@ static int mpc5121_nfc_probe(struct platform_device *op)
} }
prv->irq = irq_of_parse_and_map(dn, 0); prv->irq = irq_of_parse_and_map(dn, 0);
if (prv->irq == NO_IRQ) { if (!prv->irq) {
dev_err(dev, "Error mapping IRQ!\n"); dev_err(dev, "Error mapping IRQ!\n");
return -EINVAL; return -EINVAL;
} }

View File

@ -74,9 +74,75 @@ static int w25m02gv_select_target(struct spinand_device *spinand,
return spi_mem_exec_op(spinand->spimem, &op); return spi_mem_exec_op(spinand->spimem, &op);
} }
static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section > 3)
return -ERANGE;
region->offset = 64 + (16 * section);
region->length = 13;
return 0;
}
static int w25n02kv_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section > 3)
return -ERANGE;
region->offset = (16 * section) + 2;
region->length = 14;
return 0;
}
static const struct mtd_ooblayout_ops w25n02kv_ooblayout = {
.ecc = w25n02kv_ooblayout_ecc,
.free = w25n02kv_ooblayout_free,
};
static int w25n02kv_ecc_get_status(struct spinand_device *spinand,
u8 status)
{
struct nand_device *nand = spinand_to_nand(spinand);
u8 mbf = 0;
struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf);
switch (status & STATUS_ECC_MASK) {
case STATUS_ECC_NO_BITFLIPS:
return 0;
case STATUS_ECC_UNCOR_ERROR:
return -EBADMSG;
case STATUS_ECC_HAS_BITFLIPS:
/*
* Let's try to retrieve the real maximum number of bitflips
* in order to avoid forcing the wear-leveling layer to move
* data around if it's not necessary.
*/
if (spi_mem_exec_op(spinand->spimem, &op))
return nanddev_get_ecc_conf(nand)->strength;
mbf >>= 4;
if (WARN_ON(mbf > nanddev_get_ecc_conf(nand)->strength || !mbf))
return nanddev_get_ecc_conf(nand)->strength;
return mbf;
default:
break;
}
return -EINVAL;
}
static const struct spinand_info winbond_spinand_table[] = { static const struct spinand_info winbond_spinand_table[] = {
SPINAND_INFO("W25M02GV", SPINAND_INFO("W25M02GV",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab), SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2),
NAND_ECCREQ(1, 512), NAND_ECCREQ(1, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants, SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
@ -86,7 +152,7 @@ static const struct spinand_info winbond_spinand_table[] = {
SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
SPINAND_SELECT_TARGET(w25m02gv_select_target)), SPINAND_SELECT_TARGET(w25m02gv_select_target)),
SPINAND_INFO("W25N01GV", SPINAND_INFO("W25N01GV",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa), SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x21),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(1, 512), NAND_ECCREQ(1, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants, SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
@ -94,6 +160,15 @@ static const struct spinand_info winbond_spinand_table[] = {
&update_cache_variants), &update_cache_variants),
0, 0,
SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
SPINAND_INFO("W25N02KV",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22),
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),
0,
SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
}; };
static int winbond_spinand_init(struct spinand_device *spinand) static int winbond_spinand_init(struct spinand_device *spinand)

View File

@ -22,7 +22,7 @@ config MTD_BCM63XX_PARTS
config MTD_BRCM_U_BOOT config MTD_BRCM_U_BOOT
tristate "Broadcom's U-Boot partition parser" tristate "Broadcom's U-Boot partition parser"
depends on ARCH_BCM4908 || COMPILE_TEST depends on ARCH_BCMBCA || COMPILE_TEST
help help
Broadcom uses a custom way of storing U-Boot environment variables. Broadcom uses a custom way of storing U-Boot environment variables.
They are placed inside U-Boot partition itself at unspecified offset. They are placed inside U-Boot partition itself at unspecified offset.
@ -75,7 +75,7 @@ config MTD_OF_PARTS
This provides a open firmware device tree partition parser This provides a open firmware device tree partition parser
which derives the partition map from the children of the which derives the partition map from the children of the
flash memory node, as described in flash memory node, as described in
Documentation/devicetree/bindings/mtd/partition.txt. Documentation/devicetree/bindings/mtd/mtd.yaml.
config MTD_OF_PARTS_BCM4908 config MTD_OF_PARTS_BCM4908
bool "BCM4908 partitioning support" bool "BCM4908 partitioning support"
@ -123,6 +123,21 @@ config MTD_AFS_PARTS
for your particular device. It won't happen automatically. The for your particular device. It won't happen automatically. The
'physmap' map driver (CONFIG_MTD_PHYSMAP) does this, for example. 'physmap' map driver (CONFIG_MTD_PHYSMAP) does this, for example.
config MTD_PARSER_TPLINK_SAFELOADER
tristate "TP-Link Safeloader partitions parser"
depends on MTD && (ARCH_BCM_5301X || ATH79 || SOC_MT7620 || SOC_MT7621 || COMPILE_TEST)
help
TP-Link home routers use flash partitions to store various data. Info
about flash space layout is stored in a partitions table using a
custom ASCII-based format.
That format was first found in devices with SafeLoader bootloader and
was named after it. Later it was adapted to CFE and U-Boot
bootloaders.
This driver reads partitions table, parses it and creates MTD
partitions.
config MTD_PARSER_TRX config MTD_PARSER_TRX
tristate "Parser for TRX format partitions" tristate "Parser for TRX format partitions"
depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || RALINK || COMPILE_TEST) depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || RALINK || COMPILE_TEST)

View File

@ -10,6 +10,7 @@ ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) += ofpart_bcm4908.o
ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)+= ofpart_linksys_ns.o ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)+= ofpart_linksys_ns.o
obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o
obj-$(CONFIG_MTD_AFS_PARTS) += afs.o obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
obj-$(CONFIG_MTD_PARSER_TPLINK_SAFELOADER) += tplink_safeloader.o
obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o
obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpart.o obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpart.o
obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o

View File

@ -0,0 +1,150 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright © 2022 Rafał Miłecki <rafal@milecki.pl>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/of.h>
#include <linux/slab.h>
#define TPLINK_SAFELOADER_DATA_OFFSET 4
#define TPLINK_SAFELOADER_MAX_PARTS 32
struct safeloader_cmn_header {
__be32 size;
uint32_t unused;
} __packed;
static void *mtd_parser_tplink_safeloader_read_table(struct mtd_info *mtd)
{
struct safeloader_cmn_header hdr;
struct device_node *np;
size_t bytes_read;
size_t size;
u32 offset;
char *buf;
int err;
np = mtd_get_of_node(mtd);
if (mtd_is_partition(mtd))
of_node_get(np);
else
np = of_get_child_by_name(np, "partitions");
if (of_property_read_u32(np, "partitions-table-offset", &offset)) {
pr_err("Failed to get partitions table offset\n");
goto err_put;
}
err = mtd_read(mtd, offset, sizeof(hdr), &bytes_read, (uint8_t *)&hdr);
if (err && !mtd_is_bitflip(err)) {
pr_err("Failed to read from %s at 0x%x\n", mtd->name, offset);
goto err_put;
}
size = be32_to_cpu(hdr.size);
buf = kmalloc(size + 1, GFP_KERNEL);
if (!buf)
goto err_put;
err = mtd_read(mtd, offset + sizeof(hdr), size, &bytes_read, buf);
if (err && !mtd_is_bitflip(err)) {
pr_err("Failed to read from %s at 0x%zx\n", mtd->name, offset + sizeof(hdr));
goto err_kfree;
}
buf[size] = '\0';
of_node_put(np);
return buf;
err_kfree:
kfree(buf);
err_put:
of_node_put(np);
return NULL;
}
static int mtd_parser_tplink_safeloader_parse(struct mtd_info *mtd,
const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
struct mtd_partition *parts;
char name[65];
size_t offset;
size_t bytes;
char *buf;
int idx;
int err;
parts = kcalloc(TPLINK_SAFELOADER_MAX_PARTS, sizeof(*parts), GFP_KERNEL);
if (!parts) {
err = -ENOMEM;
goto err_out;
}
buf = mtd_parser_tplink_safeloader_read_table(mtd);
if (!buf) {
err = -ENOENT;
goto err_out;
}
for (idx = 0, offset = TPLINK_SAFELOADER_DATA_OFFSET;
idx < TPLINK_SAFELOADER_MAX_PARTS &&
sscanf(buf + offset, "partition %64s base 0x%llx size 0x%llx%zn\n",
name, &parts[idx].offset, &parts[idx].size, &bytes) == 3;
idx++, offset += bytes + 1) {
parts[idx].name = kstrdup(name, GFP_KERNEL);
if (!parts[idx].name) {
err = -ENOMEM;
goto err_free;
}
}
if (idx == TPLINK_SAFELOADER_MAX_PARTS)
pr_warn("Reached maximum number of partitions!\n");
kfree(buf);
*pparts = parts;
return idx;
err_free:
for (idx -= 1; idx >= 0; idx--)
kfree(parts[idx].name);
err_out:
return err;
};
static void mtd_parser_tplink_safeloader_cleanup(const struct mtd_partition *pparts,
int nr_parts)
{
int i;
for (i = 0; i < nr_parts; i++)
kfree(pparts[i].name);
kfree(pparts);
}
static const struct of_device_id mtd_parser_tplink_safeloader_of_match_table[] = {
{ .compatible = "tplink,safeloader-partitions" },
{},
};
MODULE_DEVICE_TABLE(of, mtd_parser_tplink_safeloader_of_match_table);
static struct mtd_part_parser mtd_parser_tplink_safeloader = {
.parse_fn = mtd_parser_tplink_safeloader_parse,
.cleanup = mtd_parser_tplink_safeloader_cleanup,
.name = "tplink-safeloader",
.of_match_table = mtd_parser_tplink_safeloader_of_match_table,
};
module_mtd_part_parser(mtd_parser_tplink_safeloader);
MODULE_LICENSE("GPL");

View File

@ -1184,6 +1184,8 @@ spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map,
continue; continue;
erase = &map->erase_type[i]; erase = &map->erase_type[i];
if (!erase->size)
continue;
/* Alignment is not mandatory for overlaid regions */ /* Alignment is not mandatory for overlaid regions */
if (region->offset & SNOR_OVERLAID_REGION && if (region->offset & SNOR_OVERLAID_REGION &&
@ -1632,6 +1634,16 @@ static const struct spi_nor_manufacturer *manufacturers[] = {
&spi_nor_xmc, &spi_nor_xmc,
}; };
static const struct flash_info spi_nor_generic_flash = {
.name = "spi-nor-generic",
/*
* JESD216 rev A doesn't specify the page size, therefore we need a
* sane default.
*/
.page_size = 256,
.parse_sfdp = true,
};
static const struct flash_info *spi_nor_match_id(struct spi_nor *nor, static const struct flash_info *spi_nor_match_id(struct spi_nor *nor,
const u8 *id) const u8 *id)
{ {
@ -1664,7 +1676,20 @@ static const struct flash_info *spi_nor_detect(struct spi_nor *nor)
return ERR_PTR(ret); return ERR_PTR(ret);
} }
/* Cache the complete flash ID. */
nor->id = devm_kmemdup(nor->dev, id, SPI_NOR_MAX_ID_LEN, GFP_KERNEL);
if (!nor->id)
return ERR_PTR(-ENOMEM);
info = spi_nor_match_id(nor, id); info = spi_nor_match_id(nor, id);
/* Fallback to a generic flash described only by its SFDP data. */
if (!info) {
ret = spi_nor_check_sfdp_signature(nor);
if (!ret)
info = &spi_nor_generic_flash;
}
if (!info) { if (!info) {
dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n", dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n",
SPI_NOR_MAX_ID_LEN, id); SPI_NOR_MAX_ID_LEN, id);
@ -1914,7 +1939,8 @@ static int spi_nor_spimem_check_readop(struct spi_nor *nor,
spi_nor_spimem_setup_op(nor, &op, read->proto); spi_nor_spimem_setup_op(nor, &op, read->proto);
/* convert the dummy cycles to the number of bytes */ /* convert the dummy cycles to the number of bytes */
op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8; op.dummy.nbytes = (read->num_mode_clocks + read->num_wait_states) *
op.dummy.buswidth / 8;
if (spi_nor_protocol_is_dtr(nor->read_proto)) if (spi_nor_protocol_is_dtr(nor->read_proto))
op.dummy.nbytes *= 2; op.dummy.nbytes *= 2;
@ -2091,8 +2117,12 @@ static int spi_nor_select_pp(struct spi_nor *nor,
* spi_nor_select_uniform_erase() - select optimum uniform erase type * spi_nor_select_uniform_erase() - select optimum uniform erase type
* @map: the erase map of the SPI NOR * @map: the erase map of the SPI NOR
* @wanted_size: the erase type size to search for. Contains the value of * @wanted_size: the erase type size to search for. Contains the value of
* info->sector_size or of the "small sector" size in case * info->sector_size, the "small sector" size in case
* CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is defined. * CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is defined or 0 if
* there is no information about the sector size. The
* latter is the case if the flash parameters are parsed
* solely by SFDP, then the largest supported erase type
* is selected.
* *
* Once the optimum uniform sector erase command is found, disable all the * Once the optimum uniform sector erase command is found, disable all the
* other. * other.
@ -2113,6 +2143,10 @@ spi_nor_select_uniform_erase(struct spi_nor_erase_map *map,
tested_erase = &map->erase_type[i]; tested_erase = &map->erase_type[i];
/* Skip masked erase types. */
if (!tested_erase->size)
continue;
/* /*
* If the current erase size is the one, stop here: * If the current erase size is the one, stop here:
* we have found the right uniform Sector Erase command. * we have found the right uniform Sector Erase command.
@ -2565,6 +2599,12 @@ static void spi_nor_init_default_params(struct spi_nor *nor)
params->hwcaps.mask |= SNOR_HWCAPS_PP; params->hwcaps.mask |= SNOR_HWCAPS_PP;
spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP], spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
SPINOR_OP_PP, SNOR_PROTO_1_1_1); SPINOR_OP_PP, SNOR_PROTO_1_1_1);
if (info->flags & SPI_NOR_QUAD_PP) {
params->hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4;
spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP_1_1_4],
SPINOR_OP_PP_1_1_4, SNOR_PROTO_1_1_4);
}
} }
/** /**
@ -2840,10 +2880,20 @@ static void spi_nor_put_device(struct mtd_info *mtd)
void spi_nor_restore(struct spi_nor *nor) void spi_nor_restore(struct spi_nor *nor)
{ {
int ret;
/* restore the addressing mode */ /* restore the addressing mode */
if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
nor->flags & SNOR_F_BROKEN_RESET) nor->flags & SNOR_F_BROKEN_RESET) {
nor->params->set_4byte_addr_mode(nor, false); ret = nor->params->set_4byte_addr_mode(nor, false);
if (ret)
/*
* Do not stop the execution in the hope that the flash
* will default to the 3-byte address mode after the
* software reset.
*/
dev_err(nor->dev, "Failed to exit 4-byte address mode, err = %d\n", ret);
}
if (nor->flags & SNOR_F_SOFT_RESET) if (nor->flags & SNOR_F_SOFT_RESET)
spi_nor_soft_reset(nor); spi_nor_soft_reset(nor);
@ -2935,6 +2985,27 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
mtd->_put_device = spi_nor_put_device; mtd->_put_device = spi_nor_put_device;
} }
static int spi_nor_hw_reset(struct spi_nor *nor)
{
struct gpio_desc *reset;
reset = devm_gpiod_get_optional(nor->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR_OR_NULL(reset))
return PTR_ERR_OR_ZERO(reset);
/*
* Experimental delay values by looking at different flash device
* vendors datasheets.
*/
usleep_range(1, 5);
gpiod_set_value_cansleep(reset, 1);
usleep_range(100, 150);
gpiod_set_value_cansleep(reset, 0);
usleep_range(1000, 1200);
return 0;
}
int spi_nor_scan(struct spi_nor *nor, const char *name, int spi_nor_scan(struct spi_nor *nor, const char *name,
const struct spi_nor_hwcaps *hwcaps) const struct spi_nor_hwcaps *hwcaps)
{ {
@ -2967,6 +3038,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
if (!nor->bouncebuf) if (!nor->bouncebuf)
return -ENOMEM; return -ENOMEM;
ret = spi_nor_hw_reset(nor);
if (ret)
return ret;
info = spi_nor_get_flash_info(nor, name); info = spi_nor_get_flash_info(nor, name);
if (IS_ERR(info)) if (IS_ERR(info))
return PTR_ERR(info); return PTR_ERR(info);

View File

@ -458,6 +458,7 @@ struct spi_nor_fixups {
* SPI_NOR_NO_ERASE: no erase command needed. * SPI_NOR_NO_ERASE: no erase command needed.
* NO_CHIP_ERASE: chip does not support chip erase. * NO_CHIP_ERASE: chip does not support chip erase.
* SPI_NOR_NO_FR: can't do fastread. * SPI_NOR_NO_FR: can't do fastread.
* SPI_NOR_QUAD_PP: flash supports Quad Input Page Program.
* *
* @no_sfdp_flags: flags that indicate support that can be discovered via SFDP. * @no_sfdp_flags: flags that indicate support that can be discovered via SFDP.
* Used when SFDP tables are not defined in the flash. These * Used when SFDP tables are not defined in the flash. These
@ -507,6 +508,7 @@ struct flash_info {
#define SPI_NOR_NO_ERASE BIT(6) #define SPI_NOR_NO_ERASE BIT(6)
#define NO_CHIP_ERASE BIT(7) #define NO_CHIP_ERASE BIT(7)
#define SPI_NOR_NO_FR BIT(8) #define SPI_NOR_NO_FR BIT(8)
#define SPI_NOR_QUAD_PP BIT(9)
u8 no_sfdp_flags; u8 no_sfdp_flags;
#define SPI_NOR_SKIP_SFDP BIT(0) #define SPI_NOR_SKIP_SFDP BIT(0)
@ -701,6 +703,9 @@ int spi_nor_controller_ops_read_reg(struct spi_nor *nor, u8 opcode,
int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode, int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode,
const u8 *buf, size_t len); const u8 *buf, size_t len);
int spi_nor_check_sfdp_signature(struct spi_nor *nor);
int spi_nor_parse_sfdp(struct spi_nor *nor);
static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
{ {
return container_of(mtd, struct spi_nor, mtd); return container_of(mtd, struct spi_nor, mtd);

View File

@ -81,7 +81,7 @@ static int spi_nor_params_show(struct seq_file *s, void *data)
int i; int i;
seq_printf(s, "name\t\t%s\n", info->name); seq_printf(s, "name\t\t%s\n", info->name);
seq_printf(s, "id\t\t%*ph\n", info->id_len, info->id); seq_printf(s, "id\t\t%*ph\n", SPI_NOR_MAX_ID_LEN, nor->id);
string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf)); string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf));
seq_printf(s, "size\t\t%s\n", buf); seq_printf(s, "size\t\t%s\n", buf);
seq_printf(s, "write size\t%u\n", params->writesize); seq_printf(s, "write size\t%u\n", params->writesize);

View File

@ -8,19 +8,29 @@
#include "core.h" #include "core.h"
static void gd25q256_default_init(struct spi_nor *nor) static int
gd25q256_post_bfpt(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt)
{ {
/* /*
* Some manufacturer like GigaDevice may use different * GD25Q256C supports the first version of JESD216 which does not define
* bit to set QE on different memories, so the MFR can't * the Quad Enable methods. Overwrite the default Quad Enable method.
* indicate the quad_enable method for this case, we need *
* to set it in the default_init fixup hook. * GD25Q256 GENERATION | SFDP MAJOR VERSION | SFDP MINOR VERSION
* GD25Q256C | SFDP_JESD216_MAJOR | SFDP_JESD216_MINOR
* GD25Q256D | SFDP_JESD216_MAJOR | SFDP_JESD216B_MINOR
* GD25Q256E | SFDP_JESD216_MAJOR | SFDP_JESD216B_MINOR
*/ */
if (bfpt_header->major == SFDP_JESD216_MAJOR &&
bfpt_header->minor == SFDP_JESD216_MINOR)
nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable; nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
return 0;
} }
static const struct spi_nor_fixups gd25q256_fixups = { static const struct spi_nor_fixups gd25q256_fixups = {
.default_init = gd25q256_default_init, .post_bfpt = gd25q256_post_bfpt,
}; };
static const struct flash_info gigadevice_nor_parts[] = { static const struct flash_info gigadevice_nor_parts[] = {

View File

@ -70,9 +70,10 @@ static const struct flash_info issi_nor_parts[] = {
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "is25wp128", INFO(0x9d7018, 0, 64 * 1024, 256) { "is25wp128", INFO(0x9d7018, 0, 64 * 1024, 256)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "is25wp256", INFO(0x9d7019, 0, 64 * 1024, 512) { "is25wp256", INFO(0x9d7019, 0, 0, 0)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) PARSE_SFDP
FIXUP_FLAGS(SPI_NOR_4B_OPCODES) FIXUP_FLAGS(SPI_NOR_4B_OPCODES)
FLAGS(SPI_NOR_QUAD_PP)
.fixups = &is25lp256_fixups }, .fixups = &is25lp256_fixups },
/* PMC */ /* PMC */

View File

@ -52,18 +52,21 @@ static int micron_st_nor_octal_dtr_en(struct spi_nor *nor)
struct spi_mem_op op; struct spi_mem_op op;
u8 *buf = nor->bouncebuf; u8 *buf = nor->bouncebuf;
int ret; int ret;
u8 addr_mode_nbytes = nor->params->addr_mode_nbytes;
/* Use 20 dummy cycles for memory array reads. */ /* Use 20 dummy cycles for memory array reads. */
*buf = 20; *buf = 20;
op = (struct spi_mem_op) op = (struct spi_mem_op)
MICRON_ST_NOR_WR_ANY_REG_OP(3, SPINOR_REG_MT_CFR1V, 1, buf); MICRON_ST_NOR_WR_ANY_REG_OP(addr_mode_nbytes,
SPINOR_REG_MT_CFR1V, 1, buf);
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
if (ret) if (ret)
return ret; return ret;
buf[0] = SPINOR_MT_OCT_DTR; buf[0] = SPINOR_MT_OCT_DTR;
op = (struct spi_mem_op) op = (struct spi_mem_op)
MICRON_ST_NOR_WR_ANY_REG_OP(3, SPINOR_REG_MT_CFR0V, 1, buf); MICRON_ST_NOR_WR_ANY_REG_OP(addr_mode_nbytes,
SPINOR_REG_MT_CFR0V, 1, buf);
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
if (ret) if (ret)
return ret; return ret;
@ -98,7 +101,8 @@ static int micron_st_nor_octal_dtr_dis(struct spi_nor *nor)
buf[0] = SPINOR_MT_EXSPI; buf[0] = SPINOR_MT_EXSPI;
buf[1] = SPINOR_REG_MT_CFR1V_DEF; buf[1] = SPINOR_REG_MT_CFR1V_DEF;
op = (struct spi_mem_op) op = (struct spi_mem_op)
MICRON_ST_NOR_WR_ANY_REG_OP(4, SPINOR_REG_MT_CFR0V, 2, buf); MICRON_ST_NOR_WR_ANY_REG_OP(nor->addr_nbytes,
SPINOR_REG_MT_CFR0V, 2, buf);
ret = spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR); ret = spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR);
if (ret) if (ret)
return ret; return ret;
@ -201,6 +205,8 @@ static const struct flash_info st_nor_parts[] = {
MFR_FLAGS(USE_FSR) MFR_FLAGS(USE_FSR)
}, },
{ "mt25qu256a", INFO6(0x20bb19, 0x104400, 64 * 1024, 512) { "mt25qu256a", INFO6(0x20bb19, 0x104400, 64 * 1024, 512)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP |
SPI_NOR_BP3_SR_BIT6)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
FIXUP_FLAGS(SPI_NOR_4B_OPCODES) FIXUP_FLAGS(SPI_NOR_4B_OPCODES)
MFR_FLAGS(USE_FSR) MFR_FLAGS(USE_FSR)

View File

@ -135,8 +135,7 @@ struct sfdp_4bait {
/** /**
* spi_nor_read_raw() - raw read of serial flash memory. read_opcode, * spi_nor_read_raw() - raw read of serial flash memory. read_opcode,
* addr_nbytes and read_dummy members of the struct spi_nor * addr_nbytes and read_dummy members of the struct spi_nor
* should be previously * should be previously set.
* set.
* @nor: pointer to a 'struct spi_nor' * @nor: pointer to a 'struct spi_nor'
* @addr: offset in the serial flash memory * @addr: offset in the serial flash memory
* @len: number of bytes to read * @len: number of bytes to read
@ -1183,10 +1182,17 @@ static int spi_nor_parse_profile1(struct spi_nor *nor,
dummy = round_up(dummy, 2); dummy = round_up(dummy, 2);
/* Update the fast read settings. */ /* Update the fast read settings. */
nor->params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR;
spi_nor_set_read_settings(&nor->params->reads[SNOR_CMD_READ_8_8_8_DTR], spi_nor_set_read_settings(&nor->params->reads[SNOR_CMD_READ_8_8_8_DTR],
0, dummy, opcode, 0, dummy, opcode,
SNOR_PROTO_8_8_8_DTR); SNOR_PROTO_8_8_8_DTR);
/*
* Page Program is "Required Command" in the xSPI Profile 1.0. Update
* the params->hwcaps.mask here.
*/
nor->params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR;
out: out:
kfree(dwords); kfree(dwords);
return ret; return ret;
@ -1249,6 +1255,33 @@ static void spi_nor_post_sfdp_fixups(struct spi_nor *nor)
nor->info->fixups->post_sfdp(nor); nor->info->fixups->post_sfdp(nor);
} }
/**
* spi_nor_check_sfdp_signature() - check for a valid SFDP signature
* @nor: pointer to a 'struct spi_nor'
*
* Used to detect if the flash supports the RDSFDP command as well as the
* presence of a valid SFDP table.
*
* Return: 0 on success, -errno otherwise.
*/
int spi_nor_check_sfdp_signature(struct spi_nor *nor)
{
u32 signature;
int err;
/* Get the SFDP header. */
err = spi_nor_read_sfdp_dma_unsafe(nor, 0, sizeof(signature),
&signature);
if (err < 0)
return err;
/* Check the SFDP signature. */
if (le32_to_cpu(signature) != SFDP_SIGNATURE)
return -EINVAL;
return 0;
}
/** /**
* spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters. * spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters.
* @nor: pointer to a 'struct spi_nor' * @nor: pointer to a 'struct spi_nor'

View File

@ -107,6 +107,4 @@ struct sfdp_parameter_header {
u8 id_msb; u8 id_msb;
}; };
int spi_nor_parse_sfdp(struct spi_nor *nor);
#endif /* __LINUX_MTD_SFDP_H */ #endif /* __LINUX_MTD_SFDP_H */

View File

@ -49,11 +49,13 @@ static int cypress_nor_octal_dtr_en(struct spi_nor *nor)
struct spi_mem_op op; struct spi_mem_op op;
u8 *buf = nor->bouncebuf; u8 *buf = nor->bouncebuf;
int ret; int ret;
u8 addr_mode_nbytes = nor->params->addr_mode_nbytes;
/* Use 24 dummy cycles for memory array reads. */ /* Use 24 dummy cycles for memory array reads. */
*buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24; *buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24;
op = (struct spi_mem_op) op = (struct spi_mem_op)
CYPRESS_NOR_WR_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR2V, 1, buf); CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes,
SPINOR_REG_CYPRESS_CFR2V, 1, buf);
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
if (ret) if (ret)
@ -64,14 +66,16 @@ static int cypress_nor_octal_dtr_en(struct spi_nor *nor)
/* Set the octal and DTR enable bits. */ /* Set the octal and DTR enable bits. */
buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN; buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN;
op = (struct spi_mem_op) op = (struct spi_mem_op)
CYPRESS_NOR_WR_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR5V, 1, buf); CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes,
SPINOR_REG_CYPRESS_CFR5V, 1, buf);
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
if (ret) if (ret)
return ret; return ret;
/* Read flash ID to make sure the switch was successful. */ /* 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); ret = spi_nor_read_id(nor, nor->addr_nbytes, 3, buf,
SNOR_PROTO_8_8_8_DTR);
if (ret) { if (ret) {
dev_dbg(nor->dev, "error %d reading JEDEC ID after enabling 8D-8D-8D mode\n", ret); dev_dbg(nor->dev, "error %d reading JEDEC ID after enabling 8D-8D-8D mode\n", ret);
return ret; return ret;
@ -97,7 +101,8 @@ static int cypress_nor_octal_dtr_dis(struct spi_nor *nor)
buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS; buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS;
buf[1] = 0; buf[1] = 0;
op = (struct spi_mem_op) op = (struct spi_mem_op)
CYPRESS_NOR_WR_ANY_REG_OP(4, SPINOR_REG_CYPRESS_CFR5V, 2, buf); CYPRESS_NOR_WR_ANY_REG_OP(nor->addr_nbytes,
SPINOR_REG_CYPRESS_CFR5V, 2, buf);
ret = spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR); ret = spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR);
if (ret) if (ret)
return ret; return ret;
@ -191,7 +196,8 @@ static int cypress_nor_quad_enable_volatile(struct spi_nor *nor)
static int cypress_nor_set_page_size(struct spi_nor *nor) static int cypress_nor_set_page_size(struct spi_nor *nor)
{ {
struct spi_mem_op op = struct spi_mem_op op =
CYPRESS_NOR_RD_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR3V, CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes,
SPINOR_REG_CYPRESS_CFR3V,
nor->bouncebuf); nor->bouncebuf);
int ret; int ret;
@ -275,13 +281,7 @@ static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
cypress_nor_octal_dtr_dis(nor); cypress_nor_octal_dtr_dis(nor);
} }
static void s28hs512t_default_init(struct spi_nor *nor) static void s28hx_t_post_sfdp_fixup(struct spi_nor *nor)
{
nor->params->octal_dtr_enable = cypress_nor_octal_dtr_enable;
nor->params->writesize = 16;
}
static void s28hs512t_post_sfdp_fixup(struct spi_nor *nor)
{ {
/* /*
* On older versions of the flash the xSPI Profile 1.0 table has the * On older versions of the flash the xSPI Profile 1.0 table has the
@ -309,17 +309,23 @@ static void s28hs512t_post_sfdp_fixup(struct spi_nor *nor)
nor->params->rdsr_addr_nbytes = 4; nor->params->rdsr_addr_nbytes = 4;
} }
static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor, static int s28hx_t_post_bfpt_fixup(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header, const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt) const struct sfdp_bfpt *bfpt)
{ {
return cypress_nor_set_page_size(nor); return cypress_nor_set_page_size(nor);
} }
static const struct spi_nor_fixups s28hs512t_fixups = { static void s28hx_t_late_init(struct spi_nor *nor)
.default_init = s28hs512t_default_init, {
.post_sfdp = s28hs512t_post_sfdp_fixup, nor->params->octal_dtr_enable = cypress_nor_octal_dtr_enable;
.post_bfpt = s28hs512t_post_bfpt_fixup, nor->params->writesize = 16;
}
static const struct spi_nor_fixups s28hx_t_fixups = {
.post_sfdp = s28hx_t_post_sfdp_fixup,
.post_bfpt = s28hx_t_post_bfpt_fixup,
.late_init = s28hx_t_late_init,
}; };
static int static int
@ -453,10 +459,21 @@ static const struct flash_info spansion_nor_parts[] = {
.fixups = &s25hx_t_fixups }, .fixups = &s25hx_t_fixups },
{ "cy15x104q", INFO6(0x042cc2, 0x7f7f7f, 512 * 1024, 1) { "cy15x104q", INFO6(0x042cc2, 0x7f7f7f, 512 * 1024, 1)
FLAGS(SPI_NOR_NO_ERASE) }, FLAGS(SPI_NOR_NO_ERASE) },
{ "s28hl512t", INFO(0x345a1a, 0, 256 * 1024, 256)
PARSE_SFDP
.fixups = &s28hx_t_fixups,
},
{ "s28hl01gt", INFO(0x345a1b, 0, 256 * 1024, 512)
PARSE_SFDP
.fixups = &s28hx_t_fixups,
},
{ "s28hs512t", INFO(0x345b1a, 0, 256 * 1024, 256) { "s28hs512t", INFO(0x345b1a, 0, 256 * 1024, 256)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_OCTAL_DTR_READ | PARSE_SFDP
SPI_NOR_OCTAL_DTR_PP) .fixups = &s28hx_t_fixups,
.fixups = &s28hs512t_fixups, },
{ "s28hs01gt", INFO(0x345b1b, 0, 256 * 1024, 512)
PARSE_SFDP
.fixups = &s28hx_t_fixups,
}, },
}; };

View File

@ -35,8 +35,10 @@ static ssize_t jedec_id_show(struct device *dev,
struct spi_device *spi = to_spi_device(dev); struct spi_device *spi = to_spi_device(dev);
struct spi_mem *spimem = spi_get_drvdata(spi); struct spi_mem *spimem = spi_get_drvdata(spi);
struct spi_nor *nor = spi_mem_get_drvdata(spimem); struct spi_nor *nor = spi_mem_get_drvdata(spimem);
const u8 *id = nor->info->id_len ? nor->info->id : nor->id;
u8 id_len = nor->info->id_len ?: SPI_NOR_MAX_ID_LEN;
return sysfs_emit(buf, "%*phN\n", nor->info->id_len, nor->info->id); return sysfs_emit(buf, "%*phN\n", id_len, id);
} }
static DEVICE_ATTR_RO(jedec_id); static DEVICE_ATTR_RO(jedec_id);
@ -67,6 +69,21 @@ static struct bin_attribute *spi_nor_sysfs_bin_entries[] = {
NULL NULL
}; };
static umode_t spi_nor_sysfs_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct spi_device *spi = to_spi_device(kobj_to_dev(kobj));
struct spi_mem *spimem = spi_get_drvdata(spi);
struct spi_nor *nor = spi_mem_get_drvdata(spimem);
if (attr == &dev_attr_manufacturer.attr && !nor->manufacturer)
return 0;
if (attr == &dev_attr_jedec_id.attr && !nor->info->id_len && !nor->id)
return 0;
return 0444;
}
static umode_t spi_nor_sysfs_is_bin_visible(struct kobject *kobj, static umode_t spi_nor_sysfs_is_bin_visible(struct kobject *kobj,
struct bin_attribute *attr, int n) struct bin_attribute *attr, int n)
{ {
@ -82,6 +99,7 @@ static umode_t spi_nor_sysfs_is_bin_visible(struct kobject *kobj,
static const struct attribute_group spi_nor_sysfs_group = { static const struct attribute_group spi_nor_sysfs_group = {
.name = "spi-nor", .name = "spi-nor",
.is_visible = spi_nor_sysfs_is_visible,
.is_bin_visible = spi_nor_sysfs_is_bin_visible, .is_bin_visible = spi_nor_sysfs_is_bin_visible,
.attrs = spi_nor_sysfs_entries, .attrs = spi_nor_sysfs_entries,
.bin_attrs = spi_nor_sysfs_bin_entries, .bin_attrs = spi_nor_sysfs_bin_entries,

View File

@ -133,6 +133,9 @@ static const struct flash_info winbond_nor_parts[] = {
{ "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024) { "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ | NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ |
SPI_NOR_DUAL_READ) }, SPI_NOR_DUAL_READ) },
{ "w25q512nwq", INFO(0xef6020, 0, 0, 0)
PARSE_SFDP
OTP_INFO(256, 3, 0x1000, 0x1000) },
{ "w25q512nwm", INFO(0xef8020, 0, 64 * 1024, 1024) { "w25q512nwm", INFO(0xef8020, 0, 64 * 1024, 1024)
PARSE_SFDP PARSE_SFDP
OTP_INFO(256, 3, 0x1000, 0x1000) }, OTP_INFO(256, 3, 0x1000, 0x1000) },

View File

@ -999,7 +999,6 @@ static inline bool nanddev_io_iter_end(struct nand_device *nand,
bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos); bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos);
bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos); bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos);
int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos);
int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos); int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos);
/* ECC related functions */ /* ECC related functions */

View File

@ -349,6 +349,8 @@ struct spi_nor_flash_parameter;
* @bouncebuf: bounce buffer used when the buffer passed by the MTD * @bouncebuf: bounce buffer used when the buffer passed by the MTD
* layer is not DMA-able * layer is not DMA-able
* @bouncebuf_size: size of the bounce buffer * @bouncebuf_size: size of the bounce buffer
* @id: The flash's ID bytes. Always contains
* SPI_NOR_MAX_ID_LEN bytes.
* @info: SPI NOR part JEDEC MFR ID and other info * @info: SPI NOR part JEDEC MFR ID and other info
* @manufacturer: SPI NOR manufacturer * @manufacturer: SPI NOR manufacturer
* @addr_nbytes: number of address bytes * @addr_nbytes: number of address bytes
@ -379,6 +381,7 @@ struct spi_nor {
struct spi_mem *spimem; struct spi_mem *spimem;
u8 *bouncebuf; u8 *bouncebuf;
size_t bouncebuf_size; size_t bouncebuf_size;
u8 *id;
const struct flash_info *info; const struct flash_info *info;
const struct spi_nor_manufacturer *manufacturer; const struct spi_nor_manufacturer *manufacturer;
u8 addr_nbytes; u8 addr_nbytes;