The ofpart parsing in physmap-core has been prioritized.
 
 There has been a series of conversion to scoped for each OF child
 loops.
 
 * Bindings
 
 The bulk of the changes consists in binding fixes/updates to restrict
 the use of undefined properties, which was almost inefficient in the
 current form because of the nesting of partition nodes and the lack of
 compatible strings sometimes.
 
 These changes are accompanied by YAML conversions and the addition of a
 dma-coherent property in the cdns,hp-nfc driver.
 
 * SPI NAND
 
 The major feature this release is the support for octal DTR
 modes (8D-8D-8D).
 
 Support for Foresee F35SQB002G chips has been added.
 
 Other changes are small misc fixes.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAmmPXI4ACgkQJWrqGEe9
 VoQKJgf/YgI8L684Hq8OejCZYCoChZpTTvMmqmZ0CgRaZ157jcYTY5Od46pUpfEs
 uSuoXW6+cuxdGeeUtyb3u5uJduO8r7UMjYLBqtMRGjvtfN9EDdhqVQWnRfCr9PoR
 MS4moQa14tEakeP/u6stsmGLpzPkgiygcdTCWS4LhNoJ0LgkLjfBusQm6P5uqc+a
 m/XS4Mg8SPj7pGjinu5KsvKWKiez6k4ExXiLM9p0TX/8iacFhGBrzKqv6zMiNyfL
 p5ZS3ZLv9d79sLKOPlycfNO9LOp6O6ufn+zmuHQpTHGdlPegiNCoF1fOB9XZxc4A
 iK9Z7b3ko9ta0HdjzRYkG/eFtTL0dQ==
 =hnfP
 -----END PGP SIGNATURE-----

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

Pull MTD updates from Miquel Raynal:
 "MTD:

   - prioritize ofpart in physmap-core probing

   - conversions to scoped for each OF child loops

  Bindings:

   - The bulk of the changes consists of binding fixes/updates to
     restrict the use of undefined properties, which was mostly
     ineffective in the current form because of the nesting of partition
     nodes and the lack of compatible strings

   - YAML conversions and the addition of a dma-coherent property in the
     cdns,hp-nfc driver

  SPI NAND:

   - support for octal DTR modes (8D-8D-8D)

   - support for Foresee F35SQB002G chips

  And small misc fixes"

* tag 'mtd/for-7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (65 commits)
  mtd: spi-nor: hisi-sfc: fix refcounting bug in hisi_spi_nor_register_all()
  mtd: spinand: fix NULL pointer dereference in spinand_support_vendor_ops()
  mtd: rawnand: pl353: Add message about ECC mode
  mtd: rawnand: pl353: Fix software ECC support
  mtd: spinand: winbond: Remove unneeded semicolon
  dt-bindings: mtd: cdns,hp-nfc: Add dma-coherent property
  mtd: spinand: Disable continuous read during probe
  mtd: spinand: add Foresee F35SQB002G flash support
  mtd: spinand: winbond: W35N octal DTR support
  mtd: spinand: Add octal DTR support
  mtd: spinand: Warn if using SSDR-only vendor commands in a non SSDR mode
  mtd: spinand: Give the bus interface to the configuration helper
  mtd: spinand: Propagate the bus interface across core helpers
  mtd: spinand: Add support for setting a bus interface
  mtd: spinand: Gather all the bus interface steps in one single function
  mtd: spinand: winbond: Configure the IO mode after the dummy cycles
  mtd: spinand: winbond: Rename IO_MODE register macro
  mtd: spinand: winbond: Fix style
  mtd: spinand: winbond: Register W35N vendor specific operation
  mtd: spinand: winbond: Register W25N vendor specific operation
  ...
This commit is contained in:
Linus Torvalds 2026-02-13 15:06:58 -08:00
commit 1b49e36325
60 changed files with 1292 additions and 608 deletions

View file

@ -32,21 +32,13 @@ properties:
patternProperties:
"^partitions(-boot[12]|-gp[14])?$":
$ref: /schemas/mtd/partitions/partitions.yaml
type: object
additionalProperties: true
patternProperties:
"^partition@[0-9a-f]+$":
$ref: /schemas/mtd/partitions/partition.yaml
properties:
reg:
description: Must be multiple of 512 as it's converted
internally from bytes to SECTOR_SIZE (512 bytes)
required:
- reg
unevaluatedProperties: false
properties:
compatible:
contains:
const: fixed-partitions
required:
- compatible

View file

@ -66,7 +66,6 @@ properties:
items:
- const: brcm,nand-iproc
- const: brcm,brcmnand-v6.1
- const: brcm,brcmnand
- description: BCM63168 SoC-specific NAND controller
items:
- const: brcm,nand-bcm63168

View file

@ -40,6 +40,8 @@ properties:
dmas:
maxItems: 1
dma-coherent: true
iommus:
maxItems: 1

View file

@ -1,18 +0,0 @@
* MTD SPI driver for Microchip 23K256 (and similar) serial SRAM
Required properties:
- #address-cells, #size-cells : Must be present if the device has sub-nodes
representing partitions.
- compatible : Must be one of "microchip,mchp23k256" or "microchip,mchp23lcv1024"
- reg : Chip-Select number
- spi-max-frequency : Maximum frequency of the SPI bus the chip can operate at
Example:
spi-sram@0 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "microchip,mchp23k256";
reg = <0>;
spi-max-frequency = <20000000>;
};

View file

@ -0,0 +1,49 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/microchip,mchp23k256.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip 23K256 SPI SRAM
maintainers:
- Richard Weinberger <richard@nod.at>
description:
The Microchip 23K256 is a 256 Kbit (32 Kbyte) serial SRAM with an
SPI interface,supporting clock frequencies up to 20 MHz. It features
a 32-byte page size for writes and supports byte, page, and
sequential access modes.
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
properties:
compatible:
enum:
- microchip,mchp23k256
- microchip,mchp23lcv1024
reg:
maxItems: 1
required:
- reg
- compatible
- spi-max-frequency
unevaluatedProperties: false
examples:
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
sram@0 {
compatible = "microchip,mchp23k256";
reg = <0>;
spi-max-frequency = <20000000>;
};
};
...

View file

@ -30,18 +30,14 @@ properties:
deprecated: true
partitions:
$ref: /schemas/mtd/partitions/partitions.yaml
type: object
required:
- compatible
patternProperties:
"@[0-9a-f]+$":
$ref: partitions/partition.yaml
deprecated: true
"^partition@[0-9a-f]+":
$ref: partitions/partition.yaml
"(^partition)?@[0-9a-f]+$":
$ref: /schemas/mtd/partitions/partition.yaml#/$defs/partition-node
deprecated: true
"^otp(-[0-9]+)?$":

View file

@ -0,0 +1,78 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/mxic,multi-itfc-v009-nand-controller.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Macronix Raw NAND Controller
maintainers:
- Mason Yang <masonccyang@mxic.com.tw>
description:
The Macronix Multi-Interface Raw NAND Controller is a versatile flash
memory controller for embedding in SoCs, capable of interfacing with
various NAND devices. It requires dedicated clock inputs for core, data
transmit, and delayed transmit paths along with register space and an
interrupt line for operation.
allOf:
- $ref: nand-controller.yaml#
properties:
compatible:
const: mxic,multi-itfc-v009-nand-controller
reg:
maxItems: 1
interrupts:
maxItems: 1
"#address-cells":
const: 1
"#size-cells":
const: 0
clocks:
minItems: 3
maxItems: 3
clock-names:
items:
- const: ps
- const: send
- const: send_dly
required:
- compatible
- reg
- interrupts
- "#address-cells"
- "#size-cells"
- clocks
- clock-names
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
nand-controller@43c30000 {
compatible = "mxic,multi-itfc-v009-nand-controller";
reg = <0x43c30000 0x10000>;
#address-cells = <1>;
#size-cells = <0>;
interrupts = <GIC_SPI 0x1d IRQ_TYPE_EDGE_RISING>;
clocks = <&clkwizard 0>, <&clkwizard 1>, <&clkc 15>;
clock-names = "ps", "send", "send_dly";
nand@0 {
reg = <0>;
nand-ecc-mode = "soft";
nand-ecc-algo = "bch";
};
};
...

View file

@ -1,36 +0,0 @@
Macronix Raw NAND Controller Device Tree Bindings
-------------------------------------------------
Required properties:
- compatible: should be "mxic,multi-itfc-v009-nand-controller"
- reg: should contain 1 entry for the registers
- #address-cells: should be set to 1
- #size-cells: should be set to 0
- interrupts: interrupt line connected to this raw NAND controller
- clock-names: should contain "ps", "send" and "send_dly"
- clocks: should contain 3 phandles for the "ps", "send" and
"send_dly" clocks
Children nodes:
- children nodes represent the available NAND chips.
See Documentation/devicetree/bindings/mtd/nand-controller.yaml
for more details on generic bindings.
Example:
nand: nand-controller@43c30000 {
compatible = "mxic,multi-itfc-v009-nand-controller";
reg = <0x43c30000 0x10000>;
#address-cells = <1>;
#size-cells = <0>;
interrupts = <GIC_SPI 0x1d IRQ_TYPE_EDGE_RISING>;
clocks = <&clkwizard 0>, <&clkwizard 1>, <&clkc 15>;
clock-names = "send", "send_dly", "ps";
nand@0 {
reg = <0>;
nand-ecc-mode = "soft";
nand-ecc-algo = "bch";
};
};

View file

@ -9,8 +9,6 @@ title: ARM Firmware Suite (AFS) Partitions
maintainers:
- Linus Walleij <linusw@kernel.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,

View file

@ -1,53 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/partitions/binman.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Binman entries
description: |
This corresponds to a binman 'entry'. It is a single partition which holds
data of a defined type.
Binman uses the type to indicate what data file / type to place in the
partition. There are quite a number of binman-specific entry types, such as
section, fill and files, to be added later.
maintainers:
- Simon Glass <sjg@chromium.org>
allOf:
- $ref: /schemas/mtd/partitions/partition.yaml#
properties:
compatible:
enum:
- u-boot # u-boot.bin from U-Boot project
- tfa-bl31 # bl31.bin or bl31.elf from TF-A project
required:
- compatible
unevaluatedProperties: false
examples:
- |
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@100000 {
compatible = "u-boot";
reg = <0x100000 0xf00000>;
align-size = <0x1000>;
align-end = <0x10000>;
};
partition@200000 {
compatible = "tfa-bl31";
reg = <0x200000 0x100000>;
align = <0x4000>;
};
};

View file

@ -17,8 +17,6 @@ description: |
maintainers:
- Rafał Miłecki <rafal@milecki.pl>
select: false
properties:
compatible:
const: brcm,bcm4908-partitions
@ -31,11 +29,7 @@ properties:
patternProperties:
"^partition@[0-9a-f]+$":
$ref: partition.yaml#
properties:
compatible:
const: brcm,bcm4908-firmware
unevaluatedProperties: false
$ref: partition.yaml#/$defs/partition-node
required:
- "#address-cells"

View file

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

View file

@ -1,45 +0,0 @@
Broadcom BCM963XX ImageTag Partition Container
==============================================
Some Broadcom BCM63XX SoC based devices contain additional, non discoverable
partitions or non standard bootloader partition sizes. For these a mixed layout
needs to be used with an explicit firmware partition.
The BCM963XX ImageTag is a simple firmware header describing the offsets and
sizes of the rootfs and kernel parts contained in the firmware.
Required properties:
- compatible : must be "brcm,bcm963xx-imagetag"
Example:
flash@1e000000 {
compatible = "cfi-flash";
reg = <0x1e000000 0x2000000>;
bank-width = <2>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
cfe@0 {
reg = <0x0 0x10000>;
read-only;
};
firmware@10000 {
reg = <0x10000 0x7d0000>;
compatible = "brcm,bcm963xx-imagetag";
};
caldata@7e0000 {
reg = <0x7e0000 0x10000>;
read-only;
};
nvram@7f0000 {
reg = <0x7f0000 0x10000>;
};
};
};

View file

@ -1,42 +0,0 @@
Broadcom TRX Container Partition
================================
TRX is Broadcom's official firmware format for the BCM947xx boards. It's used by
most of the vendors building devices based on Broadcom's BCM47xx SoCs and is
supported by the CFE bootloader.
Design of the TRX format is very minimalistic. Its header contains
identification fields, CRC32 checksum and the locations of embedded partitions.
Its purpose is to store a few partitions in a format that can be distributed as
a standalone file and written in a flash memory.
Container can hold up to 4 partitions. The first partition has to contain a
device executable binary (e.g. a kernel) as it's what the CFE bootloader starts
executing. Other partitions can be used for operating system purposes. This is
useful for systems that keep kernel and rootfs separated.
TRX doesn't enforce any strict partition boundaries or size limits. All
partitions have to be less than the 4GiB max size limit.
There are two existing/known TRX variants:
1) v1 which contains 3 partitions
2) v2 which contains 4 partitions
There aren't separated compatible bindings for them as version can be trivialy
detected by a software parsing TRX header.
Required properties:
- compatible : (required) must be "brcm,trx"
Optional properties:
- brcm,trx-magic: TRX magic, if it is different from the default magic
0x30524448 as a u32.
Example:
flash@0 {
partitions {
compatible = "brcm,trx";
};
};

View file

@ -0,0 +1,65 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/partitions/brcm,trx.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Broadcom TRX Container Partition
maintainers:
- Hauke Mehrtens <hauke@hauke-m.de>
- Rafał Miłecki <rafal@milecki.pl>
description: >
TRX is Broadcom's official firmware format for the BCM947xx boards. It's used by
most of the vendors building devices based on Broadcom's BCM47xx SoCs and is
supported by the CFE bootloader.
Design of the TRX format is very minimalistic. Its header contains
identification fields, CRC32 checksum and the locations of embedded partitions.
Its purpose is to store a few partitions in a format that can be distributed as
a standalone file and written in a flash memory.
Container can hold up to 4 partitions. The first partition has to contain a
device executable binary (e.g. a kernel) as it's what the CFE bootloader starts
executing. Other partitions can be used for operating system purposes. This is
useful for systems that keep kernel and rootfs separated.
TRX doesn't enforce any strict partition boundaries or size limits. All
partitions have to be less than the 4GiB max size limit.
There are two existing/known TRX variants:
1) v1 which contains 3 partitions
2) v2 which contains 4 partitions
There aren't separated compatible bindings for them as version can be trivially
detected by a software parsing TRX header.
properties:
compatible:
oneOf:
- items:
- const: linksys,ns-firmware
- const: brcm,trx
- const: brcm,trx
brcm,trx-magic:
description: TRX magic, if it is different from the default magic.
$ref: /schemas/types.yaml#/definitions/uint32
default: 0x30524448
required:
- compatible
allOf:
- $ref: partition.yaml#
unevaluatedProperties: false
examples:
- |
flash {
partitions {
compatible = "brcm,trx";
};
};

View file

@ -25,47 +25,25 @@ properties:
- const: sercomm,sc-partitions
- const: fixed-partitions
"#address-cells": true
"#address-cells":
enum: [ 1, 2 ]
"#size-cells": true
compression:
$ref: /schemas/types.yaml#/definitions/string
description: |
Compression algorithm used to store the data in this partition, chosen
from a list of well-known algorithms.
The contents are compressed using this algorithm.
enum:
- none
- bzip2
- gzip
- lzop
- lz4
- lzma
- xz
- zstd
"#size-cells":
enum: [ 1, 2 ]
patternProperties:
"@[0-9a-f]+$":
$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
$ref: partition.yaml#/$defs/partition-node
required:
- "#address-cells"
- "#size-cells"
additionalProperties: true
# fixed-partitions can be nested
allOf:
- $ref: partition.yaml#
unevaluatedProperties: false
examples:
- |
@ -141,7 +119,6 @@ examples:
compatible = "fixed-partitions";
label = "calibration";
reg = <0xf00000 0x100000>;
ranges = <0 0xf00000 0x100000>;
#address-cells = <1>;
#size-cells = <1>;

View file

@ -18,8 +18,6 @@ description: |
maintainers:
- Rafał Miłecki <rafal@milecki.pl>
select: false
properties:
compatible:
const: linksys,ns-partitions
@ -32,13 +30,7 @@ properties:
patternProperties:
"^partition@[0-9a-f]+$":
$ref: partition.yaml#
properties:
compatible:
items:
- const: linksys,ns-firmware
- const: brcm,trx
unevaluatedProperties: false
$ref: partition.yaml#/$defs/partition-node
required:
- "#address-cells"

View file

@ -108,17 +108,59 @@ properties:
with the padding bytes, so may grow. If align-end is not provided,
no alignment is performed.
compression:
$ref: /schemas/types.yaml#/definitions/string
description: |
Compression algorithm used to store the data in this partition, chosen
from a list of well-known algorithms.
The contents are compressed using this algorithm.
enum:
- none
- bzip2
- gzip
- lzop
- lz4
- lzma
- xz
- zstd
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
nvmem-layout:
$ref: /schemas/nvmem/layouts/nvmem-layout.yaml
if:
not:
required: [ reg ]
then:
properties:
$nodename:
pattern: '^partition-.*$'
pattern: '^partitions?(-.+)?$'
# This is a generic file other binding inherit from and extend
additionalProperties: true
$defs:
partition-node:
type: object
if:
not:
required: [ compatible ]
then:
$ref: '#'
unevaluatedProperties: false
else:
$ref: '#'
examples:
- |
partitions {

View file

@ -1,42 +0,0 @@
# 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
- $ref: tplink,safeloader-partitions.yaml
properties:
compatible: true
'#address-cells':
enum: [1, 2]
'#size-cells':
enum: [1, 2]
patternProperties:
"^partition(-.+|@[0-9a-f]+)$":
$ref: partition.yaml
required:
- compatible
unevaluatedProperties: false

View file

@ -28,10 +28,6 @@ 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

View file

@ -1,44 +0,0 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/partitions/seama.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Seattle Image Partitions
description: The SEAttle iMAge (SEAMA) partition is a type of partition
used for NAND flash devices. This type of flash image is found in some
D-Link routers such as DIR-645, DIR-842, DIR-859, DIR-860L, DIR-885L,
DIR890L and DCH-M225, as well as in WD and NEC routers on the ath79
(MIPS), Broadcom BCM53xx, and RAMIPS platforms. This partition type
does not have children defined in the device tree, they need to be
detected by software.
allOf:
- $ref: partition.yaml#
maintainers:
- Linus Walleij <linusw@kernel.org>
properties:
compatible:
const: seama
required:
- compatible
unevaluatedProperties: false
examples:
- |
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
compatible = "seama";
reg = <0x0 0x800000>;
label = "firmware";
};
};

View file

@ -0,0 +1,61 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/partitions/simple-partition.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Simple partition types
description:
Simple partition types which only define a "compatible" value and no custom
properties.
maintainers:
- Rafał Miłecki <rafal@milecki.pl>
- Simon Glass <sjg@chromium.org>
allOf:
- $ref: partition.yaml#
properties:
compatible:
oneOf:
- const: brcm,bcm4908-firmware
description:
Broadcom BCM4908 CFE bootloader firmware partition
- const: brcm,bcm963xx-imagetag
description:
The BCM963XX ImageTag is a simple firmware header describing the
offsets and sizes of the rootfs and kernel parts contained in the
firmware.
- const: seama
description:
The SEAttle iMAge (SEAMA) partition is a type of partition used for
NAND flash devices. This type of flash image is found in some D-Link
routers such as DIR-645, DIR-842, DIR-859, DIR-860L, DIR-885L, DIR890L
and DCH-M225, as well as in WD and NEC routers on the ath79 (MIPS),
Broadcom BCM53xx, and RAMIPS platforms. This partition type does not
have children defined in the device tree, they need to be detected by
software.
- const: u-boot
description: >
u-boot.bin from U-Boot project.
This corresponds to a binman 'entry'. It is a single partition which holds
data of a defined type.
Binman uses the type to indicate what data file / type to place in the
partition. There are quite a number of binman-specific entry types, such as
section, fill and files, to be added later.
- const: tfa-bl31
description: >
bl31.bin or bl31.elf from TF-A project
This corresponds to a binman 'entry'. It is a single partition which holds
data of a defined type.
unevaluatedProperties: false

View file

@ -38,7 +38,7 @@ properties:
patternProperties:
"^partition-.*$":
$ref: partition.yaml#
$ref: partition.yaml#/$defs/partition-node
required:
- partitions-table-offset

View file

@ -29,7 +29,7 @@ properties:
patternProperties:
"^partition-.*$":
$ref: partition.yaml#
$ref: partition.yaml#/$defs/partition-node
unevaluatedProperties: false

View file

@ -1,29 +0,0 @@
* SPEAr SMI
Required properties:
- compatible : "st,spear600-smi"
- reg : Address range of the mtd chip
- #address-cells, #size-cells : Must be present if the device has sub-nodes
representing partitions.
- interrupts: Should contain the STMMAC interrupts
- clock-rate : Functional clock rate of SMI in Hz
Optional properties:
- st,smi-fast-mode : Flash supports read in fast mode
Example:
smi: flash@fc000000 {
compatible = "st,spear600-smi";
#address-cells = <1>;
#size-cells = <1>;
reg = <0xfc000000 0x1000>;
interrupt-parent = <&vic1>;
interrupts = <12>;
clock-rate = <50000000>; /* 50MHz */
flash@f8000000 {
st,smi-fast-mode;
...
};
};

View file

@ -0,0 +1,72 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/st,spear600-smi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: STMicroelectronics SPEAr600 Serial Memory Interface (SMI) Controller
maintainers:
- Richard Weinberger <richard@nod.at>
description:
The SPEAr600 Serial Memory Interface (SMI) is a dedicated serial flash
controller supporting up to four chip selects for serial NOR flashes
connected in parallel. The controller is memory-mapped and the attached
flash devices appear in the CPU address space.The driver
(drivers/mtd/devices/spear_smi.c) probes the attached flashes
dynamically by sending commands (e.g., RDID) to each bank.
Flash sub nodes describe the memory range and optional per-flash
properties.
allOf:
- $ref: mtd.yaml#
properties:
compatible:
const: st,spear600-smi
reg:
maxItems: 1
interrupts:
maxItems: 1
"#address-cells":
const: 1
"#size-cells":
const: 1
clock-rate:
$ref: /schemas/types.yaml#/definitions/uint32
description: Functional clock rate of the SMI controller in Hz.
st,smi-fast-mode:
type: boolean
description: Indicates that the attached flash supports fast read mode.
required:
- compatible
- reg
- clock-rate
unevaluatedProperties: false
examples:
- |
flash@fc000000 {
compatible = "st,spear600-smi";
#address-cells = <1>;
#size-cells = <1>;
reg = <0xfc000000 0x1000>;
interrupt-parent = <&vic1>;
interrupts = <12>;
clock-rate = <50000000>; /* 50 MHz */
flash@f8000000 {
reg = <0xfc000000 0x1000>;
st,smi-fast-mode;
};
};
...

View file

@ -0,0 +1,68 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/st,spi-fsm.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: STMicroelectronics SPI FSM Serial NOR Flash Controller
maintainers:
- Angus Clark <angus.clark@st.com>
description:
The STMicroelectronics Fast Sequence Mode (FSM) controller is a dedicated
hardware accelerator integrated in older STiH4xx/STiDxxx set-top box SoCs
(such as STiH407, STiH416, STiD127). It connects directly to a single
external serial flash device used as the primary boot device. The FSM
executes hard-coded or configurable instruction sequences in hardware,
providing low-latency reads suitable for execute-in-place (XIP) boot
and high read bandwidth.
properties:
compatible:
const: st,spi-fsm
reg:
maxItems: 1
reg-names:
const: spi-fsm
interrupts:
maxItems: 1
st,syscfg:
$ref: /schemas/types.yaml#/definitions/phandle
description: Phandle to the system configuration registers used for boot-device selection.
st,boot-device-reg:
$ref: /schemas/types.yaml#/definitions/uint32
description: Offset of the boot-device register within the st,syscfg node.
st,boot-device-spi:
$ref: /schemas/types.yaml#/definitions/uint32
description: Expected boot-device value when booting from this SPI controller.
required:
- compatible
- reg
- reg-names
- interrupts
- pinctrl-0
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
spifsm@fe902000 {
compatible = "st,spi-fsm";
reg = <0xfe902000 0x1000>;
reg-names = "spi-fsm";
interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-0 = <&pinctrl_fsm>;
st,syscfg = <&syscfg_rear>;
st,boot-device-reg = <0x958>;
st,boot-device-spi = <0x1a>;
};
...

View file

@ -1,25 +0,0 @@
* ST-Microelectronics SPI FSM Serial (NOR) Flash Controller
Required properties:
- compatible : Should be "st,spi-fsm"
- reg : Contains register's location and length.
- reg-names : Should contain the reg names "spi-fsm"
- interrupts : The interrupt number
- pinctrl-0 : Standard Pinctrl phandle (see: pinctrl/pinctrl-bindings.txt)
Optional properties:
- st,syscfg : Phandle to boot-device system configuration registers
- st,boot-device-reg : Address of the aforementioned boot-device register(s)
- st,boot-device-spi : Expected boot-device value if booted via this device
Example:
spifsm: spifsm@fe902000{
compatible = "st,spi-fsm";
reg = <0xfe902000 0x1000>;
reg-names = "spi-fsm";
pinctrl-0 = <&pinctrl_fsm>;
st,syscfg = <&syscfg_rear>;
st,boot-device-reg = <0x958>;
st,boot-device-spi = <0x1a>;
};

View file

@ -24,7 +24,9 @@ properties:
- description: AEMIF control registers.
partitions:
$ref: /schemas/mtd/partitions/partitions.yaml
type: object
required:
- compatible
ti,davinci-chipselect:
description:

View file

@ -36,7 +36,7 @@ properties:
patternProperties:
"@[0-9a-f]+$":
$ref: /schemas/mtd/partitions/partition.yaml
$ref: /schemas/mtd/partitions/partition.yaml#/$defs/partition-node
allOf:
- $ref: /schemas/memory-controllers/ti,gpmc-child.yaml

View file

@ -4445,11 +4445,6 @@ F: Documentation/filesystems/bfs.rst
F: fs/bfs/
F: include/uapi/linux/bfs_fs.h
BINMAN
M: Simon Glass <sjg@chromium.org>
S: Supported
F: Documentation/devicetree/bindings/mtd/partitions/binman*
BITMAP API
M: Yury Norov <yury.norov@gmail.com>
R: Rasmus Villemoes <linux@rasmusvillemoes.dk>

View file

@ -1921,7 +1921,7 @@ static inline u32 jedec_read_mfr(struct map_info *map, uint32_t base,
*/
do {
uint32_t ofs = cfi_build_cmd_addr(0 + (bank << 8), map, cfi);
mask = (1 << (cfi->device_type * 8)) - 1;
mask = (1ULL << (cfi->device_type * 8)) - 1;
if (ofs >= map->size)
return 0;
result = map_read(map, base + ofs);
@ -1937,7 +1937,7 @@ static inline u32 jedec_read_id(struct map_info *map, uint32_t base,
map_word result;
unsigned long mask;
u32 ofs = cfi_build_cmd_addr(1, map, cfi);
mask = (1 << (cfi->device_type * 8)) -1;
mask = (1ULL << (cfi->device_type * 8)) - 1;
result = map_read(map, base + ofs);
return result.x[0] & mask;
}

View file

@ -770,6 +770,7 @@ static int intel_dg_mtd_probe(struct auxiliary_device *aux_dev,
kref_init(&nvm->refcnt);
mutex_init(&nvm->lock);
nvm->nregions = nregions;
for (n = 0, i = 0; i < INTEL_DG_NVM_REGIONS; i++) {
if (!invm->regions[i].name)
@ -777,13 +778,15 @@ static int intel_dg_mtd_probe(struct auxiliary_device *aux_dev,
char *name = kasprintf(GFP_KERNEL, "%s.%s",
dev_name(&aux_dev->dev), invm->regions[i].name);
if (!name)
continue;
if (!name) {
ret = -ENOMEM;
goto err;
}
nvm->regions[n].name = name;
nvm->regions[n].id = i;
n++;
}
nvm->nregions = n; /* in case where kasprintf fail */
ret = devm_pm_runtime_enable(device);
if (ret < 0) {

View file

@ -268,7 +268,7 @@ static const struct of_device_id of_flash_match[] = {
MODULE_DEVICE_TABLE(of, of_flash_match);
static const char * const of_default_part_probes[] = {
"cmdlinepart", "RedBoot", "ofpart", "ofoldpart", NULL
"cmdlinepart", "ofpart", "ofoldpart", "RedBoot", NULL
};
static const char * const *of_get_part_probes(struct platform_device *dev)

View file

@ -2304,10 +2304,8 @@ atmel_hsmc_nand_controller_init(struct atmel_hsmc_nand_controller *nc)
nc->sram.pool = of_gen_pool_get(nc->base.dev->of_node,
"atmel,nfc-sram", 0);
if (!nc->sram.pool) {
dev_err(nc->base.dev, "Missing SRAM\n");
return -ENOMEM;
}
if (!nc->sram.pool)
return dev_err_probe(nc->base.dev, -EPROBE_DEFER, "Missing SRAM\n");
nc->sram.virt = (void __iomem *)gen_pool_dma_alloc(nc->sram.pool,
ATMEL_NFC_SRAM_SIZE,

View file

@ -3298,7 +3298,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
{
struct brcmnand_platform_data *pd = dev_get_platdata(&pdev->dev);
struct device *dev = &pdev->dev;
struct device_node *dn = dev->of_node, *child;
struct device_node *dn = dev->of_node;
struct brcmnand_controller *ctrl;
struct brcmnand_host *host;
struct resource *res;
@ -3486,12 +3486,11 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
}
}
for_each_available_child_of_node(dn, child) {
for_each_available_child_of_node_scoped(dn, child) {
if (of_device_is_compatible(child, "brcm,nandcs")) {
host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
if (!host) {
of_node_put(child);
ret = -ENOMEM;
goto err;
}
@ -3509,10 +3508,9 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
ret = brcmnand_init_cs(host, NULL);
if (ret) {
if (ret == -EPROBE_DEFER) {
of_node_put(child);
if (ret == -EPROBE_DEFER)
goto err;
}
devm_kfree(dev, host);
continue; /* Try all chip-selects */
}

View file

@ -1066,7 +1066,7 @@ static int cadence_nand_cdma_send(struct cdns_nand_ctrl *cdns_ctrl,
}
/* Send SDMA command and wait for finish. */
static u32
static int
cadence_nand_cdma_send_and_wait(struct cdns_nand_ctrl *cdns_ctrl,
u8 thread)
{

View file

@ -115,7 +115,6 @@ static int denali_dt_probe(struct platform_device *pdev)
struct denali_dt *dt;
const struct denali_dt_data *data;
struct denali_controller *denali;
struct device_node *np;
int ret;
dt = devm_kzalloc(dev, sizeof(*dt), GFP_KERNEL);
@ -192,12 +191,10 @@ static int denali_dt_probe(struct platform_device *pdev)
if (ret)
goto out_assert_rst;
for_each_child_of_node(dev->of_node, np) {
for_each_child_of_node_scoped(dev->of_node, np) {
ret = denali_dt_chip_init(denali, np);
if (ret) {
of_node_put(np);
if (ret)
goto out_remove_denali;
}
}
platform_set_drvdata(pdev, dt);

View file

@ -438,7 +438,6 @@ static int ingenic_nand_init_chips(struct ingenic_nfc *nfc,
struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np;
int i = 0;
int ret;
int num_chips = of_get_child_count(dev->of_node);
@ -449,11 +448,10 @@ static int ingenic_nand_init_chips(struct ingenic_nfc *nfc,
return -EINVAL;
}
for_each_child_of_node(dev->of_node, np) {
for_each_child_of_node_scoped(dev->of_node, np) {
ret = ingenic_nand_init_chip(pdev, nfc, np, i);
if (ret) {
ingenic_nand_cleanup_chips(nfc);
of_node_put(np);
return ret;
}

View file

@ -970,14 +970,18 @@ static int pl35x_nand_attach_chip(struct nand_chip *chip)
switch (chip->ecc.engine_type) {
case NAND_ECC_ENGINE_TYPE_ON_DIE:
dev_dbg(nfc->dev, "Using on-die ECC\n");
/* Keep these legacy BBT descriptors for ON_DIE situations */
chip->bbt_td = &bbt_main_descr;
chip->bbt_md = &bbt_mirror_descr;
fallthrough;
case NAND_ECC_ENGINE_TYPE_NONE:
case NAND_ECC_ENGINE_TYPE_SOFT:
dev_dbg(nfc->dev, "Using software ECC (Hamming 1-bit/512B)\n");
chip->ecc.write_page_raw = nand_monolithic_write_page_raw;
break;
case NAND_ECC_ENGINE_TYPE_ON_HOST:
dev_dbg(nfc->dev, "Using hardware ECC\n");
ret = pl35x_nand_init_hw_ecc_controller(nfc, chip);
if (ret)
return ret;

View file

@ -2206,16 +2206,14 @@ err:
static int qcom_probe_nand_devices(struct qcom_nand_controller *nandc)
{
struct device *dev = nandc->dev;
struct device_node *dn = dev->of_node, *child;
struct device_node *dn = dev->of_node;
struct qcom_nand_host *host;
int ret = -ENODEV;
for_each_available_child_of_node(dn, child) {
for_each_available_child_of_node_scoped(dn, child) {
host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
if (!host) {
of_node_put(child);
if (!host)
return -ENOMEM;
}
ret = qcom_nand_host_init_and_register(nandc, host, child);
if (ret) {

View file

@ -29,12 +29,6 @@
#include <linux/iopoll.h>
#include <linux/reset.h>
/* non compile-time field get/prep */
#undef field_get
#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
#undef field_prep
#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
#define NFC_REG_CTL 0x0000
#define NFC_REG_ST 0x0004
#define NFC_REG_INT 0x0008

View file

@ -810,7 +810,6 @@ static int vf610_nfc_probe(struct platform_device *pdev)
struct vf610_nfc *nfc;
struct mtd_info *mtd;
struct nand_chip *chip;
struct device_node *child;
int err;
int irq;
@ -840,17 +839,16 @@ static int vf610_nfc_probe(struct platform_device *pdev)
return PTR_ERR(nfc->clk);
}
nfc->variant = (enum vf610_nfc_variant)device_get_match_data(&pdev->dev);
nfc->variant = (unsigned long)device_get_match_data(&pdev->dev);
if (!nfc->variant)
return -ENODEV;
for_each_available_child_of_node(nfc->dev->of_node, child) {
for_each_available_child_of_node_scoped(nfc->dev->of_node, child) {
if (of_device_is_compatible(child, "fsl,vf610-nfc-nandcs")) {
if (nand_get_flash_node(chip)) {
dev_err(nfc->dev,
"Only one NAND chip supported!\n");
of_node_put(child);
return -EINVAL;
}

View file

@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
spinand-objs := core.o otp.o
spinand-objs += alliancememory.o ato.o esmt.o fmsh.o foresee.o gigadevice.o macronix.o
spinand-objs += micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o
spinand-objs += alliancememory.o ato.o dosilicon.o esmt.o fmsh.o foresee.o gigadevice.o
spinand-objs += macronix.o micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o

View file

@ -20,10 +20,100 @@
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
static struct spi_mem_op
spinand_fill_reset_op(struct spinand_device *spinand)
{
return spinand->op_templates->reset;
}
static struct spi_mem_op
spinand_fill_readid_op(struct spinand_device *spinand,
u8 naddr, u8 ndummy, void *buf, unsigned int len)
{
struct spi_mem_op op = spinand->op_templates->readid;
op.addr.nbytes = naddr;
op.dummy.nbytes = ndummy;
op.data.buf.in = buf;
op.data.nbytes = len;
return op;
}
struct spi_mem_op
spinand_fill_wr_en_op(struct spinand_device *spinand)
{
return spinand->op_templates->wr_en;
}
static __maybe_unused struct spi_mem_op
spinand_fill_wr_dis_op(struct spinand_device *spinand)
{
return spinand->op_templates->wr_dis;
}
struct spi_mem_op
spinand_fill_set_feature_op(struct spinand_device *spinand, u64 reg, const void *valptr)
{
struct spi_mem_op op = spinand->op_templates->set_feature;
if (op.cmd.dtr && op.cmd.buswidth == 8)
reg |= reg << 8;
op.addr.val = reg;
op.data.buf.out = valptr;
return op;
}
struct spi_mem_op
spinand_fill_get_feature_op(struct spinand_device *spinand, u64 reg, void *valptr)
{
struct spi_mem_op op = spinand->op_templates->get_feature;
if (op.cmd.dtr && op.cmd.buswidth == 8)
reg |= reg << 8;
op.addr.val = reg;
op.data.buf.in = valptr;
return op;
}
static struct spi_mem_op
spinand_fill_blk_erase_op(struct spinand_device *spinand, u64 addr)
{
struct spi_mem_op op = spinand->op_templates->blk_erase;
op.addr.val = addr;
return op;
}
static struct spi_mem_op
spinand_fill_page_read_op(struct spinand_device *spinand, u64 addr)
{
struct spi_mem_op op = spinand->op_templates->page_read;
op.addr.val = addr;
return op;
}
struct spi_mem_op
spinand_fill_prog_exec_op(struct spinand_device *spinand, u64 addr)
{
struct spi_mem_op op = spinand->op_templates->prog_exec;
op.addr.val = addr;
return op;
}
int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)
{
struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(reg,
spinand->scratchbuf);
struct spi_mem_op op = SPINAND_OP(spinand, get_feature,
reg, spinand->scratchbuf);
int ret;
ret = spi_mem_exec_op(spinand->spimem, &op);
@ -36,8 +126,8 @@ int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)
int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val)
{
struct spi_mem_op op = SPINAND_SET_FEATURE_1S_1S_1S_OP(reg,
spinand->scratchbuf);
struct spi_mem_op op = SPINAND_OP(spinand, set_feature,
reg, spinand->scratchbuf);
*spinand->scratchbuf = val;
return spi_mem_exec_op(spinand->spimem, &op);
@ -177,18 +267,9 @@ static int spinand_init_cfg_cache(struct spinand_device *spinand)
return 0;
}
static int spinand_init_quad_enable(struct spinand_device *spinand)
static int spinand_init_quad_enable(struct spinand_device *spinand,
bool enable)
{
bool enable = false;
if (!(spinand->flags & SPINAND_HAS_QE_BIT))
return 0;
if (spinand->op_templates.read_cache->data.buswidth == 4 ||
spinand->op_templates.write_cache->data.buswidth == 4 ||
spinand->op_templates.update_cache->data.buswidth == 4)
enable = true;
return spinand_upd_cfg(spinand, CFG_QUAD_ENABLE,
enable ? CFG_QUAD_ENABLE : 0);
}
@ -362,7 +443,7 @@ static void spinand_ondie_ecc_save_status(struct nand_device *nand, u8 status)
int spinand_write_enable_op(struct spinand_device *spinand)
{
struct spi_mem_op op = SPINAND_WR_EN_DIS_1S_0_0_OP(true);
struct spi_mem_op op = SPINAND_OP(spinand, wr_en);
return spi_mem_exec_op(spinand->spimem, &op);
}
@ -372,7 +453,7 @@ static int spinand_load_page_op(struct spinand_device *spinand,
{
struct nand_device *nand = spinand_to_nand(spinand);
unsigned int row = nanddev_pos_to_row(nand, &req->pos);
struct spi_mem_op op = SPINAND_PAGE_READ_1S_1S_0_OP(row);
struct spi_mem_op op = SPINAND_OP(spinand, page_read, row);
return spi_mem_exec_op(spinand->spimem, &op);
}
@ -527,7 +608,7 @@ static int spinand_program_op(struct spinand_device *spinand,
{
struct nand_device *nand = spinand_to_nand(spinand);
unsigned int row = nanddev_pos_to_row(nand, &req->pos);
struct spi_mem_op op = SPINAND_PROG_EXEC_1S_1S_0_OP(row);
struct spi_mem_op op = SPINAND_OP(spinand, prog_exec, row);
return spi_mem_exec_op(spinand->spimem, &op);
}
@ -537,7 +618,7 @@ static int spinand_erase_op(struct spinand_device *spinand,
{
struct nand_device *nand = spinand_to_nand(spinand);
unsigned int row = nanddev_pos_to_row(nand, pos);
struct spi_mem_op op = SPINAND_BLK_ERASE_1S_1S_0_OP(row);
struct spi_mem_op op = SPINAND_OP(spinand, blk_erase, row);
return spi_mem_exec_op(spinand->spimem, &op);
}
@ -557,8 +638,8 @@ static int spinand_erase_op(struct spinand_device *spinand,
int spinand_wait(struct spinand_device *spinand, unsigned long initial_delay_us,
unsigned long poll_delay_us, u8 *s)
{
struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(REG_STATUS,
spinand->scratchbuf);
struct spi_mem_op op = SPINAND_OP(spinand, get_feature,
REG_STATUS, spinand->scratchbuf);
u8 status;
int ret;
@ -591,8 +672,8 @@ out:
static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr,
u8 ndummy, u8 *buf)
{
struct spi_mem_op op = SPINAND_READID_1S_1S_1S_OP(
naddr, ndummy, spinand->scratchbuf, SPINAND_MAX_ID_LEN);
struct spi_mem_op op = SPINAND_OP(spinand, readid,
naddr, ndummy, spinand->scratchbuf, SPINAND_MAX_ID_LEN);
int ret;
ret = spi_mem_exec_op(spinand->spimem, &op);
@ -604,7 +685,7 @@ static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr,
static int spinand_reset_op(struct spinand_device *spinand)
{
struct spi_mem_op op = SPINAND_RESET_1S_0_0_OP;
struct spi_mem_op op = SPINAND_OP(spinand, reset);
int ret;
ret = spi_mem_exec_op(spinand->spimem, &op);
@ -859,6 +940,14 @@ static void spinand_cont_read_init(struct spinand_device *spinand)
(engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE ||
engine_type == NAND_ECC_ENGINE_TYPE_NONE)) {
spinand->cont_read_possible = true;
/*
* Ensure continuous read is disabled on probe.
* Some devices retain this state across soft reset,
* which leaves the OOB area inaccessible and results
* in false positive returns from spinand_isbad().
*/
spinand_cont_read_enable(spinand, false);
}
}
@ -1154,7 +1243,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
info.offset = plane << fls(nand->memorg.pagesize);
info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
info.op_tmpl = *spinand->op_templates.update_cache;
info.op_tmpl = *spinand->op_templates->update_cache;
desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
spinand->spimem, &info);
if (IS_ERR(desc))
@ -1162,7 +1251,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
spinand->dirmaps[plane].wdesc = desc;
info.op_tmpl = *spinand->op_templates.read_cache;
info.op_tmpl = *spinand->op_templates->read_cache;
desc = spinand_create_rdesc(spinand, &info);
if (IS_ERR(desc))
return PTR_ERR(desc);
@ -1177,7 +1266,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
}
info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
info.op_tmpl = *spinand->op_templates.update_cache;
info.op_tmpl = *spinand->op_templates->update_cache;
info.op_tmpl.data.ecc = true;
desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
spinand->spimem, &info);
@ -1186,7 +1275,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
spinand->dirmaps[plane].wdesc_ecc = desc;
info.op_tmpl = *spinand->op_templates.read_cache;
info.op_tmpl = *spinand->op_templates->read_cache;
info.op_tmpl.data.ecc = true;
desc = spinand_create_rdesc(spinand, &info);
if (IS_ERR(desc))
@ -1227,6 +1316,7 @@ static const struct nand_ops spinand_ops = {
static const struct spinand_manufacturer *spinand_manufacturers[] = {
&alliancememory_spinand_manufacturer,
&ato_spinand_manufacturer,
&dosilicon_spinand_manufacturer,
&esmt_8c_spinand_manufacturer,
&esmt_c8_spinand_manufacturer,
&fmsh_spinand_manufacturer,
@ -1307,12 +1397,6 @@ static int spinand_manufacturer_init(struct spinand_device *spinand)
return ret;
}
if (spinand->configure_chip) {
ret = spinand->configure_chip(spinand);
if (ret)
return ret;
}
return 0;
}
@ -1323,8 +1407,101 @@ static void spinand_manufacturer_cleanup(struct spinand_device *spinand)
return spinand->manufacturer->ops->cleanup(spinand);
}
static bool spinand_op_is_odtr(const struct spi_mem_op *op)
{
return op->cmd.dtr && op->cmd.buswidth == 8;
}
static void spinand_init_ssdr_templates(struct spinand_device *spinand)
{
struct spinand_mem_ops *tmpl = &spinand->ssdr_op_templates;
tmpl->reset = (struct spi_mem_op)SPINAND_RESET_1S_0_0_OP;
tmpl->readid = (struct spi_mem_op)SPINAND_READID_1S_1S_1S_OP(0, 0, NULL, 0);
tmpl->wr_en = (struct spi_mem_op)SPINAND_WR_EN_1S_0_0_OP;
tmpl->wr_dis = (struct spi_mem_op)SPINAND_WR_DIS_1S_0_0_OP;
tmpl->set_feature = (struct spi_mem_op)SPINAND_SET_FEATURE_1S_1S_1S_OP(0, NULL);
tmpl->get_feature = (struct spi_mem_op)SPINAND_GET_FEATURE_1S_1S_1S_OP(0, NULL);
tmpl->blk_erase = (struct spi_mem_op)SPINAND_BLK_ERASE_1S_1S_0_OP(0);
tmpl->page_read = (struct spi_mem_op)SPINAND_PAGE_READ_1S_1S_0_OP(0);
tmpl->prog_exec = (struct spi_mem_op)SPINAND_PROG_EXEC_1S_1S_0_OP(0);
spinand->op_templates = &spinand->ssdr_op_templates;
spinand->bus_iface = SSDR;
}
static int spinand_support_vendor_ops(struct spinand_device *spinand,
const struct spinand_info *info,
enum spinand_bus_interface iface)
{
int i;
if (!info->vendor_ops)
return 0;
/*
* The vendor ops array is only used in order to verify this chip and all its memory
* operations are supported. If we see patterns emerging, we could ideally name these
* operations and define them at the SPI NAND core level instead.
* For now, this only serves as a sanity check.
*/
for (i = 0; i < info->vendor_ops->nops; i++) {
const struct spi_mem_op *op = &info->vendor_ops->ops[i];
if ((iface == SSDR && spinand_op_is_odtr(op)) ||
(iface == ODTR && !spinand_op_is_odtr(op)))
continue;
if (!spi_mem_supports_op(spinand->spimem, op))
return -EOPNOTSUPP;
}
return 0;
}
static int spinand_init_odtr_instruction_set(struct spinand_device *spinand)
{
struct spinand_mem_ops *tmpl = &spinand->odtr_op_templates;
tmpl->reset = (struct spi_mem_op)SPINAND_RESET_8D_0_0_OP;
if (!spi_mem_supports_op(spinand->spimem, &tmpl->reset))
return -EOPNOTSUPP;
tmpl->readid = (struct spi_mem_op)SPINAND_READID_8D_8D_8D_OP(0, 0, NULL, 0);
if (!spi_mem_supports_op(spinand->spimem, &tmpl->readid))
return -EOPNOTSUPP;
tmpl->wr_en = (struct spi_mem_op)SPINAND_WR_EN_8D_0_0_OP;
if (!spi_mem_supports_op(spinand->spimem, &tmpl->wr_en))
return -EOPNOTSUPP;
tmpl->wr_dis = (struct spi_mem_op)SPINAND_WR_DIS_8D_0_0_OP;
if (!spi_mem_supports_op(spinand->spimem, &tmpl->wr_dis))
return -EOPNOTSUPP;
tmpl->set_feature = (struct spi_mem_op)SPINAND_SET_FEATURE_8D_8D_8D_OP(0, NULL);
if (!spi_mem_supports_op(spinand->spimem, &tmpl->set_feature))
return -EOPNOTSUPP;
tmpl->get_feature = (struct spi_mem_op)SPINAND_GET_FEATURE_8D_8D_8D_OP(0, NULL);
if (!spi_mem_supports_op(spinand->spimem, &tmpl->get_feature))
return -EOPNOTSUPP;
tmpl->blk_erase = (struct spi_mem_op)SPINAND_BLK_ERASE_8D_8D_0_OP(0);
if (!spi_mem_supports_op(spinand->spimem, &tmpl->blk_erase))
return -EOPNOTSUPP;
tmpl->page_read = (struct spi_mem_op)SPINAND_PAGE_READ_8D_8D_0_OP(0);
if (!spi_mem_supports_op(spinand->spimem, &tmpl->page_read))
return -EOPNOTSUPP;
tmpl->prog_exec = (struct spi_mem_op)SPINAND_PROG_EXEC_8D_8D_0_OP(0);
if (!spi_mem_supports_op(spinand->spimem, &tmpl->prog_exec))
return -EOPNOTSUPP;
return 0;
}
static const struct spi_mem_op *
spinand_select_op_variant(struct spinand_device *spinand,
spinand_select_op_variant(struct spinand_device *spinand, enum spinand_bus_interface iface,
const struct spinand_op_variants *variants)
{
struct nand_device *nand = spinand_to_nand(spinand);
@ -1338,6 +1515,10 @@ spinand_select_op_variant(struct spinand_device *spinand,
unsigned int nbytes;
int ret;
if ((iface == SSDR && spinand_op_is_odtr(&op)) ||
(iface == ODTR && !spinand_op_is_odtr(&op)))
continue;
nbytes = nanddev_per_page_oobsize(nand) +
nanddev_page_size(nand);
@ -1389,6 +1570,7 @@ int spinand_match_and_init(struct spinand_device *spinand,
u8 *id = spinand->id.data;
struct nand_device *nand = spinand_to_nand(spinand);
unsigned int i;
int ret;
for (i = 0; i < table_size; i++) {
const struct spinand_info *info = &table[i];
@ -1413,28 +1595,59 @@ int spinand_match_and_init(struct spinand_device *spinand,
spinand->read_retries = table[i].read_retries;
spinand->set_read_retry = table[i].set_read_retry;
op = spinand_select_op_variant(spinand,
/* I/O variants selection with single-spi SDR commands */
op = spinand_select_op_variant(spinand, SSDR,
info->op_variants.read_cache);
if (!op)
return -ENOTSUPP;
return -EOPNOTSUPP;
spinand->op_templates.read_cache = op;
spinand->ssdr_op_templates.read_cache = op;
op = spinand_select_op_variant(spinand,
op = spinand_select_op_variant(spinand, SSDR,
info->op_variants.write_cache);
if (!op)
return -ENOTSUPP;
return -EOPNOTSUPP;
spinand->op_templates.write_cache = op;
spinand->ssdr_op_templates.write_cache = op;
op = spinand_select_op_variant(spinand,
op = spinand_select_op_variant(spinand, SSDR,
info->op_variants.update_cache);
spinand->op_templates.update_cache = op;
if (!op)
return -EOPNOTSUPP;
spinand->ssdr_op_templates.update_cache = op;
ret = spinand_support_vendor_ops(spinand, info, SSDR);
if (ret)
return ret;
/* I/O variants selection with octo-spi DDR commands (optional) */
ret = spinand_init_odtr_instruction_set(spinand);
if (ret)
return 0;
ret = spinand_support_vendor_ops(spinand, info, ODTR);
if (ret)
return 0;
op = spinand_select_op_variant(spinand, ODTR,
info->op_variants.read_cache);
spinand->odtr_op_templates.read_cache = op;
op = spinand_select_op_variant(spinand, ODTR,
info->op_variants.write_cache);
spinand->odtr_op_templates.write_cache = op;
op = spinand_select_op_variant(spinand, ODTR,
info->op_variants.update_cache);
spinand->odtr_op_templates.update_cache = op;
return 0;
}
return -ENOTSUPP;
return -EOPNOTSUPP;
}
static int spinand_detect(struct spinand_device *spinand)
@ -1470,6 +1683,56 @@ static int spinand_detect(struct spinand_device *spinand)
return 0;
}
static int spinand_configure_chip(struct spinand_device *spinand)
{
bool odtr = false, quad_enable = false;
int ret;
if (spinand->odtr_op_templates.read_cache &&
spinand->odtr_op_templates.write_cache &&
spinand->odtr_op_templates.update_cache)
odtr = true;
if (odtr) {
if (!spinand->configure_chip)
goto try_ssdr;
/* ODTR bus interface configuration happens here */
ret = spinand->configure_chip(spinand, ODTR);
if (ret) {
spinand->odtr_op_templates.read_cache = NULL;
spinand->odtr_op_templates.write_cache = NULL;
spinand->odtr_op_templates.update_cache = NULL;
goto try_ssdr;
}
spinand->op_templates = &spinand->odtr_op_templates;
spinand->bus_iface = ODTR;
return 0;
}
try_ssdr:
if (spinand->flags & SPINAND_HAS_QE_BIT) {
if (spinand->ssdr_op_templates.read_cache->data.buswidth == 4 ||
spinand->ssdr_op_templates.write_cache->data.buswidth == 4 ||
spinand->ssdr_op_templates.update_cache->data.buswidth == 4)
quad_enable = true;
}
ret = spinand_init_quad_enable(spinand, quad_enable);
if (ret)
return ret;
if (spinand->configure_chip) {
ret = spinand->configure_chip(spinand, SSDR);
if (ret)
return ret;
}
return ret;
}
static int spinand_init_flash(struct spinand_device *spinand)
{
struct device *dev = &spinand->spimem->spi->dev;
@ -1480,10 +1743,6 @@ static int spinand_init_flash(struct spinand_device *spinand)
if (ret)
return ret;
ret = spinand_init_quad_enable(spinand);
if (ret)
return ret;
ret = spinand_upd_cfg(spinand, CFG_OTP_ENABLE, 0);
if (ret)
return ret;
@ -1496,19 +1755,25 @@ static int spinand_init_flash(struct spinand_device *spinand)
return ret;
}
ret = spinand_configure_chip(spinand);
if (ret)
goto manuf_cleanup;
/* After power up, all blocks are locked, so unlock them here. */
for (i = 0; i < nand->memorg.ntargets; i++) {
ret = spinand_select_target(spinand, i);
if (ret)
break;
goto manuf_cleanup;
ret = spinand_lock_block(spinand, BL_ALL_UNLOCKED);
if (ret)
break;
goto manuf_cleanup;
}
if (ret)
spinand_manufacturer_cleanup(spinand);
return 0;
manuf_cleanup:
spinand_manufacturer_cleanup(spinand);
return ret;
}
@ -1529,6 +1794,32 @@ static void spinand_mtd_resume(struct mtd_info *mtd)
spinand_ecc_enable(spinand, false);
}
static int spinand_mtd_suspend(struct mtd_info *mtd)
{
struct spinand_device *spinand = mtd_to_spinand(mtd);
int ret;
/*
* Return to SSDR interface in the suspend path to make sure the
* reset operation is correctly processed upon resume.
*
* Note: Once back in SSDR mode, every operation but the page helpers
* (dirmap based I/O accessors) will work. Page accesses would require
* destroying and recreating the dirmaps twice to work, which would be
* impacting for no reason, as this is just a transitional state.
*/
if (spinand->bus_iface == ODTR) {
ret = spinand->configure_chip(spinand, SSDR);
if (ret)
return ret;
spinand->op_templates = &spinand->ssdr_op_templates;
spinand->bus_iface = SSDR;
}
return 0;
}
static int spinand_init(struct spinand_device *spinand)
{
struct device *dev = &spinand->spimem->spi->dev;
@ -1544,6 +1835,8 @@ static int spinand_init(struct spinand_device *spinand)
if (!spinand->scratchbuf)
return -ENOMEM;
spinand_init_ssdr_templates(spinand);
ret = spinand_detect(spinand);
if (ret)
goto err_free_bufs;
@ -1596,6 +1889,7 @@ static int spinand_init(struct spinand_device *spinand)
mtd->_block_isreserved = spinand_mtd_block_isreserved;
mtd->_erase = spinand_mtd_erase;
mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks;
mtd->_suspend = spinand_mtd_suspend;
mtd->_resume = spinand_mtd_resume;
if (spinand_user_otp_size(spinand) || spinand_fact_otp_size(spinand)) {

View file

@ -0,0 +1,91 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Author: Ahmed Naseef <naseefkm@gmail.com>
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/mtd/spinand.h>
#define SPINAND_MFR_DOSILICON 0xE5
static SPINAND_OP_VARIANTS(read_cache_variants,
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0));
static SPINAND_OP_VARIANTS(write_cache_variants,
SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0));
static SPINAND_OP_VARIANTS(update_cache_variants,
SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
static int ds35xx_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section > 3)
return -ERANGE;
region->offset = 8 + (section * 16);
region->length = 8;
return 0;
}
static int ds35xx_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section > 3)
return -ERANGE;
if (section == 0) {
/* reserve 2 bytes for the BBM */
region->offset = 2;
region->length = 6;
} else {
region->offset = section * 16;
region->length = 8;
}
return 0;
}
static const struct mtd_ooblayout_ops ds35xx_ooblayout = {
.ecc = ds35xx_ooblayout_ecc,
.free = ds35xx_ooblayout_free,
};
static const struct spinand_info dosilicon_spinand_table[] = {
SPINAND_INFO("DS35Q1GA",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x71),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(4, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&ds35xx_ooblayout, NULL)),
SPINAND_INFO("DS35M1GA",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x21),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(4, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&ds35xx_ooblayout, NULL)),
};
static const struct spinand_manufacturer_ops dosilicon_spinand_manuf_ops = {
};
const struct spinand_manufacturer dosilicon_spinand_manufacturer = {
.id = SPINAND_MFR_DOSILICON,
.name = "Dosilicon",
.chips = dosilicon_spinand_table,
.nchips = ARRAY_SIZE(dosilicon_spinand_table),
.ops = &dosilicon_spinand_manuf_ops,
};

View file

@ -138,8 +138,8 @@ static int f50l1g41lb_user_otp_info(struct spinand_device *spinand, size_t len,
static int f50l1g41lb_otp_lock(struct spinand_device *spinand, loff_t from,
size_t len)
{
struct spi_mem_op write_op = SPINAND_WR_EN_DIS_1S_0_0_OP(true);
struct spi_mem_op exec_op = SPINAND_PROG_EXEC_1S_1S_0_OP(0);
struct spi_mem_op write_op = SPINAND_OP(spinand, wr_en);
struct spi_mem_op exec_op = SPINAND_OP(spinand, prog_exec, 0);
u8 status;
int ret;

View file

@ -11,6 +11,11 @@
#define SPINAND_MFR_FORESEE 0xCD
#define F35SQB002G_STATUS_ECC_MASK (7 << 4)
#define F35SQB002G_STATUS_ECC_NO_BITFLIPS (0 << 4)
#define F35SQB002G_STATUS_ECC_1_3_BITFLIPS (1 << 4)
#define F35SQB002G_STATUS_ECC_UNCOR_ERROR (7 << 4)
static SPINAND_OP_VARIANTS(read_cache_variants,
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0),
@ -70,6 +75,25 @@ static int f35sqa002g_ecc_get_status(struct spinand_device *spinand, u8 status)
return -EBADMSG;
}
static int f35sqb002g_ecc_get_status(struct spinand_device *spinand, u8 status)
{
switch (status & F35SQB002G_STATUS_ECC_MASK) {
case F35SQB002G_STATUS_ECC_NO_BITFLIPS:
return 0;
case F35SQB002G_STATUS_ECC_1_3_BITFLIPS:
return 3;
case F35SQB002G_STATUS_ECC_UNCOR_ERROR:
return -EBADMSG;
default: /* (2 << 4) through (6 << 4) are 4-8 corrected errors */
return ((status & F35SQB002G_STATUS_ECC_MASK) >> 4) + 2;
}
return -EINVAL;
}
static const struct spinand_info foresee_spinand_table[] = {
SPINAND_INFO("F35SQA002G",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x72, 0x72),
@ -91,6 +115,16 @@ static const struct spinand_info foresee_spinand_table[] = {
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&f35sqa002g_ooblayout,
f35sqa002g_ecc_get_status)),
SPINAND_INFO("F35SQB002G",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x52, 0x52),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&f35sqa002g_ooblayout,
f35sqb002g_ecc_get_status)),
};
static const struct spinand_manufacturer_ops foresee_spinand_manuf_ops = {

View file

@ -266,8 +266,8 @@ static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand,
u8 status)
{
u8 status2;
struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(GD5FXGQXXEXXG_REG_STATUS2,
spinand->scratchbuf);
struct spi_mem_op op = SPINAND_OP(spinand, get_feature,
GD5FXGQXXEXXG_REG_STATUS2, spinand->scratchbuf);
int ret;
switch (status & STATUS_ECC_MASK) {
@ -309,8 +309,8 @@ static int gd5fxgq5xexxg_ecc_get_status(struct spinand_device *spinand,
u8 status)
{
u8 status2;
struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(GD5FXGQXXEXXG_REG_STATUS2,
spinand->scratchbuf);
struct spi_mem_op op = SPINAND_OP(spinand, get_feature,
GD5FXGQXXEXXG_REG_STATUS2, spinand->scratchbuf);
int ret;
switch (status & STATUS_ECC_MASK) {

View file

@ -41,6 +41,23 @@ static SPINAND_OP_VARIANTS(update_cache_variants,
SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
#define SPINAND_MACRONIX_READ_ECCSR_1S_0_1S(buf) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x7c, 1), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_DUMMY(1, 1), \
SPI_MEM_OP_DATA_IN(1, buf, 1))
static SPINAND_OP_VARIANTS(macronix_ops,
SPINAND_MACRONIX_READ_ECCSR_1S_0_1S(NULL));
static struct spi_mem_op
spinand_fill_macronix_read_eccsr_op(struct spinand_device *spinand, void *valptr)
{
WARN_ON_ONCE(spinand->bus_iface != SSDR);
return (struct spi_mem_op)SPINAND_MACRONIX_READ_ECCSR_1S_0_1S(valptr);
}
static int mx35lfxge4ab_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
@ -67,12 +84,10 @@ static const struct mtd_ooblayout_ops mx35lfxge4ab_ooblayout = {
static int macronix_get_eccsr(struct spinand_device *spinand, u8 *eccsr)
{
struct macronix_priv *priv = spinand->priv;
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x7c, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_DUMMY(1, 1),
SPI_MEM_OP_DATA_IN(1, eccsr, 1));
struct spi_mem_op op = SPINAND_OP(spinand, macronix_read_eccsr, eccsr);
int ret;
int ret = spi_mem_exec_op(spinand->spimem, &op);
ret = spi_mem_exec_op(spinand->spimem, &op);
if (ret)
return ret;
@ -148,8 +163,8 @@ static int macronix_set_cont_read(struct spinand_device *spinand, bool enable)
static int macronix_set_read_retry(struct spinand_device *spinand,
unsigned int retry_mode)
{
struct spi_mem_op op = SPINAND_SET_FEATURE_1S_1S_1S_OP(MACRONIX_FEATURE_ADDR_READ_RETRY,
spinand->scratchbuf);
struct spi_mem_op op = SPINAND_OP(spinand, set_feature,
MACRONIX_FEATURE_ADDR_READ_RETRY, spinand->scratchbuf);
*spinand->scratchbuf = retry_mode;
return spi_mem_exec_op(spinand->spimem, &op);
@ -164,6 +179,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status)),
SPINAND_INFO("MX35LF2GE4AB",
@ -185,6 +201,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_CONT_READ(macronix_set_cont_read),
@ -198,6 +215,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_CONT_READ(macronix_set_cont_read),
@ -268,6 +286,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status)),
SPINAND_INFO("MX31UF1GE4BC",
@ -278,6 +297,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status)),
@ -291,6 +311,7 @@ static const struct spinand_info macronix_spinand_table[] = {
SPINAND_HAS_QE_BIT |
SPINAND_HAS_PROG_PLANE_SELECT_BIT |
SPINAND_HAS_READ_PLANE_SELECT_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status)),
SPINAND_INFO("MX35UF4G24AD",
@ -302,6 +323,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&update_cache_variants),
SPINAND_HAS_QE_BIT |
SPINAND_HAS_PROG_PLANE_SELECT_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
@ -314,6 +336,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
@ -326,6 +349,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_CONT_READ(macronix_set_cont_read),
@ -341,6 +365,7 @@ static const struct spinand_info macronix_spinand_table[] = {
SPINAND_HAS_QE_BIT |
SPINAND_HAS_PROG_PLANE_SELECT_BIT |
SPINAND_HAS_READ_PLANE_SELECT_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status)),
SPINAND_INFO("MX35UF2G24AD",
@ -352,6 +377,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&update_cache_variants),
SPINAND_HAS_QE_BIT |
SPINAND_HAS_PROG_PLANE_SELECT_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
@ -364,6 +390,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
@ -376,6 +403,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_CONT_READ(macronix_set_cont_read),
@ -389,6 +417,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_CONT_READ(macronix_set_cont_read)),
@ -400,6 +429,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status)),
SPINAND_INFO("MX35UF1G24AD",
@ -410,6 +440,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
@ -422,6 +453,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_CONT_READ(macronix_set_cont_read),
@ -435,6 +467,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_CONT_READ(macronix_set_cont_read)),
@ -446,6 +479,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status)),
SPINAND_INFO("MX3UF2GE4BC",
@ -456,6 +490,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_INFO_VENDOR_OPS(&macronix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status)),
};

View file

@ -137,8 +137,8 @@ static const struct mtd_ooblayout_ops micron_4_ooblayout = {
static int micron_select_target(struct spinand_device *spinand,
unsigned int target)
{
struct spi_mem_op op = SPINAND_SET_FEATURE_1S_1S_1S_OP(MICRON_DIE_SELECT_REG,
spinand->scratchbuf);
struct spi_mem_op op = SPINAND_OP(spinand, set_feature,
MICRON_DIE_SELECT_REG, spinand->scratchbuf);
if (target > 1)
return -EINVAL;
@ -251,8 +251,8 @@ static int mt29f2g01abagd_user_otp_info(struct spinand_device *spinand,
static int mt29f2g01abagd_otp_lock(struct spinand_device *spinand, loff_t from,
size_t len)
{
struct spi_mem_op write_op = SPINAND_WR_EN_DIS_1S_0_0_OP(true);
struct spi_mem_op exec_op = SPINAND_PROG_EXEC_1S_1S_0_OP(0);
struct spi_mem_op write_op = SPINAND_OP(spinand, wr_en);
struct spi_mem_op exec_op = SPINAND_OP(spinand, prog_exec, 0);
u8 status;
int ret;

View file

@ -73,7 +73,8 @@ static int tx58cxgxsxraix_ecc_get_status(struct spinand_device *spinand,
{
struct nand_device *nand = spinand_to_nand(spinand);
u8 mbf = 0;
struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(0x30, spinand->scratchbuf);
struct spi_mem_op op = SPINAND_OP(spinand, get_feature,
0x30, spinand->scratchbuf);
switch (status & STATUS_ECC_MASK) {
case STATUS_ECC_NO_BITFLIPS:

View file

@ -22,7 +22,7 @@
#define W25N0XJW_SR4 0xD0
#define W25N0XJW_SR4_HS BIT(2)
#define W35N01JW_VCR_IO_MODE 0x00
#define W35N01JW_VCR_IO_MODE_REG 0x00
#define W35N01JW_VCR_IO_MODE_SINGLE_SDR 0xFF
#define W35N01JW_VCR_IO_MODE_OCTAL_SDR 0xDF
#define W35N01JW_VCR_IO_MODE_OCTAL_DDR_DS 0xE7
@ -36,6 +36,8 @@
*/
static SPINAND_OP_VARIANTS(read_cache_octal_variants,
SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(0, 24, NULL, 0, 120 * HZ_PER_MHZ),
SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(0, 16, NULL, 0, 86 * HZ_PER_MHZ),
SPINAND_PAGE_READ_FROM_CACHE_1S_1D_8D_OP(0, 3, NULL, 0, 120 * HZ_PER_MHZ),
SPINAND_PAGE_READ_FROM_CACHE_1S_1D_8D_OP(0, 2, NULL, 0, 105 * HZ_PER_MHZ),
SPINAND_PAGE_READ_FROM_CACHE_1S_8S_8S_OP(0, 20, NULL, 0, 0),
@ -48,11 +50,13 @@ static SPINAND_OP_VARIANTS(read_cache_octal_variants,
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0));
static SPINAND_OP_VARIANTS(write_cache_octal_variants,
SPINAND_PROG_LOAD_8D_8D_8D_OP(true, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_8S_8S_OP(true, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_8S_OP(0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0));
static SPINAND_OP_VARIANTS(update_cache_octal_variants,
SPINAND_PROG_LOAD_8D_8D_8D_OP(false, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_8S_8S_OP(false, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
@ -87,6 +91,47 @@ static SPINAND_OP_VARIANTS(update_cache_variants,
SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
#define SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(reg, buf) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x81, 1), \
SPI_MEM_OP_ADDR(3, reg, 1), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(1, buf, 1))
#define SPINAND_WINBOND_WRITE_VCR_8D_8D_8D(reg, buf) \
SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x81, 8), \
SPI_MEM_DTR_OP_ADDR(4, reg, 8), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_DTR_OP_DATA_OUT(2, buf, 8))
static SPINAND_OP_VARIANTS(winbond_w35_ops,
SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(0, NULL),
SPINAND_WINBOND_WRITE_VCR_8D_8D_8D(0, NULL));
static struct spi_mem_op
spinand_fill_winbond_write_vcr_op(struct spinand_device *spinand, u8 reg, void *valptr)
{
return (spinand->bus_iface == SSDR) ?
(struct spi_mem_op)SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(reg, valptr) :
(struct spi_mem_op)SPINAND_WINBOND_WRITE_VCR_8D_8D_8D(reg, valptr);
}
#define SPINAND_WINBOND_SELECT_TARGET_1S_0_1S(buf) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(1, buf, 1))
static SPINAND_OP_VARIANTS(winbond_w25_ops,
SPINAND_WINBOND_SELECT_TARGET_1S_0_1S(NULL));
static struct spi_mem_op
spinand_fill_winbond_select_target_op(struct spinand_device *spinand, void *valptr)
{
WARN_ON_ONCE(spinand->bus_iface != SSDR);
return (struct spi_mem_op)SPINAND_WINBOND_SELECT_TARGET_1S_0_1S(valptr);
}
static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
@ -119,12 +164,8 @@ static const struct mtd_ooblayout_ops w25m02gv_ooblayout = {
static int w25m02gv_select_target(struct spinand_device *spinand,
unsigned int target)
{
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1,
spinand->scratchbuf,
1));
struct spi_mem_op op = SPINAND_OP(spinand, winbond_select_target,
spinand->scratchbuf);
*spinand->scratchbuf = target;
return spi_mem_exec_op(spinand->spimem, &op);
@ -251,7 +292,8 @@ static int w25n02kv_ecc_get_status(struct spinand_device *spinand,
{
struct nand_device *nand = spinand_to_nand(spinand);
u8 mbf = 0;
struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(0x30, spinand->scratchbuf);
struct spi_mem_op op = SPINAND_OP(spinand, get_feature,
0x30, spinand->scratchbuf);
switch (status & STATUS_ECC_MASK) {
case STATUS_ECC_NO_BITFLIPS:
@ -284,14 +326,18 @@ static int w25n02kv_ecc_get_status(struct spinand_device *spinand,
return -EINVAL;
}
static int w25n0xjw_hs_cfg(struct spinand_device *spinand)
static int w25n0xjw_hs_cfg(struct spinand_device *spinand,
enum spinand_bus_interface iface)
{
const struct spi_mem_op *op;
bool hs;
u8 sr4;
int ret;
op = spinand->op_templates.read_cache;
if (iface != SSDR)
return -EOPNOTSUPP;
op = spinand->op_templates->read_cache;
if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
hs = false;
else if (op->cmd.buswidth == 1 && op->addr.buswidth == 1 &&
@ -320,11 +366,8 @@ static int w25n0xjw_hs_cfg(struct spinand_device *spinand)
static int w35n0xjw_write_vcr(struct spinand_device *spinand, u8 reg, u8 val)
{
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(0x81, 1),
SPI_MEM_OP_ADDR(3, reg, 1),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, spinand->scratchbuf, 1));
struct spi_mem_op op = SPINAND_OP(spinand, winbond_write_vcr,
reg, spinand->scratchbuf);
int ret;
*spinand->scratchbuf = val;
@ -347,32 +390,28 @@ static int w35n0xjw_write_vcr(struct spinand_device *spinand, u8 reg, u8 val)
return 0;
}
static int w35n0xjw_vcr_cfg(struct spinand_device *spinand)
static int w35n0xjw_vcr_cfg(struct spinand_device *spinand,
enum spinand_bus_interface iface)
{
const struct spi_mem_op *op;
const struct spi_mem_op *ref_op;
unsigned int dummy_cycles;
bool dtr, single;
u8 io_mode;
int ret;
op = spinand->op_templates.read_cache;
switch (iface) {
case SSDR:
ref_op = spinand->ssdr_op_templates.read_cache;
break;
case ODTR:
ref_op = spinand->odtr_op_templates.read_cache;
break;
default:
return -EOPNOTSUPP;
}
single = (op->cmd.buswidth == 1 && op->addr.buswidth == 1 && op->data.buswidth == 1);
dtr = (op->cmd.dtr || op->addr.dtr || op->data.dtr);
if (single && !dtr)
io_mode = W35N01JW_VCR_IO_MODE_SINGLE_SDR;
else if (!single && !dtr)
io_mode = W35N01JW_VCR_IO_MODE_OCTAL_SDR;
else if (!single && dtr)
io_mode = W35N01JW_VCR_IO_MODE_OCTAL_DDR;
else
return -EINVAL;
ret = w35n0xjw_write_vcr(spinand, W35N01JW_VCR_IO_MODE, io_mode);
if (ret)
return ret;
dummy_cycles = ((op->dummy.nbytes * 8) / op->dummy.buswidth) / (op->dummy.dtr ? 2 : 1);
dummy_cycles = ((ref_op->dummy.nbytes * 8) / ref_op->dummy.buswidth) /
(ref_op->dummy.dtr ? 2 : 1);
switch (dummy_cycles) {
case 8:
case 12:
@ -384,10 +423,28 @@ static int w35n0xjw_vcr_cfg(struct spinand_device *spinand)
default:
return -EINVAL;
}
ret = w35n0xjw_write_vcr(spinand, W35N01JW_VCR_DUMMY_CLOCK_REG, dummy_cycles);
if (ret)
return ret;
single = (ref_op->cmd.buswidth == 1 &&
ref_op->addr.buswidth == 1 &&
ref_op->data.buswidth == 1);
dtr = (ref_op->cmd.dtr && ref_op->addr.dtr && ref_op->data.dtr);
if (single && !dtr)
io_mode = W35N01JW_VCR_IO_MODE_SINGLE_SDR;
else if (!single && !dtr)
io_mode = W35N01JW_VCR_IO_MODE_OCTAL_SDR;
else if (!single && dtr)
io_mode = W35N01JW_VCR_IO_MODE_OCTAL_DDR;
else
return -EINVAL;
ret = w35n0xjw_write_vcr(spinand, W35N01JW_VCR_IO_MODE_REG, io_mode);
if (ret)
return ret;
return 0;
}
@ -448,6 +505,7 @@ static const struct spinand_info winbond_spinand_table[] = {
&write_cache_octal_variants,
&update_cache_octal_variants),
0,
SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
SPINAND_INFO("W35N02JW", /* 1.8V */
@ -458,6 +516,7 @@ static const struct spinand_info winbond_spinand_table[] = {
&write_cache_octal_variants,
&update_cache_octal_variants),
0,
SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
SPINAND_INFO("W35N04JW", /* 1.8V */
@ -468,6 +527,7 @@ static const struct spinand_info winbond_spinand_table[] = {
&write_cache_octal_variants,
&update_cache_octal_variants),
0,
SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
/* 2G-bit densities */
@ -479,6 +539,7 @@ static const struct spinand_info winbond_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
0,
SPINAND_INFO_VENDOR_OPS(&winbond_w25_ops),
SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
SPINAND_SELECT_TARGET(w25m02gv_select_target)),
SPINAND_INFO("W25N02JW", /* high-speed 1.8V */

View file

@ -4,12 +4,6 @@
#ifdef CONFIG_MTD_OF_PARTS_BCM4908
int bcm4908_partitions_post_parse(struct mtd_info *mtd, struct mtd_partition *parts, int nr_parts);
#else
static inline int bcm4908_partitions_post_parse(struct mtd_info *mtd, struct mtd_partition *parts,
int nr_parts)
{
return -EOPNOTSUPP;
}
#endif
#endif

View file

@ -23,13 +23,17 @@ struct fixed_partitions_quirks {
int (*post_parse)(struct mtd_info *mtd, struct mtd_partition *parts, int nr_parts);
};
#ifdef CONFIG_MTD_OF_PARTS_BCM4908
static struct fixed_partitions_quirks bcm4908_partitions_quirks = {
.post_parse = bcm4908_partitions_post_parse,
};
#endif
#ifdef CONFIG_MTD_OF_PARTS_LINKSYS_NS
static struct fixed_partitions_quirks linksys_ns_partitions_quirks = {
.post_parse = linksys_ns_partitions_post_parse,
};
#endif
static const struct of_device_id parse_ofpart_match_table[];
@ -77,6 +81,7 @@ static int parse_fixed_partitions(struct mtd_info *master,
of_id = of_match_node(parse_ofpart_match_table, ofpart_node);
if (dedicated && !of_id) {
/* The 'partitions' subnode might be used by another parser */
of_node_put(ofpart_node);
return 0;
}
@ -91,12 +96,18 @@ static int parse_fixed_partitions(struct mtd_info *master,
nr_parts++;
}
if (nr_parts == 0)
if (nr_parts == 0) {
if (dedicated)
of_node_put(ofpart_node);
return 0;
}
parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL);
if (!parts)
if (!parts) {
if (dedicated)
of_node_put(ofpart_node);
return -ENOMEM;
}
i = 0;
for_each_child_of_node(ofpart_node, pp) {
@ -175,6 +186,9 @@ static int parse_fixed_partitions(struct mtd_info *master,
if (quirks && quirks->post_parse)
quirks->post_parse(master, parts, nr_parts);
if (dedicated)
of_node_put(ofpart_node);
*pparts = parts;
return nr_parts;
@ -183,6 +197,8 @@ ofpart_fail:
master->name, pp, mtd_node);
ret = -EINVAL;
ofpart_none:
if (dedicated)
of_node_put(ofpart_node);
of_node_put(pp);
kfree(parts);
return ret;
@ -192,8 +208,12 @@ static const struct of_device_id parse_ofpart_match_table[] = {
/* Generic */
{ .compatible = "fixed-partitions" },
/* Customized */
#ifdef CONFIG_MTD_OF_PARTS_BCM4908
{ .compatible = "brcm,bcm4908-partitions", .data = &bcm4908_partitions_quirks, },
#endif
#ifdef CONFIG_MTD_OF_PARTS_LINKSYS_NS
{ .compatible = "linksys,ns-partitions", .data = &linksys_ns_partitions_quirks, },
#endif
{},
};
MODULE_DEVICE_TABLE(of, parse_ofpart_match_table);

View file

@ -6,13 +6,6 @@
int linksys_ns_partitions_post_parse(struct mtd_info *mtd,
struct mtd_partition *parts,
int nr_parts);
#else
static inline int linksys_ns_partitions_post_parse(struct mtd_info *mtd,
struct mtd_partition *parts,
int nr_parts)
{
return -EOPNOTSUPP;
}
#endif
#endif

View file

@ -116,6 +116,7 @@ static int mtd_parser_tplink_safeloader_parse(struct mtd_info *mtd,
return idx;
err_free:
kfree(buf);
for (idx -= 1; idx >= 0; idx--)
kfree(parts[idx].name);
err_free_parts:

View file

@ -394,19 +394,15 @@ static void hisi_spi_nor_unregister_all(struct hifmc_host *host)
static int hisi_spi_nor_register_all(struct hifmc_host *host)
{
struct device *dev = host->dev;
struct device_node *np;
int ret;
for_each_available_child_of_node(dev->of_node, np) {
for_each_available_child_of_node_scoped(dev->of_node, np) {
ret = hisi_spi_nor_register(np, host);
if (ret) {
of_node_put(np);
if (ret)
goto fail;
}
if (host->num_chip == HIFMC_MAX_CHIP_NUM) {
dev_warn(dev, "Flash device number exceeds the maximum chipselect number\n");
of_node_put(np);
break;
}
}

View file

@ -26,8 +26,14 @@
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPINAND_WR_EN_DIS_1S_0_0_OP(enable) \
SPI_MEM_OP(SPI_MEM_OP_CMD((enable) ? 0x06 : 0x04, 1), \
#define SPINAND_WR_EN_1S_0_0_OP \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x06, 1), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPINAND_WR_DIS_1S_0_0_OP \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x04, 1), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
@ -233,10 +239,75 @@
SPI_MEM_OP_DATA_OUT(len, buf, 8))
/**
* Standard SPI NAND flash commands
* Octal DDR SPI NAND flash operations
*/
#define SPINAND_CMD_PROG_LOAD_X4 0x32
#define SPINAND_CMD_PROG_LOAD_RDM_DATA_X4 0x34
#define SPINAND_RESET_8D_0_0_OP \
SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0xff, 8), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPINAND_READID_8D_8D_8D_OP(naddr, ndummy, buf, len) \
SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x9f, 8), \
SPI_MEM_DTR_OP_ADDR(naddr, 0, 8), \
SPI_MEM_DTR_OP_DUMMY(ndummy, 8), \
SPI_MEM_DTR_OP_DATA_IN(len, buf, 8))
#define SPINAND_WR_EN_8D_0_0_OP \
SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x06, 8), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPINAND_WR_DIS_8D_0_0_OP \
SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x04, 8), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPINAND_SET_FEATURE_8D_8D_8D_OP(reg, valptr) \
SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x1f, 8), \
SPI_MEM_DTR_OP_RPT_ADDR(reg, 8), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_DTR_OP_DATA_OUT(2, valptr, 8))
#define SPINAND_GET_FEATURE_8D_8D_8D_OP(reg, valptr) \
SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x0f, 8), \
SPI_MEM_DTR_OP_RPT_ADDR(reg, 8), \
SPI_MEM_DTR_OP_DUMMY(14, 8), \
SPI_MEM_DTR_OP_DATA_IN(2, valptr, 8))
#define SPINAND_BLK_ERASE_8D_8D_0_OP(addr) \
SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0xd8, 8), \
SPI_MEM_DTR_OP_ADDR(2, addr, 8), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPINAND_PAGE_READ_8D_8D_0_OP(addr) \
SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x13, 8), \
SPI_MEM_DTR_OP_ADDR(2, addr, 8), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x9d, 8), \
SPI_MEM_DTR_OP_ADDR(2, addr, 8), \
SPI_MEM_DTR_OP_DUMMY(ndummy, 8), \
SPI_MEM_DTR_OP_DATA_IN(len, buf, 8), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PROG_EXEC_8D_8D_0_OP(addr) \
SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x10, 8), \
SPI_MEM_DTR_OP_ADDR(2, addr, 8), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPINAND_PROG_LOAD_8D_8D_8D_OP(reset, addr, buf, len) \
SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD((reset ? 0xc2 : 0xc4), 8), \
SPI_MEM_DTR_OP_ADDR(2, addr, 8), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_DTR_OP_DATA_OUT(len, buf, 8))
/* feature register */
#define REG_BLOCK_LOCK 0xa0
@ -261,7 +332,7 @@
struct spinand_op;
struct spinand_device;
#define SPINAND_MAX_ID_LEN 5
#define SPINAND_MAX_ID_LEN 6
/*
* For erase, write and read operation, we got the following timings :
* tBERS (erase) 1ms to 4ms
@ -287,7 +358,7 @@ struct spinand_device;
/**
* struct spinand_id - SPI NAND id structure
* @data: buffer containing the id bytes. Currently 4 bytes large, but can
* @data: buffer containing the id bytes. Currently 6 bytes large, but can
* be extended if required
* @len: ID length
*/
@ -354,6 +425,7 @@ struct spinand_manufacturer {
/* SPI NAND manufacturers */
extern const struct spinand_manufacturer alliancememory_spinand_manufacturer;
extern const struct spinand_manufacturer ato_spinand_manufacturer;
extern const struct spinand_manufacturer dosilicon_spinand_manufacturer;
extern const struct spinand_manufacturer esmt_8c_spinand_manufacturer;
extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer;
extern const struct spinand_manufacturer fmsh_spinand_manufacturer;
@ -481,6 +553,16 @@ struct spinand_user_otp {
const struct spinand_user_otp_ops *ops;
};
/**
* enum spinand_bus_interface - SPI NAND bus interface types
* @SSDR: Bus configuration supporting all 1S-XX-XX operations, including dual and quad
* @ODTR: Bus configuration supporting only 8D-8D-8D operations
*/
enum spinand_bus_interface {
SSDR,
ODTR,
};
/**
* struct spinand_info - Structure used to describe SPI NAND chips
* @model: model name
@ -493,6 +575,7 @@ struct spinand_user_otp {
* @op_variants.read_cache: variants of the read-cache operation
* @op_variants.write_cache: variants of the write-cache operation
* @op_variants.update_cache: variants of the update-cache operation
* @vendor_ops: vendor specific operations
* @select_target: function used to select a target/die. Required only for
* multi-die chips
* @configure_chip: Align the chip configuration with the core settings
@ -517,9 +600,11 @@ struct spinand_info {
const struct spinand_op_variants *write_cache;
const struct spinand_op_variants *update_cache;
} op_variants;
const struct spinand_op_variants *vendor_ops;
int (*select_target)(struct spinand_device *spinand,
unsigned int target);
int (*configure_chip)(struct spinand_device *spinand);
int (*configure_chip)(struct spinand_device *spinand,
enum spinand_bus_interface iface);
int (*set_cont_read)(struct spinand_device *spinand,
bool enable);
struct spinand_fact_otp fact_otp;
@ -543,6 +628,9 @@ struct spinand_info {
.update_cache = __update, \
}
#define SPINAND_INFO_VENDOR_OPS(__ops) \
.vendor_ops = __ops
#define SPINAND_ECCINFO(__ooblayout, __get_status) \
.eccinfo = { \
.ooblayout = __ooblayout, \
@ -599,6 +687,36 @@ struct spinand_dirmap {
struct spi_mem_dirmap_desc *rdesc_ecc;
};
/**
* struct spinand_mem_ops - SPI NAND memory operations
* @reset: reset op template
* @readid: read ID op template
* @wr_en: write enable op template
* @wr_dis: write disable op template
* @set_feature: set feature op template
* @get_feature: get feature op template
* @blk_erase: blk erase op template
* @page_read: page read op template
* @prog_exec: prog exec op template
* @read_cache: read cache op template
* @write_cache: write cache op template
* @update_cache: update cache op template
*/
struct spinand_mem_ops {
struct spi_mem_op reset;
struct spi_mem_op readid;
struct spi_mem_op wr_en;
struct spi_mem_op wr_dis;
struct spi_mem_op set_feature;
struct spi_mem_op get_feature;
struct spi_mem_op blk_erase;
struct spi_mem_op page_read;
struct spi_mem_op prog_exec;
const struct spi_mem_op *read_cache;
const struct spi_mem_op *write_cache;
const struct spi_mem_op *update_cache;
};
/**
* struct spinand_device - SPI NAND device instance
* @base: NAND device instance
@ -606,10 +724,10 @@ struct spinand_dirmap {
* @lock: lock used to serialize accesses to the NAND
* @id: NAND ID as returned by READ_ID
* @flags: NAND flags
* @op_templates: various SPI mem op templates
* @op_templates.read_cache: read cache op template
* @op_templates.write_cache: write cache op template
* @op_templates.update_cache: update cache op template
* @ssdr_op_templates: Templates for all single SDR SPI mem operations
* @odtr_op_templates: Templates for all octal DTR SPI mem operations
* @op_templates: Templates for all SPI mem operations
* @bus_iface: Current bus interface
* @select_target: select a specific target/die. Usually called before sending
* a command addressing a page or an eraseblock embedded in
* this die. Only required if your chip exposes several dies
@ -643,11 +761,10 @@ struct spinand_device {
struct spinand_id id;
u32 flags;
struct {
const struct spi_mem_op *read_cache;
const struct spi_mem_op *write_cache;
const struct spi_mem_op *update_cache;
} op_templates;
struct spinand_mem_ops ssdr_op_templates;
struct spinand_mem_ops odtr_op_templates;
struct spinand_mem_ops *op_templates;
enum spinand_bus_interface bus_iface;
struct spinand_dirmap *dirmaps;
@ -664,7 +781,8 @@ struct spinand_device {
const struct spinand_manufacturer *manufacturer;
void *priv;
int (*configure_chip)(struct spinand_device *spinand);
int (*configure_chip)(struct spinand_device *spinand,
enum spinand_bus_interface iface);
bool cont_read_possible;
int (*set_cont_read)(struct spinand_device *spinand,
bool enable);
@ -677,6 +795,14 @@ struct spinand_device {
unsigned int retry_mode);
};
struct spi_mem_op spinand_fill_wr_en_op(struct spinand_device *spinand);
struct spi_mem_op spinand_fill_set_feature_op(struct spinand_device *spinand, u64 reg, const void *valptr);
struct spi_mem_op spinand_fill_get_feature_op(struct spinand_device *spinand, u64 reg, void *valptr);
struct spi_mem_op spinand_fill_prog_exec_op(struct spinand_device *spinand, u64 addr);
#define SPINAND_OP(spinand, op_name, ...) \
spinand_fill_ ## op_name ## _op(spinand, ##__VA_ARGS__)
/**
* mtd_to_spinand() - Get the SPI NAND device attached to an MTD instance
* @mtd: MTD instance

View file

@ -51,6 +51,14 @@
.dtr = true, \
}
#define SPI_MEM_DTR_OP_RPT_ADDR(__val, __buswidth) \
{ \
.nbytes = 2, \
.val = __val | __val << 8, \
.buswidth = __buswidth, \
.dtr = true, \
}
#define SPI_MEM_OP_NO_ADDR { }
#define SPI_MEM_OP_DUMMY(__nbytes, __buswidth) \