Commit 1e4fa020 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents a594533d a34506e0
......@@ -5,6 +5,9 @@ Contact: linux-mtd@lists.infradead.org
Description: (RO) The JEDEC ID of the SPI NOR flash as reported by the
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
Date: April 2021
......@@ -12,6 +15,9 @@ KernelVersion: 5.14
Contact: linux-mtd@lists.infradead.org
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
Date: April 2021
......
......@@ -14,9 +14,6 @@ maintainers:
- Maxime Ripard <mripard@kernel.org>
properties:
"#address-cells": true
"#size-cells": true
compatible:
enum:
- allwinner,sun4i-a10-nand
......@@ -49,12 +46,8 @@ properties:
dma-names:
const: rxtx
pinctrl-names: true
patternProperties:
"^pinctrl-[0-9]+$": true
"^nand@[a-f0-9]+$":
"^nand@[a-f0-9]$":
type: object
properties:
reg:
......@@ -91,6 +84,29 @@ required:
- clocks
- 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>;
};
...
......@@ -35,9 +35,6 @@ properties:
interrupts:
maxItems: 1
"#address-cells": true
"#size-cells": true
required:
- compatible
- reg
......@@ -45,7 +42,7 @@ required:
- clock-names
- interrupts
additionalProperties: true
unevaluatedProperties: true
examples:
- |
......
......@@ -34,20 +34,20 @@ unevaluatedProperties: false
examples:
- |
smcc: memory-controller@e000e000 {
compatible = "arm,pl353-smc-r2p1", "arm,primecell";
reg = <0xe000e000 0x0001000>;
clock-names = "memclk", "apb_pclk";
clocks = <&clkc 11>, <&clkc 44>;
ranges = <0x0 0x0 0xe1000000 0x1000000 /* Nand CS region */
0x1 0x0 0xe2000000 0x2000000 /* SRAM/NOR CS0 region */
0x2 0x0 0xe4000000 0x2000000>; /* SRAM/NOR CS1 region */
#address-cells = <2>;
#size-cells = <1>;
nfc0: nand-controller@0,0 {
compatible = "arm,pl353-nand-r2p1";
reg = <0 0 0x1000000>;
#address-cells = <1>;
#size-cells = <0>;
};
compatible = "arm,pl353-smc-r2p1", "arm,primecell";
reg = <0xe000e000 0x0001000>;
clock-names = "memclk", "apb_pclk";
clocks = <&clkc 11>, <&clkc 44>;
ranges = <0x0 0x0 0xe1000000 0x1000000 /* Nand CS region */
0x1 0x0 0xe2000000 0x2000000 /* SRAM/NOR CS0 region */
0x2 0x0 0xe4000000 0x2000000>; /* SRAM/NOR CS1 region */
#address-cells = <2>;
#size-cells = <1>;
nfc0: nand-controller@0,0 {
compatible = "arm,pl353-nand-r2p1";
reg = <0 0 0x1000000>;
#address-cells = <1>;
#size-cells = <0>;
};
};
......@@ -45,10 +45,8 @@ Optional properties:
- atmel,rb: an integer identifying the native Ready/Busy pin. Only meaningful
on sama5 SoCs.
All generic properties described in
Documentation/devicetree/bindings/mtd/{common,nand}.txt also apply to the NAND
device node, and NAND partitions should be defined under the NAND node as
described in Documentation/devicetree/bindings/mtd/partition.txt.
All generic properties are described in the generic yaml files under
Documentation/devicetree/bindings/mtd/.
* ECC engine (PMECC) bindings:
......
......@@ -86,15 +86,15 @@ properties:
minItems: 1
items:
- description: NAND CTLRDY interrupt
- description: FLASH_DMA_DONE if flash DMA is available
- description: FLASH_EDU_DONE if EDU is available
- description: FLASH_DMA_DONE (if flash DMA is available) or FLASH_EDU_DONE (if EDU is available)
interrupt-names:
minItems: 1
items:
- const: nand_ctlrdy
- const: flash_dma_done
- const: flash_edu_done
- enum:
- flash_dma_done
- flash_edu_done
clocks:
maxItems: 1
......@@ -173,6 +173,13 @@ allOf:
- const: nand
- const: iproc-idm
- const: iproc-ext
- if:
properties:
interrupts:
minItems: 2
then:
required:
- interrupt-names
unevaluatedProperties: false
......@@ -184,51 +191,52 @@ required:
examples:
- |
nand-controller@f0442800 {
compatible = "brcm,brcmnand-v7.0", "brcm,brcmnand";
reg = <0xf0442800 0x600>,
<0xf0443000 0x100>;
reg-names = "nand", "flash-dma";
interrupt-parent = <&hif_intr2_intc>;
interrupts = <24>, <4>;
compatible = "brcm,brcmnand-v7.0", "brcm,brcmnand";
reg = <0xf0442800 0x600>,
<0xf0443000 0x100>;
reg-names = "nand", "flash-dma";
interrupt-parent = <&hif_intr2_intc>;
interrupts = <24>, <4>;
interrupt-names = "nand_ctlrdy", "flash_dma_done";
#address-cells = <1>;
#size-cells = <0>;
nand@1 {
compatible = "brcm,nandcs";
reg = <1>; // Chip select 1
nand-on-flash-bbt;
nand-ecc-strength = <12>;
nand-ecc-step-size = <512>;
#address-cells = <1>;
#size-cells = <0>;
nand@1 {
compatible = "brcm,nandcs";
reg = <1>; // Chip select 1
nand-on-flash-bbt;
nand-ecc-strength = <12>;
nand-ecc-step-size = <512>;
#address-cells = <1>;
#size-cells = <1>;
};
#size-cells = <1>;
};
};
- |
nand-controller@10000200 {
compatible = "brcm,nand-bcm63168", "brcm,nand-bcm6368",
"brcm,brcmnand-v4.0", "brcm,brcmnand";
reg = <0x10000200 0x180>,
<0x100000b0 0x10>,
<0x10000600 0x200>;
reg-names = "nand", "nand-int-base", "nand-cache";
interrupt-parent = <&periph_intc>;
interrupts = <50>;
clocks = <&periph_clk 20>;
clock-names = "nand";
compatible = "brcm,nand-bcm63168", "brcm,nand-bcm6368",
"brcm,brcmnand-v4.0", "brcm,brcmnand";
reg = <0x10000200 0x180>,
<0x100000b0 0x10>,
<0x10000600 0x200>;
reg-names = "nand", "nand-int-base", "nand-cache";
interrupt-parent = <&periph_intc>;
interrupts = <50>;
clocks = <&periph_clk 20>;
clock-names = "nand";
#address-cells = <1>;
#size-cells = <0>;
nand@0 {
compatible = "brcm,nandcs";
reg = <0>;
nand-on-flash-bbt;
nand-ecc-strength = <1>;
nand-ecc-step-size = <512>;
#address-cells = <1>;
#size-cells = <0>;
nand@0 {
compatible = "brcm,nandcs";
reg = <0>;
nand-on-flash-bbt;
nand-ecc-strength = <1>;
nand-ecc-step-size = <512>;
#address-cells = <1>;
#size-cells = <1>;
};
#size-cells = <1>;
};
};
......@@ -145,6 +145,6 @@ examples:
#size-cells = <0>;
nand@0 {
reg = <0>;
reg = <0>;
};
};
......@@ -32,9 +32,9 @@ properties:
partitions:
type: object
deprecated: true
description:
Node containing description of fixed partitions.
See Documentation/devicetree/bindings/mtd/partition.txt
patternProperties:
"^nand@[a-f0-9]$":
......@@ -58,78 +58,78 @@ examples:
- |
#include <dt-bindings/clock/ingenic,jz4780-cgu.h>
memory-controller@13410000 {
compatible = "ingenic,jz4780-nemc";
reg = <0x13410000 0x10000>;
#address-cells = <2>;
#size-cells = <1>;
ranges = <1 0 0x1b000000 0x1000000>,
<2 0 0x1a000000 0x1000000>,
<3 0 0x19000000 0x1000000>,
<4 0 0x18000000 0x1000000>,
<5 0 0x17000000 0x1000000>,
<6 0 0x16000000 0x1000000>;
clocks = <&cgu JZ4780_CLK_NEMC>;
nand-controller@1 {
compatible = "ingenic,jz4780-nand";
reg = <1 0 0x1000000>;
#address-cells = <1>;
#size-cells = <0>;
ecc-engine = <&bch>;
ingenic,nemc-tAS = <10>;
ingenic,nemc-tAH = <5>;
ingenic,nemc-tBP = <10>;
ingenic,nemc-tAW = <15>;
ingenic,nemc-tSTRV = <100>;
pinctrl-names = "default";
pinctrl-0 = <&pins_nemc>;
nand@1 {
reg = <1>;
nand-ecc-step-size = <1024>;
nand-ecc-strength = <24>;
nand-ecc-mode = "hw";
nand-on-flash-bbt;
pinctrl-names = "default";
pinctrl-0 = <&pins_nemc_cs1>;
partitions {
compatible = "fixed-partitions";
#address-cells = <2>;
#size-cells = <2>;
partition@0 {
label = "u-boot-spl";
reg = <0x0 0x0 0x0 0x800000>;
compatible = "ingenic,jz4780-nemc";
reg = <0x13410000 0x10000>;
#address-cells = <2>;
#size-cells = <1>;
ranges = <1 0 0x1b000000 0x1000000>,
<2 0 0x1a000000 0x1000000>,
<3 0 0x19000000 0x1000000>,
<4 0 0x18000000 0x1000000>,
<5 0 0x17000000 0x1000000>,
<6 0 0x16000000 0x1000000>;
clocks = <&cgu JZ4780_CLK_NEMC>;
nand-controller@1 {
compatible = "ingenic,jz4780-nand";
reg = <1 0 0x1000000>;
#address-cells = <1>;
#size-cells = <0>;
ecc-engine = <&bch>;
ingenic,nemc-tAS = <10>;
ingenic,nemc-tAH = <5>;
ingenic,nemc-tBP = <10>;
ingenic,nemc-tAW = <15>;
ingenic,nemc-tSTRV = <100>;
pinctrl-names = "default";
pinctrl-0 = <&pins_nemc>;
nand@1 {
reg = <1>;
nand-ecc-step-size = <1024>;
nand-ecc-strength = <24>;
nand-ecc-mode = "hw";
nand-on-flash-bbt;
pinctrl-names = "default";
pinctrl-0 = <&pins_nemc_cs1>;
partitions {
compatible = "fixed-partitions";
#address-cells = <2>;
#size-cells = <2>;
partition@0 {
label = "u-boot-spl";
reg = <0x0 0x0 0x0 0x800000>;
};
partition@800000 {
label = "u-boot";
reg = <0x0 0x800000 0x0 0x200000>;
};
partition@a00000 {
label = "u-boot-env";
reg = <0x0 0xa00000 0x0 0x200000>;
};
partition@c00000 {
label = "boot";
reg = <0x0 0xc00000 0x0 0x4000000>;
};
partition@4c00000 {
label = "system";
reg = <0x0 0x4c00000 0x1 0xfb400000>;
};
};
};
partition@800000 {
label = "u-boot";
reg = <0x0 0x800000 0x0 0x200000>;
};
partition@a00000 {
label = "u-boot-env";
reg = <0x0 0xa00000 0x0 0x200000>;
};
partition@c00000 {
label = "boot";
reg = <0x0 0xc00000 0x0 0x4000000>;
};
partition@4c00000 {
label = "system";
reg = <0x0 0x4c00000 0x1 0xfb400000>;
};
};
};
};
};
......@@ -39,14 +39,8 @@ properties:
- const: tx
- const: rx
"#address-cells":
const: 1
"#size-cells":
const: 0
patternProperties:
"^nand@[a-f0-9]+$":
"^nand@[a-f0-9]$":
type: object
properties:
reg:
......@@ -67,33 +61,31 @@ required:
- clocks
- dmas
- dma-names
- "#address-cells"
- "#size-cells"
additionalProperties: false
unevaluatedProperties: false
examples:
- |
nand-controller@e0f00000 {
compatible = "intel,lgm-ebunand";
reg = <0xe0f00000 0x100>,
<0xe1000000 0x300>,
<0xe1400000 0x8000>,
<0xe1c00000 0x1000>,
<0x17400000 0x4>,
<0x17c00000 0x4>;
reg-names = "ebunand", "hsnand", "nand_cs0", "nand_cs1",
"addr_sel0", "addr_sel1";
clocks = <&cgu0 125>;
dmas = <&dma0 8>, <&dma0 9>;
dma-names = "tx", "rx";
#address-cells = <1>;
#size-cells = <0>;
nand@0 {
reg = <0>;
nand-ecc-mode = "hw";
};
compatible = "intel,lgm-ebunand";
reg = <0xe0f00000 0x100>,
<0xe1000000 0x300>,
<0xe1400000 0x8000>,
<0xe1c00000 0x1000>,
<0x17400000 0x4>,
<0x17c00000 0x4>;
reg-names = "ebunand", "hsnand", "nand_cs0", "nand_cs1",
"addr_sel0", "addr_sel1";
clocks = <&cgu0 125>;
dmas = <&dma0 8>, <&dma0 9>;
dma-names = "tx", "rx";
#address-cells = <1>;
#size-cells = <0>;
nand@0 {
reg = <0>;
nand-ecc-mode = "hw";
};
};
...
......@@ -70,24 +70,17 @@ properties:
be used on such systems, to denote the absence of a reliable reset
mechanism.
partitions:
type: object
'#address-cells': true
'#size-cells': true
patternProperties:
# Note: use 'partitions' node for new users
'^partition@':
type: object
"^otp(-[0-9]+)?$":
type: object
reset-gpios:
description:
A GPIO line connected to the RESET (active low) signal of the device.
If "broken-flash-reset" is present then having this property does not
make any difference.
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
......@@ -97,6 +90,7 @@ examples:
reg = <0>;
spi-max-frequency = <40000000>;
m25p,fast-read;
reset-gpios = <&gpio 12 GPIO_ACTIVE_LOW>;
};
};
...
......@@ -19,7 +19,7 @@ accuracy:)
- nxp,wr_low: WR_LOW
Optional subnodes:
- Partitions, see Documentation/devicetree/bindings/mtd/partition.txt
- Partitions, see Documentation/devicetree/bindings/mtd/mtd.yaml
Example:
......
......@@ -20,7 +20,7 @@ clock speed:)
- nxp,rsetup: Read setup time (R_SETUP)
Optional subnodes:
- Partitions, see Documentation/devicetree/bindings/mtd/partition.txt
- Partitions, see Documentation/devicetree/bindings/mtd/mtd.yaml
Example:
......
......@@ -34,13 +34,13 @@ unevaluatedProperties: false
examples:
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
eeram@0 {
compatible = "microchip,48l640";
reg = <0>;
spi-max-frequency = <20000000>;
};
#address-cells = <1>;
#size-cells = <0>;
eeram@0 {
compatible = "microchip,48l640";
reg = <0>;
spi-max-frequency = <20000000>;
};
};
...
......@@ -13,6 +13,9 @@ description: |
Flash chips (Memory Technology Devices) are often used for solid state
file systems on embedded devices.
allOf:
- $ref: "mtd.yaml#"
properties:
compatible:
oneOf:
......@@ -121,10 +124,6 @@ properties:
big-endian: true
little-endian: true
patternProperties:
'@[0-9a-f]+$':
$ref: partitions/partition.yaml
required:
- compatible
- reg
......
......@@ -12,7 +12,7 @@ maintainers:
properties:
$nodename:
pattern: "^flash(@.*)?$"
pattern: "^(flash|.*sram)(@.*)?$"
label:
description:
......@@ -21,9 +21,28 @@ properties:
based name) in order to ease flash device identification and/or
describe what they are used for.
'#address-cells':
deprecated: true
'#size-cells':
deprecated: true
partitions:
$ref: /schemas/mtd/partitions/partitions.yaml
required:
- compatible
patternProperties:
"@[0-9a-f]+$":
$ref: partitions/partition.yaml
deprecated: true
"^partition@[0-9a-f]+":
$ref: partitions/partition.yaml
deprecated: true
"^otp(-[0-9]+)?$":
type: object
$ref: ../nvmem/nvmem.yaml#
description: |
......@@ -40,6 +59,7 @@ patternProperties:
required:
- compatible
# This is a generic file other binding inherit from
additionalProperties: true
examples:
......
......@@ -131,7 +131,7 @@ Example:
};
NAND chip optional subnodes:
- Partitions, see Documentation/devicetree/bindings/mtd/partition.txt
- Partitions, see Documentation/devicetree/bindings/mtd/mtd.yaml
Example:
nand@0 {
......
......@@ -9,6 +9,9 @@ title: NAND Chip and NAND Controller Generic Binding
maintainers:
- Miquel Raynal <miquel.raynal@bootlin.com>
allOf:
- $ref: "mtd.yaml#"
description: |
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
......@@ -67,4 +70,5 @@ properties:
required:
- reg
# This file can be referenced by more specific devices (like spi-nands)
additionalProperties: true
......@@ -51,7 +51,6 @@ properties:
patternProperties:
"^nand@[a-f0-9]$":
type: object
$ref: "nand-chip.yaml#"
properties:
......@@ -130,6 +129,7 @@ required:
- "#address-cells"
- "#size-cells"
# This is a generic file other binding inherit from and extend
additionalProperties: true
examples:
......
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.
......@@ -9,6 +9,8 @@ title: ARM Firmware Suite (AFS) Partitions
maintainers:
- Linus Walleij <linus.walleij@linaro.org>
select: false
description: |
The ARM Firmware Suite is a flash partitioning system found on the
ARM reference designs: Integrator AP, Integrator CP, Versatile AB,
......
......@@ -17,6 +17,8 @@ description: |
maintainers:
- Rafał Miłecki <rafal@milecki.pl>
select: false
properties:
compatible:
const: brcm,bcm4908-partitions
......
......@@ -35,6 +35,8 @@ description: |
maintainers:
- Rafał Miłecki <rafal@milecki.pl>
select: false
properties:
compatible:
const: brcm,bcm947xx-cfe-partitions
......
......@@ -31,24 +31,17 @@ properties:
patternProperties:
"@[0-9a-f]+$":
allOf:
- $ref: "partition.yaml#"
- if:
properties:
compatible:
contains:
const: sercomm,sc-partitions
then:
properties:
sercomm,scpart-id:
description: Partition id in Sercomm partition map. Mtd
parser uses this id to find a record in the partition map
containing offset and size of the current partition. The
values from partition map overrides partition offset and
size defined in reg property of the dts. Frequently these
values are the same, but may differ if device has bad
eraseblocks on a flash.
$ref: /schemas/types.yaml#/definitions/uint32
$ref: partition.yaml#
properties:
sercomm,scpart-id:
description: Partition id in Sercomm partition map. Mtd parser
uses this id to find a record in the partition map containing
offset and size of the current partition. The values from
partition map overrides partition offset and size defined in
reg property of the dts. Frequently these values are the same,
but may differ if device has bad eraseblocks on a flash.
$ref: /schemas/types.yaml#/definitions/uint32
required:
- "#address-cells"
......@@ -84,6 +77,7 @@ examples:
partition@0 {
label = "filesystem";
reg = <0x00000000 0x1 0x00000000>;
linux,rootfs;
};
};
......
......@@ -18,6 +18,8 @@ description: |
maintainers:
- Rafał Miłecki <rafal@milecki.pl>
select: false
properties:
compatible:
const: linksys,ns-partitions
......
......@@ -17,6 +17,7 @@ maintainers:
- Ansuel Smith <ansuelsmth@gmail.com>
allOf:
- $ref: /schemas/mtd/partitions/partition.yaml#
- $ref: /schemas/nvmem/nvmem.yaml#
properties:
......@@ -26,7 +27,7 @@ properties:
required:
- compatible
additionalProperties: true
unevaluatedProperties: false
examples:
- |
......@@ -84,7 +85,6 @@ examples:
compatible = "nvmem-cells";
label = "calibration";
reg = <0xf00000 0x100000>;
ranges = <0 0xf00000 0x100000>;
#address-cells = <1>;
#size-cells = <1>;
......
......@@ -52,6 +52,10 @@ properties:
immune to paired-pages corruptions
type: boolean
linux,rootfs:
description: Marks partition that contains root filesystem to mount and boot
user space from
if:
not:
required: [ reg ]
......@@ -60,4 +64,5 @@ then:
$nodename:
pattern: '^partition-.*$'
# This is a generic file other binding inherit from and extend
additionalProperties: true
# 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
......@@ -15,13 +15,15 @@ description: |
varies between partition table revisions. V3 supports maximum 16 partitions
and V4 supports 48 partitions.
select: false
properties:
compatible:
const: qcom,smem-part
patternProperties:
"^partition-[0-9a-z]+$":
$ref: partition.yaml#
$ref: nvmem-cells.yaml
required:
- compatible
......@@ -39,22 +41,22 @@ examples:
- |
/* Example declaring dynamic partition */
flash {
partitions {
compatible = "qcom,smem-part";
partition-art {
compatible = "nvmem-cells";
#address-cells = <1>;
#size-cells = <1>;
label = "0:art";
macaddr_art_0: macaddr@0 {
reg = <0x0 0x6>;
};
macaddr_art_6: macaddr@6 {
reg = <0x6 0x6>;
};
partitions {
compatible = "qcom,smem-part";
partition-art {
compatible = "nvmem-cells";
#address-cells = <1>;
#size-cells = <1>;
label = "0:art";
macaddr_art_0: macaddr@0 {
reg = <0x0 0x6>;
};
macaddr_art_6: macaddr@6 {
reg = <0x6 0x6>;
};
};
};
};
};
......@@ -16,6 +16,8 @@ description: The FLASH Image System (FIS) directory is a flash description
maintainers:
- Linus Walleij <linus.walleij@linaro.org>
select: false
properties:
compatible:
const: redboot-fis
......@@ -26,6 +28,10 @@ properties:
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.
'#address-cells': false
'#size-cells': false
required:
- compatible
- fis-index-block
......
# 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;
};
};
......@@ -31,9 +31,6 @@ properties:
- const: core
- const: aon
"#address-cells": true
"#size-cells": true
patternProperties:
"^nand@[a-f0-9]$":
type: object
......@@ -139,85 +136,85 @@ examples:
- |
#include <dt-bindings/clock/qcom,gcc-ipq806x.h>
nand-controller@1ac00000 {
compatible = "qcom,ipq806x-nand";
reg = <0x1ac00000 0x800>;
compatible = "qcom,ipq806x-nand";
reg = <0x1ac00000 0x800>;
clocks = <&gcc EBI2_CLK>,
<&gcc EBI2_AON_CLK>;
clock-names = "core", "aon";
clocks = <&gcc EBI2_CLK>,
<&gcc EBI2_AON_CLK>;
clock-names = "core", "aon";
dmas = <&adm_dma 3>;
dma-names = "rxtx";
qcom,cmd-crci = <15>;
qcom,data-crci = <3>;
dmas = <&adm_dma 3>;
dma-names = "rxtx";
qcom,cmd-crci = <15>;
qcom,data-crci = <3>;
#address-cells = <1>;
#size-cells = <0>;
#address-cells = <1>;
#size-cells = <0>;
nand@0 {
reg = <0>;
nand@0 {
reg = <0>;
nand-ecc-strength = <4>;
nand-bus-width = <8>;
nand-ecc-strength = <4>;
nand-bus-width = <8>;
qcom,boot-partitions = <0x0 0x58a0000>;
qcom,boot-partitions = <0x0 0x58a0000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "boot-nand";
reg = <0 0x58a0000>;
};
partition@0 {
label = "boot-nand";
reg = <0 0x58a0000>;
};
partition@58a0000 {
label = "fs-nand";
reg = <0x58a0000 0x4000000>;
};
partition@58a0000 {
label = "fs-nand";
reg = <0x58a0000 0x4000000>;
};
};
};
};
};
#include <dt-bindings/clock/qcom,gcc-ipq4019.h>
nand-controller@79b0000 {
compatible = "qcom,ipq4019-nand";
reg = <0x79b0000 0x1000>;
clocks = <&gcc GCC_QPIC_CLK>,
<&gcc GCC_QPIC_AHB_CLK>;
clock-names = "core", "aon";
dmas = <&qpicbam 0>,
<&qpicbam 1>,
<&qpicbam 2>;
dma-names = "tx", "rx", "cmd";
#address-cells = <1>;
#size-cells = <0>;
nand@0 {
reg = <0>;
nand-ecc-strength = <4>;
nand-bus-width = <8>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "boot-nand";
reg = <0 0x58a0000>;
};
partition@58a0000 {
label = "fs-nand";
reg = <0x58a0000 0x4000000>;
};
compatible = "qcom,ipq4019-nand";
reg = <0x79b0000 0x1000>;
clocks = <&gcc GCC_QPIC_CLK>,
<&gcc GCC_QPIC_AHB_CLK>;
clock-names = "core", "aon";
dmas = <&qpicbam 0>,
<&qpicbam 1>,
<&qpicbam 2>;
dma-names = "tx", "rx", "cmd";
#address-cells = <1>;
#size-cells = <0>;
nand@0 {
reg = <0>;
nand-ecc-strength = <4>;
nand-bus-width = <8>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "boot-nand";
reg = <0 0x58a0000>;
};
partition@58a0000 {
label = "fs-nand";
reg = <0x58a0000 0x4000000>;
};
};
};
};
};
...
......@@ -19,7 +19,9 @@ properties:
- const: rockchip,rk2928-nfc
- const: rockchip,rv1108-nfc
- items:
- const: rockchip,rk3036-nfc
- enum:
- rockchip,rk3036-nfc
- rockchip,rk3128-nfc
- const: rockchip,rk2928-nfc
- items:
- const: rockchip,rk3308-nfc
......
......@@ -101,31 +101,32 @@ examples:
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/stm32mp1-clks.h>
#include <dt-bindings/reset/stm32mp1-resets.h>
nand-controller@58002000 {
compatible = "st,stm32mp15-fmc2";
reg = <0x58002000 0x1000>,
<0x80000000 0x1000>,
<0x88010000 0x1000>,
<0x88020000 0x1000>,
<0x81000000 0x1000>,
<0x89010000 0x1000>,
<0x89020000 0x1000>;
interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&mdma1 20 0x2 0x12000a02 0x0 0x0>,
<&mdma1 20 0x2 0x12000a08 0x0 0x0>,
<&mdma1 21 0x2 0x12000a0a 0x0 0x0>;
dma-names = "tx", "rx", "ecc";
clocks = <&rcc FMC_K>;
resets = <&rcc FMC_R>;
#address-cells = <1>;
#size-cells = <0>;
nand@0 {
reg = <0>;
nand-on-flash-bbt;
compatible = "st,stm32mp15-fmc2";
reg = <0x58002000 0x1000>,
<0x80000000 0x1000>,
<0x88010000 0x1000>,
<0x88020000 0x1000>,
<0x81000000 0x1000>,
<0x89010000 0x1000>,
<0x89020000 0x1000>;
interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&mdma1 20 0x2 0x12000a02 0x0 0x0>,
<&mdma1 20 0x2 0x12000a08 0x0 0x0>,
<&mdma1 21 0x2 0x12000a0a 0x0 0x0>;
dma-names = "tx", "rx", "ecc";
clocks = <&rcc FMC_K>;
resets = <&rcc FMC_R>;
#address-cells = <1>;
#size-cells = <1>;
};
#size-cells = <0>;
nand@0 {
reg = <0>;
nand-on-flash-bbt;
#address-cells = <1>;
#size-cells = <1>;
};
};
...
......@@ -44,26 +44,26 @@ additionalProperties: false
examples:
- |
bus {
#address-cells = <2>;
#size-cells = <2>;
hbmc: memory-controller@47034000 {
compatible = "ti,am654-hbmc";
reg = <0x0 0x47034000 0x0 0x100>,
<0x5 0x00000000 0x1 0x0000000>;
ranges = <0x0 0x0 0x5 0x00000000 0x4000000>, /* CS0 - 64MB */
<0x1 0x0 0x5 0x04000000 0x4000000>; /* CS1 - 64MB */
clocks = <&k3_clks 102 0>;
#address-cells = <2>;
#size-cells = <1>;
power-domains = <&k3_pds 55>;
mux-controls = <&hbmc_mux 0>;
#size-cells = <2>;
flash@0,0 {
compatible = "cypress,hyperflash", "cfi-flash";
reg = <0x0 0x0 0x4000000>;
#address-cells = <1>;
hbmc: memory-controller@47034000 {
compatible = "ti,am654-hbmc";
reg = <0x0 0x47034000 0x0 0x100>,
<0x5 0x00000000 0x1 0x0000000>;
ranges = <0x0 0x0 0x5 0x00000000 0x4000000>, /* CS0 - 64MB */
<0x1 0x0 0x5 0x04000000 0x4000000>; /* CS1 - 64MB */
clocks = <&k3_clks 102 0>;
#address-cells = <2>;
#size-cells = <1>;
power-domains = <&k3_pds 55>;
mux-controls = <&hbmc_mux 0>;
flash@0,0 {
compatible = "cypress,hyperflash", "cfi-flash";
reg = <0x0 0x0 0x4000000>;
#address-cells = <1>;
#size-cells = <1>;
};
};
};
};
......@@ -15,6 +15,9 @@ description:
as child nodes of the GPMC controller.
properties:
$nodename:
pattern: "^onenand@[0-9],[0,9]$"
compatible:
const: ti,omap2-onenand
......
......@@ -13464,7 +13464,7 @@ MESON NAND CONTROLLER DRIVER FOR AMLOGIC SOCS
M: Liang Yang <liang.yang@amlogic.com>
L: linux-mtd@lists.infradead.org
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_*
MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS
......
......@@ -136,14 +136,6 @@ config MTD_PHRAM
doesn't have access to, memory beyond the mem=xxx limit, nvram,
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
tristate "Test driver using RAM"
help
......
......@@ -9,7 +9,6 @@ obj-$(CONFIG_MTD_PHRAM) += phram.o
obj-$(CONFIG_MTD_PMC551) += pmc551.o
obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o
obj-$(CONFIG_MTD_MTDRAM) += mtdram.o
obj-$(CONFIG_MTD_LART) += lart.o
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
obj-$(CONFIG_MTD_MCHP23K256) += mchp23k256.o
......
// 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");
......@@ -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.
* So go through and erase each unit in chain, oldest first. (This
* 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);
......
......@@ -433,6 +433,8 @@ static int lpddr2_nvm_probe(struct platform_device *pdev)
/* lpddr2_nvm address range */
add_range = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!add_range)
return -ENODEV;
/* Populate map_info data structure */
*map = (struct map_info) {
......
......@@ -64,6 +64,7 @@ static int pxa2xx_flash_probe(struct platform_device *pdev)
if (!info->map.virt) {
printk(KERN_WARNING "Failed to ioremap %s\n",
info->map.name);
kfree(info);
return -ENOMEM;
}
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);
if (info->map.cached)
iounmap(info->map.cached);
kfree(info);
return -EIO;
}
info->mtd->dev.parent = &pdev->dev;
......
......@@ -28,6 +28,7 @@
#include <linux/leds.h>
#include <linux/debugfs.h>
#include <linux/nvmem-provider.h>
#include <linux/root_dev.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
......@@ -551,22 +552,22 @@ static void mtd_check_of_node(struct mtd_info *mtd)
struct device_node *partitions, *parent_dn, *mtd_dn = NULL;
const char *pname, *prefix = "partition-";
int plen, mtd_name_len, offset, prefix_len;
struct mtd_info *parent;
bool found = false;
/* Check if MTD already has a device node */
if (dev_of_node(&mtd->dev))
if (mtd_get_of_node(mtd))
return;
/* Check if a partitions node exist */
if (!mtd_is_partition(mtd))
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)
return;
partitions = of_get_child_by_name(parent_dn, "partitions");
if (mtd_is_partition(mtd->parent))
partitions = of_node_get(parent_dn);
else
partitions = of_get_child_by_name(parent_dn, "partitions");
if (!partitions)
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 */
for_each_child_of_node(partitions, mtd_dn) {
offset = 0;
/* Skip partition with no/wrong prefix */
if (!of_node_name_prefix(mtd_dn, "partition-"))
if (!of_node_name_prefix(mtd_dn, prefix))
continue;
/* Label have priority. Check that first */
if (of_property_read_string(mtd_dn, "label", &pname)) {
of_property_read_string(mtd_dn, "name", &pname);
if (!of_property_read_string(mtd_dn, "label", &pname)) {
offset = 0;
} else {
pname = mtd_dn->name;
offset = prefix_len;
}
plen = strlen(pname) - offset;
if (plen == mtd_name_len &&
!strncmp(mtd->name, pname + offset, plen)) {
found = true;
mtd_set_of_node(mtd, mtd_dn);
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);
exit_parent:
of_node_put(parent_dn);
......@@ -723,8 +716,10 @@ int add_mtd_device(struct mtd_info *mtd)
mtd_check_of_node(mtd);
of_node_get(mtd_get_of_node(mtd));
error = device_register(&mtd->dev);
if (error)
if (error) {
put_device(&mtd->dev);
goto fail_added;
}
/* Add the nvmem provider */
error = mtd_nvmem_add(mtd);
......@@ -743,6 +738,17 @@ int add_mtd_device(struct mtd_info *mtd)
not->add(mtd);
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
our caller is still holding us here. So none
of this try_ nonsense, and no bitching about it
......@@ -774,6 +780,7 @@ int del_mtd_device(struct mtd_info *mtd)
{
int ret;
struct mtd_notifier *not;
struct device_node *mtd_of_node;
mutex_lock(&mtd_table_mutex);
......@@ -792,6 +799,7 @@ int del_mtd_device(struct mtd_info *mtd)
mtd->index, mtd->name, mtd->usecount);
ret = -EBUSY;
} else {
mtd_of_node = mtd_get_of_node(mtd);
debugfs_remove_recursive(mtd->dbg.dfs_dir);
/* 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));
idr_remove(&mtd_idr, mtd->index);
of_node_put(mtd_get_of_node(mtd));
of_node_put(mtd_of_node);
module_put(THIS_MODULE);
ret = 0;
......@@ -2483,6 +2491,7 @@ static int __init init_mtd(void)
out_procfs:
if (proc_mtd)
remove_proc_entry("mtd", NULL);
bdi_unregister(mtd_bdi);
bdi_put(mtd_bdi);
err_bdi:
class_unregister(&mtd_class);
......
......@@ -7,6 +7,8 @@
* Author: Richard Purdie <rpurdie@openedhand.com>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/console.h>
......@@ -93,9 +95,9 @@ static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset)
ret = mtd_erase(mtd, &erase);
if (ret) {
printk(KERN_WARNING "mtdoops: erase of region [0x%llx, 0x%llx] on \"%s\" failed\n",
(unsigned long long)erase.addr,
(unsigned long long)erase.len, mtddev);
pr_warn("erase of region [0x%llx, 0x%llx] on \"%s\" failed\n",
(unsigned long long)erase.addr,
(unsigned long long)erase.len, mtddev);
return ret;
}
......@@ -106,29 +108,8 @@ static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset)
return 0;
}
static void mtdoops_inc_counter(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)
static void mtdoops_erase(struct mtdoops_context *cxt)
{
struct mtdoops_context *cxt =
container_of(work, struct mtdoops_context, work_erase);
struct mtd_info *mtd = cxt->mtd;
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) {
badblock:
printk(KERN_WARNING "mtdoops: bad block at %08lx\n",
cxt->nextpage * record_size);
pr_warn("bad block at %08lx\n",
cxt->nextpage * record_size);
i++;
cxt->nextpage = cxt->nextpage + (mtd->erasesize / record_size);
if (cxt->nextpage >= cxt->oops_pages)
cxt->nextpage = 0;
if (i == cxt->oops_pages / (mtd->erasesize / record_size)) {
printk(KERN_ERR "mtdoops: all blocks bad!\n");
pr_err("all blocks bad!\n");
return;
}
}
if (ret < 0) {
printk(KERN_ERR "mtdoops: mtd_block_isbad failed, aborting\n");
pr_err("mtd_block_isbad failed, aborting\n");
return;
}
......@@ -166,21 +147,55 @@ static void mtdoops_workfunc_erase(struct work_struct *work)
ret = mtdoops_erase_block(cxt, cxt->nextpage * record_size);
if (ret >= 0) {
printk(KERN_DEBUG "mtdoops: ready %d, %d\n",
cxt->nextpage, cxt->nextcount);
pr_debug("ready %d, %d\n",
cxt->nextpage, cxt->nextcount);
return;
}
if (ret == -EIO) {
ret = mtd_block_markbad(mtd, cxt->nextpage * record_size);
if (ret < 0 && ret != -EOPNOTSUPP) {
printk(KERN_ERR "mtdoops: block_markbad failed, aborting\n");
pr_err("block_markbad failed, aborting\n");
return;
}
}
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)
{
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,
record_size, &retlen, cxt->oops_buf);
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;
}
} else
......@@ -209,12 +224,12 @@ static void mtdoops_write(struct mtdoops_context *cxt, int panic)
record_size, &retlen, cxt->oops_buf);
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);
mark_page_used(cxt, cxt->nextpage);
memset(cxt->oops_buf, 0xff, record_size);
mtdoops_inc_counter(cxt);
mtdoops_inc_counter(cxt, panic);
out:
clear_bit(0, &cxt->oops_buf_busy);
}
......@@ -244,7 +259,7 @@ static void find_next_position(struct mtdoops_context *cxt)
&retlen, (u_char *)&hdr);
if (retlen != sizeof(hdr) ||
(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);
continue;
}
......@@ -279,7 +294,7 @@ static void find_next_position(struct mtdoops_context *cxt)
cxt->nextcount = maxcount;
}
mtdoops_inc_counter(cxt);
mtdoops_inc_counter(cxt, 0);
}
static void mtdoops_do_dump(struct kmsg_dumper *dumper,
......@@ -324,17 +339,17 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
return;
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);
return;
}
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);
return;
}
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);
return;
}
......@@ -345,7 +360,7 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
DIV_ROUND_UP(mtdoops_pages,
BITS_PER_LONG)));
if (!cxt->oops_page_used) {
printk(KERN_ERR "mtdoops: could not allocate page array\n");
pr_err("could not allocate page array\n");
return;
}
......@@ -353,7 +368,7 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
cxt->dump.dump = mtdoops_do_dump;
err = kmsg_dump_register(&cxt->dump);
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);
cxt->oops_page_used = NULL;
return;
......@@ -362,7 +377,7 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
cxt->mtd = mtd;
cxt->oops_pages = (int)mtd->size / record_size;
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)
......@@ -373,7 +388,7 @@ static void mtdoops_notify_remove(struct mtd_info *mtd)
return;
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;
flush_work(&cxt->work_erase);
......@@ -393,15 +408,15 @@ static int __init mtdoops_init(void)
char *endp;
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;
}
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;
}
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;
}
......
......@@ -126,7 +126,7 @@ EXPORT_SYMBOL_GPL(nanddev_isreserved);
*
* 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)) {
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);
}
EXPORT_SYMBOL_GPL(nanddev_erase);
/**
* nanddev_mtd_erase() - Generic mtd->_erase() implementation for NAND devices
......
......@@ -415,7 +415,7 @@ config MTD_NAND_PLATFORM
config MTD_NAND_CADENCE
tristate "Support Cadence NAND (HPNFC) controller"
depends on (OF || COMPILE_TEST) && HAS_IOMEM
depends on OF && HAS_IOMEM
help
Enable the driver for NAND flash on platforms using a Cadence NAND
controller.
......@@ -430,7 +430,7 @@ config MTD_NAND_ARASAN
config MTD_NAND_INTEL_LGM
tristate "Support for NAND controller on Intel LGM SoC"
depends on OF || COMPILE_TEST
depends on OF
depends on HAS_IOMEM
help
Enables support for NAND Flash chips on Intel's LGM SoC.
......@@ -450,7 +450,7 @@ config MTD_NAND_ROCKCHIP
config MTD_NAND_PL35X
tristate "ARM PL35X NAND controller"
depends on OF || COMPILE_TEST
depends on OF
depends on PL353_SMC
help
Enables support for PrimeCell SMC PL351 and PL353 NAND
......
......@@ -1184,6 +1184,14 @@ static int cadence_nand_hw_init(struct cdns_nand_ctrl *cdns_ctrl)
if (cadence_nand_read_bch_caps(cdns_ctrl))
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.
* 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;
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 */
ioread32_rep(cdns_ctrl->io.virt, buf, len_in_words);
if (data_dma_width == 4)
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) {
int read_bytes = (data_dma_width == 4) ?
len_in_words << 2 : len_in_words << 3;
/* read rest data from slave DMA interface if any */
ioread32_rep(cdns_ctrl->io.virt, cdns_ctrl->buf,
sdma_size / 4 - len_in_words);
if (data_dma_width == 4)
ioread32_rep(cdns_ctrl->io.virt,
cdns_ctrl->buf,
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 */
memcpy(buf + (len_in_words << 2), cdns_ctrl->buf,
len - (len_in_words << 2));
memcpy(buf + read_bytes, cdns_ctrl->buf,
len - read_bytes);
}
return 0;
}
......@@ -1936,16 +1963,35 @@ static int cadence_nand_write_buf(struct cdns_nand_ctrl *cdns_ctrl,
return status;
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);
#ifdef CONFIG_64BIT
else
writesq(cdns_ctrl->io.virt, buf, len_in_words);
#endif
iowrite32_rep(cdns_ctrl->io.virt, buf, len_in_words);
if (sdma_size > len) {
int written_bytes = (data_dma_width == 4) ?
len_in_words << 2 : len_in_words << 3;
/* copy rest of data */
memcpy(cdns_ctrl->buf, buf + (len_in_words << 2),
len - (len_in_words << 2));
memcpy(cdns_ctrl->buf, buf + written_bytes,
len - written_bytes);
/* write all expected by nand controller data */
iowrite32_rep(cdns_ctrl->io.virt, cdns_ctrl->buf,
sdma_size / 4 - len_in_words);
if (data_dma_width == 4)
iowrite32_rep(cdns_ctrl->io.virt,
cdns_ctrl->buf,
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;
......
......@@ -148,11 +148,9 @@ static int gpmi_init(struct gpmi_nand_data *this)
struct resources *r = &this->resources;
int ret;
ret = pm_runtime_get_sync(this->dev);
if (ret < 0) {
pm_runtime_put_noidle(this->dev);
ret = pm_runtime_resume_and_get(this->dev);
if (ret < 0)
return ret;
}
ret = gpmi_reset_block(r->gpmi_regs, false);
if (ret)
......@@ -2504,11 +2502,9 @@ static int gpmi_nfc_exec_op(struct nand_chip *chip,
for (i = 0; i < GPMI_MAX_TRANSFERS; i++)
this->transfers[i].direction = DMA_NONE;
ret = pm_runtime_get_sync(this->dev);
if (ret < 0) {
pm_runtime_put_noidle(this->dev);
ret = pm_runtime_resume_and_get(this->dev);
if (ret < 0)
return ret;
}
/*
* This driver currently supports only one NAND chip. Plus, dies share
......
......@@ -25,7 +25,7 @@
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/mtd/lpc32xx_mlc.h>
#include <linux/io.h>
#include <linux/mm.h>
......@@ -122,7 +122,6 @@ struct lpc32xx_nand_cfg_mlc {
uint32_t rd_low;
uint32_t wr_high;
uint32_t wr_low;
int wp_gpio;
struct mtd_partition *parts;
unsigned num_parts;
};
......@@ -177,6 +176,7 @@ struct lpc32xx_nand_host {
struct nand_chip nand_chip;
struct lpc32xx_mlc_platform_data *pdata;
struct clk *clk;
struct gpio_desc *wp_gpio;
void __iomem *io_base;
int irq;
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)
{
if (gpio_is_valid(host->ncfg->wp_gpio))
gpio_set_value(host->ncfg->wp_gpio, 0);
if (host->wp_gpio)
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)
{
if (gpio_is_valid(host->ncfg->wp_gpio))
gpio_set_value(host->ncfg->wp_gpio, 1);
if (host->wp_gpio)
gpiod_set_value_cansleep(host->wp_gpio, 0);
}
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;
}
ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0);
return ncfg;
}
......@@ -713,14 +711,18 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
"Missing or bad NAND config from device tree\n");
return -ENOENT;
}
if (host->ncfg->wp_gpio == -EPROBE_DEFER)
return -EPROBE_DEFER;
if (gpio_is_valid(host->ncfg->wp_gpio) &&
gpio_request(host->ncfg->wp_gpio, "NAND WP")) {
dev_err(&pdev->dev, "GPIO not available\n");
return -EBUSY;
/* Start with WP disabled, if available */
host->wp_gpio = gpiod_get_optional(&pdev->dev, NULL, GPIOD_OUT_LOW);
res = PTR_ERR_OR_ZERO(host->wp_gpio);
if (res) {
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);
......@@ -817,7 +819,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
clk_put(host->clk);
free_gpio:
lpc32xx_wp_enable(host);
gpio_free(host->ncfg->wp_gpio);
gpiod_put(host->wp_gpio);
return res;
}
......@@ -843,12 +845,11 @@ static int lpc32xx_nand_remove(struct platform_device *pdev)
clk_put(host->clk);
lpc32xx_wp_enable(host);
gpio_free(host->ncfg->wp_gpio);
gpiod_put(host->wp_gpio);
return 0;
}
#ifdef CONFIG_PM
static int lpc32xx_nand_resume(struct platform_device *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;
}
#else
#define lpc32xx_nand_resume NULL
#define lpc32xx_nand_suspend NULL
#endif
static const struct of_device_id lpc32xx_nand_match[] = {
{ .compatible = "nxp,lpc3220-mlc" },
{ /* sentinel */ },
......@@ -894,8 +890,8 @@ MODULE_DEVICE_TABLE(of, lpc32xx_nand_match);
static struct platform_driver lpc32xx_nand_driver = {
.probe = lpc32xx_nand_probe,
.remove = lpc32xx_nand_remove,
.resume = lpc32xx_nand_resume,
.suspend = lpc32xx_nand_suspend,
.resume = pm_ptr(lpc32xx_nand_resume),
.suspend = pm_ptr(lpc32xx_nand_suspend),
.driver = {
.name = DRV_NAME,
.of_match_table = lpc32xx_nand_match,
......
......@@ -23,9 +23,8 @@
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/mtd/lpc32xx_slc.h>
#define LPC32XX_MODNAME "lpc32xx-nand"
......@@ -208,7 +207,6 @@ struct lpc32xx_nand_cfg_slc {
uint32_t rwidth;
uint32_t rhold;
uint32_t rsetup;
int wp_gpio;
struct mtd_partition *parts;
unsigned num_parts;
};
......@@ -217,6 +215,7 @@ struct lpc32xx_nand_host {
struct nand_chip nand_chip;
struct lpc32xx_slc_platform_data *pdata;
struct clk *clk;
struct gpio_desc *wp_gpio;
void __iomem *io_base;
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)
{
if (gpio_is_valid(host->ncfg->wp_gpio))
gpio_set_value(host->ncfg->wp_gpio, 0);
if (host->wp_gpio)
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)
{
if (gpio_is_valid(host->ncfg->wp_gpio))
gpio_set_value(host->ncfg->wp_gpio, 1);
if (host->wp_gpio)
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;
}
ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0);
return ncfg;
}
......@@ -852,14 +849,18 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
"Missing or bad NAND config from device tree\n");
return -ENOENT;
}
if (host->ncfg->wp_gpio == -EPROBE_DEFER)
return -EPROBE_DEFER;
if (gpio_is_valid(host->ncfg->wp_gpio) && devm_gpio_request(&pdev->dev,
host->ncfg->wp_gpio, "NAND WP")) {
dev_err(&pdev->dev, "GPIO not available\n");
return -EBUSY;
/* Start with WP disabled, if available */
host->wp_gpio = gpiod_get_optional(&pdev->dev, NULL, GPIOD_OUT_LOW);
res = PTR_ERR_OR_ZERO(host->wp_gpio);
if (res) {
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);
......@@ -968,7 +969,6 @@ static int lpc32xx_nand_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM
static int lpc32xx_nand_resume(struct platform_device *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;
}
#else
#define lpc32xx_nand_resume NULL
#define lpc32xx_nand_suspend NULL
#endif
static const struct of_device_id lpc32xx_nand_match[] = {
{ .compatible = "nxp,lpc3220-slc" },
{ /* sentinel */ },
......@@ -1021,8 +1016,8 @@ MODULE_DEVICE_TABLE(of, lpc32xx_nand_match);
static struct platform_driver lpc32xx_nand_driver = {
.probe = lpc32xx_nand_probe,
.remove = lpc32xx_nand_remove,
.resume = lpc32xx_nand_resume,
.suspend = lpc32xx_nand_suspend,
.resume = pm_ptr(lpc32xx_nand_resume),
.suspend = pm_ptr(lpc32xx_nand_suspend),
.driver = {
.name = LPC32XX_MODNAME,
.of_match_table = lpc32xx_nand_match,
......
......@@ -114,6 +114,7 @@
#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_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_ND_GATE BIT(2)
#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_ECC_CLK_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,
GENCONF_CLK_GATING_CTRL_ND_GATE,
......
......@@ -663,7 +663,7 @@ static int mpc5121_nfc_probe(struct platform_device *op)
}
prv->irq = irq_of_parse_and_map(dn, 0);
if (prv->irq == NO_IRQ) {
if (!prv->irq) {
dev_err(dev, "Error mapping IRQ!\n");
return -EINVAL;
}
......
......@@ -74,9 +74,75 @@ static int w25m02gv_select_target(struct spinand_device *spinand,
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[] = {
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_ECCREQ(1, 512),
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_SELECT_TARGET(w25m02gv_select_target)),
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_ECCREQ(1, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
......@@ -94,6 +160,15 @@ static const struct spinand_info winbond_spinand_table[] = {
&update_cache_variants),
0,
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)
......
......@@ -22,7 +22,7 @@ config MTD_BCM63XX_PARTS
config MTD_BRCM_U_BOOT
tristate "Broadcom's U-Boot partition parser"
depends on ARCH_BCM4908 || COMPILE_TEST
depends on ARCH_BCMBCA || COMPILE_TEST
help
Broadcom uses a custom way of storing U-Boot environment variables.
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
which derives the partition map from the children of the
flash memory node, as described in
Documentation/devicetree/bindings/mtd/partition.txt.
Documentation/devicetree/bindings/mtd/mtd.yaml.
config MTD_OF_PARTS_BCM4908
bool "BCM4908 partitioning support"
......@@ -123,6 +123,21 @@ config MTD_AFS_PARTS
for your particular device. It won't happen automatically. The
'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
tristate "Parser for TRX format partitions"
depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || RALINK || COMPILE_TEST)
......
......@@ -10,6 +10,7 @@ ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) += ofpart_bcm4908.o
ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)+= ofpart_linksys_ns.o
obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.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_SERCOMM_PARTS) += scpart.o
obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o
......
// 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");
......@@ -1184,6 +1184,8 @@ spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map,
continue;
erase = &map->erase_type[i];
if (!erase->size)
continue;
/* Alignment is not mandatory for overlaid regions */
if (region->offset & SNOR_OVERLAID_REGION &&
......@@ -1632,6 +1634,16 @@ static const struct spi_nor_manufacturer *manufacturers[] = {
&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,
const u8 *id)
{
......@@ -1664,7 +1676,20 @@ static const struct flash_info *spi_nor_detect(struct spi_nor *nor)
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);
/* 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) {
dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n",
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);
/* 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))
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
* @map: the erase map of the SPI NOR
* @wanted_size: the erase type size to search for. Contains the value of
* info->sector_size or of the "small sector" size in case
* CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is defined.
* info->sector_size, the "small sector" size in case
* 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
* other.
......@@ -2113,6 +2143,10 @@ spi_nor_select_uniform_erase(struct spi_nor_erase_map *map,
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:
* 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;
spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
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)
{
int ret;
/* restore the addressing mode */
if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
nor->flags & SNOR_F_BROKEN_RESET)
nor->params->set_4byte_addr_mode(nor, false);
nor->flags & SNOR_F_BROKEN_RESET) {
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)
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;
}
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,
const struct spi_nor_hwcaps *hwcaps)
{
......@@ -2967,6 +3038,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
if (!nor->bouncebuf)
return -ENOMEM;
ret = spi_nor_hw_reset(nor);
if (ret)
return ret;
info = spi_nor_get_flash_info(nor, name);
if (IS_ERR(info))
return PTR_ERR(info);
......
......@@ -458,6 +458,7 @@ struct spi_nor_fixups {
* SPI_NOR_NO_ERASE: no erase command needed.
* NO_CHIP_ERASE: chip does not support chip erase.
* SPI_NOR_NO_FR: can't do fastread.
* SPI_NOR_QUAD_PP: flash supports Quad Input Page Program.
*
* @no_sfdp_flags: flags that indicate support that can be discovered via SFDP.
* 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 NO_CHIP_ERASE BIT(7)
#define SPI_NOR_NO_FR BIT(8)
#define SPI_NOR_QUAD_PP BIT(9)
u8 no_sfdp_flags;
#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,
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)
{
return container_of(mtd, struct spi_nor, mtd);
......
......@@ -81,7 +81,7 @@ static int spi_nor_params_show(struct seq_file *s, void *data)
int i;
seq_printf(s, "name\t\t%s\n", info->name);
seq_printf(s, "id\t\t%*ph\n", info->id_len, info->id);
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));
seq_printf(s, "size\t\t%s\n", buf);
seq_printf(s, "write size\t%u\n", params->writesize);
......
......@@ -8,19 +8,29 @@
#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
* bit to set QE on different memories, so the MFR can't
* indicate the quad_enable method for this case, we need
* to set it in the default_init fixup hook.
* GD25Q256C supports the first version of JESD216 which does not define
* the Quad Enable methods. Overwrite the default Quad Enable method.
*
* 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
*/
nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
if (bfpt_header->major == SFDP_JESD216_MAJOR &&
bfpt_header->minor == SFDP_JESD216_MINOR)
nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
return 0;
}
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[] = {
......
......@@ -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) },
{ "is25wp128", INFO(0x9d7018, 0, 64 * 1024, 256)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "is25wp256", INFO(0x9d7019, 0, 64 * 1024, 512)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
{ "is25wp256", INFO(0x9d7019, 0, 0, 0)
PARSE_SFDP
FIXUP_FLAGS(SPI_NOR_4B_OPCODES)
FLAGS(SPI_NOR_QUAD_PP)
.fixups = &is25lp256_fixups },
/* PMC */
......
......@@ -52,18 +52,21 @@ static int micron_st_nor_octal_dtr_en(struct spi_nor *nor)
struct spi_mem_op op;
u8 *buf = nor->bouncebuf;
int ret;
u8 addr_mode_nbytes = nor->params->addr_mode_nbytes;
/* Use 20 dummy cycles for memory array reads. */
*buf = 20;
op = (struct spi_mem_op)
MICRON_ST_NOR_WR_ANY_REG_OP(3, SPINOR_REG_MT_CFR1V, 1, buf);
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);
if (ret)
return ret;
buf[0] = SPINOR_MT_OCT_DTR;
op = (struct spi_mem_op)
MICRON_ST_NOR_WR_ANY_REG_OP(3, SPINOR_REG_MT_CFR0V, 1, buf);
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);
if (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[1] = SPINOR_REG_MT_CFR1V_DEF;
op = (struct spi_mem_op)
MICRON_ST_NOR_WR_ANY_REG_OP(4, SPINOR_REG_MT_CFR0V, 2, buf);
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);
if (ret)
return ret;
......@@ -201,6 +205,8 @@ static const struct flash_info st_nor_parts[] = {
MFR_FLAGS(USE_FSR)
},
{ "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)
FIXUP_FLAGS(SPI_NOR_4B_OPCODES)
MFR_FLAGS(USE_FSR)
......
......@@ -135,8 +135,7 @@ struct sfdp_4bait {
/**
* spi_nor_read_raw() - raw read of serial flash memory. read_opcode,
* addr_nbytes and read_dummy members of the struct spi_nor
* should be previously
* set.
* should be previously set.
* @nor: pointer to a 'struct spi_nor'
* @addr: offset in the serial flash memory
* @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);
/* 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],
0, dummy, opcode,
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:
kfree(dwords);
return ret;
......@@ -1249,6 +1255,33 @@ static void spi_nor_post_sfdp_fixups(struct spi_nor *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.
* @nor: pointer to a 'struct spi_nor'
......
......@@ -107,6 +107,4 @@ struct sfdp_parameter_header {
u8 id_msb;
};
int spi_nor_parse_sfdp(struct spi_nor *nor);
#endif /* __LINUX_MTD_SFDP_H */
......@@ -49,11 +49,13 @@ static int cypress_nor_octal_dtr_en(struct spi_nor *nor)
struct spi_mem_op op;
u8 *buf = nor->bouncebuf;
int ret;
u8 addr_mode_nbytes = nor->params->addr_mode_nbytes;
/* Use 24 dummy cycles for memory array reads. */
*buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24;
op = (struct spi_mem_op)
CYPRESS_NOR_WR_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR2V, 1, buf);
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);
if (ret)
......@@ -64,14 +66,16 @@ static int cypress_nor_octal_dtr_en(struct spi_nor *nor)
/* Set the octal and DTR enable bits. */
buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN;
op = (struct spi_mem_op)
CYPRESS_NOR_WR_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR5V, 1, buf);
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);
if (ret)
return ret;
/* 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) {
dev_dbg(nor->dev, "error %d reading JEDEC ID after enabling 8D-8D-8D mode\n", 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[1] = 0;
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);
if (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)
{
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);
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);
}
static void s28hs512t_default_init(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)
static void s28hx_t_post_sfdp_fixup(struct spi_nor *nor)
{
/*
* 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;
}
static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt)
static int s28hx_t_post_bfpt_fixup(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt)
{
return cypress_nor_set_page_size(nor);
}
static const struct spi_nor_fixups s28hs512t_fixups = {
.default_init = s28hs512t_default_init,
.post_sfdp = s28hs512t_post_sfdp_fixup,
.post_bfpt = s28hs512t_post_bfpt_fixup,
static void s28hx_t_late_init(struct spi_nor *nor)
{
nor->params->octal_dtr_enable = cypress_nor_octal_dtr_enable;
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
......@@ -453,10 +459,21 @@ static const struct flash_info spansion_nor_parts[] = {
.fixups = &s25hx_t_fixups },
{ "cy15x104q", INFO6(0x042cc2, 0x7f7f7f, 512 * 1024, 1)
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)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_OCTAL_DTR_READ |
SPI_NOR_OCTAL_DTR_PP)
.fixups = &s28hs512t_fixups,
PARSE_SFDP
.fixups = &s28hx_t_fixups,
},
{ "s28hs01gt", INFO(0x345b1b, 0, 256 * 1024, 512)
PARSE_SFDP
.fixups = &s28hx_t_fixups,
},
};
......
......@@ -35,8 +35,10 @@ static ssize_t jedec_id_show(struct device *dev,
struct spi_device *spi = to_spi_device(dev);
struct spi_mem *spimem = spi_get_drvdata(spi);
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);
......@@ -67,6 +69,21 @@ static struct bin_attribute *spi_nor_sysfs_bin_entries[] = {
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,
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 = {
.name = "spi-nor",
.is_visible = spi_nor_sysfs_is_visible,
.is_bin_visible = spi_nor_sysfs_is_bin_visible,
.attrs = spi_nor_sysfs_entries,
.bin_attrs = spi_nor_sysfs_bin_entries,
......
......@@ -133,6 +133,9 @@ static const struct flash_info winbond_nor_parts[] = {
{ "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ |
SPI_NOR_DUAL_READ) },
{ "w25q512nwq", INFO(0xef6020, 0, 0, 0)
PARSE_SFDP
OTP_INFO(256, 3, 0x1000, 0x1000) },
{ "w25q512nwm", INFO(0xef8020, 0, 64 * 1024, 1024)
PARSE_SFDP
OTP_INFO(256, 3, 0x1000, 0x1000) },
......
......@@ -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_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);
/* ECC related functions */
......
......@@ -349,6 +349,8 @@ struct spi_nor_flash_parameter;
* @bouncebuf: bounce buffer used when the buffer passed by the MTD
* layer is not DMA-able
* @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
* @manufacturer: SPI NOR manufacturer
* @addr_nbytes: number of address bytes
......@@ -379,6 +381,7 @@ struct spi_nor {
struct spi_mem *spimem;
u8 *bouncebuf;
size_t bouncebuf_size;
u8 *id;
const struct flash_info *info;
const struct spi_nor_manufacturer *manufacturer;
u8 addr_nbytes;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment